diff --git a/.hgtags b/.hgtags index 50c1586057d..0d5f45226f5 100644 --- a/.hgtags +++ b/.hgtags @@ -334,3 +334,5 @@ f61a63b7d1e52e307abc0bfc751203155d362ec4 jdk9-b83 4a0312f2894bcbe1fd20266c8fda8d983bd2fcf6 jdk9-b89 d131f4b8433a79408f935eff9bf92a0664229b60 jdk9-b90 8077fd2f055d31e50b46fcf62d9c035bc385a215 jdk9-b91 +f242d4332f563648426a1b0fa02d8741beba19ef jdk9-b92 +09206c6513b300e1ac8541f3be012e1a49312104 jdk9-b93 diff --git a/.hgtags-top-repo b/.hgtags-top-repo index 77827b83110..14bd56395f0 100644 --- a/.hgtags-top-repo +++ b/.hgtags-top-repo @@ -334,3 +334,5 @@ fd4f4f7561074dc0dbc1772c8489c7b902b6b8a9 jdk9-b87 895353113f382d24e623191fdab0e29a3ce34738 jdk9-b89 cf1dc4c035fb84693d4ae5ad818785cb4d1465d1 jdk9-b90 122142a185381ce5cea959bf13b923d8cc333628 jdk9-b91 +106c06398f7ab330eef9e335fbd3a5a8ead23b77 jdk9-b92 +331fda57dfd323c61804ba0472776790de572937 jdk9-b93 diff --git a/common/autoconf/configure.ac b/common/autoconf/configure.ac index 9fd553f0d86..eb053466068 100644 --- a/common/autoconf/configure.ac +++ b/common/autoconf/configure.ac @@ -155,6 +155,9 @@ SRCDIRS_SETUP_OUTPUT_DIRS # ############################################################################### +# See if we are doing a complete static build or not +JDKOPT_SETUP_STATIC_BUILD + # First determine the toolchain type (compiler family) TOOLCHAIN_DETERMINE_TOOLCHAIN_TYPE diff --git a/common/autoconf/flags.m4 b/common/autoconf/flags.m4 index 383f359fad3..b44c78e4300 100644 --- a/common/autoconf/flags.m4 +++ b/common/autoconf/flags.m4 @@ -221,7 +221,11 @@ AC_DEFUN_ONCE([FLAGS_SETUP_COMPILER_FLAGS_FOR_LIBS], if test "x$OPENJDK_TARGET_OS" = xmacosx; then # Linking is different on MacOSX - SHARED_LIBRARY_FLAGS="-dynamiclib -compatibility_version 1.0.0 -current_version 1.0.0 $PICFLAG" + if test "x$STATIC_BUILD" = xtrue; then + SHARED_LIBRARY_FLAGS ='-undefined dynamic_lookup' + else + SHARED_LIBRARY_FLAGS="-dynamiclib -compatibility_version 1.0.0 -current_version 1.0.0 $PICFLAG" + fi SET_EXECUTABLE_ORIGIN='-Xlinker -rpath -Xlinker @loader_path/.' SET_SHARED_LIBRARY_ORIGIN="$SET_EXECUTABLE_ORIGIN" SET_SHARED_LIBRARY_NAME='-Xlinker -install_name -Xlinker @rpath/[$]1' @@ -696,7 +700,9 @@ AC_DEFUN_ONCE([FLAGS_SETUP_COMPILER_FLAGS_FOR_JDK], COMMON_CCXXFLAGS_JDK="$COMMON_CCXXFLAGS_JDK \ -I${JDK_TOPDIR}/src/java.base/share/native/include \ -I${JDK_TOPDIR}/src/java.base/$OPENJDK_TARGET_OS/native/include \ - -I${JDK_TOPDIR}/src/java.base/$OPENJDK_TARGET_OS_TYPE/native/include" + -I${JDK_TOPDIR}/src/java.base/$OPENJDK_TARGET_OS_TYPE/native/include \ + -I${JDK_TOPDIR}/src/java.base/share/native/libjava \ + -I${JDK_TOPDIR}/src/java.base/$OPENJDK_TARGET_OS_TYPE/native/libjava" # The shared libraries are compiled using the picflag. CFLAGS_JDKLIB="$COMMON_CCXXFLAGS_JDK $CFLAGS_JDK $PICFLAG $CFLAGS_JDKLIB_EXTRA" diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh index 18e11b034c8..1b278126644 100644 --- a/common/autoconf/generated-configure.sh +++ b/common/autoconf/generated-configure.sh @@ -803,6 +803,7 @@ STATIC_LIBRARY_SUFFIX SHARED_LIBRARY_SUFFIX LIBRARY_PREFIX TOOLCHAIN_TYPE +STATIC_BUILD BUILD_HOTSPOT HOTSPOT_DIST BUILD_OUTPUT @@ -1074,6 +1075,7 @@ with_override_hotspot with_override_nashorn with_override_jdk with_import_hotspot +enable_static_build with_toolchain_type with_extra_cflags with_extra_cxxflags @@ -1852,6 +1854,7 @@ Optional Features: run the Queens test after Hotspot build [disabled] --enable-unlimited-crypto Enable unlimited crypto policy [disabled] + --enable-static-build enable static library build [disabled] --disable-warnings-as-errors do not consider native warnings to be an error [enabled] @@ -3989,6 +3992,15 @@ pkgadd_help() { # +################################################################################ +# +# Static build support. When enabled will generate static +# libraries instead of shared libraries for all JDK libs. +# + + + + # # Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -29018,6 +29030,40 @@ $as_echo "yes from $HOTSPOT_DIST" >&6; } # ############################################################################### +# See if we are doing a complete static build or not + + # Check whether --enable-static-build was given. +if test "${enable_static_build+set}" = set; then : + enableval=$enable_static_build; +fi + + STATIC_BUILD=false + if test "x$enable_static_build" = "xyes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if static build is enabled" >&5 +$as_echo_n "checking if static build is enabled... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + if test "x$OPENJDK_TARGET_OS" != "xmacosx"; then + as_fn_error $? "--enable-static-build is only supported for macosx builds" "$LINENO" 5 + fi + STATIC_BUILD_CFLAGS="-DSTATIC_BUILD=1" + LEGACY_EXTRA_CFLAGS="$LEGACY_EXTRA_CFLAGS $STATIC_BUILD_CFLAGS" + LEGACY_EXTRA_CXXFLAGS="$LEGACY_EXTRA_CXXFLAGS $STATIC_BUILD_CFLAGS" + CFLAGS_JDKLIB_EXTRA="$CFLAGS_JDKLIB_EXTRA $STATIC_BUILD_CFLAGS" + CXXFLAGS_JDKLIB_EXTRA="$CXXFLAGS_JDKLIB_EXTRA $STATIC_BUILD_CFLAGS" + STATIC_BUILD=true + elif test "x$enable_static_build" = "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if static build is enabled" >&5 +$as_echo_n "checking if static build is enabled... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + elif test "x$enable_static_build" != "x"; then + as_fn_error $? "--enable-static-build can only be assigned \"yes\" or \"no\"" "$LINENO" 5 + fi + + + + # First determine the toolchain type (compiler family) @@ -29126,8 +29172,19 @@ $as_echo "$as_me: Valid toolchains: $VALID_TOOLCHAINS." >&6;} OBJ_SUFFIX='.o' EXE_SUFFIX='' if test "x$OPENJDK_TARGET_OS" = xmacosx; then - SHARED_LIBRARY='lib$1.dylib' - SHARED_LIBRARY_SUFFIX='.dylib' + # For full static builds, we're overloading the SHARED_LIBRARY + # variables in order to limit the amount of changes required. + # It would be better to remove SHARED and just use LIBRARY and + # LIBRARY_SUFFIX for libraries that can be built either + # shared or static and use STATIC_* for libraries that are + # always built statically. + if test "x$STATIC_BUILD" = xtrue; then + SHARED_LIBRARY='lib$1.a' + SHARED_LIBRARY_SUFFIX='.a' + else + SHARED_LIBRARY='lib$1.dylib' + SHARED_LIBRARY_SUFFIX='.dylib' + fi fi fi @@ -44314,7 +44371,11 @@ $as_echo "$ac_cv_c_bigendian" >&6; } if test "x$OPENJDK_TARGET_OS" = xmacosx; then # Linking is different on MacOSX - SHARED_LIBRARY_FLAGS="-dynamiclib -compatibility_version 1.0.0 -current_version 1.0.0 $PICFLAG" + if test "x$STATIC_BUILD" = xtrue; then + SHARED_LIBRARY_FLAGS ='-undefined dynamic_lookup' + else + SHARED_LIBRARY_FLAGS="-dynamiclib -compatibility_version 1.0.0 -current_version 1.0.0 $PICFLAG" + fi SET_EXECUTABLE_ORIGIN='-Xlinker -rpath -Xlinker @loader_path/.' SET_SHARED_LIBRARY_ORIGIN="$SET_EXECUTABLE_ORIGIN" SET_SHARED_LIBRARY_NAME='-Xlinker -install_name -Xlinker @rpath/$1' @@ -44818,7 +44879,9 @@ $as_echo "$supports" >&6; } COMMON_CCXXFLAGS_JDK="$COMMON_CCXXFLAGS_JDK \ -I${JDK_TOPDIR}/src/java.base/share/native/include \ -I${JDK_TOPDIR}/src/java.base/$OPENJDK_TARGET_OS/native/include \ - -I${JDK_TOPDIR}/src/java.base/$OPENJDK_TARGET_OS_TYPE/native/include" + -I${JDK_TOPDIR}/src/java.base/$OPENJDK_TARGET_OS_TYPE/native/include \ + -I${JDK_TOPDIR}/src/java.base/share/native/libjava \ + -I${JDK_TOPDIR}/src/java.base/$OPENJDK_TARGET_OS_TYPE/native/libjava" # The shared libraries are compiled using the picflag. CFLAGS_JDKLIB="$COMMON_CCXXFLAGS_JDK $CFLAGS_JDK $PICFLAG $CFLAGS_JDKLIB_EXTRA" diff --git a/common/autoconf/jdk-options.m4 b/common/autoconf/jdk-options.m4 index ccf77879508..7c1492ed24c 100644 --- a/common/autoconf/jdk-options.m4 +++ b/common/autoconf/jdk-options.m4 @@ -665,3 +665,37 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_CODE_COVERAGE], AC_SUBST(GCOV_ENABLED) ]) + +################################################################################ +# +# Static build support. When enabled will generate static +# libraries instead of shared libraries for all JDK libs. +# +AC_DEFUN_ONCE([JDKOPT_SETUP_STATIC_BUILD], +[ + AC_ARG_ENABLE([static-build], [AS_HELP_STRING([--enable-static-build], + [enable static library build @<:@disabled@:>@])]) + STATIC_BUILD=false + if test "x$enable_static_build" = "xyes"; then + AC_MSG_CHECKING([if static build is enabled]) + AC_MSG_RESULT([yes]) + if test "x$OPENJDK_TARGET_OS" != "xmacosx"; then + AC_MSG_ERROR([--enable-static-build is only supported for macosx builds]) + fi + STATIC_BUILD_CFLAGS="-DSTATIC_BUILD=1" + LEGACY_EXTRA_CFLAGS="$LEGACY_EXTRA_CFLAGS $STATIC_BUILD_CFLAGS" + LEGACY_EXTRA_CXXFLAGS="$LEGACY_EXTRA_CXXFLAGS $STATIC_BUILD_CFLAGS" + CFLAGS_JDKLIB_EXTRA="$CFLAGS_JDKLIB_EXTRA $STATIC_BUILD_CFLAGS" + CXXFLAGS_JDKLIB_EXTRA="$CXXFLAGS_JDKLIB_EXTRA $STATIC_BUILD_CFLAGS" + STATIC_BUILD=true + elif test "x$enable_static_build" = "xno"; then + AC_MSG_CHECKING([if static build is enabled]) + AC_MSG_RESULT([no]) + elif test "x$enable_static_build" != "x"; then + AC_MSG_ERROR([--enable-static-build can only be assigned "yes" or "no"]) + fi + + AC_SUBST(STATIC_BUILD) +]) + + diff --git a/common/autoconf/spec.gmk.in b/common/autoconf/spec.gmk.in index d8212483ec9..0be7456fa2c 100644 --- a/common/autoconf/spec.gmk.in +++ b/common/autoconf/spec.gmk.in @@ -417,6 +417,7 @@ SHARED_LIBRARY_SUFFIX:=@SHARED_LIBRARY_SUFFIX@ STATIC_LIBRARY_SUFFIX:=@STATIC_LIBRARY_SUFFIX@ EXE_SUFFIX:=@EXE_SUFFIX@ OBJ_SUFFIX:=@OBJ_SUFFIX@ +STATIC_BUILD:=@STATIC_BUILD@ STRIPFLAGS:=@STRIPFLAGS@ diff --git a/common/autoconf/toolchain.m4 b/common/autoconf/toolchain.m4 index 32f4881f608..18924c92be7 100644 --- a/common/autoconf/toolchain.m4 +++ b/common/autoconf/toolchain.m4 @@ -72,8 +72,19 @@ AC_DEFUN([TOOLCHAIN_SETUP_FILENAME_PATTERNS], OBJ_SUFFIX='.o' EXE_SUFFIX='' if test "x$OPENJDK_TARGET_OS" = xmacosx; then - SHARED_LIBRARY='lib[$]1.dylib' - SHARED_LIBRARY_SUFFIX='.dylib' + # For full static builds, we're overloading the SHARED_LIBRARY + # variables in order to limit the amount of changes required. + # It would be better to remove SHARED and just use LIBRARY and + # LIBRARY_SUFFIX for libraries that can be built either + # shared or static and use STATIC_* for libraries that are + # always built statically. + if test "x$STATIC_BUILD" = xtrue; then + SHARED_LIBRARY='lib[$]1.a' + SHARED_LIBRARY_SUFFIX='.a' + else + SHARED_LIBRARY='lib[$]1.dylib' + SHARED_LIBRARY_SUFFIX='.dylib' + fi fi fi diff --git a/corba/.hgtags b/corba/.hgtags index 6d0a137add8..87c538d45b5 100644 --- a/corba/.hgtags +++ b/corba/.hgtags @@ -334,3 +334,5 @@ a5c40ac9b916ff44d512ee764fa919ed2097e149 jdk9-b87 c847a53b38d2fffb87afc483c74db05eced9b4f4 jdk9-b89 29cc8228d62319af21cad7c90817671e0813b6bd jdk9-b90 75843e0a9371d445a3c9b440bab85e50b5dc287c jdk9-b91 +f7d70caad89ad0c43bb057bca0aad6f17ce05a6a jdk9-b92 +27e9c8d8091e2447ea7ef3e3103e9b7dd286e03a jdk9-b93 diff --git a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/corba/RequestImpl.java b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/corba/RequestImpl.java index c6eb5f7051a..3c357f61c38 100644 --- a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/corba/RequestImpl.java +++ b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/corba/RequestImpl.java @@ -65,7 +65,6 @@ import com.sun.corba.se.spi.presentation.rmi.StubAdapter; import com.sun.corba.se.spi.logging.CORBALogDomains; import com.sun.corba.se.impl.logging.ORBUtilSystemException; import com.sun.corba.se.impl.corba.AsynchInvoke; -import com.sun.corba.se.impl.transport.ManagedLocalsThread; public class RequestImpl extends Request @@ -256,7 +255,7 @@ public class RequestImpl public synchronized void send_deferred() { AsynchInvoke invokeObject = new AsynchInvoke(_orb, this, false); - new ManagedLocalsThread(invokeObject).start(); + new sun.misc.ManagedLocalsThread(invokeObject).start(); } public synchronized boolean poll_response() diff --git a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/javax/rmi/CORBA/Util.java b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/javax/rmi/CORBA/Util.java index 24996d51ed0..d9dceac7ba8 100644 --- a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/javax/rmi/CORBA/Util.java +++ b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/javax/rmi/CORBA/Util.java @@ -112,7 +112,6 @@ import com.sun.corba.se.impl.util.JDKBridge; import com.sun.corba.se.impl.logging.UtilSystemException; import com.sun.corba.se.spi.logging.CORBALogDomains; import sun.corba.SharedSecrets; -import com.sun.corba.se.impl.transport.ManagedLocalsThread; /** @@ -752,7 +751,7 @@ public class Util implements javax.rmi.CORBA.UtilDelegate } } -class KeepAlive extends ManagedLocalsThread +class KeepAlive extends sun.misc.ManagedLocalsThread { boolean quit = false; diff --git a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/oa/poa/POAImpl.java b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/oa/poa/POAImpl.java index d10f4e3eccd..546914d623d 100644 --- a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/oa/poa/POAImpl.java +++ b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/oa/poa/POAImpl.java @@ -103,7 +103,6 @@ import com.sun.corba.se.impl.orbutil.concurrent.Sync ; import com.sun.corba.se.impl.orbutil.concurrent.SyncUtil ; import com.sun.corba.se.impl.orbutil.concurrent.ReentrantMutex ; import com.sun.corba.se.impl.orbutil.concurrent.CondVar ; -import com.sun.corba.se.impl.transport.ManagedLocalsThread; /** * POAImpl is the implementation of the Portable Object Adapter. It @@ -517,7 +516,7 @@ public class POAImpl extends ObjectAdapterBase implements POA // Converted from anonymous class to local class // so that we can call performDestroy() directly. - static class DestroyThread extends ManagedLocalsThread { + static class DestroyThread extends sun.misc.ManagedLocalsThread { private boolean wait ; private boolean etherealize ; private boolean debug ; diff --git a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/oa/poa/POAManagerImpl.java b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/oa/poa/POAManagerImpl.java index 41100a4b3bd..22610dda9ba 100644 --- a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/oa/poa/POAManagerImpl.java +++ b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/oa/poa/POAManagerImpl.java @@ -48,7 +48,6 @@ import com.sun.corba.se.spi.protocol.PIHandler ; import com.sun.corba.se.impl.logging.POASystemException ; import com.sun.corba.se.impl.orbutil.ORBUtility ; -import com.sun.corba.se.impl.transport.ManagedLocalsThread; /** POAManagerImpl is the implementation of the POAManager interface. * Its public methods are activate(), hold_requests(), discard_requests() @@ -358,7 +357,7 @@ public class POAManagerImpl extends org.omg.CORBA.LocalObject implements if (wait_for_completion) deactivator.run() ; else { - Thread thr = new ManagedLocalsThread(deactivator) ; + Thread thr = new sun.misc.ManagedLocalsThread(deactivator) ; thr.start() ; } } finally { diff --git a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/oa/poa/POAPolicyMediatorImpl_R_USM.java b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/oa/poa/POAPolicyMediatorImpl_R_USM.java index 21a5c340612..9e9fce8a47d 100644 --- a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/oa/poa/POAPolicyMediatorImpl_R_USM.java +++ b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/oa/poa/POAPolicyMediatorImpl_R_USM.java @@ -49,7 +49,6 @@ import com.sun.corba.se.impl.javax.rmi.CORBA.Util ; import com.sun.corba.se.spi.oa.OAInvocationInfo ; import com.sun.corba.se.spi.oa.NullServant ; -import com.sun.corba.se.impl.transport.ManagedLocalsThread; /** Implementation of POARequesHandler that provides policy specific * operations on the POA. @@ -303,7 +302,7 @@ public class POAPolicyMediatorImpl_R_USM extends POAPolicyMediatorBase_R { throw new WrongPolicy(); } - class Etherealizer extends ManagedLocalsThread { + class Etherealizer extends sun.misc.ManagedLocalsThread { private POAPolicyMediatorImpl_R_USM mediator ; private ActiveObjectMap.Key key ; private AOMEntry entry ; diff --git a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orb/ORBImpl.java b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orb/ORBImpl.java index bb67c3cd654..82da35a7921 100644 --- a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orb/ORBImpl.java +++ b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orb/ORBImpl.java @@ -165,7 +165,6 @@ import com.sun.corba.se.impl.util.Utility; import com.sun.corba.se.impl.logging.ORBUtilSystemException; import com.sun.corba.se.impl.copyobject.CopierManagerImpl; import com.sun.corba.se.impl.presentation.rmi.PresentationManagerImpl; -import com.sun.corba.se.impl.transport.ManagedLocalsThread; /** * The JavaIDL ORB implementation. @@ -692,7 +691,7 @@ public class ORBImpl extends com.sun.corba.se.spi.orb.ORB for (int i = 0; i < req.length; i++) { AsynchInvoke invokeObject = new AsynchInvoke( this, (com.sun.corba.se.impl.corba.RequestImpl)req[i], true); - new ManagedLocalsThread(invokeObject).start(); + new sun.misc.ManagedLocalsThread(invokeObject).start(); } } diff --git a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orbutil/threadpool/ThreadPoolImpl.java b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orbutil/threadpool/ThreadPoolImpl.java index 12a8127d061..9a0e56afc4f 100644 --- a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orbutil/threadpool/ThreadPoolImpl.java +++ b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orbutil/threadpool/ThreadPoolImpl.java @@ -54,7 +54,6 @@ import com.sun.corba.se.spi.monitoring.LongMonitoredAttributeBase; import com.sun.corba.se.impl.logging.ORBUtilSystemException; import com.sun.corba.se.impl.orbutil.ORBConstants; import com.sun.corba.se.spi.logging.CORBALogDomains; -import com.sun.corba.se.impl.transport.ManagedLocalsThread; public class ThreadPoolImpl implements ThreadPool { @@ -460,7 +459,7 @@ public class ThreadPoolImpl implements ThreadPool } - private class WorkerThread extends ManagedLocalsThread implements Closeable + private class WorkerThread extends sun.misc.ManagedLocalsThread implements Closeable { private Work currentWork; private int threadId = 0; // unique id for the thread diff --git a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/transport/ManagedLocalsThread.java b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/transport/ManagedLocalsThread.java deleted file mode 100644 index b888de30228..00000000000 --- a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/transport/ManagedLocalsThread.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2015, 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 com.sun.corba.se.impl.transport; - -import sun.misc.Unsafe; -import java.lang.reflect.Field; -import java.security.AccessController; -import java.security.PrivilegedAction; - -/** - * A thread that has it's thread locals, and inheritable thread - * locals erased on construction. - */ -public class ManagedLocalsThread extends Thread { - private static final Unsafe UNSAFE; - private static final long THREAD_LOCALS; - private static final long INHERITABLE_THREAD_LOCALS; - - public ManagedLocalsThread () { - super(); - } - - public ManagedLocalsThread(String name) { - super(name); - eraseThreadLocals(); - } - public ManagedLocalsThread(Runnable target) { - super(target); - eraseThreadLocals(); - } - - public ManagedLocalsThread(Runnable target, String name) { - super(target, name); - eraseThreadLocals(); - } - - public ManagedLocalsThread(ThreadGroup group, Runnable target, String name) { - super(group, target, name); - eraseThreadLocals(); - } - - public ManagedLocalsThread(ThreadGroup group, String name) { - super(group, name); - eraseThreadLocals(); - } - - /** - * Drops all thread locals (and inherited thread locals). - */ - public final void eraseThreadLocals() { - UNSAFE.putObject(this, THREAD_LOCALS, null); - UNSAFE.putObject(this, INHERITABLE_THREAD_LOCALS, null); - } - - private static Unsafe getUnsafe() { - PrivilegedAction pa = () -> { - Class unsafeClass = sun.misc.Unsafe.class; - try { - Field f = unsafeClass.getDeclaredField("theUnsafe"); - f.setAccessible(true); - return (Unsafe) f.get(null); - } catch (Exception e) { - throw new Error(e); - } - }; - return AccessController.doPrivileged(pa); - } - - private static long getThreadFieldOffset(String fieldName) { - PrivilegedAction pa = () -> { - Class t = Thread.class; - long fieldOffset; - try { - fieldOffset = UNSAFE.objectFieldOffset(t - .getDeclaredField("inheritableThreadLocals")); - } catch (Exception e) { - throw new Error(e); - } - return fieldOffset; - }; - return AccessController.doPrivileged(pa); - } - - static { - UNSAFE = getUnsafe(); - try { - THREAD_LOCALS = getThreadFieldOffset("threadLocals"); - INHERITABLE_THREAD_LOCALS = getThreadFieldOffset("inheritableThreadLocals"); - } catch (Exception e) { - throw new Error(e); - } - } -} diff --git a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/transport/SelectorImpl.java b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/transport/SelectorImpl.java index 4acac0a176e..c43c1dfa7b5 100644 --- a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/transport/SelectorImpl.java +++ b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/transport/SelectorImpl.java @@ -57,7 +57,7 @@ import com.sun.corba.se.impl.orbutil.ORBUtility; */ class SelectorImpl extends - ManagedLocalsThread + sun.misc.ManagedLocalsThread implements com.sun.corba.se.pept.transport.Selector { diff --git a/corba/src/java.corba/share/classes/sun/corba/Bridge.java b/corba/src/java.corba/share/classes/sun/corba/Bridge.java index 987ee097376..1891612219c 100644 --- a/corba/src/java.corba/share/classes/sun/corba/Bridge.java +++ b/corba/src/java.corba/share/classes/sun/corba/Bridge.java @@ -36,7 +36,7 @@ import java.security.AccessController; import java.security.Permission; import java.security.PrivilegedAction; -import sun.misc.Unsafe ; +import jdk.internal.misc.Unsafe ; import sun.reflect.ReflectionFactory ; /** This class provides the methods for fundamental JVM operations @@ -120,7 +120,7 @@ public final class Bridge Field fld = null ; try { - Class unsafeClass = sun.misc.Unsafe.class ; + Class unsafeClass = jdk.internal.misc.Unsafe.class ; fld = unsafeClass.getDeclaredField( "theUnsafe" ) ; fld.setAccessible( true ) ; return fld ; diff --git a/corba/src/java.corba/share/classes/sun/corba/SharedSecrets.java b/corba/src/java.corba/share/classes/sun/corba/SharedSecrets.java index b4c56080f9f..19ff5bddcfa 100644 --- a/corba/src/java.corba/share/classes/sun/corba/SharedSecrets.java +++ b/corba/src/java.corba/share/classes/sun/corba/SharedSecrets.java @@ -26,7 +26,7 @@ package sun.corba; import com.sun.corba.se.impl.io.ValueUtility; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import java.lang.reflect.Field; import java.security.AccessController; @@ -48,7 +48,7 @@ public class SharedSecrets { private static Unsafe getUnsafe() { PrivilegedAction pa = () -> { - Class unsafeClass = sun.misc.Unsafe.class ; + Class unsafeClass = jdk.internal.misc.Unsafe.class ; try { Field f = unsafeClass.getDeclaredField("theUnsafe"); f.setAccessible(true); diff --git a/hotspot/.hgtags b/hotspot/.hgtags index 1a17516ef3d..d06c977e71a 100644 --- a/hotspot/.hgtags +++ b/hotspot/.hgtags @@ -495,3 +495,5 @@ bc48b669bc6610fac97e16593050c0f559cf6945 jdk9-b88 7fe46dc64bb3a8df554b24cde0153ffb24f39c5e jdk9-b90 3fd5c2ca4c20c183628b6dbeb8df821a961419e3 jdk9-b91 53cb98d68a1aeb08d29c89d6da748de60c448e37 jdk9-b92 +d8b24776484cc4dfd19f50b23eaa18a80a161371 jdk9-b93 +a22b7c80529f5f05c847e932e017456e83c46233 jdk9-b94 diff --git a/hotspot/make/Makefile b/hotspot/make/Makefile index cf1a4167e18..91f5a9ae76e 100644 --- a/hotspot/make/Makefile +++ b/hotspot/make/Makefile @@ -44,9 +44,7 @@ # ALT_EXPORT_PATH Directory to export hotspot build to # ALT_JDK_IMPORT_PATH Current JDK build (only for create_jdk rules) # ALT_JDK_TARGET_IMPORT_PATH Current JDK build when cross-compiling -# ALT_BUILD_WIN_SA Building SA on Windows is disabled by default. -# Set ALT_BUILD_WIN_SA=1 to enable building SA on -# Windows. +# # Version strings and numbers: # JDK_VERSION Current JDK version (e.g. 1.6.0) # PREVIOUS_JDK_VERSION Previous (bootdir) JDK version (e.g. 1.5.0) diff --git a/hotspot/make/aix/Makefile b/hotspot/make/aix/Makefile index bbb92c9d75d..e8e679268f6 100644 --- a/hotspot/make/aix/Makefile +++ b/hotspot/make/aix/Makefile @@ -46,15 +46,6 @@ # # make REMOTE="rsh -l me myotherlinuxbox" -# Along with VM, Serviceability Agent (SA) is built for SA/JDI binding. -# JDI binding on SA produces two binaries: -# 1. sa-jdi.jar - This is built before building libjvm.so -# Please refer to ./makefiles/sa.make -# 2. libsa.so - Native library for SA - This is built after -# libjsig.so (signal interposition library) -# Please refer to ./makefiles/vm.make -# If $(GAMMADIR)/agent dir is not present, SA components are not built. - # No tests on Aix. TEST_IN_BUILD=false diff --git a/hotspot/make/aix/makefiles/buildtree.make b/hotspot/make/aix/makefiles/buildtree.make index aac5e3f2a8d..70755aa0c8e 100644 --- a/hotspot/make/aix/makefiles/buildtree.make +++ b/hotspot/make/aix/makefiles/buildtree.make @@ -50,7 +50,6 @@ # adlc.make - # trace.make - generate tracing event and type definitions # jvmti.make - generate JVMTI bindings from the spec (JSR-163) -# sa.make - generate SA jar file and natives # # The makefiles are split this way so that "make foo" will run faster by not # having to read the dependency files for the vm. @@ -125,7 +124,7 @@ SUBMAKE_DIRS = $(addprefix $(PLATFORM_DIR)/,$(TARGETS)) # For dependencies and recursive makes. BUILDTREE_MAKE = $(GAMMADIR)/make/$(OS_FAMILY)/makefiles/buildtree.make -BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make trace.make sa.make +BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make trace.make BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OS_FAMILY) \ SRCARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) VARIANT=$(VARIANT) @@ -212,7 +211,6 @@ flags.make: $(BUILDTREE_MAKE) ../shared_dirs.lst echo "TARGET = $(TARGET)"; \ echo "HS_BUILD_VER = $(HS_BUILD_VER)"; \ echo "JRE_RELEASE_VER = $(JRE_RELEASE_VERSION)"; \ - echo "SA_BUILD_VERSION = $(HS_BUILD_VER)"; \ echo "HOTSPOT_BUILD_USER = $(HOTSPOT_BUILD_USER)"; \ echo "HOTSPOT_VM_DISTRO = $(HOTSPOT_VM_DISTRO)"; \ echo "OPENJDK = $(OPENJDK)"; \ @@ -351,16 +349,6 @@ trace.make: $(BUILDTREE_MAKE) echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ ) > $@ -sa.make: $(BUILDTREE_MAKE) - @echo $(LOG_INFO) Creating $@ ... - $(QUIETLY) ( \ - $(BUILDTREE_COMMENT); \ - echo; \ - echo include flags.make; \ - echo; \ - echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ - ) > $@ - FORCE: .PHONY: all FORCE diff --git a/hotspot/make/aix/makefiles/defs.make b/hotspot/make/aix/makefiles/defs.make index f902830eeb2..a8ce94d12fb 100644 --- a/hotspot/make/aix/makefiles/defs.make +++ b/hotspot/make/aix/makefiles/defs.make @@ -219,11 +219,3 @@ ifeq ($(JVM_VARIANT_CLIENT),true) # endif # endif endif - -# Serviceability Binaries -# No SA Support for PPC or zero -ADD_SA_BINARIES/ppc = -ADD_SA_BINARIES/ppc64 = -ADD_SA_BINARIES/zero = - -EXPORT_LIST += $(ADD_SA_BINARIES/$(HS_ARCH)) diff --git a/hotspot/make/aix/makefiles/rules.make b/hotspot/make/aix/makefiles/rules.make index 2c4c38658c6..effca45effa 100644 --- a/hotspot/make/aix/makefiles/rules.make +++ b/hotspot/make/aix/makefiles/rules.make @@ -103,8 +103,6 @@ BOOT_JAVA_HOME = $(JAVA_HOME) else # take from the PATH, if ALT_BOOTDIR, BOOTDIR and JAVA_HOME are not defined -# note that this is to support hotspot build without SA. To build -# SA along with hotspot, you need to define ALT_BOOTDIR, BOOTDIR or JAVA_HOME RUN.JAVA = java RUN.JAVAP = javap diff --git a/hotspot/make/aix/makefiles/sa.make b/hotspot/make/aix/makefiles/sa.make deleted file mode 100644 index ffd5edb00ca..00000000000 --- a/hotspot/make/aix/makefiles/sa.make +++ /dev/null @@ -1,112 +0,0 @@ -# -# Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2013 SAP AG. 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. -# -# - -# This makefile (sa.make) is included from the sa.make in the -# build directories. - -# This makefile is used to build Serviceability Agent java code -# and generate JNI header file for native methods. - -include $(GAMMADIR)/make/aix/makefiles/rules.make - -include $(GAMMADIR)/make/defs.make - -AGENT_DIR = $(GAMMADIR)/agent - -include $(GAMMADIR)/make/sa.files - -TOPDIR = $(shell echo `pwd`) -GENERATED = $(TOPDIR)/../generated - -# tools.jar is needed by the JDI - SA binding -SA_CLASSPATH = $(BOOT_JAVA_HOME)/lib/tools.jar - -# TODO: if it's a modules image, check if SA module is installed. -MODULELIB_PATH= $(BOOT_JAVA_HOME)/lib/modules - -AGENT_FILES_LIST := $(GENERATED)/agent.classes.list - -SA_CLASSDIR = $(GENERATED)/saclasses - -SA_BUILD_VERSION_PROP = "sun.jvm.hotspot.runtime.VM.saBuildVersion=$(SA_BUILD_VERSION)" - -SA_PROPERTIES = $(SA_CLASSDIR)/sa.properties - -# if $(AGENT_DIR) does not exist, we don't build SA -# also, we don't build SA on Itanium, PowerPC, ARM or zero. - -all: - if [ -d $(AGENT_DIR) -a "$(SRCARCH)" != "ia64" \ - -a "$(SRCARCH)" != "arm" \ - -a "$(SRCARCH)" != "ppc" \ - -a "$(SRCARCH)" != "zero" ] ; then \ - $(MAKE) -f sa.make $(GENERATED)/sa-jdi.jar; \ - fi - -$(GENERATED)/sa-jdi.jar: $(AGENT_FILES) - $(QUIETLY) echo $(LOG_INFO) "Making $@" - $(QUIETLY) if [ "$(BOOT_JAVA_HOME)" = "" ]; then \ - echo "ALT_BOOTDIR, BOOTDIR or JAVA_HOME needs to be defined to build SA"; \ - exit 1; \ - fi - $(QUIETLY) if [ ! -f $(SA_CLASSPATH) -a ! -d $(MODULELIB_PATH) ] ; then \ - echo "Missing $(SA_CLASSPATH) file. Use 1.6.0 or later version of JDK";\ - echo ""; \ - exit 1; \ - fi - $(QUIETLY) if [ ! -d $(SA_CLASSDIR) ] ; then \ - mkdir -p $(SA_CLASSDIR); \ - fi -# Note: When indented, make tries to execute the '$(shell' comment. -# In some environments, cmd processors have limited line length. -# To prevent the javac invocation in the next block from using -# a very long cmd line, we use javac's @file-list option. We -# generate the file lists using make's built-in 'foreach' control -# flow which also avoids cmd processor line length issues. Since -# the 'foreach' is done as part of make's macro expansion phase, -# the initialization of the lists is also done in the same phase -# using '$(shell rm ...' instead of using the more traditional -# 'rm ...' rule. - $(shell rm -rf $(AGENT_FILES_LIST)) -# gnumake 3.78.1 does not accept the *'s that -# are in AGENT_FILES, so use the shell to expand them. -# Be extra carefull to not produce too long command lines in the shell! - $(foreach file,$(AGENT_FILES),$(shell ls -1 $(file) >> $(AGENT_FILES_LIST))) - $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -h $(GENERATED) -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -d $(SA_CLASSDIR) @$(AGENT_FILES_LIST) - $(QUIETLY) $(REMOTE) $(COMPILE.RMIC) -classpath $(SA_CLASSDIR) -d $(SA_CLASSDIR) sun.jvm.hotspot.debugger.remote.RemoteDebuggerServer - $(QUIETLY) echo "$(SA_BUILD_VERSION_PROP)" > $(SA_PROPERTIES) - $(QUIETLY) rm -f $(SA_CLASSDIR)/sun/jvm/hotspot/utilities/soql/sa.js - $(QUIETLY) $(CP) $(AGENT_SRC_DIR)/sun/jvm/hotspot/utilities/soql/sa.js $(SA_CLASSDIR)/sun/jvm/hotspot/utilities/soql - $(QUIETLY) mkdir -p $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources - $(QUIETLY) rm -f $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources/* - $(QUIETLY) $(CP) $(AGENT_SRC_DIR)/sun/jvm/hotspot/ui/resources/*.png $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources/ - $(QUIETLY) $(CP) -r $(AGENT_SRC_DIR)/images/* $(SA_CLASSDIR)/ - $(QUIETLY) $(REMOTE) $(RUN.JAR) cf $@ -C $(SA_CLASSDIR)/ . - $(QUIETLY) $(REMOTE) $(RUN.JAR) uf $@ -C $(AGENT_SRC_DIR) META-INF/services/com.sun.jdi.connect.Connector - -clean: - rm -rf $(SA_CLASSDIR) - rm -rf $(GENERATED)/sa-jdi.jar - rm -rf $(AGENT_FILES_LIST) diff --git a/hotspot/make/aix/makefiles/saproc.make b/hotspot/make/aix/makefiles/saproc.make deleted file mode 100644 index 509d8f621e4..00000000000 --- a/hotspot/make/aix/makefiles/saproc.make +++ /dev/null @@ -1,117 +0,0 @@ -# -# Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. -# Copyright 2012, 2013 SAP AG. 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. -# -# -include $(GAMMADIR)/make/defs.make - -# Rules to build serviceability agent library, used by vm.make - -# libsaproc.so: serviceability agent - -SAPROC = saproc -LIBSAPROC = lib$(SAPROC).so - -LIBSAPROC_DEBUGINFO = lib$(SAPROC).debuginfo -LIBSAPROC_DIZ = lib$(SAPROC).diz - -AGENT_DIR = $(GAMMADIR)/agent - -SASRCDIR = $(AGENT_DIR)/src/os/$(Platform_os_family) - -SASRCFILES = $(SASRCDIR)/salibelf.c \ - $(SASRCDIR)/symtab.c \ - $(SASRCDIR)/libproc_impl.c \ - $(SASRCDIR)/ps_proc.c \ - $(SASRCDIR)/ps_core.c \ - $(SASRCDIR)/LinuxDebuggerLocal.c \ - -SAMAPFILE = $(SASRCDIR)/mapfile - -DEST_SAPROC = $(JDK_LIBDIR)/$(LIBSAPROC) -DEST_SAPROC_DEBUGINFO = $(JDK_LIBDIR)/$(LIBSAPROC_DEBUGINFO) -DEST_SAPROC_DIZ = $(JDK_LIBDIR)/$(LIBSAPROC_DIZ) - -# DEBUG_BINARIES overrides everything, use full -g debug information -ifeq ($(DEBUG_BINARIES), true) - SA_DEBUG_CFLAGS = -g -endif - -# if $(AGENT_DIR) does not exist, we don't build SA -# also, we don't build SA on Itanium, PPC, ARM or zero. - -ifneq ($(wildcard $(AGENT_DIR)),) -ifneq ($(filter-out ia64 arm ppc zero,$(SRCARCH)),) - BUILDLIBSAPROC = $(LIBSAPROC) -endif -endif - - -SA_LFLAGS = $(MAPFLAG:FILENAME=$(SAMAPFILE)) $(LDFLAGS_HASH_STYLE) - -$(LIBSAPROC): $(SASRCFILES) $(SAMAPFILE) - $(QUIETLY) if [ "$(BOOT_JAVA_HOME)" = "" ]; then \ - echo "ALT_BOOTDIR, BOOTDIR or JAVA_HOME needs to be defined to build SA"; \ - exit 1; \ - fi - @echo $(LOG_INFO) Making SA debugger back-end... - $(QUIETLY) $(CC) -D$(BUILDARCH) -D_GNU_SOURCE \ - -D_FILE_OFFSET_BITS=64 \ - $(SYMFLAG) $(ARCHFLAG) $(SHARED_FLAG) $(PICFLAG) \ - $(BIN_UTILS) \ - -I$(SASRCDIR) \ - -I$(GENERATED) \ - -I$(BOOT_JAVA_HOME)/include \ - -I$(BOOT_JAVA_HOME)/include/$(Platform_os_family) \ - $(SASRCFILES) \ - $(SA_LFLAGS) \ - $(SA_DEBUG_CFLAGS) \ - -o $@ \ - -lthread_db -ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) - $(QUIETLY) $(OBJCOPY) --only-keep-debug $@ $(LIBSAPROC_DEBUGINFO) - $(QUIETLY) $(OBJCOPY) --add-gnu-debuglink=$(LIBSAPROC_DEBUGINFO) $@ - ifeq ($(STRIP_POLICY),all_strip) - $(QUIETLY) $(STRIP) $@ - else - ifeq ($(STRIP_POLICY),min_strip) - $(QUIETLY) $(STRIP) -g $@ - # implied else here is no stripping at all - endif - endif - ifeq ($(ZIP_DEBUGINFO_FILES),1) - $(ZIPEXE) -q -y $(LIBSAPROC_DIZ) $(LIBSAPROC_DEBUGINFO) - $(RM) $(LIBSAPROC_DEBUGINFO) - endif -endif - -install_saproc: $(BUILDLIBSAPROC) - $(QUIETLY) if [ -e $(LIBSAPROC) ] ; then \ - echo "Copying $(LIBSAPROC) to $(DEST_SAPROC)"; \ - test -f $(LIBSAPROC_DEBUGINFO) && \ - $(CP) -f $(LIBSAPROC_DEBUGINFO) $(DEST_SAPROC_DEBUGINFO); \ - test -f $(LIBSAPROC_DIZ) && \ - $(CP) -f $(LIBSAPROC_DIZ) $(DEST_SAPROC_DIZ); \ - $(CP) -f $(LIBSAPROC) $(DEST_SAPROC) && echo "Done"; \ - fi - -.PHONY: install_saproc diff --git a/hotspot/make/aix/makefiles/top.make b/hotspot/make/aix/makefiles/top.make index bfe3c530955..fa9107c039d 100644 --- a/hotspot/make/aix/makefiles/top.make +++ b/hotspot/make/aix/makefiles/top.make @@ -28,7 +28,6 @@ # It also: # -builds and runs adlc via adlc.make # -generates JVMTI source and docs via jvmti.make (JSR-163) -# -generate sa-jdi.jar (JDI binding to core files) # It assumes the following flags are set: # CFLAGS Platform_file, Src_Dirs_I, Src_Dirs_V, SYSDEFS, AOUT, Obj_Files @@ -86,7 +85,7 @@ default: vm_build_preliminaries the_vm @echo All done. # This is an explicit dependency for the sake of parallel makes. -vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) trace_stuff jvmti_stuff sa_stuff +vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) trace_stuff jvmti_stuff @# We need a null action here, so implicit rules don't get consulted. $(Cached_plat): $(Plat_File) @@ -104,10 +103,6 @@ jvmti_stuff: $(Cached_plat) $(adjust-mflags) trace_stuff: jvmti_stuff $(Cached_plat) $(adjust-mflags) @$(MAKE) -f trace.make $(MFLAGS-adjusted) -# generate SA jar files and native header -sa_stuff: - @$(MAKE) -f sa.make $(MFLAGS-adjusted) - # and the VM: must use other makefile with dependencies included # We have to go to great lengths to get control over the -jN argument @@ -146,7 +141,7 @@ realclean: rm -fr $(GENERATED) .PHONY: default vm_build_preliminaries -.PHONY: lists ad_stuff jvmti_stuff sa_stuff the_vm clean realclean +.PHONY: lists ad_stuff jvmti_stuff the_vm clean realclean .PHONY: checks check_os_version install .NOTPARALLEL: diff --git a/hotspot/make/aix/makefiles/vm.make b/hotspot/make/aix/makefiles/vm.make index 11bb0294ede..e2c32f65eaa 100644 --- a/hotspot/make/aix/makefiles/vm.make +++ b/hotspot/make/aix/makefiles/vm.make @@ -61,7 +61,7 @@ Src_Dirs_I += $(GENERATED) # The order is important for the precompiled headers to work. INCLUDES += $(PRECOMPILED_HEADER_DIR:%=-I%) $(Src_Dirs_I:%=-I%) -# SYMFLAG is used by {jsig,saproc}.make +# SYMFLAG is used by jsig.make ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) # always build with debug info when we can create .debuginfo files SYMFLAG = -g @@ -359,13 +359,10 @@ install_jvm: $(LIBJVM) # Signal interposition library include $(MAKEFILES_DIR)/jsig.make -# Serviceability agent -include $(MAKEFILES_DIR)/saproc.make - #---------------------------------------------------------------------- -build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) $(LIBJVM_DB) $(BUILDLIBSAPROC) +build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) $(LIBJVM_DB) -install: install_jvm install_jsig install_saproc +install: install_jvm install_jsig .PHONY: default build install install_jvm diff --git a/hotspot/make/bsd/Makefile b/hotspot/make/bsd/Makefile index 4d45841475e..0eef9c08f58 100644 --- a/hotspot/make/bsd/Makefile +++ b/hotspot/make/bsd/Makefile @@ -45,15 +45,6 @@ # # make REMOTE="rsh -l me myotherlinuxbox" -# Along with VM, Serviceability Agent (SA) is built for SA/JDI binding. -# JDI binding on SA produces two binaries: -# 1. sa-jdi.jar - This is built before building libjvm.so -# Please refer to ./makefiles/sa.make -# 2. libsa.so - Native library for SA - This is built after -# libjsig.so (signal interposition library) -# Please refer to ./makefiles/vm.make -# If $(GAMMADIR)/agent dir is not present, SA components are not built. - ifeq ($(GAMMADIR),) include ../../make/defs.make else diff --git a/hotspot/make/bsd/makefiles/buildtree.make b/hotspot/make/bsd/makefiles/buildtree.make index e48d6aba104..0e2a80fec5e 100644 --- a/hotspot/make/bsd/makefiles/buildtree.make +++ b/hotspot/make/bsd/makefiles/buildtree.make @@ -49,7 +49,6 @@ # adlc.make - # trace.make - generate tracing event and type definitions # jvmti.make - generate JVMTI bindings from the spec (JSR-163) -# sa.make - generate SA jar file and natives # # The makefiles are split this way so that "make foo" will run faster by not # having to read the dependency files for the vm. @@ -130,7 +129,7 @@ SUBMAKE_DIRS = $(addprefix $(PLATFORM_DIR)/,$(TARGETS)) BUILDTREE_MAKE = $(GAMMADIR)/make/$(OS_FAMILY)/makefiles/buildtree.make # dtrace.make is used on BSD versions that implement Dtrace (like MacOS X) -BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make trace.make sa.make dtrace.make +BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make trace.make dtrace.make BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OS_FAMILY) \ SRCARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) VARIANT=$(VARIANT) @@ -218,7 +217,6 @@ flags.make: $(BUILDTREE_MAKE) ../shared_dirs.lst echo "TARGET = $(TARGET)"; \ echo "HS_BUILD_VER = $(HS_BUILD_VER)"; \ echo "JRE_RELEASE_VER = $(JRE_RELEASE_VERSION)"; \ - echo "SA_BUILD_VERSION = $(HS_BUILD_VER)"; \ echo "HOTSPOT_BUILD_USER = $(HOTSPOT_BUILD_USER)"; \ echo "HOTSPOT_VM_DISTRO = $(HOTSPOT_VM_DISTRO)"; \ echo "OPENJDK = $(OPENJDK)"; \ @@ -360,16 +358,6 @@ trace.make: $(BUILDTREE_MAKE) echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ ) > $@ -sa.make: $(BUILDTREE_MAKE) - @echo $(LOG_INFO) Creating $@ ... - $(QUIETLY) ( \ - $(BUILDTREE_COMMENT); \ - echo; \ - echo include flags.make; \ - echo; \ - echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ - ) > $@ - dtrace.make: $(BUILDTREE_MAKE) @echo $(LOG_INFO) Creating $@ ... $(QUIETLY) ( \ diff --git a/hotspot/make/bsd/makefiles/defs.make b/hotspot/make/bsd/makefiles/defs.make index a418eeb8e1d..f168eacb50a 100644 --- a/hotspot/make/bsd/makefiles/defs.make +++ b/hotspot/make/bsd/makefiles/defs.make @@ -188,13 +188,13 @@ ifeq ($(JDK6_OR_EARLIER),0) ifneq ($(BUILD_FLAVOR),) # FULL_DEBUG_SYMBOLS not created for individual static libraries ifeq ($(STATIC_BUILD),false) - ifeq ($(BUILD_FLAVOR), product) - FULL_DEBUG_SYMBOLS ?= 1 - ENABLE_FULL_DEBUG_SYMBOLS = $(FULL_DEBUG_SYMBOLS) - else - # debug variants always get Full Debug Symbols (if available) - ENABLE_FULL_DEBUG_SYMBOLS = 1 - endif + ifeq ($(BUILD_FLAVOR), product) + FULL_DEBUG_SYMBOLS ?= 1 + ENABLE_FULL_DEBUG_SYMBOLS = $(FULL_DEBUG_SYMBOLS) + else + # debug variants always get Full Debug Symbols (if available) + ENABLE_FULL_DEBUG_SYMBOLS = 1 + endif endif $(eval $(call print_info, "ENABLE_FULL_DEBUG_SYMBOLS=$(ENABLE_FULL_DEBUG_SYMBOLS)")) # since objcopy is optional, we set ZIP_DEBUGINFO_FILES later @@ -260,11 +260,11 @@ JDK_INCLUDE_SUBDIR=bsd # Library suffix ifneq ($(STATIC_BUILD),true) - ifeq ($(OS_VENDOR),Darwin) - LIBRARY_SUFFIX=dylib - else - LIBRARY_SUFFIX=so - endif +ifeq ($(OS_VENDOR),Darwin) + LIBRARY_SUFFIX=dylib +else + LIBRARY_SUFFIX=so +endif else LIBRARY_SUFFIX=a endif @@ -275,7 +275,7 @@ EXPORT_LIST += $(EXPORT_DOCS_DIR)/platform/jvmti/jvmti.html # jsig library not needed for static builds ifneq ($(STATIC_BUILD),true) # client and server subdirectories have symbolic links to ../libjsig.so - EXPORT_LIST += $(EXPORT_LIB_ARCH_DIR)/libjsig.$(LIBRARY_SUFFIX) +EXPORT_LIST += $(EXPORT_LIB_ARCH_DIR)/libjsig.$(LIBRARY_SUFFIX) endif ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) @@ -342,47 +342,6 @@ ifeq ($(JVM_VARIANT_MINIMAL1),true) endif endif -# Serviceability Binaries -# No SA Support for PPC, IA64, ARM or zero -ADD_SA_BINARIES/x86 = $(EXPORT_LIB_ARCH_DIR)/libsaproc.$(LIBRARY_SUFFIX) \ - $(EXPORT_LIB_DIR)/sa-jdi.jar - -ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) - ifeq ($(ZIP_DEBUGINFO_FILES),1) - ADD_SA_BINARIES/x86 += $(EXPORT_LIB_ARCH_DIR)/libsaproc.diz - else - ifeq ($(OS_VENDOR), Darwin) - ADD_SA_BINARIES/x86 += $(EXPORT_LIB_ARCH_DIR)/libsaproc.$(LIBRARY_SUFFIX).dSYM - else - ADD_SA_BINARIES/x86 += $(EXPORT_LIB_ARCH_DIR)/libsaproc.debuginfo - endif - endif -endif - -ADD_SA_BINARIES/sparc = $(EXPORT_LIB_ARCH_DIR)/libsaproc.$(LIBRARY_SUFFIX) \ - $(EXPORT_LIB_DIR)/sa-jdi.jar -ADD_SA_BINARIES/universal = $(EXPORT_LIB_ARCH_DIR)/libsaproc.$(LIBRARY_SUFFIX) \ - $(EXPORT_LIB_DIR)/sa-jdi.jar - -ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) - ifeq ($(ZIP_DEBUGINFO_FILES),1) - ADD_SA_BINARIES/universal += $(EXPORT_LIB_ARCH_DIR)/libsaproc.diz - else - ifeq ($(OS_VENDOR), Darwin) - ADD_SA_BINARIES/universal += $(EXPORT_LIB_ARCH_DIR)/libsaproc.$(LIBRARY_SUFFIX).dSYM - else - ADD_SA_BINARIES/universal += $(EXPORT_LIB_ARCH_DIR)/libsaproc.debuginfo - endif - endif -endif - -ADD_SA_BINARIES/ppc = -ADD_SA_BINARIES/ia64 = -ADD_SA_BINARIES/arm = -ADD_SA_BINARIES/zero = - -EXPORT_LIST += $(ADD_SA_BINARIES/$(HS_ARCH)) - # Universal build settings ifeq ($(OS_VENDOR), Darwin) # Build universal binaries by default on Mac OS X @@ -409,9 +368,8 @@ ifeq ($(OS_VENDOR), Darwin) # Binaries to 'universalize' if built ifneq ($(STATIC_BUILD),true) - UNIVERSAL_LIPO_LIST += $(EXPORT_LIB_DIR)/libjsig.$(LIBRARY_SUFFIX) + UNIVERSAL_LIPO_LIST += $(EXPORT_LIB_DIR)/libjsig.$(LIBRARY_SUFFIX) endif - UNIVERSAL_LIPO_LIST += $(EXPORT_LIB_DIR)/libsaproc.$(LIBRARY_SUFFIX) UNIVERSAL_LIPO_LIST += $(EXPORT_LIB_DIR)/server/libjvm.$(LIBRARY_SUFFIX) UNIVERSAL_LIPO_LIST += $(EXPORT_LIB_DIR)/client/libjvm.$(LIBRARY_SUFFIX) @@ -430,12 +388,10 @@ ifeq ($(OS_VENDOR), Darwin) UNIVERSAL_COPY_LIST += $(EXPORT_LIB_DIR)/server/libjvm.diz UNIVERSAL_COPY_LIST += $(EXPORT_LIB_DIR)/client/libjvm.diz UNIVERSAL_COPY_LIST += $(EXPORT_LIB_DIR)/libjsig.diz - UNIVERSAL_COPY_LIST += $(EXPORT_LIB_DIR)/libsaproc.diz else UNIVERSAL_COPY_LIST += $(EXPORT_LIB_DIR)/server/libjvm.$(LIBRARY_SUFFIX).dSYM UNIVERSAL_COPY_LIST += $(EXPORT_LIB_DIR)/client/libjvm.$(LIBRARY_SUFFIX).dSYM UNIVERSAL_COPY_LIST += $(EXPORT_LIB_DIR)/libjsig.$(LIBRARY_SUFFIX).dSYM - UNIVERSAL_COPY_LIST += $(EXPORT_LIB_DIR)/libsaproc.$(LIBRARY_SUFFIX).dSYM endif endif diff --git a/hotspot/make/bsd/makefiles/rules.make b/hotspot/make/bsd/makefiles/rules.make index d2334a70ffb..bb36f19a473 100644 --- a/hotspot/make/bsd/makefiles/rules.make +++ b/hotspot/make/bsd/makefiles/rules.make @@ -107,8 +107,6 @@ BOOT_JAVA_HOME = $(JAVA_HOME) else # take from the PATH, if ALT_BOOTDIR, BOOTDIR and JAVA_HOME are not defined -# note that this is to support hotspot build without SA. To build -# SA along with hotspot, you need to define ALT_BOOTDIR, BOOTDIR or JAVA_HOME RUN.JAVA = java RUN.JAVAP = javap diff --git a/hotspot/make/bsd/makefiles/sa.make b/hotspot/make/bsd/makefiles/sa.make deleted file mode 100644 index 0b8f9511260..00000000000 --- a/hotspot/make/bsd/makefiles/sa.make +++ /dev/null @@ -1,138 +0,0 @@ -# -# Copyright (c) 2003, 2015, 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. -# -# - -# This makefile (sa.make) is included from the sa.make in the -# build directories. - -define print_info - ifneq ($$(LOG_LEVEL), warn) - $$(shell echo >&2 "INFO: $1") - endif -endef - -# This makefile is used to build Serviceability Agent java code -# and generate JNI header file for native methods. - -include $(GAMMADIR)/make/bsd/makefiles/rules.make - -AGENT_DIR = $(GAMMADIR)/agent - -include $(GAMMADIR)/make/sa.files - --include $(HS_ALT_MAKE)/bsd/makefiles/sa.make - -TOPDIR = $(shell echo `pwd`) -GENERATED = $(TOPDIR)/../generated - -# SA-JDI depends on the standard JDI classes. -# Default SA_CLASSPATH location: -DEF_SA_CLASSPATH=$(BOOT_JAVA_HOME)/lib/tools.jar -ifeq ($(ALT_SA_CLASSPATH),) - # no alternate specified; see if default exists - SA_CLASSPATH=$(shell test -f $(DEF_SA_CLASSPATH) && echo $(DEF_SA_CLASSPATH)) - ifeq ($(SA_CLASSPATH),) - # the default doesn't exist - ifeq ($(OS_VENDOR), Darwin) - # A JDK from Apple doesn't have tools.jar; the JDI classes are - # are in the regular classes.jar file. - APPLE_JAR=$(BOOT_JAVA_HOME)/bundle/Classes/classes.jar - SA_CLASSPATH=$(shell test -f $(APPLE_JAR) && echo $(APPLE_JAR)) - endif - endif -else - $(eval $(call print_info, "ALT_SA_CLASSPATH=$(ALT_SA_CLASSPATH)")) - SA_CLASSPATH=$(shell test -f $(ALT_SA_CLASSPATH) && echo $(ALT_SA_CLASSPATH)) -endif - -ifneq ($(SA_CLASSPATH),) - SA_CLASSPATH_ARG := -classpath $(SA_CLASSPATH) -endif - -# TODO: if it's a modules image, check if SA module is installed. -MODULELIB_PATH= $(BOOT_JAVA_HOME)/lib/modules - -AGENT_FILES_LIST := $(GENERATED)/agent.classes.list - -SA_CLASSDIR = $(GENERATED)/saclasses - -SA_BUILD_VERSION_PROP = "sun.jvm.hotspot.runtime.VM.saBuildVersion=$(SA_BUILD_VERSION)" - -SA_PROPERTIES = $(SA_CLASSDIR)/sa.properties - -# if $(AGENT_DIR) does not exist, we don't build SA -# also, we don't build SA on Itanium, PowerPC, ARM or zero. - -all: - if [ -d $(AGENT_DIR) -a "$(SRCARCH)" != "ia64" \ - -a "$(SRCARCH)" != "arm" \ - -a "$(SRCARCH)" != "ppc" \ - -a "$(SRCARCH)" != "zero" ] ; then \ - $(MAKE) -f sa.make $(GENERATED)/sa-jdi.jar; \ - fi - -$(GENERATED)/sa-jdi.jar: $(AGENT_FILES) - $(QUIETLY) echo $(LOG_INFO) "Making $@" - $(QUIETLY) if [ "$(BOOT_JAVA_HOME)" = "" ]; then \ - echo "ALT_BOOTDIR, BOOTDIR or JAVA_HOME needs to be defined to build SA"; \ - exit 1; \ - fi - $(QUIETLY) if [ ! -f "$(SA_CLASSPATH)" -a ! -d $(MODULELIB_PATH) ] ; then \ - echo "Cannot find JDI classes. Use 1.6.0 or later version of JDK."; \ - echo ""; \ - exit 1; \ - fi - $(QUIETLY) if [ ! -d $(SA_CLASSDIR) ] ; then \ - mkdir -p $(SA_CLASSDIR); \ - fi -# Note: When indented, make tries to execute the '$(shell' comment. -# In some environments, cmd processors have limited line length. -# To prevent the javac invocation in the next block from using -# a very long cmd line, we use javac's @file-list option. We -# generate the file lists using make's built-in 'foreach' control -# flow which also avoids cmd processor line length issues. Since -# the 'foreach' is done as part of make's macro expansion phase, -# the initialization of the lists is also done in the same phase -# using '$(shell rm ...' instead of using the more traditional -# 'rm ...' rule. - $(shell rm -rf $(AGENT_FILES_LIST)) -# gnumake 3.78.1 does not accept the *'s that -# are in AGENT_FILES, so use the shell to expand them. -# Be extra carefull to not produce too long command lines in the shell! - $(foreach file,$(AGENT_FILES),$(shell ls -1 $(file) >> $(AGENT_FILES_LIST))) - $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -h $(GENERATED) $(SA_CLASSPATH_ARG) -sourcepath $(AGENT_SRC_DIR) -d $(SA_CLASSDIR) @$(AGENT_FILES_LIST) - $(QUIETLY) $(REMOTE) $(COMPILE.RMIC) -classpath $(SA_CLASSDIR) -d $(SA_CLASSDIR) sun.jvm.hotspot.debugger.remote.RemoteDebuggerServer - $(QUIETLY) echo "$(SA_BUILD_VERSION_PROP)" > $(SA_PROPERTIES) - $(QUIETLY) rm -f $(SA_CLASSDIR)/sun/jvm/hotspot/utilities/soql/sa.js - $(QUIETLY) $(CP) $(AGENT_SRC_DIR)/sun/jvm/hotspot/utilities/soql/sa.js $(SA_CLASSDIR)/sun/jvm/hotspot/utilities/soql - $(QUIETLY) mkdir -p $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources - $(QUIETLY) rm -f $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources/* - $(QUIETLY) $(CP) $(AGENT_SRC_DIR)/sun/jvm/hotspot/ui/resources/*.png $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources/ - $(QUIETLY) $(CP) -r $(AGENT_SRC_DIR)/images/* $(SA_CLASSDIR)/ - $(QUIETLY) $(REMOTE) $(RUN.JAR) cf $@ -C $(SA_CLASSDIR)/ . - $(QUIETLY) $(REMOTE) $(RUN.JAR) uf $@ -C $(AGENT_SRC_DIR) META-INF/services/com.sun.jdi.connect.Connector - -clean: - rm -rf $(SA_CLASSDIR) - rm -rf $(GENERATED)/sa-jdi.jar - rm -rf $(AGENT_FILES_LIST) diff --git a/hotspot/make/bsd/makefiles/top.make b/hotspot/make/bsd/makefiles/top.make index 1df2d37fc67..47394d64a95 100644 --- a/hotspot/make/bsd/makefiles/top.make +++ b/hotspot/make/bsd/makefiles/top.make @@ -28,7 +28,6 @@ # It also: # -builds and runs adlc via adlc.make # -generates JVMTI source and docs via jvmti.make (JSR-163) -# -generate sa-jdi.jar (JDI binding to core files) # It assumes the following flags are set: # CFLAGS Platform_file, Src_Dirs_I, Src_Dirs_V, SYSDEFS, AOUT, Obj_Files @@ -86,7 +85,7 @@ default: vm_build_preliminaries the_vm @echo All done. # This is an explicit dependency for the sake of parallel makes. -vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) jvmti_stuff trace_stuff sa_stuff dtrace_stuff +vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) jvmti_stuff trace_stuff dtrace_stuff @# We need a null action here, so implicit rules don't get consulted. $(Cached_plat): $(Plat_File) @@ -113,10 +112,6 @@ dtrace_stuff: @# We need a null action here, so implicit rules don't get consulted. endif -# generate SA jar files and native header -sa_stuff: - @$(MAKE) -f sa.make $(MFLAGS-adjusted) - # and the VM: must use other makefile with dependencies included # We have to go to great lengths to get control over the -jN argument @@ -155,7 +150,7 @@ realclean: rm -fr $(GENERATED) .PHONY: default vm_build_preliminaries -.PHONY: lists ad_stuff jvmti_stuff sa_stuff the_vm clean realclean +.PHONY: lists ad_stuff jvmti_stuff the_vm clean realclean .PHONY: checks check_os_version install .NOTPARALLEL: diff --git a/hotspot/make/bsd/makefiles/vm.make b/hotspot/make/bsd/makefiles/vm.make index 71012e96193..f868a807eaa 100644 --- a/hotspot/make/bsd/makefiles/vm.make +++ b/hotspot/make/bsd/makefiles/vm.make @@ -60,7 +60,7 @@ Src_Dirs_I += $(GENERATED) # The order is important for the precompiled headers to work. INCLUDES += $(PRECOMPILED_HEADER_DIR:%=-I%) $(Src_Dirs_I:%=-I%) -# SYMFLAG is used by {jsig,saproc}.make +# SYMFLAG is used by jsig.make ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) # always build with debug info when we can create .dSYM/.debuginfo files SYMFLAG = -g @@ -299,9 +299,9 @@ else ifeq ($(OS_VENDOR), Darwin) ifneq ($(STATIC_BUILD),true) - LFLAGS_VM += -Xlinker -rpath -Xlinker @loader_path/. - LFLAGS_VM += -Xlinker -rpath -Xlinker @loader_path/.. - LFLAGS_VM += -Xlinker -install_name -Xlinker @rpath/$(@F) + LFLAGS_VM += -Xlinker -rpath -Xlinker @loader_path/. + LFLAGS_VM += -Xlinker -rpath -Xlinker @loader_path/.. + LFLAGS_VM += -Xlinker -install_name -Xlinker @rpath/$(@F) endif else LFLAGS_VM += -Wl,-z,defs @@ -421,19 +421,16 @@ endif # Signal interposition library include $(MAKEFILES_DIR)/jsig.make -# Serviceability agent -include $(MAKEFILES_DIR)/saproc.make - #---------------------------------------------------------------------- ifeq ($(OS_VENDOR), Darwin) # no libjvm_db for macosx -build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) $(BUILDLIBSAPROC) dtraceCheck $(EXPORTED_SYMBOLS) +build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) dtraceCheck $(EXPORTED_SYMBOLS) echo "Doing vm.make build:" else -build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) $(LIBJVM_DB) $(BUILDLIBSAPROC) $(EXPORTED_SYMBOLS) +build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) $(LIBJVM_DB) $(EXPORTED_SYMBOLS) endif -install: install_jvm install_jsig install_saproc +install: install_jvm install_jsigs .PHONY: default build install install_jvm diff --git a/hotspot/make/excludeSrc.make b/hotspot/make/excludeSrc.make index dac7372c6fc..9feb96861ee 100644 --- a/hotspot/make/excludeSrc.make +++ b/hotspot/make/excludeSrc.make @@ -73,8 +73,9 @@ ifeq ($(INCLUDE_CDS), false) CXXFLAGS += -DINCLUDE_CDS=0 CFLAGS += -DINCLUDE_CDS=0 - Src_Files_EXCLUDE += filemap.cpp metaspaceShared*.cpp sharedPathsMiscInfo.cpp \ - systemDictionaryShared.cpp classLoaderExt.cpp sharedClassUtil.cpp + Src_Files_EXCLUDE += classListParser.cpp classLoaderExt.cpp \ + filemap.cpp metaspaceShared*.cpp sharedClassUtil.cpp sharedPathsMiscInfo.cpp \ + systemDictionaryShared.cpp endif ifeq ($(INCLUDE_ALL_GCS), false) diff --git a/hotspot/make/gensrc/Gensrc-jdk.hotspot.agent.gmk b/hotspot/make/gensrc/Gensrc-jdk.hotspot.agent.gmk new file mode 100644 index 00000000000..706804b87c5 --- /dev/null +++ b/hotspot/make/gensrc/Gensrc-jdk.hotspot.agent.gmk @@ -0,0 +1,47 @@ +# +# Copyright (c) 2015, 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. +# + +default: all + +include $(SPEC) +include MakeBase.gmk + +################################################################################ + +SA_PROPERTIES := $(SUPPORT_OUTPUTDIR)/gensrc/jdk.hotspot.agent/sa.properties + +SA_BUILD_VERSION_PROP_NAME := "sun.jvm.hotspot.runtime.VM.saBuildVersion" + +$(SA_PROPERTIES): $(call DependOnVariable, FULL_VERSION) + $(MKDIR) -p $(@D) + $(ECHO) "$(SA_BUILD_VERSION_PROP_NAME)=$(FULL_VERSION)" > $@ + +TARGETS += $(SA_PROPERTIES) + +################################################################################ + +all: $(TARGETS) + +.PHONY: all default diff --git a/hotspot/make/lib/Lib-jdk.hotspot.agent.gmk b/hotspot/make/lib/Lib-jdk.hotspot.agent.gmk new file mode 100644 index 00000000000..a3febc1c302 --- /dev/null +++ b/hotspot/make/lib/Lib-jdk.hotspot.agent.gmk @@ -0,0 +1,130 @@ +# +# Copyright (c) 2015, 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. +# + +include NativeCompilation.gmk + +$(eval $(call IncludeCustomExtension, hotspot, lib/Lib-jdk.hotspot.agent.gmk)) + +################################################################################ + +SA_TOPDIR := $(HOTSPOT_TOPDIR)/agent + +# SA has a slightly different OS naming scheme +ifeq ($(OPENJDK_TARGET_OS), windows) + SA_TARGET_OS := win32 +else ifeq ($(OPENJDK_TARGET_OS), macosx) + SA_TARGET_OS := bsd +else + SA_TARGET_OS := $(OPENJDK_TARGET_OS) +endif + +# Defaults for most platforms +SA_TOOLCHAIN := TOOLCHAIN_DEFAULT +SA_NAME := saproc +SA_SRC += $(SA_TOPDIR)/src/share/native $(SA_TOPDIR)/src/os/$(SA_TARGET_OS) +SA_MAPFILE := $(SA_TOPDIR)/src/os/$(OPENJDK_TARGET_OS)/mapfile +SA_INCLUDES := \ + $(addprefix -I, $(SA_SRC)) \ + -I$(SUPPORT_OUTPUTDIR)/headers/jdk.hotspot.agent \ + -I$(HOTSPOT_TOPDIR)/src/os/$(OPENJDK_TARGET_OS) \ + # + +ifeq ($(OPENJDK_TARGET_CPU), x86_64) + SA_MACHINE_FLAG_windows := -machine:AMD64 +else ifeq ($(OPENJDK_TARGET_CPU), x86) + SA_MACHINE_FLAG_linux := -march=i586 + SA_MACHINE_FLAG_windows := -machine:I386 +endif + +ifeq ($(OPENJDK_TARGET_OS), linux) + SA_CFLAGS := $(CFLAGS_JDKLIB) -D_FILE_OFFSET_BITS=64 \ + $(SA_MACHINE_FLAG_linux) + SA_LDFLAGS := $(LDFLAGS_JDKLIB) $(SA_MACHINE_FLAG_linux) + SA_LIBS := -lthread_db $(LIBDL) + +else ifeq ($(OPENJDK_TARGET_OS), solaris) + SA_TOOLCHAIN := TOOLCHAIN_LINK_CXX + SA_MAPFILE := $(SA_TOPDIR)/src/os/solaris/proc/mapfile + COMMON_CFLAGS := -I$(SA_TOPDIR)/src/os/$(OPENJDK_TARGET_OS)/proc \ + -DSOLARIS_11_B159_OR_LATER + SA_CFLAGS := $(CFLAGS_JDKLIB) $(COMMON_CFLAGS) + SA_CXXFLAGS := $(CXXFLAGS_JDKLIB) $(COMMON_CFLAGS) + SA_LDFLAGS := $(subst -z defs,, $(LDFLAGS_JDKLIB)) \ + -mt $(LDFLAGS_CXX_JDK) + SA_LIBS := -ldl -ldemangle -lthread -lc + +else ifeq ($(OPENJDK_TARGET_OS), macosx) + SA_EXCLUDE_FILES := BsdDebuggerLocal.c ps_proc.c salibelf.c StubDebuggerLocal.c + SA_CFLAGS := $(CFLAGS_JDKLIB) \ + -Damd64 -D_GNU_SOURCE -mno-omit-leaf-frame-pointer \ + -mstack-alignment=16 -fPIC + SA_LDFLAGS := $(LDFLAGS_JDKLIB) + SA_LIBS := -framework Foundation -framework JavaNativeFoundation \ + -framework Security -framework CoreFoundation + +else ifeq ($(OPENJDK_TARGET_OS), windows) + SA_NAME := sawindbg + COMMON_CFLAGS := -D_WINDOWS -D_DEBUG -D_CONSOLE -D_MBCS -EHsc -FD + SA_CFLAGS := $(subst -DWIN32_LEAN_AND_MEAN,, $(CFLAGS_JDKLIB)) \ + $(COMMON_CFLAGS) + SA_CXXFLAGS := $(subst -DWIN32_LEAN_AND_MEAN,, $(CXXFLAGS_JDKLIB)) \ + $(COMMON_CFLAGS) + SA_LDFLAGS := $(LDFLAGS_JDKLIB) \ + $(SA_MACHINE_FLAG_windows) -manifest \ + -subsystem:console -map + SA_LIBS := dbgeng.lib + ifeq ($(OPENJDK_TARGET_CPU), x86_64) + SA_CXXFLAGS += -DWIN64 + else + SA_CXXFLAGS += -RTC1 -ZI + SA_LDFLAGS += -SAFESEH + endif +endif + +################################################################################ + +$(eval $(call SetupNativeCompilation, BUILD_LIBSA, \ + TOOLCHAIN := $(SA_TOOLCHAIN), \ + OPTIMIZATION := NONE, \ + DISABLED_WARNINGS_microsoft := 4267, \ + DISABLED_WARNINGS_gcc := sign-compare, \ + DISABLED_WARNINGS_CXX_solstudio := truncwarn unknownpragma, \ + LIBRARY := $(SA_NAME), \ + OUTPUT_DIR := $(call FindLibDirForModule, $(MODULE)), \ + SRC := $(SA_SRC), \ + EXCLUDE_FILES := test.c saproc_audit.cpp $(SA_EXCLUDE_FILES), \ + CFLAGS := $(SA_INCLUDES) $(SA_CFLAGS) $(SA_CUSTOM_CFLAGS), \ + CXXFLAGS := $(SA_INCLUDES) $(SA_CXXFLAGS) $(SA_CUSTOM_CXXFLAGS), \ + LDFLAGS := $(SA_LDFLAGS) $(SA_CUSTOM_LDFLAGS), \ + LIBS := $(SA_LIBS), \ + MAPFILE := $(SA_MAPFILE), \ + OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libsa, \ + DEBUG_SYMBOLS := true, \ + STRIP_SYMBOLS := true, \ +)) + +TARGETS += $(BUILD_LIBSA) + +################################################################################ diff --git a/hotspot/make/linux/Makefile b/hotspot/make/linux/Makefile index a447f5482cc..03c6393541e 100644 --- a/hotspot/make/linux/Makefile +++ b/hotspot/make/linux/Makefile @@ -45,15 +45,6 @@ # # make REMOTE="rsh -l me myotherlinuxbox" -# Along with VM, Serviceability Agent (SA) is built for SA/JDI binding. -# JDI binding on SA produces two binaries: -# 1. sa-jdi.jar - This is built before building libjvm.so -# Please refer to ./makefiles/sa.make -# 2. libsa.so - Native library for SA - This is built after -# libjsig.so (signal interposition library) -# Please refer to ./makefiles/vm.make -# If $(GAMMADIR)/agent dir is not present, SA components are not built. - ifeq ($(GAMMADIR),) include ../../make/defs.make else diff --git a/hotspot/make/linux/makefiles/buildtree.make b/hotspot/make/linux/makefiles/buildtree.make index 723f44e1f85..e60aae410ce 100644 --- a/hotspot/make/linux/makefiles/buildtree.make +++ b/hotspot/make/linux/makefiles/buildtree.make @@ -49,7 +49,6 @@ # adlc.make - # trace.make - generate tracing event and type definitions # jvmti.make - generate JVMTI bindings from the spec (JSR-163) -# sa.make - generate SA jar file and natives # # The makefiles are split this way so that "make foo" will run faster by not # having to read the dependency files for the vm. @@ -127,7 +126,7 @@ SUBMAKE_DIRS = $(addprefix $(PLATFORM_DIR)/,$(TARGETS)) # For dependencies and recursive makes. BUILDTREE_MAKE = $(GAMMADIR)/make/$(OS_FAMILY)/makefiles/buildtree.make -BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make trace.make sa.make dtrace.make +BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make trace.make dtrace.make BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OS_FAMILY) \ SRCARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) VARIANT=$(VARIANT) @@ -219,7 +218,6 @@ flags.make: $(BUILDTREE_MAKE) ../shared_dirs.lst echo "TARGET = $(TARGET)"; \ echo "HS_BUILD_VER = $(HS_BUILD_VER)"; \ echo "JRE_RELEASE_VER = $(JRE_RELEASE_VERSION)"; \ - echo "SA_BUILD_VERSION = $(HS_BUILD_VER)"; \ echo "HOTSPOT_BUILD_USER = $(HOTSPOT_BUILD_USER)"; \ echo "HOTSPOT_VM_DISTRO = $(HOTSPOT_VM_DISTRO)"; \ echo "OPENJDK = $(OPENJDK)"; \ @@ -358,16 +356,6 @@ trace.make: $(BUILDTREE_MAKE) echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ ) > $@ -sa.make: $(BUILDTREE_MAKE) - @echo $(LOG_INFO) Creating $@ ... - $(QUIETLY) ( \ - $(BUILDTREE_COMMENT); \ - echo; \ - echo include flags.make; \ - echo; \ - echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ - ) > $@ - dtrace.make: $(BUILDTREE_MAKE) @echo $(LOG_INFO) Creating $@ ... $(QUIETLY) ( \ diff --git a/hotspot/make/linux/makefiles/defs.make b/hotspot/make/linux/makefiles/defs.make index 472621ffbea..dad86a239b9 100644 --- a/hotspot/make/linux/makefiles/defs.make +++ b/hotspot/make/linux/makefiles/defs.make @@ -293,24 +293,4 @@ ifeq ($(JVM_VARIANT_MINIMAL1),true) endif endif -# Serviceability Binaries - -ADD_SA_BINARIES/DEFAULT = $(EXPORT_LIB_ARCH_DIR)/libsaproc.$(LIBRARY_SUFFIX) \ - $(EXPORT_LIB_DIR)/sa-jdi.jar - -ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) - ifeq ($(ZIP_DEBUGINFO_FILES),1) - ADD_SA_BINARIES/DEFAULT += $(EXPORT_LIB_ARCH_DIR)/libsaproc.diz - else - ADD_SA_BINARIES/DEFAULT += $(EXPORT_LIB_ARCH_DIR)/libsaproc.debuginfo - endif -endif - -ADD_SA_BINARIES/$(HS_ARCH) = $(ADD_SA_BINARIES/DEFAULT) - -# No SA Support for zero -ADD_SA_BINARIES/zero = - -include $(HS_ALT_MAKE)/linux/makefiles/defs.make - -EXPORT_LIST += $(ADD_SA_BINARIES/$(HS_ARCH)) diff --git a/hotspot/make/linux/makefiles/rules.make b/hotspot/make/linux/makefiles/rules.make index 596d5f423bd..82b952d2988 100644 --- a/hotspot/make/linux/makefiles/rules.make +++ b/hotspot/make/linux/makefiles/rules.make @@ -103,8 +103,6 @@ BOOT_JAVA_HOME = $(JAVA_HOME) else # take from the PATH, if ALT_BOOTDIR, BOOTDIR and JAVA_HOME are not defined -# note that this is to support hotspot build without SA. To build -# SA along with hotspot, you need to define ALT_BOOTDIR, BOOTDIR or JAVA_HOME RUN.JAVA = java RUN.JAVAP = javap diff --git a/hotspot/make/linux/makefiles/sa.make b/hotspot/make/linux/makefiles/sa.make deleted file mode 100644 index 0e9404928ef..00000000000 --- a/hotspot/make/linux/makefiles/sa.make +++ /dev/null @@ -1,115 +0,0 @@ -# -# Copyright (c) 2003, 2015, 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. -# -# - -# This makefile (sa.make) is included from the sa.make in the -# build directories. - -# This makefile is used to build Serviceability Agent java code -# and generate JNI header file for native methods. - -include $(GAMMADIR)/make/linux/makefiles/rules.make - -include $(GAMMADIR)/make/defs.make -include $(GAMMADIR)/make/altsrc.make - -AGENT_DIR = $(GAMMADIR)/agent - -include $(GAMMADIR)/make/sa.files - --include $(HS_ALT_MAKE)/linux/makefiles/sa.make - - -TOPDIR = $(shell echo `pwd`) -GENERATED = $(TOPDIR)/../generated - -# tools.jar is needed by the JDI - SA binding -SA_CLASSPATH = $(BOOT_JAVA_HOME)/lib/tools.jar - -# TODO: if it's a modules image, check if SA module is installed. -MODULELIB_PATH= $(BOOT_JAVA_HOME)/lib/modules - -AGENT_FILES_LIST := $(GENERATED)/agent.classes.list - -SA_CLASSDIR = $(GENERATED)/saclasses - -SA_BUILD_VERSION_PROP = "sun.jvm.hotspot.runtime.VM.saBuildVersion=$(SA_BUILD_VERSION)" - -SA_PROPERTIES = $(SA_CLASSDIR)/sa.properties - -# if $(AGENT_DIR) does not exist, we don't build SA -# also, we don't build SA on Itanium or zero. - -all: - if [ -d $(AGENT_DIR) -a "$(SRCARCH)" != "ia64" \ - -a "$(SRCARCH)" != "zero" ] ; then \ - $(MAKE) -f sa.make $(GENERATED)/sa-jdi.jar; \ - fi - -$(GENERATED)/sa-jdi.jar:: $(AGENT_FILES) - $(QUIETLY) echo $(LOG_INFO) "Making $@" - $(QUIETLY) if [ "$(BOOT_JAVA_HOME)" = "" ]; then \ - echo "ALT_BOOTDIR, BOOTDIR or JAVA_HOME needs to be defined to build SA"; \ - exit 1; \ - fi - $(QUIETLY) if [ ! -f $(SA_CLASSPATH) -a ! -d $(MODULELIB_PATH) ] ; then \ - echo "Missing $(SA_CLASSPATH) file. Use 1.6.0 or later version of JDK";\ - echo ""; \ - exit 1; \ - fi - $(QUIETLY) if [ ! -d $(SA_CLASSDIR) ] ; then \ - mkdir -p $(SA_CLASSDIR); \ - fi -# Note: When indented, make tries to execute the '$(shell' comment. -# In some environments, cmd processors have limited line length. -# To prevent the javac invocation in the next block from using -# a very long cmd line, we use javac's @file-list option. We -# generate the file lists using make's built-in 'foreach' control -# flow which also avoids cmd processor line length issues. Since -# the 'foreach' is done as part of make's macro expansion phase, -# the initialization of the lists is also done in the same phase -# using '$(shell rm ...' instead of using the more traditional -# 'rm ...' rule. - $(shell rm -rf $(AGENT_FILES_LIST)) -# gnumake 3.78.1 does not accept the *'s that -# are in AGENT_FILES, so use the shell to expand them. -# Be extra carefull to not produce too long command lines in the shell! - $(foreach file,$(AGENT_FILES),$(shell ls -1 $(file) >> $(AGENT_FILES_LIST))) - $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -h $(GENERATED) -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -d $(SA_CLASSDIR) @$(AGENT_FILES_LIST) - $(QUIETLY) $(REMOTE) $(COMPILE.RMIC) -classpath $(SA_CLASSDIR) -d $(SA_CLASSDIR) sun.jvm.hotspot.debugger.remote.RemoteDebuggerServer - $(QUIETLY) echo "$(SA_BUILD_VERSION_PROP)" > $(SA_PROPERTIES) - $(QUIETLY) rm -f $(SA_CLASSDIR)/sun/jvm/hotspot/utilities/soql/sa.js - $(QUIETLY) $(CP) $(AGENT_SRC_DIR)/sun/jvm/hotspot/utilities/soql/sa.js $(SA_CLASSDIR)/sun/jvm/hotspot/utilities/soql - $(QUIETLY) mkdir -p $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources - $(QUIETLY) rm -f $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources/* - $(QUIETLY) $(CP) $(AGENT_SRC_DIR)/sun/jvm/hotspot/ui/resources/*.png $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources/ - $(QUIETLY) $(CP) -r $(AGENT_SRC_DIR)/images/* $(SA_CLASSDIR)/ - $(QUIETLY) $(REMOTE) $(RUN.JAR) cf $@ -C $(SA_CLASSDIR)/ . - $(QUIETLY) $(REMOTE) $(RUN.JAR) uf $@ -C $(AGENT_SRC_DIR) META-INF/services/com.sun.jdi.connect.Connector - -clean: - rm -rf $(SA_CLASSDIR) - rm -rf $(GENERATED)/sa-jdi.jar - rm -rf $(AGENT_FILES_LIST) - --include $(HS_ALT_MAKE)/linux/makefiles/sa-rules.make diff --git a/hotspot/make/linux/makefiles/saproc.make b/hotspot/make/linux/makefiles/saproc.make deleted file mode 100644 index 15bdba1d396..00000000000 --- a/hotspot/make/linux/makefiles/saproc.make +++ /dev/null @@ -1,127 +0,0 @@ -# -# Copyright (c) 2005, 2015, 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. -# -# -include $(GAMMADIR)/make/defs.make -include $(GAMMADIR)/make/altsrc.make - -# Rules to build serviceability agent library, used by vm.make - -# libsaproc.so: serviceability agent - -SAPROC = saproc -LIBSAPROC = lib$(SAPROC).so - -LIBSAPROC_DEBUGINFO = lib$(SAPROC).debuginfo -LIBSAPROC_DIZ = lib$(SAPROC).diz - -AGENT_DIR = $(GAMMADIR)/agent - -SASRCDIR = $(AGENT_DIR)/src/os/$(Platform_os_family) - -SASRCFILES = $(SASRCDIR)/salibelf.c \ - $(SASRCDIR)/symtab.c \ - $(SASRCDIR)/libproc_impl.c \ - $(SASRCDIR)/ps_proc.c \ - $(SASRCDIR)/ps_core.c \ - $(SASRCDIR)/LinuxDebuggerLocal.c \ - $(AGENT_DIR)/src/share/native/sadis.c - --include $(HS_ALT_MAKE)/linux/makefiles/saproc.make - -SAMAPFILE = $(SASRCDIR)/mapfile - -DEST_SAPROC = $(JDK_LIBDIR)/$(LIBSAPROC) -DEST_SAPROC_DEBUGINFO = $(JDK_LIBDIR)/$(LIBSAPROC_DEBUGINFO) -DEST_SAPROC_DIZ = $(JDK_LIBDIR)/$(LIBSAPROC_DIZ) - -# DEBUG_BINARIES overrides everything, use full -g debug information -ifeq ($(DEBUG_BINARIES), true) - SA_DEBUG_CFLAGS = -g -endif - -# if $(AGENT_DIR) does not exist, we don't build SA -# also, we don't build SA on Itanium or zero. - -ifneq ($(wildcard $(AGENT_DIR)),) -ifneq ($(filter-out ia64 zero,$(SRCARCH)),) - BUILDLIBSAPROC = $(LIBSAPROC) -endif -endif - -ifneq ($(ALT_SASRCDIR),) -ALT_SAINCDIR=-I$(ALT_SASRCDIR) -DALT_SASRCDIR -else -ALT_SAINCDIR= -endif -SA_LFLAGS = $(MAPFLAG:FILENAME=$(SAMAPFILE)) $(LDFLAGS_HASH_STYLE) - -SAARCH ?= $(BUILDARCH) - -$(LIBSAPROC): $(SASRCFILES) $(SAMAPFILE) - $(QUIETLY) if [ "$(BOOT_JAVA_HOME)" = "" ]; then \ - echo "ALT_BOOTDIR, BOOTDIR or JAVA_HOME needs to be defined to build SA"; \ - exit 1; \ - fi - @echo $(LOG_INFO) Making SA debugger back-end... - $(QUIETLY) $(CC) -D$(SAARCH) -D_GNU_SOURCE \ - -D_FILE_OFFSET_BITS=64 \ - $(SYMFLAG) $(ARCHFLAG) $(SHARED_FLAG) $(PICFLAG) \ - -I$(SASRCDIR) \ - -I$(GENERATED) \ - -I$(BOOT_JAVA_HOME)/include \ - -I$(BOOT_JAVA_HOME)/include/$(Platform_os_family) \ - $(ALT_SAINCDIR) \ - $(SASRCFILES) \ - $(SA_LFLAGS) \ - $(SA_DEBUG_CFLAGS) \ - $(EXTRA_CFLAGS) \ - -o $@ \ - -lthread_db -ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) - $(QUIETLY) $(OBJCOPY) --only-keep-debug $@ $(LIBSAPROC_DEBUGINFO) - $(QUIETLY) $(OBJCOPY) --add-gnu-debuglink=$(LIBSAPROC_DEBUGINFO) $@ - ifeq ($(STRIP_POLICY),all_strip) - $(QUIETLY) $(STRIP) $@ - else - ifeq ($(STRIP_POLICY),min_strip) - $(QUIETLY) $(STRIP) -g $@ - # implied else here is no stripping at all - endif - endif - ifeq ($(ZIP_DEBUGINFO_FILES),1) - $(ZIPEXE) -q -y $(LIBSAPROC_DIZ) $(LIBSAPROC_DEBUGINFO) - $(RM) $(LIBSAPROC_DEBUGINFO) - endif -endif - -install_saproc: $(BUILDLIBSAPROC) - $(QUIETLY) if [ -e $(LIBSAPROC) ] ; then \ - echo "Copying $(LIBSAPROC) to $(DEST_SAPROC)"; \ - test ! -f $(LIBSAPROC_DEBUGINFO) || \ - $(CP) -f $(LIBSAPROC_DEBUGINFO) $(DEST_SAPROC_DEBUGINFO); \ - test ! -f $(LIBSAPROC_DIZ) || \ - $(CP) -f $(LIBSAPROC_DIZ) $(DEST_SAPROC_DIZ); \ - $(CP) -f $(LIBSAPROC) $(DEST_SAPROC) && echo "Done"; \ - fi - -.PHONY: install_saproc diff --git a/hotspot/make/linux/makefiles/top.make b/hotspot/make/linux/makefiles/top.make index 7659a3da2d6..d798d42851e 100644 --- a/hotspot/make/linux/makefiles/top.make +++ b/hotspot/make/linux/makefiles/top.make @@ -28,7 +28,6 @@ # It also: # -builds and runs adlc via adlc.make # -generates JVMTI source and docs via jvmti.make (JSR-163) -# -generate sa-jdi.jar (JDI binding to core files) # It assumes the following flags are set: # CFLAGS Platform_file, Src_Dirs_I, Src_Dirs_V, SYSDEFS, AOUT, Obj_Files @@ -86,7 +85,7 @@ default: vm_build_preliminaries the_vm @echo All done. # This is an explicit dependency for the sake of parallel makes. -vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) trace_stuff jvmti_stuff sa_stuff dtrace_stuff +vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) trace_stuff jvmti_stuff dtrace_stuff @# We need a null action here, so implicit rules don't get consulted. $(Cached_plat): $(Plat_File) @@ -104,10 +103,6 @@ jvmti_stuff: $(Cached_plat) $(adjust-mflags) trace_stuff: jvmti_stuff $(Cached_plat) $(adjust-mflags) @$(MAKE) -f trace.make $(MFLAGS-adjusted) -# generate SA jar files and native header -sa_stuff: - @$(MAKE) -f sa.make $(MFLAGS-adjusted) - dtrace_stuff: $(Cached_plat) $(adjust-mflags) @$(MAKE) -f dtrace.make dtrace_gen_headers $(MFLAGS-adjusted) GENERATED=$(GENERATED) @@ -149,7 +144,7 @@ realclean: rm -fr $(GENERATED) .PHONY: default vm_build_preliminaries -.PHONY: lists ad_stuff jvmti_stuff sa_stuff the_vm clean realclean +.PHONY: lists ad_stuff jvmti_stuff the_vm clean realclean .PHONY: checks check_os_version install .NOTPARALLEL: diff --git a/hotspot/make/linux/makefiles/vm.make b/hotspot/make/linux/makefiles/vm.make index 6f1cbe4e035..6c52118e37f 100644 --- a/hotspot/make/linux/makefiles/vm.make +++ b/hotspot/make/linux/makefiles/vm.make @@ -62,7 +62,7 @@ Src_Dirs_I += $(GENERATED) # The order is important for the precompiled headers to work. INCLUDES += $(PRECOMPILED_HEADER_DIR:%=-I%) $(Src_Dirs_I:%=-I%) -# SYMFLAG is used by {jsig,saproc}.make +# SYMFLAG is used by jsig.make ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) # always build with debug info when we can create .debuginfo files SYMFLAG = -g @@ -396,13 +396,10 @@ install_jvm: $(LIBJVM) # Signal interposition library include $(MAKEFILES_DIR)/jsig.make -# Serviceability agent -include $(MAKEFILES_DIR)/saproc.make - #---------------------------------------------------------------------- -build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) $(LIBJVM_DB) $(BUILDLIBSAPROC) dtraceCheck +build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) $(LIBJVM_DB) dtraceCheck -install: install_jvm install_jsig install_saproc +install: install_jvm install_jsig .PHONY: default build install install_jvm $(HS_ALT_MAKE)/$(Platform_os_family)/makefiles/$(BUILDARCH).make $(HS_ALT_MAKE)/$(Platform_os_family)/makefiles/vm.make diff --git a/hotspot/make/linux/makefiles/zeroshark.make b/hotspot/make/linux/makefiles/zeroshark.make index 3c10770d42a..240946fee3a 100644 --- a/hotspot/make/linux/makefiles/zeroshark.make +++ b/hotspot/make/linux/makefiles/zeroshark.make @@ -1,5 +1,5 @@ # -# Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. # Copyright 2007, 2008 Red Hat, Inc. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # @@ -25,8 +25,16 @@ # Setup common to Zero (non-Shark) and Shark versions of VM -# override this from the main file because some version of llvm do not like -Wundef -WARNING_FLAGS = -Wpointer-arith -Wsign-compare -Wunused-function -Wunused-value +# Some versions of llvm do not like -Wundef +ifeq ($(USE_CLANG), true) + WARNING_FLAGS += -Wno-undef +endif +# Suppress some warning flags that are normally turned on for hotspot, +# because some of the zero code has not been updated accordingly. +WARNING_FLAGS += -Wno-return-type \ + -Wno-format-nonliteral -Wno-format-security \ + -Wno-maybe-uninitialized + # The copied fdlibm routines in sharedRuntimeTrig.o must not be optimized OPT_CFLAGS/sharedRuntimeTrig.o = $(OPT_CFLAGS/NOOPT) @@ -42,5 +50,3 @@ endif ifeq ($(ARCH_DATA_MODEL), 64) CFLAGS += -D_LP64=1 endif - -OPT_CFLAGS/compactingPermGenGen.o = -O1 diff --git a/hotspot/make/sa.files b/hotspot/make/sa.files deleted file mode 100644 index 7fb15bb29b8..00000000000 --- a/hotspot/make/sa.files +++ /dev/null @@ -1,133 +0,0 @@ -# -# Copyright (c) 2003, 2015, 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. -# -# - -# This filelist macro is included in platform specific sa.make -# included all packages/*.java. package list can be generated by -# $(GAMMADIR)/agent/make/build-pkglist. - -# define AGENT_DIR before including this file in sa.make - -AGENT_SRC_DIR = $(AGENT_DIR)/src/share/classes - -# Splitted the set of files into two sets because on linux plaform -# listing or compiling all the files results in 'Argument list too long' error. - -AGENT_FILES = \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/asm/sparc/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/c1/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/ci/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/classfile/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/code/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/compiler/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/amd64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/aarch64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/bsd/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/bsd/amd64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/bsd/x86/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/cdbg/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/cdbg/basic/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/dummy/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/ia64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/amd64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/ia64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/ppc64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/aarch64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/x86/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/linux/sparc/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/posix/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/posix/elf/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/ppc64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/amd64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/ppc64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/aarch64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/sparc/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/proc/x86/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/amd64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/ppc64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/sparc/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/x86/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/remote/aarch64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/sparc/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/win32/coff/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windbg/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windbg/amd64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windbg/ia64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windbg/x86/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windows/x86/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/windows/amd64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/debugger/x86/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/gc/cms/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/gc/g1/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/gc/parallel/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/gc/serial/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/gc/shared/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/interpreter/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/jdi/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/memory/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/oops/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/opto/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/prims/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/amd64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/aarch64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/bsd/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/bsd_amd64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/bsd_x86/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_amd64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_aarch64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_x86/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_sparc/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/linux_ppc64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/posix/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/solaris_amd64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/solaris_sparc/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/solaris_x86/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/sparc/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/x86/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/win32_amd64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/win32_x86/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/runtime/ppc64/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/tools/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/tools/jcore/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/tools/soql/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/types/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/types/basic/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/utilities/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/utilities/memo/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/utilities/soql/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/ui/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/ui/action/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/ui/classbrowser/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/ui/table/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/ui/tree/*.java \ -$(AGENT_SRC_DIR)/sun/jvm/hotspot/ui/treetable/*.java \ -$(AGENT_SRC_DIR)/com/sun/java/swing/action/*.java \ -$(AGENT_SRC_DIR)/com/sun/java/swing/ui/*.java diff --git a/hotspot/make/share/makefiles/mapfile-vers b/hotspot/make/share/makefiles/mapfile-vers index 57b5ad37c41..9672bfe3fc9 100644 --- a/hotspot/make/share/makefiles/mapfile-vers +++ b/hotspot/make/share/makefiles/mapfile-vers @@ -7,6 +7,7 @@ JVM_ActiveProcessorCount; JVM_ArrayCopy; JVM_AssertionStatusDirectives; + JVM_CallStackWalk; JVM_ClassDepth; JVM_ClassLoaderDepth; JVM_Clone; @@ -31,12 +32,12 @@ JVM_CurrentTimeMillis; JVM_DefineClass; JVM_DefineClassWithSource; - JVM_DefineClassWithSourceCond; JVM_DesiredAssertionStatus; JVM_DoPrivileged; JVM_DumpAllStacks; JVM_DumpThreads; JVM_FillInStackTrace; + JVM_FillStackFrames; JVM_FindClassFromCaller; JVM_FindClassFromClass; JVM_FindClassFromBootLoader; @@ -134,6 +135,7 @@ JVM_MonitorNotify; JVM_MonitorNotifyAll; JVM_MonitorWait; + JVM_MoreStackWalk; JVM_NanoTime; JVM_NativePath; JVM_NewArray; @@ -151,6 +153,7 @@ JVM_SetClassSigners; JVM_SetNativeThreadName; JVM_SetPrimitiveArrayElement; + JVM_SetMethodInfo; JVM_SetThreadPriority; JVM_Sleep; JVM_StartThread; diff --git a/hotspot/make/solaris/Makefile b/hotspot/make/solaris/Makefile index f8fb06c8725..eb9bd2ab875 100644 --- a/hotspot/make/solaris/Makefile +++ b/hotspot/make/solaris/Makefile @@ -36,15 +36,6 @@ # or BOOTDIR has to be set. We do *not* search javac, javah, rmic etc. # from the PATH. -# Along with VM, Serviceability Agent (SA) is built for SA/JDI binding. -# JDI binding on SA produces two binaries: -# 1. sa-jdi.jar - This is built before building libjvm.so -# Please refer to ./makefiles/sa.make -# 2. libsaproc.so - Native library for SA - This is built after -# libjsig.so (signal interposition library) -# Please refer to ./makefiles/vm.make -# If $(GAMMADIR)/agent dir is not present, SA components are not built. - ifeq ($(GAMMADIR),) include ../../make/defs.make else diff --git a/hotspot/make/solaris/makefiles/buildtree.make b/hotspot/make/solaris/makefiles/buildtree.make index b1586d41132..dde03b5963f 100644 --- a/hotspot/make/solaris/makefiles/buildtree.make +++ b/hotspot/make/solaris/makefiles/buildtree.make @@ -49,7 +49,6 @@ # adlc.make - # trace.make - generate tracing event and type definitions # jvmti.make - generate JVMTI bindings from the spec (JSR-163) -# sa.make - generate SA jar file and natives # # The makefiles are split this way so that "make foo" will run faster by not # having to read the dependency files for the vm. @@ -117,7 +116,7 @@ SUBMAKE_DIRS = $(addprefix $(PLATFORM_DIR)/,$(TARGETS)) # For dependencies and recursive makes. BUILDTREE_MAKE = $(GAMMADIR)/make/$(OS_FAMILY)/makefiles/buildtree.make -BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make trace.make sa.make dtrace.make +BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make trace.make dtrace.make BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OS_FAMILY) \ ARCH=$(ARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) VARIANT=$(VARIANT) @@ -205,7 +204,6 @@ flags.make: $(BUILDTREE_MAKE) ../shared_dirs.lst echo "TARGET = $(TARGET)"; \ echo "HS_BUILD_VER = $(HS_BUILD_VER)"; \ echo "JRE_RELEASE_VER = $(JRE_RELEASE_VERSION)"; \ - echo "SA_BUILD_VERSION = $(HS_BUILD_VER)"; \ echo "HOTSPOT_BUILD_USER = $(HOTSPOT_BUILD_USER)"; \ echo "HOTSPOT_VM_DISTRO = $(HOTSPOT_VM_DISTRO)"; \ echo "OPENJDK = $(OPENJDK)"; \ @@ -344,16 +342,6 @@ trace.make: $(BUILDTREE_MAKE) echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ ) > $@ -sa.make: $(BUILDTREE_MAKE) - @echo $(LOG_INFO) Creating $@ ... - $(QUIETLY) ( \ - $(BUILDTREE_COMMENT); \ - echo; \ - echo include flags.make; \ - echo; \ - echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ - ) > $@ - dtrace.make: $(BUILDTREE_MAKE) @echo $(LOG_INFO) Creating $@ ... $(QUIETLY) ( \ diff --git a/hotspot/make/solaris/makefiles/defs.make b/hotspot/make/solaris/makefiles/defs.make index 8fa75e904d7..0ef3996d3dc 100644 --- a/hotspot/make/solaris/makefiles/defs.make +++ b/hotspot/make/solaris/makefiles/defs.make @@ -294,13 +294,3 @@ ifeq ($(JVM_VARIANT_CLIENT),true) endif endif endif - -EXPORT_LIST += $(EXPORT_LIB_ARCH_DIR)/libsaproc.$(LIBRARY_SUFFIX) -ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) - ifeq ($(ZIP_DEBUGINFO_FILES),1) - EXPORT_LIST += $(EXPORT_LIB_ARCH_DIR)/libsaproc.diz - else - EXPORT_LIST += $(EXPORT_LIB_ARCH_DIR)/libsaproc.debuginfo - endif -endif -EXPORT_LIST += $(EXPORT_LIB_DIR)/sa-jdi.jar diff --git a/hotspot/make/solaris/makefiles/rules.make b/hotspot/make/solaris/makefiles/rules.make index e809b49f691..a35b9dacd8c 100644 --- a/hotspot/make/solaris/makefiles/rules.make +++ b/hotspot/make/solaris/makefiles/rules.make @@ -95,8 +95,6 @@ BOOT_JAVA_HOME = $(JAVA_HOME) else # take from the PATH, if ALT_BOOTDIR, BOOTDIR and JAVA_HOME are not defined -# note that this is to support hotspot build without SA. To build -# SA along with hotspot, you need to define ALT_BOOTDIR, BOOTDIR or JAVA_HOME RUN.JAVA = java RUN.JAVAP = javap diff --git a/hotspot/make/solaris/makefiles/sa.make b/hotspot/make/solaris/makefiles/sa.make deleted file mode 100644 index c2b9331d57d..00000000000 --- a/hotspot/make/solaris/makefiles/sa.make +++ /dev/null @@ -1,104 +0,0 @@ -# -# Copyright (c) 2003, 2015, 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. -# -# - -# This makefile (sa.make) is included from the sa.make in the -# build directories. - -# This makefile is used to build Serviceability Agent java code -# and generate JNI header file for native methods. - -include $(GAMMADIR)/make/solaris/makefiles/rules.make -include $(GAMMADIR)/make/defs.make -AGENT_DIR = $(GAMMADIR)/agent -include $(GAMMADIR)/make/sa.files - --include $(HS_ALT_MAKE)/solaris/makefiles/sa.make - -GENERATED = ../generated - -# tools.jar is needed by the JDI - SA binding -SA_CLASSPATH = $(BOOT_JAVA_HOME)/lib/tools.jar - -# TODO: if it's a modules image, check if SA module is installed. -MODULELIB_PATH= $(BOOT_JAVA_HOME)/lib/modules - -AGENT_FILES_LIST := $(GENERATED)/agent.classes.list - -SA_CLASSDIR = $(GENERATED)/saclasses - -SA_BUILD_VERSION_PROP = "sun.jvm.hotspot.runtime.VM.saBuildVersion=$(SA_BUILD_VERSION)" - -SA_PROPERTIES = $(SA_CLASSDIR)/sa.properties - -# if $(AGENT_DIR) does not exist, we don't build SA. -all: - $(QUIETLY) if [ -d $(AGENT_DIR) ] ; then \ - $(MAKE) -f sa.make $(GENERATED)/sa-jdi.jar; \ - fi - -$(GENERATED)/sa-jdi.jar: $(AGENT_FILES) - $(QUIETLY) echo $(LOG_INFO) "Making $@"; - $(QUIETLY) if [ "$(BOOT_JAVA_HOME)" = "" ]; then \ - echo "ALT_BOOTDIR, BOOTDIR or JAVA_HOME needs to be defined to build SA"; \ - exit 1; \ - fi - $(QUIETLY) if [ ! -f $(SA_CLASSPATH) -a ! -d $(MODULELIB_PATH) ] ; then \ - echo "Missing $(SA_CLASSPATH) file. Use 1.6.0 or later version of JDK";\ - echo ""; \ - exit 1; \ - fi - $(QUIETLY) if [ ! -d $(SA_CLASSDIR) ] ; then \ - mkdir -p $(SA_CLASSDIR); \ - fi -# Note: When indented, make tries to execute the '$(shell' comment. -# In some environments, cmd processors have limited line length. -# To prevent the javac invocation in the next block from using -# a very long cmd line, we use javac's @file-list option. We -# generate the file lists using make's built-in 'foreach' control -# flow which also avoids cmd processor line length issues. Since -# the 'foreach' is done as part of make's macro expansion phase, -# the initialization of the lists is also done in the same phase -# using '$(shell rm ...' instead of using the more traditional -# 'rm ...' rule. - $(shell rm -rf $(AGENT_FILES_LIST)) -# gnumake 3.78.1 does not accept the *'s that -# are in AGENT_FILES, so use the shell to expand them. -# Be extra carefull to not produce too long command lines in the shell! - $(foreach file,$(AGENT_FILES),$(shell ls -1 $(file) >> $(AGENT_FILES_LIST))) - $(QUIETLY) $(COMPILE.JAVAC) -h $(GENERATED) -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -d $(SA_CLASSDIR) @$(AGENT_FILES_LIST) - $(QUIETLY) $(COMPILE.RMIC) -classpath $(SA_CLASSDIR) -d $(SA_CLASSDIR) sun.jvm.hotspot.debugger.remote.RemoteDebuggerServer - $(QUIETLY) echo "$(SA_BUILD_VERSION_PROP)" > $(SA_PROPERTIES) - $(QUIETLY) rm -f $(SA_CLASSDIR)/sun/jvm/hotspot/utilities/soql/sa.js - $(QUIETLY) $(CP) $(AGENT_SRC_DIR)/sun/jvm/hotspot/utilities/soql/sa.js $(SA_CLASSDIR)/sun/jvm/hotspot/utilities/soql - $(QUIETLY) mkdir -p $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources - $(QUIETLY) rm -f $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources/* - $(QUIETLY) $(CP) $(AGENT_SRC_DIR)/sun/jvm/hotspot/ui/resources/*.png $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources/ - $(QUIETLY) $(CP) -r $(AGENT_SRC_DIR)/images/* $(SA_CLASSDIR)/ - $(QUIETLY) $(RUN.JAR) cf $@ -C $(SA_CLASSDIR)/ . - $(QUIETLY) $(RUN.JAR) uf $@ -C $(AGENT_SRC_DIR) META-INF/services/com.sun.jdi.connect.Connector - -clean: - rm -rf $(SA_CLASSDIR) - rm -rf $(GENERATED)/sa-jdi.jar - rm -rf $(AGENT_FILES_LIST) diff --git a/hotspot/make/solaris/makefiles/saproc.make b/hotspot/make/solaris/makefiles/saproc.make deleted file mode 100644 index 3daecc4f20c..00000000000 --- a/hotspot/make/solaris/makefiles/saproc.make +++ /dev/null @@ -1,151 +0,0 @@ -# -# Copyright (c) 2005, 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. -# -# - -# Rules to build serviceability agent library, used by vm.make - -# libsaproc.so: serviceability agent - -SAPROC = saproc -SADIS = sadis -LIBSAPROC = lib$(SAPROC).so -SADISOBJ = $(SADIS).o - -LIBSAPROC_DEBUGINFO = lib$(SAPROC).debuginfo -LIBSAPROC_DIZ = lib$(SAPROC).diz - -AGENT_DIR = $(GAMMADIR)/agent - -SASRCDIR = $(AGENT_DIR)/src/os/$(Platform_os_family)/proc - -SASRCFILES = $(SASRCDIR)/saproc.cpp - -SADISSRCFILES = $(AGENT_DIR)/src/share/native/sadis.c - -SAMAPFILE = $(SASRCDIR)/mapfile - -DEST_SAPROC = $(JDK_LIBDIR)/$(LIBSAPROC) -DEST_SAPROC_DEBUGINFO = $(JDK_LIBDIR)/$(LIBSAPROC_DEBUGINFO) -DEST_SAPROC_DIZ = $(JDK_LIBDIR)/$(LIBSAPROC_DIZ) - -# if $(AGENT_DIR) does not exist, we don't build SA - -ifneq ($(wildcard $(AGENT_DIR)),) - BUILDLIBSAPROC = $(LIBSAPROC) -endif - -SA_LFLAGS = $(MAPFLAG:FILENAME=$(SAMAPFILE)) - -ifdef USE_GCC -SA_LFLAGS += -D_REENTRANT -else -SA_LFLAGS += -mt -xnolib -norunpath -endif - -# The libproc Pstack_iter() interface changed in Nevada-B159. -# Use 'uname -r -v' to determine the Solaris version as per -# Solaris Nevada team request. This logic needs to match: -# agent/src/os/solaris/proc/saproc.cpp: set_has_newer_Pstack_iter(): -# - skip SunOS 4 or older -# - skip Solaris 10 or older -# - skip two digit internal Nevada builds -# - skip three digit internal Nevada builds thru 149 -# - skip internal Nevada builds 150-158 -# - if not skipped, print define for Nevada-B159 or later -SOLARIS_11_B159_OR_LATER := \ -$(shell uname -r -v \ - | sed -n \ - -e '/^[0-4]\. /b' \ - -e '/^5\.[0-9] /b' \ - -e '/^5\.10 /b' \ - -e '/ snv_[0-9][0-9]$$/b' \ - -e '/ snv_[01][0-4][0-9]$$/b' \ - -e '/ snv_15[0-8]$$/b' \ - -e 's/.*/-DSOLARIS_11_B159_OR_LATER/' \ - -e 'p' \ - ) - -# Uncomment the following to simulate building on Nevada-B159 or later -# when actually building on Nevada-B158 or earlier: -#SOLARIS_11_B159_OR_LATER=-DSOLARIS_11_B159_OR_LATER - -$(SADISOBJ): $(SADISSRCFILES) - $(QUIETLY) $(CC) \ - $(SYMFLAG) $(ARCHFLAG) $(SHARED_FLAG) $(PICFLAG) \ - -I$(SASRCDIR) \ - -I$(GENERATED) \ - -I$(BOOT_JAVA_HOME)/include \ - -I$(BOOT_JAVA_HOME)/include/$(Platform_os_family) \ - $(SOLARIS_11_B159_OR_LATER) \ - $(EXTRA_CFLAGS) \ - $(SADISSRCFILES) \ - -c -o $(SADISOBJ) - -$(LIBSAPROC): $(SASRCFILES) $(SADISOBJ) $(SAMAPFILE) - $(QUIETLY) if [ "$(BOOT_JAVA_HOME)" = "" ]; then \ - echo "ALT_BOOTDIR, BOOTDIR or JAVA_HOME needs to be defined to build SA"; \ - exit 1; \ - fi - @echo $(LOG_INFO) Making SA debugger back-end... - $(QUIETLY) $(CXX) \ - $(SYMFLAG) $(ARCHFLAG) $(SHARED_FLAG) $(PICFLAG) \ - -I$(SASRCDIR) \ - -I$(GENERATED) \ - -I$(BOOT_JAVA_HOME)/include \ - -I$(BOOT_JAVA_HOME)/include/$(Platform_os_family) \ - $(SOLARIS_11_B159_OR_LATER) \ - $(EXTRA_CXXFLAGS) $(EXTRA_LDFLAGS) \ - $(SADISOBJ) \ - $(SASRCFILES) \ - $(SA_LFLAGS) \ - -o $@ \ - -ldl -ldemangle -lthread -lc - -ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) - $(QUIETLY) $(OBJCOPY) --only-keep-debug $@ $(LIBSAPROC_DEBUGINFO) - $(QUIETLY) $(OBJCOPY) --add-gnu-debuglink=$(LIBSAPROC_DEBUGINFO) $@ - ifeq ($(STRIP_POLICY),all_strip) - $(QUIETLY) $(STRIP) $@ - else - ifeq ($(STRIP_POLICY),min_strip) - $(QUIETLY) $(STRIP) -x $@ - # implied else here is no stripping at all - endif - endif - ifeq ($(ZIP_DEBUGINFO_FILES),1) - $(ZIPEXE) -q -y $(LIBSAPROC_DIZ) $(LIBSAPROC_DEBUGINFO) - $(RM) $(LIBSAPROC_DEBUGINFO) - endif -endif - -install_saproc: $(BULDLIBSAPROC) - $(QUIETLY) if [ -f $(LIBSAPROC) ] ; then \ - echo "Copying $(LIBSAPROC) to $(DEST_SAPROC)"; \ - test ! -f $(LIBSAPROC_DEBUGINFO) || \ - $(CP) -f $(LIBSAPROC_DEBUGINFO) $(DEST_SAPROC_DEBUGINFO); \ - test ! -f $(LIBSAPROC_DIZ) || \ - $(CP) -f $(LIBSAPROC_DIZ) $(DEST_SAPROC_DIZ); \ - $(CP) -f $(LIBSAPROC) $(DEST_SAPROC) && echo "Done"; \ - fi - -.PHONY: install_saproc diff --git a/hotspot/make/solaris/makefiles/top.make b/hotspot/make/solaris/makefiles/top.make index 9b3bfa3ddbc..0786477af79 100644 --- a/hotspot/make/solaris/makefiles/top.make +++ b/hotspot/make/solaris/makefiles/top.make @@ -28,7 +28,6 @@ # It also: # -builds and runs adlc via adlc.make # -generates JVMTI source and docs via jvmti.make (JSR-163) -# -generate sa-jdi.jar (JDI binding to core files) # It assumes the following flags are set: # CFLAGS Platform_file, Src_Dirs_I, Src_Dirs_V, SYSDEFS, AOUT, Jvm_Obj_Files @@ -79,7 +78,7 @@ default: vm_build_preliminaries the_vm @echo All done. # This is an explicit dependency for the sake of parallel makes. -vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) jvmti_stuff trace_stuff sa_stuff dtrace_stuff +vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) jvmti_stuff trace_stuff dtrace_stuff @# We need a null action here, so implicit rules don't get consulted. $(Cached_plat): $(Plat_File) @@ -97,10 +96,6 @@ jvmti_stuff: $(Cached_plat) $(adjust-mflags) trace_stuff: jvmti_stuff $(Cached_plat) $(adjust-mflags) @$(MAKE) -f trace.make $(MFLAGS-adjusted) -# generate SA jar files and native header -sa_stuff: - @$(MAKE) -f sa.make $(MFLAGS-adjusted) - dtrace_stuff: $(Cached_plat) $(adjust-mflags) @$(MAKE) -f dtrace.make dtrace_gen_headers $(MFLAGS-adjusted) GENERATED=$(GENERATED) @@ -140,7 +135,7 @@ realclean: rm -fr $(GENERATED) .PHONY: default vm_build_preliminaries -.PHONY: lists ad_stuff jvmti_stuff trace_stuff sa_stuff the_vm clean realclean +.PHONY: lists ad_stuff jvmti_stuff trace_stuff the_vm clean realclean .PHONY: checks check_os_version install .NOTPARALLEL: diff --git a/hotspot/make/solaris/makefiles/vm.make b/hotspot/make/solaris/makefiles/vm.make index 799a3dbee69..875d176cc2a 100644 --- a/hotspot/make/solaris/makefiles/vm.make +++ b/hotspot/make/solaris/makefiles/vm.make @@ -55,7 +55,7 @@ VPATH += $(Src_Dirs_V:%=%:) Src_Dirs_I += $(GENERATED) INCLUDES += $(Src_Dirs_I:%=-I%) -# SYMFLAG is used by {dtrace,jsig,saproc}.make. +# SYMFLAG is used by {dtrace,jsig}.make. ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) # always build with debug info when we can create .debuginfo files # and disable 'lazy debug info' so the .so has everything. @@ -341,13 +341,10 @@ install_jvm: $(LIBJVM) # Signal interposition library include $(MAKEFILES_DIR)/jsig.make -# Serviceability agent -include $(MAKEFILES_DIR)/saproc.make - #---------------------------------------------------------------------- -build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) $(LIBJVM_DB) $(LIBJVM_DTRACE) $(BUILDLIBSAPROC) dtraceCheck +build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) $(LIBJVM_DB) $(LIBJVM_DTRACE) dtraceCheck -install: install_jvm install_jsig install_saproc +install: install_jvm install_jsig .PHONY: default build install install_jvm diff --git a/hotspot/make/test/JtregNative.gmk b/hotspot/make/test/JtregNative.gmk index b9d1dfa5894..09c48d77aba 100644 --- a/hotspot/make/test/JtregNative.gmk +++ b/hotspot/make/test/JtregNative.gmk @@ -48,6 +48,17 @@ BUILD_HOTSPOT_JTREG_NATIVE_SRC := \ $(HOTSPOT_TOPDIR)/test/runtime/SameObject \ # +# Add conditional directories here when needed. +ifeq ($(OPENJDK_TARGET_OS)-$(OPENJDK_TARGET_CPU_ARCH), solaris-sparc) +BUILD_HOTSPOT_JTREG_NATIVE_SRC += \ + $(HOTSPOT_TOPDIR)/test/runtime/libadimalloc.solaris.sparc \ + $(HOTSPOT_TOPDIR)/test/runtime/ThreadSignalMask +endif + +ifeq ($(TOOLCHAIN_TYPE), solstudio) + BUILD_HOTSPOT_JTREG_LIBRARIES_LDFLAGS_liboverflow := -lc +endif + BUILD_HOTSPOT_JTREG_OUTPUT_DIR := $(BUILD_OUTPUT)/support/test/hotspot/jtreg/native BUILD_HOTSPOT_JTREG_IMAGE_DIR := $(TEST_IMAGE_DIR)/hotspot/jtreg diff --git a/hotspot/make/windows/build.make b/hotspot/make/windows/build.make index cd4ae4fa938..564675333f1 100644 --- a/hotspot/make/windows/build.make +++ b/hotspot/make/windows/build.make @@ -30,14 +30,6 @@ # Note: Running nmake or build.bat from the Windows command shell requires # that "sh" be accessible on the PATH. An MKS install does this. -# SA components are built if BUILD_WIN_SA=1 is specified. -# See notes in README. This produces files: -# 1. sa-jdi.jar - This is built before building jvm.dll -# 2. sawindbg.dll - Native library for SA - This is built after jvm.dll -# - Also, .lib, .map, .pdb. -# -# Please refer to ./makefiles/sa.make - # If we haven't set an ARCH yet use x86 # create.bat and build.bat will set it, if used. !ifndef ARCH @@ -202,30 +194,6 @@ OPENJDK=true !endif !endif -# We don't support SA on ia64, and we can't -# build it if we are using a version of Vis Studio -# older than .Net 2003. -# SA_INCLUDE and SA_LIB are hold-overs from a previous -# implementation in which we could build SA using -# Debugging Tools For Windows, in which the .h/.lib files -# and the .dlls are in different places than -# they are for Vis Studio .Net 2003. -# If that code ever needs to be resurrected, these vars -# can be set here. They are used in makefiles/sa.make. - -checkSA:: - -!if "$(BUILD_WIN_SA)" != "1" -checkSA:: - @echo Not building SA: BUILD_WIN_SA != 1 - -!elseif "$(ARCH)" == "ia64" -BUILD_WIN_SA = 0 -checkSA:: - @echo Not building SA: ARCH = ia64 - -!endif # ! "$(BUILD_WIN_SA)" != "1" - ######################################################################### defaultTarget: product @@ -282,10 +250,6 @@ $(variantDir)\local.make: checks @ echo HS_COPYRIGHT=$(HOTSPOT_VM_COPYRIGHT) >> $@ @ echo HS_NAME=$(PRODUCT_NAME) $(JDK_MKTG_VERSION) >> $@ @ echo HS_BUILD_VER=$(HS_BUILD_VER) >> $@ - @ echo BUILD_WIN_SA=$(BUILD_WIN_SA) >> $@ - @ echo SA_BUILD_VERSION=$(HS_BUILD_VER) >> $@ - @ echo SA_INCLUDE=$(SA_INCLUDE) >> $@ - @ echo SA_LIB=$(SA_LIB) >> $@ @ echo JDK_VER=$(JDK_VER) >> $@ @ echo JDK_DOTVER=$(JDK_DOTVER) >> $@ @ echo JRE_RELEASE_VER=$(JRE_RELEASE_VER) >> $@ @@ -304,7 +268,7 @@ $(variantDir)\local.make: checks @ if "$(MV)" NEQ "" echo MV=$(MV) >> $@ @ if "$(ZIPEXE)" NEQ "" echo ZIPEXE=$(ZIPEXE) >> $@ -checks: checkVariant checkWorkSpace checkSA +checks: checkVariant checkWorkSpace checkVariant: @ if "$(Variant)"=="" echo Need to specify "Variant=[tiered|compiler2|compiler1|core]" && false diff --git a/hotspot/make/windows/build_vm_def.sh b/hotspot/make/windows/build_vm_def.sh index 05298eaa3f3..7224970058a 100644 --- a/hotspot/make/windows/build_vm_def.sh +++ b/hotspot/make/windows/build_vm_def.sh @@ -54,20 +54,6 @@ RM="$MKS_HOME/rm.exe" DUMPBIN="link.exe /dump" export VS_UNICODE_OUTPUT= -if [ "$1" = "-nosa" ]; then - echo EXPORTS > vm.def - echo "" - echo "***" - echo "*** Not building SA: BUILD_WIN_SA != 1" - echo "*** C++ Vtables NOT included in vm.def" - echo "*** This jvm.dll will NOT work properly with SA." - echo "***" - echo "*** When in doubt, set BUILD_WIN_SA=1, clean and rebuild." - echo "***" - echo "" - exit -fi - echo "EXPORTS" > vm1.def # When called from IDE the first param should contain the link version, otherwise may be nill diff --git a/hotspot/make/windows/makefiles/debug.make b/hotspot/make/windows/makefiles/debug.make index f4e54fc52fb..024881bfe43 100644 --- a/hotspot/make/windows/makefiles/debug.make +++ b/hotspot/make/windows/makefiles/debug.make @@ -19,13 +19,12 @@ # 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. -# +# # HS_INTERNAL_NAME=jvm HS_FNAME=$(HS_INTERNAL_NAME).dll AOUT=$(HS_FNAME) -SAWINDBG=sawindbg.dll GENERATED=../generated # Allow the user to turn off precompiled headers from the command line. @@ -33,7 +32,7 @@ GENERATED=../generated BUILD_PCH_FILE=_build_pch_file.obj !endif -default:: $(BUILD_PCH_FILE) $(AOUT) checkAndBuildSA +default:: $(BUILD_PCH_FILE) $(AOUT) !include ../local.make !include compile.make @@ -67,4 +66,3 @@ $(AOUT): $(Res_Files) $(Obj_Files) vm.def !endif !include $(WorkSpace)/make/windows/makefiles/shared.make -!include $(WorkSpace)/make/windows/makefiles/sa.make diff --git a/hotspot/make/windows/makefiles/defs.make b/hotspot/make/windows/makefiles/defs.make index 5deb916e49c..86c7a32e808 100644 --- a/hotspot/make/windows/makefiles/defs.make +++ b/hotspot/make/windows/makefiles/defs.make @@ -236,21 +236,6 @@ else ifeq ($(USING_MINGW), true) ABS_OS_MAKEFILE := $(subst /,\\,$(shell $(CD) $(HS_MAKE_DIR)/$(OSNAME);$(PWD))/build.make) endif -# Disable building SA on windows until we are sure -# we want to release it. If we build it here, -# the SDK makefiles will copy it over and put it into -# the created image. -BUILD_WIN_SA = 1 -ifneq ($(ALT_BUILD_WIN_SA),) - BUILD_WIN_SA = $(ALT_BUILD_WIN_SA) -endif - -ifeq ($(BUILD_WIN_SA), 1) - ifeq ($(ARCH),ia64) - BUILD_WIN_SA = 0 - endif -endif - EXPORT_SERVER_DIR = $(EXPORT_BIN_DIR)/server EXPORT_CLIENT_DIR = $(EXPORT_BIN_DIR)/client @@ -281,21 +266,6 @@ endif EXPORT_LIST += $(EXPORT_LIB_DIR)/jvm.lib -ifeq ($(BUILD_WIN_SA), 1) - EXPORT_LIST += $(EXPORT_BIN_DIR)/sawindbg.$(LIBRARY_SUFFIX) - ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) - ifeq ($(ZIP_DEBUGINFO_FILES),1) - EXPORT_LIST += $(EXPORT_BIN_DIR)/sawindbg.diz - else - EXPORT_LIST += $(EXPORT_BIN_DIR)/sawindbg.pdb - EXPORT_LIST += $(EXPORT_BIN_DIR)/sawindbg.map - endif - endif - EXPORT_LIST += $(EXPORT_LIB_DIR)/sa-jdi.jar - # Must pass this down to nmake. - MAKE_ARGS += BUILD_WIN_SA=1 -endif - # Propagate compiler and tools paths from configure to nmake. # Need to make sure they contain \\ and not /. ifneq ($(SPEC),) diff --git a/hotspot/make/windows/makefiles/fastdebug.make b/hotspot/make/windows/makefiles/fastdebug.make index e3138d0cb2f..34353b39271 100644 --- a/hotspot/make/windows/makefiles/fastdebug.make +++ b/hotspot/make/windows/makefiles/fastdebug.make @@ -19,13 +19,12 @@ # 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. -# +# # HS_INTERNAL_NAME=jvm HS_FNAME=$(HS_INTERNAL_NAME).dll AOUT=$(HS_FNAME) -SAWINDBG=sawindbg.dll GENERATED=../generated # Allow the user to turn off precompiled headers from the command line. @@ -33,7 +32,7 @@ GENERATED=../generated BUILD_PCH_FILE=_build_pch_file.obj !endif -default:: $(BUILD_PCH_FILE) $(AOUT) checkAndBuildSA +default:: $(BUILD_PCH_FILE) $(AOUT) !include ../local.make !include compile.make @@ -66,4 +65,3 @@ $(AOUT): $(Res_Files) $(Obj_Files) vm.def !endif !include $(WorkSpace)/make/windows/makefiles/shared.make -!include $(WorkSpace)/make/windows/makefiles/sa.make diff --git a/hotspot/make/windows/makefiles/generated.make b/hotspot/make/windows/makefiles/generated.make index aaaa8e7c2b6..e376c7499cb 100644 --- a/hotspot/make/windows/makefiles/generated.make +++ b/hotspot/make/windows/makefiles/generated.make @@ -19,7 +19,7 @@ # 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. -# +# # !include ../local.make @@ -34,9 +34,6 @@ JvmtiOutDir=jvmtifiles TraceOutDir=tracefiles !include $(WorkSpace)/make/windows/makefiles/trace.make -# Pick up rules for building SA -!include $(WorkSpace)/make/windows/makefiles/sa.make - AdlcOutDir=adfiles !if ("$(Variant)" == "compiler2") || ("$(Variant)" == "tiered") diff --git a/hotspot/make/windows/makefiles/product.make b/hotspot/make/windows/makefiles/product.make index c1ce35a163c..667101b0f21 100644 --- a/hotspot/make/windows/makefiles/product.make +++ b/hotspot/make/windows/makefiles/product.make @@ -19,7 +19,7 @@ # 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. -# +# # HS_INTERNAL_NAME=jvm @@ -32,7 +32,7 @@ GENERATED=../generated BUILD_PCH_FILE=_build_pch_file.obj !endif -default:: $(BUILD_PCH_FILE) $(AOUT) checkAndBuildSA +default:: $(BUILD_PCH_FILE) $(AOUT) !include ../local.make !include compile.make @@ -69,4 +69,3 @@ $(AOUT): $(Res_Files) $(Obj_Files) vm.def !endif !include $(WorkSpace)/make/windows/makefiles/shared.make -!include $(WorkSpace)/make/windows/makefiles/sa.make diff --git a/hotspot/make/windows/makefiles/projectcreator.make b/hotspot/make/windows/makefiles/projectcreator.make index 180851d835e..97f04611464 100644 --- a/hotspot/make/windows/makefiles/projectcreator.make +++ b/hotspot/make/windows/makefiles/projectcreator.make @@ -90,10 +90,6 @@ ProjectCreatorIDEOptions = \ -disablePch getThread_windows_$(Platform_arch).cpp \ -disablePch_compiler2 opcodes.cpp -!if "$(BUILD_WIN_SA)" != "1" -BUILD_VM_DEF_FLAG=-nosa -!endif - # Common options for the IDE builds for c1, and c2 ProjectCreatorIDEOptions=\ $(ProjectCreatorIDEOptions) \ @@ -106,7 +102,7 @@ ProjectCreatorIDEOptions=\ -jdkTargetRoot $(HOTSPOTJDKDIST) \ -define ALIGN_STACK_FRAMES \ -define VM_LITTLE_ENDIAN \ - -prelink "" "Generating vm.def..." "cd $(HOTSPOTBUILDSPACE)\%f\%b set HOTSPOTMKSHOME=$(HOTSPOTMKSHOME) set JAVA_HOME=$(HOTSPOTJDKDIST) $(HOTSPOTMKSHOME)\sh $(HOTSPOTWORKSPACE)\make\windows\build_vm_def.sh $(BUILD_VM_DEF_FLAG) $(LD_VER)" \ + -prelink "" "Generating vm.def..." "cd $(HOTSPOTBUILDSPACE)\%f\%b set HOTSPOTMKSHOME=$(HOTSPOTMKSHOME) set JAVA_HOME=$(HOTSPOTJDKDIST) $(HOTSPOTMKSHOME)\sh $(HOTSPOTWORKSPACE)\make\windows\build_vm_def.sh $(LD_VER)" \ -ignoreFile jsig.c \ -ignoreFile jvmtiEnvRecommended.cpp \ -ignoreFile jvmtiEnvStub.cpp \ diff --git a/hotspot/make/windows/makefiles/sa.make b/hotspot/make/windows/makefiles/sa.make deleted file mode 100644 index 2e41a022534..00000000000 --- a/hotspot/make/windows/makefiles/sa.make +++ /dev/null @@ -1,165 +0,0 @@ -# -# Copyright (c) 2003, 2015, 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. -# -# - -# This makefile is used to build Serviceability Agent code -# and generate JNI header file for native methods. - -AGENT_DIR = $(WorkSpace)/agent -checkAndBuildSA:: - -!if "$(BUILD_WIN_SA)" != "1" -# Already warned about this in build.make -!else - -# This first part is used to build sa-jdi.jar -!include $(WorkSpace)/make/windows/makefiles/rules.make -!include $(WorkSpace)/make/sa.files - -GENERATED = ../generated - -HS_COMMON_SRC_REL = src - -!if "$(OPENJDK)" != "true" -HS_ALT_SRC_REL=src/closed -HS_ALT_SRC = $(WorkSpace)/$(HS_ALT_SRC_REL) -!ifndef HS_ALT_MAKE -HS_ALT_MAKE=$(WorkSpace)/make/closed -!endif -!endif - -HS_COMMON_SRC = $(WorkSpace)/$(HS_COMMON_SRC_REL) - -!ifdef HS_ALT_MAKE -!include $(HS_ALT_MAKE)/windows/makefiles/sa.make -!endif - -# tools.jar is needed by the JDI - SA binding -SA_CLASSPATH = $(BOOT_JAVA_HOME)/lib/tools.jar - -SA_CLASSDIR = $(GENERATED)/saclasses - -SA_BUILD_VERSION_PROP = sun.jvm.hotspot.runtime.VM.saBuildVersion=$(SA_BUILD_VERSION) - -SA_PROPERTIES = $(SA_CLASSDIR)/sa.properties - -default:: $(GENERATED)/sa-jdi.jar - -# Remove the space between $(SA_BUILD_VERSION_PROP) and > below as it adds a white space -# at the end of SA version string and causes a version mismatch with the target VM version. - -$(GENERATED)/sa-jdi.jar: $(AGENT_FILES) - $(QUIETLY) mkdir -p $(SA_CLASSDIR) - @echo ...Building sa-jdi.jar into $(SA_CLASSDIR) - @echo ...$(COMPILE_JAVAC) -classpath $(SA_CLASSPATH) -d $(SA_CLASSDIR) .... - @$(COMPILE_JAVAC) -h $(GENERATED) -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -d $(SA_CLASSDIR) $(AGENT_FILES) - $(COMPILE_RMIC) -classpath $(SA_CLASSDIR) -d $(SA_CLASSDIR) sun.jvm.hotspot.debugger.remote.RemoteDebuggerServer - $(QUIETLY) echo $(SA_BUILD_VERSION_PROP)> $(SA_PROPERTIES) - $(QUIETLY) rm -f $(SA_CLASSDIR)/sun/jvm/hotspot/utilities/soql/sa.js - $(QUIETLY) $(CP) $(AGENT_SRC_DIR)/sun/jvm/hotspot/utilities/soql/sa.js $(SA_CLASSDIR)/sun/jvm/hotspot/utilities/soql - $(QUIETLY) rm -rf $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources - $(QUIETLY) mkdir $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources - $(QUIETLY) $(CP) $(AGENT_SRC_DIR)/sun/jvm/hotspot/ui/resources/*.png $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources - $(QUIETLY) $(CP) -r $(AGENT_SRC_DIR)/images/* $(SA_CLASSDIR) - $(RUN_JAR) cf $@ -C $(SA_CLASSDIR) . - $(RUN_JAR) uf $@ -C $(AGENT_SRC_DIR) META-INF/services/com.sun.jdi.connect.Connector - - - -# This second part is used to build sawindbg.dll -# We currently build it the same way for product, debug, and fastdebug. - -SAWINDBG=sawindbg.dll - -# Resource file containing VERSIONINFO -SA_Res_Files=.\version.sares - -checkAndBuildSA:: $(SAWINDBG) - -# These do not need to be optimized (don't run a lot of code) and it -# will be useful to have the assertion checks in place - -!if "$(BUILDARCH)" == "ia64" -SA_CFLAGS = -nologo $(MS_RUNTIME_OPTION) -W3 $(GX_OPTION) -Od -D "WIN32" -D "WIN64" -D "_WINDOWS" -D "_DEBUG" -D "_CONSOLE" -D "_MBCS" -FD -c -!elseif "$(BUILDARCH)" == "amd64" -SA_CFLAGS = -nologo $(MS_RUNTIME_OPTION) -W3 $(GX_OPTION) -Od -D "WIN32" -D "WIN64" -D "_WINDOWS" -D "_DEBUG" -D "_CONSOLE" -D "_MBCS" -FD -c -!else -SA_CFLAGS = -nologo $(MS_RUNTIME_OPTION) -W3 $(GX_OPTION) -Od -D "WIN32" -D "_WINDOWS" -D "_DEBUG" -D "_CONSOLE" -D "_MBCS" -FD -RTC1 -c -!if "$(ENABLE_FULL_DEBUG_SYMBOLS)" == "1" -SA_CFLAGS = $(SA_CFLAGS) -ZI -!endif -!endif -!if "$(MT)" != "" -SA_LD_FLAGS = -manifest $(SA_LD_FLAGS) -!endif - -SASRCFILES = $(AGENT_DIR)/src/os/win32/windbg/sawindbg.cpp \ - $(AGENT_DIR)/src/share/native/sadis.c - -SA_LFLAGS = $(SA_LD_FLAGS) -nologo -subsystem:console -machine:$(MACHINE) -!if "$(ENABLE_FULL_DEBUG_SYMBOLS)" == "1" -SA_LFLAGS = $(SA_LFLAGS) -map -debug -!endif -!if "$(BUILDARCH)" == "i486" -SA_LFLAGS = /SAFESEH $(SA_LFLAGS) -!endif - -SA_CFLAGS = $(SA_CFLAGS) $(MP_FLAG) - -# Note that we do not keep sawindbj.obj around as it would then -# get included in the dumpbin command in build_vm_def.sh - -# Force resources to be rebuilt every time -$(SA_Res_Files): FORCE - -# In VS2005 or VS2008 the link command creates a .manifest file that we want -# to insert into the linked artifact so we do not need to track it separately. -# Use ";#2" for .dll and ";#1" for .exe in the MT command below: -$(SAWINDBG): $(SASRCFILES) $(SA_Res_Files) - set INCLUDE=$(SA_INCLUDE)$(INCLUDE) - $(CXX) @<< - -I"$(BootStrapDir)/include" -I"$(BootStrapDir)/include/win32" - -I"$(GENERATED)" $(SA_CFLAGS) - $(SASRCFILES) - -out:$*.obj -<< - set LIB=$(SA_LIB)$(LIB) - $(LD) -out:$@ -DLL sawindbg.obj sadis.obj dbgeng.lib $(SA_LFLAGS) $(SA_Res_Files) -!if "$(MT)" != "" - $(MT) -manifest $(@F).manifest -outputresource:$(@F);#2 -!endif -!if "$(ENABLE_FULL_DEBUG_SYMBOLS)" == "1" -!if "$(ZIP_DEBUGINFO_FILES)" == "1" - $(ZIPEXE) -q $*.diz $*.map $*.pdb - $(RM) $*.map $*.pdb -!endif -!endif - -@rm -f $*.obj - -{$(COMMONSRC)\os\windows\vm}.rc.sares: - @$(RC) $(RC_FLAGS) /D "HS_FNAME=$(SAWINDBG)" /fo"$@" $< - -cleanall : - rm -rf $(GENERATED)/saclasses - rm -rf $(GENERATED)/sa-jdi.jar -!endif diff --git a/hotspot/make/windows/makefiles/vm.make b/hotspot/make/windows/makefiles/vm.make index 9381c5b097d..957cb86776a 100644 --- a/hotspot/make/windows/makefiles/vm.make +++ b/hotspot/make/windows/makefiles/vm.make @@ -408,10 +408,5 @@ _build_pch_file.obj: @echo #include "precompiled.hpp" > ../generated/_build_pch_file.cpp $(CXX) $(CXX_FLAGS) /Fp"vm.pch" /Yc"precompiled.hpp" /c ../generated/_build_pch_file.cpp -!if "$(BUILD_WIN_SA)" != "1" -BUILD_VM_DEF_FLAG=-nosa -!endif - vm.def: $(Obj_Files) - sh $(WorkSpace)/make/windows/build_vm_def.sh $(BUILD_VM_DEF_FLAG) - + sh $(WorkSpace)/make/windows/build_vm_def.sh diff --git a/hotspot/src/cpu/aarch64/vm/aarch64.ad b/hotspot/src/cpu/aarch64/vm/aarch64.ad index 0861f782775..802db1b24b0 100644 --- a/hotspot/src/cpu/aarch64/vm/aarch64.ad +++ b/hotspot/src/cpu/aarch64/vm/aarch64.ad @@ -1079,10 +1079,10 @@ source %{ // and for a volatile write we need // // stlr - // + // // Alternatively, we can implement them by pairing a normal // load/store with a memory barrier. For a volatile read we need - // + // // ldr // dmb ishld // @@ -1240,7 +1240,7 @@ source %{ // Alternatively, we can elide generation of the dmb instructions // and plant the alternative CompareAndSwap macro-instruction // sequence (which uses ldaxr). - // + // // Of course, the above only applies when we see these signature // configurations. We still want to plant dmb instructions in any // other cases where we may see a MemBarAcquire, MemBarRelease or @@ -1367,7 +1367,7 @@ source %{ opcode = parent->Opcode(); return opcode == Op_MemBarRelease; } - + // 2) card mark detection helper // helper predicate which can be used to detect a volatile membar @@ -1383,7 +1383,7 @@ source %{ // true // // iii) the node's Mem projection feeds a StoreCM node. - + bool is_card_mark_membar(const MemBarNode *barrier) { if (!UseG1GC && !(UseConcMarkSweepGC && UseCondCardMark)) { @@ -1402,7 +1402,7 @@ source %{ return true; } } - + return false; } @@ -1430,7 +1430,7 @@ source %{ // where // || and \\ represent Ctl and Mem feeds via Proj nodes // | \ and / indicate further routing of the Ctl and Mem feeds - // + // // this is the graph we see for non-object stores. however, for a // volatile Object store (StoreN/P) we may see other nodes below the // leading membar because of the need for a GC pre- or post-write @@ -1592,7 +1592,7 @@ source %{ // ordering but neither will a releasing store (stlr). The latter // guarantees that the object put is visible but does not guarantee // that writes by other threads have also been observed. - // + // // So, returning to the task of translating the object put and the // leading/trailing membar nodes: what do the non-normal node graph // look like for these 2 special cases? and how can we determine the @@ -1731,7 +1731,7 @@ source %{ // | | | | // C | M | M | M | // \ | | / - // . . . + // . . . // (post write subtree elided) // . . . // C \ M / @@ -1812,12 +1812,12 @@ source %{ // | | | / / // | Region . . . Phi[M] _____/ // | / | / - // | | / + // | | / // | . . . . . . | / // | / | / // Region | | Phi[M] // | | | / Bot - // \ MergeMem + // \ MergeMem // \ / // MemBarVolatile // @@ -1858,7 +1858,7 @@ source %{ // to a trailing barrier via a MergeMem. That feed is either direct // (for CMS) or via 2 or 3 Phi nodes merging the leading barrier // memory flow (for G1). - // + // // The predicates controlling generation of instructions for store // and barrier nodes employ a few simple helper functions (described // below) which identify the presence or absence of all these @@ -2112,8 +2112,8 @@ source %{ x = x->in(MemNode::Memory); } else { // the merge should get its Bottom mem feed from the leading membar - x = mm->in(Compile::AliasIdxBot); - } + x = mm->in(Compile::AliasIdxBot); + } // ensure this is a non control projection if (!x->is_Proj() || x->is_CFG()) { @@ -2190,12 +2190,12 @@ source %{ // . . . // | // MemBarVolatile (card mark) - // | | + // | | // | StoreCM // | | // | . . . - // Bot | / - // MergeMem + // Bot | / + // MergeMem // | // | // MemBarVolatile {trailing} @@ -2203,10 +2203,10 @@ source %{ // 2) // MemBarRelease/CPUOrder (leading) // | - // | + // | // |\ . . . - // | \ | - // | \ MemBarVolatile (card mark) + // | \ | + // | \ MemBarVolatile (card mark) // | \ | | // \ \ | StoreCM . . . // \ \ | @@ -2231,7 +2231,7 @@ source %{ // | \ \ | StoreCM . . . // | \ \ | // \ \ Phi - // \ \ / + // \ \ / // \ Phi // \ / // Phi . . . @@ -2506,7 +2506,7 @@ bool unnecessary_acquire(const Node *barrier) return (x->is_Load() && x->as_Load()->is_acquire()); } - + // now check for an unsafe volatile get // need to check for @@ -2644,7 +2644,7 @@ bool needs_acquiring_load(const Node *n) } membar = child_membar(membar); - + if (!membar || !membar->Opcode() == Op_MemBarCPUOrder) { return false; } @@ -2703,7 +2703,7 @@ bool unnecessary_volatile(const Node *n) // first we check if this is part of a card mark. if so then we have // to generate a StoreLoad barrier - + if (is_card_mark_membar(mbvol)) { return false; } @@ -2769,7 +2769,7 @@ bool needs_releasing_store(const Node *n) if (!is_card_mark_membar(mbvol)) { return true; } - + // we found a card mark -- just make sure we have a trailing barrier return (card_mark_to_trailing(mbvol) != NULL); @@ -2808,7 +2808,7 @@ bool needs_acquiring_load_exclusive(const Node *n) assert(barrier->Opcode() == Op_MemBarCPUOrder, "CAS not fed by cpuorder membar!"); - + MemBarNode *b = parent_membar(barrier); assert ((b != NULL && b->Opcode() == Op_MemBarRelease), "CAS not fed by cpuorder+release membar pair!"); @@ -3463,6 +3463,17 @@ const bool Matcher::match_rule_supported(int opcode) { return true; // Per default match rules are supported. } +const bool Matcher::match_rule_supported_vector(int opcode, int vlen) { + + // TODO + // identify extra cases that we might want to provide match rules for + // e.g. Op_ vector nodes and other intrinsics while guarding with vlen + bool ret_value = match_rule_supported(opcode); + // Add rules here. + + return ret_value; // Per default match rules are supported. +} + const int Matcher::float_pressure(int default_pressure_threshold) { return default_pressure_threshold; } @@ -4663,7 +4674,7 @@ encode %{ call = __ trampoline_call(Address(addr, relocInfo::static_call_type), &cbuf); } if (call == NULL) { - ciEnv::current()->record_failure("CodeCache is full"); + ciEnv::current()->record_failure("CodeCache is full"); return; } @@ -4671,7 +4682,7 @@ encode %{ // Emit stub for static call address stub = CompiledStaticCall::emit_to_interp_stub(cbuf); if (stub == NULL) { - ciEnv::current()->record_failure("CodeCache is full"); + ciEnv::current()->record_failure("CodeCache is full"); return; } } @@ -4681,7 +4692,7 @@ encode %{ MacroAssembler _masm(&cbuf); address call = __ ic_call((address)$meth$$method); if (call == NULL) { - ciEnv::current()->record_failure("CodeCache is full"); + ciEnv::current()->record_failure("CodeCache is full"); return; } %} @@ -4706,7 +4717,7 @@ encode %{ if (cb) { address call = __ trampoline_call(Address(entry, relocInfo::runtime_call_type)); if (call == NULL) { - ciEnv::current()->record_failure("CodeCache is full"); + ciEnv::current()->record_failure("CodeCache is full"); return; } } else { diff --git a/hotspot/src/cpu/aarch64/vm/c1_CodeStubs_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/c1_CodeStubs_aarch64.cpp index bdfc017aa82..865b4c20374 100644 --- a/hotspot/src/cpu/aarch64/vm/c1_CodeStubs_aarch64.cpp +++ b/hotspot/src/cpu/aarch64/vm/c1_CodeStubs_aarch64.cpp @@ -41,7 +41,9 @@ void CounterOverflowStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); - ce->store_parameter(_method->as_register(), 1); + Metadata *m = _method->as_constant_ptr()->as_metadata(); + __ mov_metadata(rscratch1, m); + ce->store_parameter(rscratch1, 1); ce->store_parameter(_bci, 0); __ far_call(RuntimeAddress(Runtime1::entry_for(Runtime1::counter_overflow_id))); ce->add_call_info_here(_info); diff --git a/hotspot/src/cpu/aarch64/vm/c1_LIRGenerator_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/c1_LIRGenerator_aarch64.cpp index fb2c0ae3343..21c7cfc93ff 100644 --- a/hotspot/src/cpu/aarch64/vm/c1_LIRGenerator_aarch64.cpp +++ b/hotspot/src/cpu/aarch64/vm/c1_LIRGenerator_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -70,6 +70,7 @@ LIR_Opr LIRGenerator::divInOpr() { Unimplemented(); return LIR_OprFact::i LIR_Opr LIRGenerator::divOutOpr() { Unimplemented(); return LIR_OprFact::illegalOpr; } LIR_Opr LIRGenerator::remOutOpr() { Unimplemented(); return LIR_OprFact::illegalOpr; } LIR_Opr LIRGenerator::shiftCountOpr() { Unimplemented(); return LIR_OprFact::illegalOpr; } +LIR_Opr LIRGenerator::syncLockOpr() { return new_register(T_INT); } LIR_Opr LIRGenerator::syncTempOpr() { return FrameMap::r0_opr; } LIR_Opr LIRGenerator::getThreadTemp() { return LIR_OprFact::illegalOpr; } diff --git a/hotspot/src/cpu/aarch64/vm/c1_MacroAssembler_aarch64.hpp b/hotspot/src/cpu/aarch64/vm/c1_MacroAssembler_aarch64.hpp index 5ee1e9b8d46..fe41973b106 100644 --- a/hotspot/src/cpu/aarch64/vm/c1_MacroAssembler_aarch64.hpp +++ b/hotspot/src/cpu/aarch64/vm/c1_MacroAssembler_aarch64.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2014, Red Hat Inc. All rights reserved. + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2015, Red Hat Inc. 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 @@ -105,4 +105,7 @@ void zero_memory(Register addr, Register len, Register t1); void invalidate_registers(bool inv_r0, bool inv_r19, bool inv_r2, bool inv_r3, bool inv_r4, bool inv_r5) PRODUCT_RETURN; + // This platform only uses signal-based null checks. The Label is not needed. + void null_check(Register r, Label *Lnull = NULL) { MacroAssembler::null_check(r); } + #endif // CPU_AARCH64_VM_C1_MACROASSEMBLER_AARCH64_HPP diff --git a/hotspot/src/cpu/aarch64/vm/c1_Runtime1_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/c1_Runtime1_aarch64.cpp index a68da825e62..77498c35972 100644 --- a/hotspot/src/cpu/aarch64/vm/c1_Runtime1_aarch64.cpp +++ b/hotspot/src/cpu/aarch64/vm/c1_Runtime1_aarch64.cpp @@ -1169,12 +1169,12 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { const Register tmp = rscratch1; Address in_progress(thread, in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_active())); + SATBMarkQueue::byte_offset_of_active())); Address queue_index(thread, in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_index())); + SATBMarkQueue::byte_offset_of_index())); Address buffer(thread, in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_buf())); + SATBMarkQueue::byte_offset_of_buf())); Label done; Label runtime; @@ -1219,9 +1219,9 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { const Register thread = rthread; Address queue_index(thread, in_bytes(JavaThread::dirty_card_queue_offset() + - PtrQueue::byte_offset_of_index())); + DirtyCardQueue::byte_offset_of_index())); Address buffer(thread, in_bytes(JavaThread::dirty_card_queue_offset() + - PtrQueue::byte_offset_of_buf())); + DirtyCardQueue::byte_offset_of_buf())); const Register card_addr = rscratch2; ExternalAddress cardtable((address) ct->byte_map_base); diff --git a/hotspot/src/cpu/aarch64/vm/c2_globals_aarch64.hpp b/hotspot/src/cpu/aarch64/vm/c2_globals_aarch64.hpp index 7ab673f2151..342f07bdb04 100644 --- a/hotspot/src/cpu/aarch64/vm/c2_globals_aarch64.hpp +++ b/hotspot/src/cpu/aarch64/vm/c2_globals_aarch64.hpp @@ -73,6 +73,7 @@ define_pd_global(bool, UseCISCSpill, true); define_pd_global(bool, OptoScheduling, false); define_pd_global(bool, OptoBundling, false); define_pd_global(bool, OptoRegScheduling, false); +define_pd_global(bool, SuperWordLoopUnrollAnalysis, false); define_pd_global(intx, ReservedCodeCacheSize, 48*M); define_pd_global(intx, NonProfiledCodeHeapSize, 21*M); diff --git a/hotspot/src/cpu/aarch64/vm/interp_masm_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/interp_masm_aarch64.cpp index 8fe6b38f0c0..d463f059978 100644 --- a/hotspot/src/cpu/aarch64/vm/interp_masm_aarch64.cpp +++ b/hotspot/src/cpu/aarch64/vm/interp_masm_aarch64.cpp @@ -189,10 +189,11 @@ void InterpreterMacroAssembler::get_cache_and_index_and_bytecode_at_bcp(Register get_cache_and_index_at_bcp(cache, index, bcp_offset, index_size); // We use a 32-bit load here since the layout of 64-bit words on // little-endian machines allow us that. - // n.b. unlike x86 cache alreeady includes the index offset - ldrw(bytecode, Address(cache, + // n.b. unlike x86 cache already includes the index offset + lea(bytecode, Address(cache, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset())); + ldarw(bytecode, bytecode); const int shift_count = (1 + byte_no) * BitsPerByte; ubfx(bytecode, bytecode, shift_count, BitsPerByte); } diff --git a/hotspot/src/cpu/aarch64/vm/jvmciCodeInstaller_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/jvmciCodeInstaller_aarch64.cpp index f68c3e854da..c96bf597d80 100644 --- a/hotspot/src/cpu/aarch64/vm/jvmciCodeInstaller_aarch64.cpp +++ b/hotspot/src/cpu/aarch64/vm/jvmciCodeInstaller_aarch64.cpp @@ -29,16 +29,16 @@ #include "runtime/sharedRuntime.hpp" #include "vmreg_aarch64.inline.hpp" -jint CodeInstaller::pd_next_offset(NativeInstruction* inst, jint pc_offset, oop method) { +jint CodeInstaller::pd_next_offset(NativeInstruction* inst, jint pc_offset, Handle method, TRAPS) { Unimplemented(); return 0; } -void CodeInstaller::pd_patch_OopConstant(int pc_offset, Handle& constant) { +void CodeInstaller::pd_patch_OopConstant(int pc_offset, Handle constant, TRAPS) { Unimplemented(); } -void CodeInstaller::pd_patch_MetaspaceConstant(int pc_offset, Handle& constant) { +void CodeInstaller::pd_patch_MetaspaceConstant(int pc_offset, Handle constant, TRAPS) { Unimplemented(); } @@ -46,20 +46,20 @@ void CodeInstaller::pd_patch_DataSectionReference(int pc_offset, int data_offset Unimplemented(); } -void CodeInstaller::pd_relocate_ForeignCall(NativeInstruction* inst, jlong foreign_call_destination) { +void CodeInstaller::pd_relocate_ForeignCall(NativeInstruction* inst, jlong foreign_call_destination, TRAPS) { Unimplemented(); } -void CodeInstaller::pd_relocate_JavaMethod(oop hotspot_method, jint pc_offset) { +void CodeInstaller::pd_relocate_JavaMethod(Handle hotspot_method, jint pc_offset, TRAPS) { Unimplemented(); } -void CodeInstaller::pd_relocate_poll(address pc, jint mark) { +void CodeInstaller::pd_relocate_poll(address pc, jint mark, TRAPS) { Unimplemented(); } // convert JVMCI register indices (as used in oop maps) to HotSpot registers -VMReg CodeInstaller::get_hotspot_reg(jint jvmci_reg) { +VMReg CodeInstaller::get_hotspot_reg(jint jvmci_reg, TRAPS) { return NULL; } diff --git a/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.cpp index a827da7a5be..c9608bc7492 100644 --- a/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.cpp +++ b/hotspot/src/cpu/aarch64/vm/macroAssembler_aarch64.cpp @@ -91,20 +91,18 @@ int MacroAssembler::pd_patch_instruction_size(address branch, address target) { unsigned offset_lo = dest & 0xfff; offset = adr_page - pc_page; - // We handle 3 types of PC relative addressing + // We handle 4 types of PC relative addressing // 1 - adrp Rx, target_page // ldr/str Ry, [Rx, #offset_in_page] // 2 - adrp Rx, target_page // add Ry, Rx, #offset_in_page // 3 - adrp Rx, target_page (page aligned reloc, offset == 0) - // In the first 2 cases we must check that Rx is the same in the adrp and the - // subsequent ldr/str or add instruction. Otherwise we could accidentally end - // up treating a type 3 relocation as a type 1 or 2 just because it happened - // to be followed by a random unrelated ldr/str or add instruction. - // - // In the case of a type 3 relocation, we know that these are only generated - // for the safepoint polling page, or for the card type byte map base so we - // assert as much and of course that the offset is 0. + // movk Rx, #imm16<<32 + // 4 - adrp Rx, target_page (page aligned reloc, offset == 0) + // In the first 3 cases we must check that Rx is the same in the adrp and the + // subsequent ldr/str, add or movk instruction. Otherwise we could accidentally end + // up treating a type 4 relocation as a type 1, 2 or 3 just because it happened + // to be followed by a random unrelated ldr/str, add or movk instruction. // unsigned insn2 = ((unsigned*)branch)[1]; if (Instruction_aarch64::extract(insn2, 29, 24) == 0b111001 && @@ -123,13 +121,13 @@ int MacroAssembler::pd_patch_instruction_size(address branch, address target) { Instruction_aarch64::patch(branch + sizeof (unsigned), 21, 10, offset_lo); instructions = 2; - } else { - assert((jbyte *)target == - ((CardTableModRefBS*)(Universe::heap()->barrier_set()))->byte_map_base || - target == StubRoutines::crc_table_addr() || - (address)target == os::get_polling_page(), - "adrp must be polling page or byte map base"); - assert(offset_lo == 0, "offset must be 0 for polling page or byte map base"); + } else if (Instruction_aarch64::extract(insn2, 31, 21) == 0b11110010110 && + Instruction_aarch64::extract(insn, 4, 0) == + Instruction_aarch64::extract(insn2, 4, 0)) { + // movk #imm16<<32 + Instruction_aarch64::patch(branch + 4, 20, 5, (uint64_t)target >> 32); + offset &= (1<<20)-1; + instructions = 2; } } int offset_lo = offset & 3; @@ -212,16 +210,16 @@ address MacroAssembler::target_addr_for_insn(address insn_addr, unsigned insn) { // Return the target address for the following sequences // 1 - adrp Rx, target_page // ldr/str Ry, [Rx, #offset_in_page] - // 2 - adrp Rx, target_page ] + // 2 - adrp Rx, target_page // add Ry, Rx, #offset_in_page // 3 - adrp Rx, target_page (page aligned reloc, offset == 0) + // movk Rx, #imm12<<32 + // 4 - adrp Rx, target_page (page aligned reloc, offset == 0) // // In the first two cases we check that the register is the same and // return the target_page + the offset within the page. // Otherwise we assume it is a page aligned relocation and return - // the target page only. The only cases this is generated is for - // the safepoint polling page or for the card table byte map base so - // we assert as much. + // the target page only. // unsigned insn2 = ((unsigned*)insn_addr)[1]; if (Instruction_aarch64::extract(insn2, 29, 24) == 0b111001 && @@ -238,10 +236,12 @@ address MacroAssembler::target_addr_for_insn(address insn_addr, unsigned insn) { unsigned int byte_offset = Instruction_aarch64::extract(insn2, 21, 10); return address(target_page + byte_offset); } else { - assert((jbyte *)target_page == - ((CardTableModRefBS*)(Universe::heap()->barrier_set()))->byte_map_base || - (address)target_page == os::get_polling_page(), - "adrp must be polling page or byte map base"); + if (Instruction_aarch64::extract(insn2, 31, 21) == 0b11110010110 && + Instruction_aarch64::extract(insn, 4, 0) == + Instruction_aarch64::extract(insn2, 4, 0)) { + target_page = (target_page & 0xffffffff) | + ((uint64_t)Instruction_aarch64::extract(insn2, 20, 5) << 32); + } return (address)target_page; } } else { @@ -3487,18 +3487,18 @@ void MacroAssembler::g1_write_barrier_pre(Register obj, assert_different_registers(obj, pre_val, tmp); Address in_progress(thread, in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_active())); + SATBMarkQueue::byte_offset_of_active())); Address index(thread, in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_index())); + SATBMarkQueue::byte_offset_of_index())); Address buffer(thread, in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_buf())); + SATBMarkQueue::byte_offset_of_buf())); // Is marking active? - if (in_bytes(PtrQueue::byte_width_of_active()) == 4) { + if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { ldrw(tmp, in_progress); } else { - assert(in_bytes(PtrQueue::byte_width_of_active()) == 1, "Assumption"); + assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); ldrb(tmp, in_progress); } cbzw(tmp, done); @@ -3566,9 +3566,9 @@ void MacroAssembler::g1_write_barrier_post(Register store_addr, assert(thread == rthread, "must be"); Address queue_index(thread, in_bytes(JavaThread::dirty_card_queue_offset() + - PtrQueue::byte_offset_of_index())); + DirtyCardQueue::byte_offset_of_index())); Address buffer(thread, in_bytes(JavaThread::dirty_card_queue_offset() + - PtrQueue::byte_offset_of_buf())); + DirtyCardQueue::byte_offset_of_buf())); BarrierSet* bs = Universe::heap()->barrier_set(); CardTableModRefBS* ct = (CardTableModRefBS*)bs; @@ -3964,22 +3964,26 @@ address MacroAssembler::read_polling_page(Register r, relocInfo::relocType rtype void MacroAssembler::adrp(Register reg1, const Address &dest, unsigned long &byte_offset) { relocInfo::relocType rtype = dest.rspec().reloc()->type(); - if (uabs(pc() - dest.target()) >= (1LL << 32)) { - guarantee(rtype == relocInfo::none - || rtype == relocInfo::external_word_type - || rtype == relocInfo::poll_type - || rtype == relocInfo::poll_return_type, - "can only use a fixed address with an ADRP"); - // Out of range. This doesn't happen very often, but we have to - // handle it - mov(reg1, dest); - byte_offset = 0; - } else { - InstructionMark im(this); - code_section()->relocate(inst_mark(), dest.rspec()); - byte_offset = (uint64_t)dest.target() & 0xfff; + unsigned long low_page = (unsigned long)CodeCache::low_bound() >> 12; + unsigned long high_page = (unsigned long)(CodeCache::high_bound()-1) >> 12; + unsigned long dest_page = (unsigned long)dest.target() >> 12; + long offset_low = dest_page - low_page; + long offset_high = dest_page - high_page; + + InstructionMark im(this); + code_section()->relocate(inst_mark(), dest.rspec()); + // 8143067: Ensure that the adrp can reach the dest from anywhere within + // the code cache so that if it is relocated we know it will still reach + if (offset_high >= -(1<<20) && offset_low < (1<<20)) { _adrp(reg1, dest.target()); + } else { + unsigned long pc_page = (unsigned long)pc() >> 12; + long offset = dest_page - pc_page; + offset = (offset & ((1<<20)-1)) << 12; + _adrp(reg1, pc()+offset); + movk(reg1, ((unsigned long)dest.target() >> 32) & 0xffff, 32); } + byte_offset = (unsigned long)dest.target() & 0xfff; } void MacroAssembler::build_frame(int framesize) { diff --git a/hotspot/src/cpu/aarch64/vm/sharedRuntime_aarch64.cpp b/hotspot/src/cpu/aarch64/vm/sharedRuntime_aarch64.cpp index b22e5789380..1533c8d024e 100644 --- a/hotspot/src/cpu/aarch64/vm/sharedRuntime_aarch64.cpp +++ b/hotspot/src/cpu/aarch64/vm/sharedRuntime_aarch64.cpp @@ -2384,6 +2384,7 @@ void SharedRuntime::generate_deopt_blob() { } #endif // ASSERT __ mov(c_rarg0, rthread); + __ mov(c_rarg1, rcpool); __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::fetch_unroll_info))); __ blrt(rscratch1, 1, 0, 1); __ bind(retaddr); @@ -2397,6 +2398,7 @@ void SharedRuntime::generate_deopt_blob() { // Load UnrollBlock* into rdi __ mov(r5, r0); + __ ldrw(rcpool, Address(r5, Deoptimization::UnrollBlock::unpack_kind_offset_in_bytes())); Label noException; __ cmpw(rcpool, Deoptimization::Unpack_exception); // Was exception pending? __ br(Assembler::NE, noException); @@ -2609,6 +2611,7 @@ void SharedRuntime::generate_uncommon_trap_blob() { // n.b. 2 gp args, 0 fp args, integral return type __ mov(c_rarg0, rthread); + __ movw(c_rarg2, (unsigned)Deoptimization::Unpack_uncommon_trap); __ lea(rscratch1, RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap))); @@ -2628,6 +2631,16 @@ void SharedRuntime::generate_uncommon_trap_blob() { // move UnrollBlock* into r4 __ mov(r4, r0); +#ifdef ASSERT + { Label L; + __ ldrw(rscratch1, Address(r4, Deoptimization::UnrollBlock::unpack_kind_offset_in_bytes())); + __ cmpw(rscratch1, (unsigned)Deoptimization::Unpack_uncommon_trap); + __ br(Assembler::EQ, L); + __ stop("SharedRuntime::generate_deopt_blob: last_Java_fp not cleared"); + __ bind(L); + } +#endif + // Pop all the frames we must move/replace. // // Frame picture (youngest to oldest) diff --git a/hotspot/src/cpu/ppc/vm/c2_globals_ppc.hpp b/hotspot/src/cpu/ppc/vm/c2_globals_ppc.hpp index d41e391b4bc..20174522381 100644 --- a/hotspot/src/cpu/ppc/vm/c2_globals_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/c2_globals_ppc.hpp @@ -61,6 +61,7 @@ define_pd_global(bool, OptoPeephole, false); define_pd_global(bool, UseCISCSpill, false); define_pd_global(bool, OptoBundling, false); define_pd_global(bool, OptoRegScheduling, false); +define_pd_global(bool, SuperWordLoopUnrollAnalysis, false); // GL: // Detected a problem with unscaled compressed oops and // narrow_oop_use_complex_address() == false. diff --git a/hotspot/src/cpu/ppc/vm/cppInterpreter_ppc.cpp b/hotspot/src/cpu/ppc/vm/cppInterpreter_ppc.cpp index 3ff878b9328..01597d02026 100644 --- a/hotspot/src/cpu/ppc/vm/cppInterpreter_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/cppInterpreter_ppc.cpp @@ -2697,7 +2697,7 @@ address CppInterpreterGenerator::generate_normal_entry(bool synchronized) { // Provide a debugger breakpoint in the frame manager if breakpoints // in osr'd methods are requested. #ifdef COMPILER2 - NOT_PRODUCT( if (OptoBreakpointOSR) { __ illtrap(); } ) + if (OptoBreakpointOSR) { __ illtrap(); } #endif // Load callee's pointer to locals array from callee's state. diff --git a/hotspot/src/cpu/ppc/vm/interpreter_ppc.cpp b/hotspot/src/cpu/ppc/vm/interpreter_ppc.cpp index 08e4a4fc54f..0fbf97085ca 100644 --- a/hotspot/src/cpu/ppc/vm/interpreter_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/interpreter_ppc.cpp @@ -297,8 +297,16 @@ address AbstractInterpreterGenerator::generate_slow_signature_handler() { __ bind(do_float); __ lfs(floatSlot, 0, arg_java); #if defined(LINUX) + // Linux uses ELF ABI. Both original ELF and ELFv2 ABIs have float + // in the least significant word of an argument slot. +#if defined(VM_LITTLE_ENDIAN) + __ stfs(floatSlot, 0, arg_c); +#else __ stfs(floatSlot, 4, arg_c); +#endif #elif defined(AIX) + // Although AIX runs on big endian CPU, float is in most significant + // word of an argument slot. __ stfs(floatSlot, 0, arg_c); #else #error "unknown OS" diff --git a/hotspot/src/cpu/ppc/vm/jvmciCodeInstaller_ppc.cpp b/hotspot/src/cpu/ppc/vm/jvmciCodeInstaller_ppc.cpp index 13185cc36bf..e5d4a9b0e51 100644 --- a/hotspot/src/cpu/ppc/vm/jvmciCodeInstaller_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/jvmciCodeInstaller_ppc.cpp @@ -29,16 +29,16 @@ #include "runtime/sharedRuntime.hpp" #include "vmreg_ppc.inline.hpp" -jint CodeInstaller::pd_next_offset(NativeInstruction* inst, jint pc_offset, oop method) { +jint CodeInstaller::pd_next_offset(NativeInstruction* inst, jint pc_offset, Handle method, TRAPS) { Unimplemented(); return 0; } -void CodeInstaller::pd_patch_OopConstant(int pc_offset, Handle& constant) { +void CodeInstaller::pd_patch_OopConstant(int pc_offset, Handle constant, TRAPS) { Unimplemented(); } -void CodeInstaller::pd_patch_MetaspaceConstant(int pc_offset, Handle& constant) { +void CodeInstaller::pd_patch_MetaspaceConstant(int pc_offset, Handle constant, TRAPS) { Unimplemented(); } @@ -46,20 +46,20 @@ void CodeInstaller::pd_patch_DataSectionReference(int pc_offset, int data_offset Unimplemented(); } -void CodeInstaller::pd_relocate_ForeignCall(NativeInstruction* inst, jlong foreign_call_destination) { +void CodeInstaller::pd_relocate_ForeignCall(NativeInstruction* inst, jlong foreign_call_destination, TRAPS) { Unimplemented(); } -void CodeInstaller::pd_relocate_JavaMethod(oop hotspot_method, jint pc_offset) { +void CodeInstaller::pd_relocate_JavaMethod(Handle hotspot_method, jint pc_offset, TRAPS) { Unimplemented(); } -void CodeInstaller::pd_relocate_poll(address pc, jint mark) { +void CodeInstaller::pd_relocate_poll(address pc, jint mark, TRAPS) { Unimplemented(); } // convert JVMCI register indices (as used in oop maps) to HotSpot registers -VMReg CodeInstaller::get_hotspot_reg(jint jvmci_reg) { +VMReg CodeInstaller::get_hotspot_reg(jint jvmci_reg, TRAPS) { return NULL; } diff --git a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.cpp b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.cpp index 3310f37f57a..38cf28d094a 100644 --- a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.cpp @@ -2633,11 +2633,11 @@ void MacroAssembler::g1_write_barrier_pre(Register Robj, RegisterOrConstant offs Label runtime, filtered; // Is marking active? - if (in_bytes(PtrQueue::byte_width_of_active()) == 4) { - lwz(Rtmp1, in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_active()), R16_thread); + if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { + lwz(Rtmp1, in_bytes(JavaThread::satb_mark_queue_offset() + SATBMarkQueue::byte_offset_of_active()), R16_thread); } else { - guarantee(in_bytes(PtrQueue::byte_width_of_active()) == 1, "Assumption"); - lbz(Rtmp1, in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_active()), R16_thread); + guarantee(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); + lbz(Rtmp1, in_bytes(JavaThread::satb_mark_queue_offset() + SATBMarkQueue::byte_offset_of_active()), R16_thread); } cmpdi(CCR0, Rtmp1, 0); beq(CCR0, filtered); @@ -2672,13 +2672,13 @@ void MacroAssembler::g1_write_barrier_pre(Register Robj, RegisterOrConstant offs // (The index field is typed as size_t.) const Register Rbuffer = Rtmp1, Rindex = Rtmp2; - ld(Rindex, in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_index()), R16_thread); + ld(Rindex, in_bytes(JavaThread::satb_mark_queue_offset() + SATBMarkQueue::byte_offset_of_index()), R16_thread); cmpdi(CCR0, Rindex, 0); beq(CCR0, runtime); // If index == 0, goto runtime. - ld(Rbuffer, in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_buf()), R16_thread); + ld(Rbuffer, in_bytes(JavaThread::satb_mark_queue_offset() + SATBMarkQueue::byte_offset_of_buf()), R16_thread); addi(Rindex, Rindex, -wordSize); // Decrement index. - std(Rindex, in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_index()), R16_thread); + std(Rindex, in_bytes(JavaThread::satb_mark_queue_offset() + SATBMarkQueue::byte_offset_of_index()), R16_thread); // Record the previous value. stdx(Rpre_val, Rbuffer, Rindex); @@ -2757,13 +2757,13 @@ void MacroAssembler::g1_write_barrier_post(Register Rstore_addr, Register Rnew_v const Register Rqueue_index = Rtmp2, Rqueue_buf = Rtmp3; - ld(Rqueue_index, in_bytes(JavaThread::dirty_card_queue_offset() + PtrQueue::byte_offset_of_index()), R16_thread); + ld(Rqueue_index, in_bytes(JavaThread::dirty_card_queue_offset() + DirtyCardQueue::byte_offset_of_index()), R16_thread); cmpdi(CCR0, Rqueue_index, 0); beq(CCR0, runtime); // index == 0 then jump to runtime - ld(Rqueue_buf, in_bytes(JavaThread::dirty_card_queue_offset() + PtrQueue::byte_offset_of_buf()), R16_thread); + ld(Rqueue_buf, in_bytes(JavaThread::dirty_card_queue_offset() + DirtyCardQueue::byte_offset_of_buf()), R16_thread); addi(Rqueue_index, Rqueue_index, -wordSize); // decrement index - std(Rqueue_index, in_bytes(JavaThread::dirty_card_queue_offset() + PtrQueue::byte_offset_of_index()), R16_thread); + std(Rqueue_index, in_bytes(JavaThread::dirty_card_queue_offset() + DirtyCardQueue::byte_offset_of_index()), R16_thread); stdx(Rcard_addr, Rqueue_buf, Rqueue_index); // store card b(filtered); diff --git a/hotspot/src/cpu/ppc/vm/ppc.ad b/hotspot/src/cpu/ppc/vm/ppc.ad index 51eb16cbe76..daa35899360 100644 --- a/hotspot/src/cpu/ppc/vm/ppc.ad +++ b/hotspot/src/cpu/ppc/vm/ppc.ad @@ -2064,6 +2064,17 @@ const bool Matcher::match_rule_supported(int opcode) { return true; // Per default match rules are supported. } +const bool Matcher::match_rule_supported_vector(int opcode, int vlen) { + + // TODO + // identify extra cases that we might want to provide match rules for + // e.g. Op_ vector nodes and other intrinsics while guarding with vlen + bool ret_value = match_rule_supported(opcode); + // Add rules here. + + return ret_value; // Per default match rules are supported. +} + const int Matcher::float_pressure(int default_pressure_threshold) { return default_pressure_threshold; } @@ -3416,7 +3427,7 @@ encode %{ // The stub for call to interpreter. address stub = CompiledStaticCall::emit_to_interp_stub(cbuf); if (stub == NULL) { - ciEnv::current()->record_failure("CodeCache is full"); + ciEnv::current()->record_failure("CodeCache is full"); return; } } @@ -3465,7 +3476,7 @@ encode %{ // The stub for call to interpreter. address stub = CompiledStaticCall::emit_to_interp_stub(cbuf); if (stub == NULL) { - ciEnv::current()->record_failure("CodeCache is full"); + ciEnv::current()->record_failure("CodeCache is full"); return; } @@ -6911,7 +6922,7 @@ instruct decodeN_Disjoint_isel_Ex(iRegPdst dst, iRegNsrc src, flagsReg crx) %{ n_compare->_opnds[0] = op_crx; n_compare->_opnds[1] = op_src; n_compare->_opnds[2] = new immN_0Oper(TypeNarrowOop::NULL_PTR); - + decodeN_mergeDisjointNode *n2 = new decodeN_mergeDisjointNode(); n2->add_req(n_region, n_src, n1); n2->_opnds[0] = op_dst; @@ -10588,7 +10599,7 @@ instruct cmpP_reg_imm16(flagsReg crx, iRegPsrc src1, immL16 src2) %{ instruct cmpFUnordered_reg_reg(flagsReg crx, regF src1, regF src2) %{ // Needs matchrule, see cmpDUnordered. - match(Set crx (CmpF src1 src2)); + match(Set crx (CmpF src1 src2)); // no match-rule, false predicate predicate(false); @@ -10697,13 +10708,13 @@ instruct cmpF3_reg_reg_ExEx(iRegIdst dst, regF src1, regF src2) %{ %} instruct cmpDUnordered_reg_reg(flagsReg crx, regD src1, regD src2) %{ - // Needs matchrule so that ideal opcode is Cmp. This causes that gcm places the - // node right before the conditional move using it. + // Needs matchrule so that ideal opcode is Cmp. This causes that gcm places the + // node right before the conditional move using it. // In jck test api/java_awt/geom/QuadCurve2DFloat/index.html#SetCurveTesttestCase7, // compilation of java.awt.geom.RectangularShape::getBounds()Ljava/awt/Rectangle // crashed in register allocation where the flags Reg between cmpDUnoredered and a // conditional move was supposed to be spilled. - match(Set crx (CmpD src1 src2)); + match(Set crx (CmpD src1 src2)); // False predicate, shall not be matched. predicate(false); diff --git a/hotspot/src/cpu/ppc/vm/sharedRuntime_ppc.cpp b/hotspot/src/cpu/ppc/vm/sharedRuntime_ppc.cpp index e89c578e9e7..141891d0a16 100644 --- a/hotspot/src/cpu/ppc/vm/sharedRuntime_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/sharedRuntime_ppc.cpp @@ -753,6 +753,21 @@ int SharedRuntime::c_calling_convention(const BasicType *sig_bt, // in farg_reg[j] if argument i is the j-th float argument of this call. // case T_FLOAT: +#if defined(LINUX) + // Linux uses ELF ABI. Both original ELF and ELFv2 ABIs have float + // in the least significant word of an argument slot. +#if defined(VM_LITTLE_ENDIAN) +#define FLOAT_WORD_OFFSET_IN_SLOT 0 +#else +#define FLOAT_WORD_OFFSET_IN_SLOT 1 +#endif +#elif defined(AIX) + // Although AIX runs on big endian CPU, float is in the most + // significant word of an argument slot. +#define FLOAT_WORD_OFFSET_IN_SLOT 0 +#else +#error "unknown OS" +#endif if (freg < Argument::n_float_register_parameters_c) { // Put float in register ... reg = farg_reg[freg]; @@ -766,14 +781,14 @@ int SharedRuntime::c_calling_convention(const BasicType *sig_bt, if (arg >= Argument::n_regs_not_on_stack_c) { // ... and on the stack. guarantee(regs2 != NULL, "must pass float in register and stack slot"); - VMReg reg2 = VMRegImpl::stack2reg(stk LINUX_ONLY(+1)); + VMReg reg2 = VMRegImpl::stack2reg(stk + FLOAT_WORD_OFFSET_IN_SLOT); regs2[i].set1(reg2); stk += inc_stk_for_intfloat; } } else { // Put float on stack. - reg = VMRegImpl::stack2reg(stk LINUX_ONLY(+1)); + reg = VMRegImpl::stack2reg(stk + FLOAT_WORD_OFFSET_IN_SLOT); stk += inc_stk_for_intfloat; } regs[i].set1(reg); @@ -2802,7 +2817,7 @@ void SharedRuntime::generate_deopt_blob() { __ set_last_Java_frame(R1_SP, noreg); // With EscapeAnalysis turned on, this call may safepoint! - __ call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::fetch_unroll_info), R16_thread); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::fetch_unroll_info), R16_thread, exec_mode_reg); address calls_return_pc = __ last_calls_return_pc(); // Set an oopmap for the call site that describes all our saved registers. oop_maps->add_gc_map(calls_return_pc - start, map); @@ -2815,6 +2830,8 @@ void SharedRuntime::generate_deopt_blob() { // by save_volatile_registers(...). RegisterSaver::restore_result_registers(masm, first_frame_size_in_bytes); + // reload the exec mode from the UnrollBlock (it might have changed) + __ lwz(exec_mode_reg, Deoptimization::UnrollBlock::unpack_kind_offset_in_bytes(), unroll_block_reg); // In excp_deopt_mode, restore and clear exception oop which we // stored in the thread during exception entry above. The exception // oop will be the return value of this stub. @@ -2945,8 +2962,9 @@ void SharedRuntime::generate_uncommon_trap_blob() { __ set_last_Java_frame(/*sp*/R1_SP, /*pc*/R11_scratch1); __ mr(klass_index_reg, R3); + __ li(R5_ARG3, Deoptimization::Unpack_uncommon_trap); __ call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap), - R16_thread, klass_index_reg); + R16_thread, klass_index_reg, R5_ARG3); // Set an oopmap for the call site. oop_maps->add_gc_map(gc_map_pc - start, map); @@ -2966,6 +2984,12 @@ void SharedRuntime::generate_uncommon_trap_blob() { // stack: (caller_of_deoptee, ...). +#ifdef ASSERT + __ lwz(R22_tmp2, Deoptimization::UnrollBlock::unpack_kind_offset_in_bytes(), unroll_block_reg); + __ cmpdi(CCR0, R22_tmp2, (unsigned)Deoptimization::Unpack_uncommon_trap); + __ asm_assert_eq("SharedRuntime::generate_deopt_blob: expected Unpack_uncommon_trap", 0); +#endif + // Allocate new interpreter frame(s) and possibly a c2i adapter // frame. push_skeleton_frames(masm, false/*deopt*/, diff --git a/hotspot/src/cpu/ppc/vm/stubGenerator_ppc.cpp b/hotspot/src/cpu/ppc/vm/stubGenerator_ppc.cpp index 1f0d252229d..1dbb5c04f29 100644 --- a/hotspot/src/cpu/ppc/vm/stubGenerator_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/stubGenerator_ppc.cpp @@ -630,11 +630,11 @@ class StubGenerator: public StubCodeGenerator { Label filtered; // Is marking active? - if (in_bytes(PtrQueue::byte_width_of_active()) == 4) { - __ lwz(Rtmp1, in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_active()), R16_thread); + if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { + __ lwz(Rtmp1, in_bytes(JavaThread::satb_mark_queue_offset() + SATBMarkQueue::byte_offset_of_active()), R16_thread); } else { - guarantee(in_bytes(PtrQueue::byte_width_of_active()) == 1, "Assumption"); - __ lbz(Rtmp1, in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_active()), R16_thread); + guarantee(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); + __ lbz(Rtmp1, in_bytes(JavaThread::satb_mark_queue_offset() + SATBMarkQueue::byte_offset_of_active()), R16_thread); } __ cmpdi(CCR0, Rtmp1, 0); __ beq(CCR0, filtered); diff --git a/hotspot/src/cpu/sparc/vm/c1_CodeStubs_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_CodeStubs_sparc.cpp index 4f511dce70d..fec821ac6c7 100644 --- a/hotspot/src/cpu/sparc/vm/c1_CodeStubs_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/c1_CodeStubs_sparc.cpp @@ -94,8 +94,10 @@ void PredicateFailedStub::emit_code(LIR_Assembler* ce) { void CounterOverflowStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); __ set(_bci, G4); + Metadata *m = _method->as_constant_ptr()->as_metadata(); + __ set_metadata_constant(m, G5); __ call(Runtime1::entry_for(Runtime1::counter_overflow_id), relocInfo::runtime_call_type); - __ delayed()->mov_or_nop(_method->as_register(), G5); + __ delayed()->nop(); ce->add_call_info_here(_info); ce->verify_oop_map(_info); diff --git a/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp index 496c9e914da..aa242573d66 100644 --- a/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp @@ -2812,7 +2812,23 @@ void LIR_Assembler::monitor_address(int monitor_no, LIR_Opr dst_opr) { } void LIR_Assembler::emit_updatecrc32(LIR_OpUpdateCRC32* op) { - fatal("CRC32 intrinsic is not implemented on this platform"); + assert(op->crc()->is_single_cpu(), "crc must be register"); + assert(op->val()->is_single_cpu(), "byte value must be register"); + assert(op->result_opr()->is_single_cpu(), "result must be register"); + Register crc = op->crc()->as_register(); + Register val = op->val()->as_register(); + Register table = op->result_opr()->as_register(); + Register res = op->result_opr()->as_register(); + + assert_different_registers(val, crc, table); + + __ set(ExternalAddress(StubRoutines::crc_table_addr()), table); + __ not1(crc); + __ clruwu(crc); + __ update_byte_crc32(crc, val, table); + __ not1(crc); + + __ mov(crc, res); } void LIR_Assembler::emit_lock(LIR_OpLock* op) { diff --git a/hotspot/src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp index e91142ee011..6185a1d59cd 100644 --- a/hotspot/src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2015, 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 @@ -68,6 +68,7 @@ void LIRItem::load_nonconstant() { LIR_Opr LIRGenerator::exceptionOopOpr() { return FrameMap::Oexception_opr; } LIR_Opr LIRGenerator::exceptionPcOpr() { return FrameMap::Oissuing_pc_opr; } +LIR_Opr LIRGenerator::syncLockOpr() { return new_register(T_INT); } LIR_Opr LIRGenerator::syncTempOpr() { return new_register(T_OBJECT); } LIR_Opr LIRGenerator::getThreadTemp() { return rlock_callee_saved(NOT_LP64(T_INT) LP64_ONLY(T_LONG)); } @@ -785,7 +786,86 @@ void LIRGenerator::do_ArrayCopy(Intrinsic* x) { } void LIRGenerator::do_update_CRC32(Intrinsic* x) { - fatal("CRC32 intrinsic is not implemented on this platform"); + // Make all state_for calls early since they can emit code + LIR_Opr result = rlock_result(x); + int flags = 0; + switch (x->id()) { + case vmIntrinsics::_updateCRC32: { + LIRItem crc(x->argument_at(0), this); + LIRItem val(x->argument_at(1), this); + // val is destroyed by update_crc32 + val.set_destroys_register(); + crc.load_item(); + val.load_item(); + __ update_crc32(crc.result(), val.result(), result); + break; + } + case vmIntrinsics::_updateBytesCRC32: + case vmIntrinsics::_updateByteBufferCRC32: { + + bool is_updateBytes = (x->id() == vmIntrinsics::_updateBytesCRC32); + + LIRItem crc(x->argument_at(0), this); + LIRItem buf(x->argument_at(1), this); + LIRItem off(x->argument_at(2), this); + LIRItem len(x->argument_at(3), this); + + buf.load_item(); + off.load_nonconstant(); + + LIR_Opr index = off.result(); + int offset = is_updateBytes ? arrayOopDesc::base_offset_in_bytes(T_BYTE) : 0; + if(off.result()->is_constant()) { + index = LIR_OprFact::illegalOpr; + offset += off.result()->as_jint(); + } + + LIR_Opr base_op = buf.result(); + + if (index->is_valid()) { + LIR_Opr tmp = new_register(T_LONG); + __ convert(Bytecodes::_i2l, index, tmp); + index = tmp; + if (index->is_constant()) { + offset += index->as_constant_ptr()->as_jint(); + index = LIR_OprFact::illegalOpr; + } else if (index->is_register()) { + LIR_Opr tmp2 = new_register(T_LONG); + LIR_Opr tmp3 = new_register(T_LONG); + __ move(base_op, tmp2); + __ move(index, tmp3); + __ add(tmp2, tmp3, tmp2); + base_op = tmp2; + } else { + ShouldNotReachHere(); + } + } + + LIR_Address* a = new LIR_Address(base_op, offset, T_BYTE); + + BasicTypeList signature(3); + signature.append(T_INT); + signature.append(T_ADDRESS); + signature.append(T_INT); + CallingConvention* cc = frame_map()->c_calling_convention(&signature); + const LIR_Opr result_reg = result_register_for(x->type()); + + LIR_Opr addr = new_pointer_register(); + __ leal(LIR_OprFact::address(a), addr); + + crc.load_item_force(cc->at(0)); + __ move(addr, cc->at(1)); + len.load_item_force(cc->at(2)); + + __ call_runtime_leaf(StubRoutines::updateBytesCRC32(), getThreadTemp(), result_reg, cc->args()); + __ move(result_reg, result); + + break; + } + default: { + ShouldNotReachHere(); + } + } } // _i2l, _i2f, _i2d, _l2i, _l2f, _l2d, _f2i, _f2l, _f2d, _d2i, _d2l, _d2f diff --git a/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.hpp b/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.hpp index c0178e5e0f8..492ca678d89 100644 --- a/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.hpp +++ b/hotspot/src/cpu/sparc/vm/c1_MacroAssembler_sparc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, 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 @@ -88,4 +88,7 @@ void invalidate_registers(bool iregisters, bool lregisters, bool oregisters, Register preserve1 = noreg, Register preserve2 = noreg); + // This platform only uses signal-based null checks. The Label is not needed. + void null_check(Register r, Label *Lnull = NULL) { MacroAssembler::null_check(r); } + #endif // CPU_SPARC_VM_C1_MACROASSEMBLER_SPARC_HPP diff --git a/hotspot/src/cpu/sparc/vm/c1_Runtime1_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_Runtime1_sparc.cpp index 766b00a6bd8..45bfab26f45 100644 --- a/hotspot/src/cpu/sparc/vm/c1_Runtime1_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/c1_Runtime1_sparc.cpp @@ -857,13 +857,13 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { bool with_frame = false; // I don't know if we can do with-frame. int satb_q_index_byte_offset = in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_index()); + SATBMarkQueue::byte_offset_of_index()); int satb_q_buf_byte_offset = in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_buf()); + SATBMarkQueue::byte_offset_of_buf()); __ bind(restart); - // Load the index into the SATB buffer. PtrQueue::_index is a + // Load the index into the SATB buffer. SATBMarkQueue::_index is a // size_t so ld_ptr is appropriate __ ld_ptr(G2_thread, satb_q_index_byte_offset, tmp); @@ -961,14 +961,14 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { int dirty_card_q_index_byte_offset = in_bytes(JavaThread::dirty_card_queue_offset() + - PtrQueue::byte_offset_of_index()); + DirtyCardQueue::byte_offset_of_index()); int dirty_card_q_buf_byte_offset = in_bytes(JavaThread::dirty_card_queue_offset() + - PtrQueue::byte_offset_of_buf()); + DirtyCardQueue::byte_offset_of_buf()); __ bind(restart); - // Get the index into the update buffer. PtrQueue::_index is + // Get the index into the update buffer. DirtyCardQueue::_index is // a size_t so ld_ptr is appropriate here. __ ld_ptr(G2_thread, dirty_card_q_index_byte_offset, tmp3); diff --git a/hotspot/src/cpu/sparc/vm/c2_globals_sparc.hpp b/hotspot/src/cpu/sparc/vm/c2_globals_sparc.hpp index 39592b6e0eb..152529b9beb 100644 --- a/hotspot/src/cpu/sparc/vm/c2_globals_sparc.hpp +++ b/hotspot/src/cpu/sparc/vm/c2_globals_sparc.hpp @@ -65,6 +65,7 @@ define_pd_global(bool, UseCISCSpill, false); define_pd_global(bool, OptoBundling, false); define_pd_global(bool, OptoScheduling, true); define_pd_global(bool, OptoRegScheduling, false); +define_pd_global(bool, SuperWordLoopUnrollAnalysis, false); #ifdef _LP64 // We need to make sure that all generated code is within diff --git a/hotspot/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp b/hotspot/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp index 287e9f2971a..f0513d4035b 100644 --- a/hotspot/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp +++ b/hotspot/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2015, 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 @@ -43,8 +43,9 @@ void generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue); void generate_counter_overflow(Label& Lcontinue); + address generate_CRC32_update_entry(); + address generate_CRC32_updateBytes_entry(AbstractInterpreter::MethodKind kind); + // Not supported - address generate_CRC32_update_entry() { return NULL; } - address generate_CRC32_updateBytes_entry(AbstractInterpreter::MethodKind kind) { return NULL; } address generate_CRC32C_updateBytes_entry(AbstractInterpreter::MethodKind kind) { return NULL; } #endif // CPU_SPARC_VM_INTERPRETERGENERATOR_SPARC_HPP diff --git a/hotspot/src/cpu/sparc/vm/jvmciCodeInstaller_sparc.cpp b/hotspot/src/cpu/sparc/vm/jvmciCodeInstaller_sparc.cpp index 45b27ab0a78..c50844963d0 100644 --- a/hotspot/src/cpu/sparc/vm/jvmciCodeInstaller_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/jvmciCodeInstaller_sparc.cpp @@ -29,7 +29,7 @@ #include "runtime/sharedRuntime.hpp" #include "vmreg_sparc.inline.hpp" -jint CodeInstaller::pd_next_offset(NativeInstruction* inst, jint pc_offset, oop method) { +jint CodeInstaller::pd_next_offset(NativeInstruction* inst, jint pc_offset, Handle method, TRAPS) { if (inst->is_call() || inst->is_jump()) { return pc_offset + NativeCall::instruction_size; } else if (inst->is_call_reg()) { @@ -37,12 +37,12 @@ jint CodeInstaller::pd_next_offset(NativeInstruction* inst, jint pc_offset, oop } else if (inst->is_sethi()) { return pc_offset + NativeFarCall::instruction_size; } else { - fatal("unsupported type of instruction for call site"); + JVMCI_ERROR_0("unsupported type of instruction for call site"); return 0; } } -void CodeInstaller::pd_patch_OopConstant(int pc_offset, Handle& constant) { +void CodeInstaller::pd_patch_OopConstant(int pc_offset, Handle constant, TRAPS) { address pc = _instructions->start() + pc_offset; Handle obj = HotSpotObjectConstantImpl::object(constant); jobject value = JNIHandles::make_local(obj()); @@ -52,7 +52,7 @@ void CodeInstaller::pd_patch_OopConstant(int pc_offset, Handle& constant) { RelocationHolder rspec = oop_Relocation::spec(oop_index); _instructions->relocate(pc, rspec, 1); #else - fatal("compressed oop on 32bit"); + JVMCI_ERROR("compressed oop on 32bit"); #endif } else { NativeMovConstReg* move = nativeMovConstReg_at(pc); @@ -66,20 +66,20 @@ void CodeInstaller::pd_patch_OopConstant(int pc_offset, Handle& constant) { } } -void CodeInstaller::pd_patch_MetaspaceConstant(int pc_offset, Handle& constant) { +void CodeInstaller::pd_patch_MetaspaceConstant(int pc_offset, Handle constant, TRAPS) { address pc = _instructions->start() + pc_offset; if (HotSpotMetaspaceConstantImpl::compressed(constant)) { #ifdef _LP64 NativeMovConstReg32* move = nativeMovConstReg32_at(pc); - narrowKlass narrowOop = record_narrow_metadata_reference(constant); + narrowKlass narrowOop = record_narrow_metadata_reference(constant, CHECK); move->set_data((intptr_t)narrowOop); TRACE_jvmci_3("relocating (narrow metaspace constant) at %p/%p", pc, narrowOop); #else - fatal("compressed Klass* on 32bit"); + JVMCI_ERROR("compressed Klass* on 32bit"); #endif } else { NativeMovConstReg* move = nativeMovConstReg_at(pc); - Metadata* reference = record_metadata_reference(constant); + Metadata* reference = record_metadata_reference(constant, CHECK); move->set_data((intptr_t)reference); TRACE_jvmci_3("relocating (metaspace constant) at %p/%p", pc, reference); } @@ -106,7 +106,7 @@ void CodeInstaller::pd_patch_DataSectionReference(int pc_offset, int data_offset } } -void CodeInstaller::pd_relocate_ForeignCall(NativeInstruction* inst, jlong foreign_call_destination) { +void CodeInstaller::pd_relocate_ForeignCall(NativeInstruction* inst, jlong foreign_call_destination, TRAPS) { address pc = (address) inst; if (inst->is_call()) { NativeCall* call = nativeCall_at(pc); @@ -117,17 +117,17 @@ void CodeInstaller::pd_relocate_ForeignCall(NativeInstruction* inst, jlong forei jump->set_jump_destination((address) foreign_call_destination); _instructions->relocate(jump->instruction_address(), runtime_call_Relocation::spec()); } else { - fatal(err_msg("unknown call or jump instruction at " PTR_FORMAT, p2i(pc))); + JVMCI_ERROR("unknown call or jump instruction at " PTR_FORMAT, p2i(pc)); } TRACE_jvmci_3("relocating (foreign call) at " PTR_FORMAT, p2i(inst)); } -void CodeInstaller::pd_relocate_JavaMethod(oop hotspot_method, jint pc_offset) { +void CodeInstaller::pd_relocate_JavaMethod(Handle hotspot_method, jint pc_offset, TRAPS) { #ifdef ASSERT Method* method = NULL; // we need to check, this might also be an unresolved method if (hotspot_method->is_a(HotSpotResolvedJavaMethodImpl::klass())) { - method = getMethodFromHotSpotMethod(hotspot_method); + method = getMethodFromHotSpotMethod(hotspot_method()); } #endif switch (_next_call_type) { @@ -156,33 +156,33 @@ void CodeInstaller::pd_relocate_JavaMethod(oop hotspot_method, jint pc_offset) { break; } default: - fatal("invalid _next_call_type value"); + JVMCI_ERROR("invalid _next_call_type value"); break; } } -void CodeInstaller::pd_relocate_poll(address pc, jint mark) { +void CodeInstaller::pd_relocate_poll(address pc, jint mark, TRAPS) { switch (mark) { case POLL_NEAR: - fatal("unimplemented"); + JVMCI_ERROR("unimplemented"); break; case POLL_FAR: _instructions->relocate(pc, relocInfo::poll_type); break; case POLL_RETURN_NEAR: - fatal("unimplemented"); + JVMCI_ERROR("unimplemented"); break; case POLL_RETURN_FAR: _instructions->relocate(pc, relocInfo::poll_return_type); break; default: - fatal("invalid mark value"); + JVMCI_ERROR("invalid mark value"); break; } } // convert JVMCI register indices (as used in oop maps) to HotSpot registers -VMReg CodeInstaller::get_hotspot_reg(jint jvmci_reg) { +VMReg CodeInstaller::get_hotspot_reg(jint jvmci_reg, TRAPS) { // JVMCI Registers are numbered as follows: // 0..31: Thirty-two General Purpose registers (CPU Registers) // 32..63: Thirty-two single precision float registers @@ -199,7 +199,7 @@ VMReg CodeInstaller::get_hotspot_reg(jint jvmci_reg) { } else if(jvmci_reg < 112) { floatRegisterNumber = 4 * (jvmci_reg - 96); } else { - fatal("Unknown jvmci register"); + JVMCI_ERROR_NULL("invalid register number: %d", jvmci_reg); } return as_FloatRegister(floatRegisterNumber)->as_VMReg(); } diff --git a/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.cpp b/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.cpp index a6d5cd68e47..59b5b41a0d8 100644 --- a/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.cpp @@ -3632,19 +3632,19 @@ static void generate_satb_log_enqueue(bool with_frame) { int satb_q_index_byte_offset = in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_index()); + SATBMarkQueue::byte_offset_of_index()); int satb_q_buf_byte_offset = in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_buf()); + SATBMarkQueue::byte_offset_of_buf()); - assert(in_bytes(PtrQueue::byte_width_of_index()) == sizeof(intptr_t) && - in_bytes(PtrQueue::byte_width_of_buf()) == sizeof(intptr_t), + assert(in_bytes(SATBMarkQueue::byte_width_of_index()) == sizeof(intptr_t) && + in_bytes(SATBMarkQueue::byte_width_of_buf()) == sizeof(intptr_t), "check sizes in assembly below"); __ bind(restart); - // Load the index into the SATB buffer. PtrQueue::_index is a size_t + // Load the index into the SATB buffer. SATBMarkQueue::_index is a size_t // so ld_ptr is appropriate. __ ld_ptr(G2_thread, satb_q_index_byte_offset, L0); @@ -3736,17 +3736,17 @@ void MacroAssembler::g1_write_barrier_pre(Register obj, } // Is marking active? - if (in_bytes(PtrQueue::byte_width_of_active()) == 4) { + if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { ld(G2, in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_active()), + SATBMarkQueue::byte_offset_of_active()), tmp); } else { - guarantee(in_bytes(PtrQueue::byte_width_of_active()) == 1, + guarantee(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); ldsb(G2, in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_active()), + SATBMarkQueue::byte_offset_of_active()), tmp); } @@ -3847,13 +3847,13 @@ static void generate_dirty_card_log_enqueue(jbyte* byte_map_base) { int dirty_card_q_index_byte_offset = in_bytes(JavaThread::dirty_card_queue_offset() + - PtrQueue::byte_offset_of_index()); + DirtyCardQueue::byte_offset_of_index()); int dirty_card_q_buf_byte_offset = in_bytes(JavaThread::dirty_card_queue_offset() + - PtrQueue::byte_offset_of_buf()); + DirtyCardQueue::byte_offset_of_buf()); __ bind(restart); - // Load the index into the update buffer. PtrQueue::_index is + // Load the index into the update buffer. DirtyCardQueue::_index is // a size_t so ld_ptr is appropriate here. __ ld_ptr(G2_thread, dirty_card_q_index_byte_offset, L0); @@ -4771,3 +4771,243 @@ void MacroAssembler::movftoi_revbytes(FloatRegister src, Register dst, Register movdtox(src, tmp1); reverse_bytes_32(tmp1, dst, tmp2); } + +void MacroAssembler::fold_128bit_crc32(Register xcrc_hi, Register xcrc_lo, Register xK_hi, Register xK_lo, Register xtmp_hi, Register xtmp_lo, Register buf, int offset) { + xmulx(xcrc_hi, xK_hi, xtmp_lo); + xmulxhi(xcrc_hi, xK_hi, xtmp_hi); + xmulxhi(xcrc_lo, xK_lo, xcrc_hi); + xmulx(xcrc_lo, xK_lo, xcrc_lo); + xor3(xcrc_lo, xtmp_lo, xcrc_lo); + xor3(xcrc_hi, xtmp_hi, xcrc_hi); + ldxl(buf, G0, xtmp_lo); + inc(buf, 8); + ldxl(buf, G0, xtmp_hi); + inc(buf, 8); + xor3(xcrc_lo, xtmp_lo, xcrc_lo); + xor3(xcrc_hi, xtmp_hi, xcrc_hi); +} + +void MacroAssembler::fold_128bit_crc32(Register xcrc_hi, Register xcrc_lo, Register xK_hi, Register xK_lo, Register xtmp_hi, Register xtmp_lo, Register xbuf_hi, Register xbuf_lo) { + mov(xcrc_lo, xtmp_lo); + mov(xcrc_hi, xtmp_hi); + xmulx(xtmp_hi, xK_hi, xtmp_lo); + xmulxhi(xtmp_hi, xK_hi, xtmp_hi); + xmulxhi(xcrc_lo, xK_lo, xcrc_hi); + xmulx(xcrc_lo, xK_lo, xcrc_lo); + xor3(xcrc_lo, xbuf_lo, xcrc_lo); + xor3(xcrc_hi, xbuf_hi, xcrc_hi); + xor3(xcrc_lo, xtmp_lo, xcrc_lo); + xor3(xcrc_hi, xtmp_hi, xcrc_hi); +} + +void MacroAssembler::fold_8bit_crc32(Register xcrc, Register table, Register xtmp, Register tmp) { + and3(xcrc, 0xFF, tmp); + sllx(tmp, 2, tmp); + lduw(table, tmp, xtmp); + srlx(xcrc, 8, xcrc); + xor3(xtmp, xcrc, xcrc); +} + +void MacroAssembler::fold_8bit_crc32(Register crc, Register table, Register tmp) { + and3(crc, 0xFF, tmp); + srlx(crc, 8, crc); + sllx(tmp, 2, tmp); + lduw(table, tmp, tmp); + xor3(tmp, crc, crc); +} + +#define CRC32_TMP_REG_NUM 18 + +#define CRC32_CONST_64 0x163cd6124 +#define CRC32_CONST_96 0x0ccaa009e +#define CRC32_CONST_160 0x1751997d0 +#define CRC32_CONST_480 0x1c6e41596 +#define CRC32_CONST_544 0x154442bd4 + +void MacroAssembler::kernel_crc32(Register crc, Register buf, Register len, Register table) { + + Label L_cleanup_loop, L_cleanup_check, L_align_loop, L_align_check; + Label L_main_loop_prologue; + Label L_fold_512b, L_fold_512b_loop, L_fold_128b; + Label L_fold_tail, L_fold_tail_loop; + Label L_8byte_fold_loop, L_8byte_fold_check; + + const Register tmp[CRC32_TMP_REG_NUM] = {L0, L1, L2, L3, L4, L5, L6, G1, I0, I1, I2, I3, I4, I5, I7, O4, O5, G3}; + + Register const_64 = tmp[CRC32_TMP_REG_NUM-1]; + Register const_96 = tmp[CRC32_TMP_REG_NUM-1]; + Register const_160 = tmp[CRC32_TMP_REG_NUM-2]; + Register const_480 = tmp[CRC32_TMP_REG_NUM-1]; + Register const_544 = tmp[CRC32_TMP_REG_NUM-2]; + + set(ExternalAddress(StubRoutines::crc_table_addr()), table); + + not1(crc); // ~c + clruwu(crc); // clear upper 32 bits of crc + + // Check if below cutoff, proceed directly to cleanup code + mov(31, G4); + cmp_and_br_short(len, G4, Assembler::lessEqualUnsigned, Assembler::pt, L_cleanup_check); + + // Align buffer to 8 byte boundry + mov(8, O5); + and3(buf, 0x7, O4); + sub(O5, O4, O5); + and3(O5, 0x7, O5); + sub(len, O5, len); + ba(L_align_check); + delayed()->nop(); + + // Alignment loop, table look up method for up to 7 bytes + bind(L_align_loop); + ldub(buf, 0, O4); + inc(buf); + dec(O5); + xor3(O4, crc, O4); + and3(O4, 0xFF, O4); + sllx(O4, 2, O4); + lduw(table, O4, O4); + srlx(crc, 8, crc); + xor3(O4, crc, crc); + bind(L_align_check); + nop(); + cmp_and_br_short(O5, 0, Assembler::notEqual, Assembler::pt, L_align_loop); + + // Aligned on 64-bit (8-byte) boundry at this point + // Check if still above cutoff (31-bytes) + mov(31, G4); + cmp_and_br_short(len, G4, Assembler::lessEqualUnsigned, Assembler::pt, L_cleanup_check); + // At least 32 bytes left to process + + // Free up registers by storing them to FP registers + for (int i = 0; i < CRC32_TMP_REG_NUM; i++) { + movxtod(tmp[i], as_FloatRegister(2*i)); + } + + // Determine which loop to enter + // Shared prologue + ldxl(buf, G0, tmp[0]); + inc(buf, 8); + ldxl(buf, G0, tmp[1]); + inc(buf, 8); + xor3(tmp[0], crc, tmp[0]); // Fold CRC into first few bytes + and3(crc, 0, crc); // Clear out the crc register + // Main loop needs 128-bytes at least + mov(128, G4); + mov(64, tmp[2]); + cmp_and_br_short(len, G4, Assembler::greaterEqualUnsigned, Assembler::pt, L_main_loop_prologue); + // Less than 64 bytes + nop(); + cmp_and_br_short(len, tmp[2], Assembler::lessUnsigned, Assembler::pt, L_fold_tail); + // Between 64 and 127 bytes + set64(CRC32_CONST_96, const_96, tmp[8]); + set64(CRC32_CONST_160, const_160, tmp[9]); + fold_128bit_crc32(tmp[1], tmp[0], const_96, const_160, tmp[2], tmp[3], buf, 0); + fold_128bit_crc32(tmp[1], tmp[0], const_96, const_160, tmp[4], tmp[5], buf, 16); + fold_128bit_crc32(tmp[1], tmp[0], const_96, const_160, tmp[6], tmp[7], buf, 32); + dec(len, 48); + ba(L_fold_tail); + delayed()->nop(); + + bind(L_main_loop_prologue); + for (int i = 2; i < 8; i++) { + ldxl(buf, G0, tmp[i]); + inc(buf, 8); + } + + // Fold total 512 bits of polynomial on each iteration, + // 128 bits per each of 4 parallel streams + set64(CRC32_CONST_480, const_480, tmp[8]); + set64(CRC32_CONST_544, const_544, tmp[9]); + + mov(128, G4); + bind(L_fold_512b_loop); + fold_128bit_crc32(tmp[1], tmp[0], const_480, const_544, tmp[9], tmp[8], buf, 0); + fold_128bit_crc32(tmp[3], tmp[2], const_480, const_544, tmp[11], tmp[10], buf, 16); + fold_128bit_crc32(tmp[5], tmp[4], const_480, const_544, tmp[13], tmp[12], buf, 32); + fold_128bit_crc32(tmp[7], tmp[6], const_480, const_544, tmp[15], tmp[14], buf, 64); + dec(len, 64); + cmp_and_br_short(len, G4, Assembler::greaterEqualUnsigned, Assembler::pt, L_fold_512b_loop); + + // Fold 512 bits to 128 bits + bind(L_fold_512b); + set64(CRC32_CONST_96, const_96, tmp[8]); + set64(CRC32_CONST_160, const_160, tmp[9]); + + fold_128bit_crc32(tmp[1], tmp[0], const_96, const_160, tmp[8], tmp[9], tmp[3], tmp[2]); + fold_128bit_crc32(tmp[1], tmp[0], const_96, const_160, tmp[8], tmp[9], tmp[5], tmp[4]); + fold_128bit_crc32(tmp[1], tmp[0], const_96, const_160, tmp[8], tmp[9], tmp[7], tmp[6]); + dec(len, 48); + + // Fold the rest of 128 bits data chunks + bind(L_fold_tail); + mov(32, G4); + cmp_and_br_short(len, G4, Assembler::lessEqualUnsigned, Assembler::pt, L_fold_128b); + + set64(CRC32_CONST_96, const_96, tmp[8]); + set64(CRC32_CONST_160, const_160, tmp[9]); + + bind(L_fold_tail_loop); + fold_128bit_crc32(tmp[1], tmp[0], const_96, const_160, tmp[2], tmp[3], buf, 0); + sub(len, 16, len); + cmp_and_br_short(len, G4, Assembler::greaterEqualUnsigned, Assembler::pt, L_fold_tail_loop); + + // Fold the 128 bits in tmps 0 - 1 into tmp 1 + bind(L_fold_128b); + + set64(CRC32_CONST_64, const_64, tmp[4]); + + xmulx(const_64, tmp[0], tmp[2]); + xmulxhi(const_64, tmp[0], tmp[3]); + + srl(tmp[2], G0, tmp[4]); + xmulx(const_64, tmp[4], tmp[4]); + + srlx(tmp[2], 32, tmp[2]); + sllx(tmp[3], 32, tmp[3]); + or3(tmp[2], tmp[3], tmp[2]); + + xor3(tmp[4], tmp[1], tmp[4]); + xor3(tmp[4], tmp[2], tmp[1]); + dec(len, 8); + + // Use table lookup for the 8 bytes left in tmp[1] + dec(len, 8); + + // 8 8-bit folds to compute 32-bit CRC. + for (int j = 0; j < 4; j++) { + fold_8bit_crc32(tmp[1], table, tmp[2], tmp[3]); + } + srl(tmp[1], G0, crc); // move 32 bits to general register + for (int j = 0; j < 4; j++) { + fold_8bit_crc32(crc, table, tmp[3]); + } + + bind(L_8byte_fold_check); + + // Restore int registers saved in FP registers + for (int i = 0; i < CRC32_TMP_REG_NUM; i++) { + movdtox(as_FloatRegister(2*i), tmp[i]); + } + + ba(L_cleanup_check); + delayed()->nop(); + + // Table look-up method for the remaining few bytes + bind(L_cleanup_loop); + ldub(buf, 0, O4); + inc(buf); + dec(len); + xor3(O4, crc, O4); + and3(O4, 0xFF, O4); + sllx(O4, 2, O4); + lduw(table, O4, O4); + srlx(crc, 8, crc); + xor3(O4, crc, crc); + bind(L_cleanup_check); + nop(); + cmp_and_br_short(len, 0, Assembler::greaterUnsigned, Assembler::pt, L_cleanup_loop); + + not1(crc); +} + diff --git a/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.hpp b/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.hpp index 792a493bc01..d58fc54f1c9 100644 --- a/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.hpp +++ b/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.hpp @@ -904,7 +904,9 @@ public: inline void ldf(FloatRegisterImpl::Width w, const Address& a, FloatRegister d, int offset = 0); // little-endian - inline void ldxl(Register s1, Register s2, Register d) { ldxa(s1, s2, ASI_PRIMARY_LITTLE, d); } + inline void lduwl(Register s1, Register s2, Register d) { lduwa(s1, s2, ASI_PRIMARY_LITTLE, d); } + inline void ldswl(Register s1, Register s2, Register d) { ldswa(s1, s2, ASI_PRIMARY_LITTLE, d);} + inline void ldxl( Register s1, Register s2, Register d) { ldxa(s1, s2, ASI_PRIMARY_LITTLE, d); } inline void ldfl(FloatRegisterImpl::Width w, Register s1, Register s2, FloatRegister d) { ldfa(w, s1, s2, ASI_PRIMARY_LITTLE, d); } // membar psuedo instruction. takes into account target memory model. @@ -1469,6 +1471,15 @@ public: void movitof_revbytes(Register src, FloatRegister dst, Register tmp1, Register tmp2); void movftoi_revbytes(FloatRegister src, Register dst, Register tmp1, Register tmp2); + // CRC32 code for java.util.zip.CRC32::updateBytes0() instrinsic. + void kernel_crc32(Register crc, Register buf, Register len, Register table); + // Fold 128-bit data chunk + void fold_128bit_crc32(Register xcrc_hi, Register xcrc_lo, Register xK_hi, Register xK_lo, Register xtmp_hi, Register xtmp_lo, Register buf, int offset); + void fold_128bit_crc32(Register xcrc_hi, Register xcrc_lo, Register xK_hi, Register xK_lo, Register xtmp_hi, Register xtmp_lo, Register xbuf_hi, Register xbuf_lo); + // Fold 8-bit data + void fold_8bit_crc32(Register xcrc, Register table, Register xtmp, Register tmp); + void fold_8bit_crc32(Register crc, Register table, Register tmp); + #undef VIRTUAL }; diff --git a/hotspot/src/cpu/sparc/vm/sharedRuntime_sparc.cpp b/hotspot/src/cpu/sparc/vm/sharedRuntime_sparc.cpp index 0c78b651e44..4357bdc7298 100644 --- a/hotspot/src/cpu/sparc/vm/sharedRuntime_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/sharedRuntime_sparc.cpp @@ -3036,6 +3036,7 @@ void SharedRuntime::generate_deopt_blob() { __ mov((int32_t)Deoptimization::Unpack_reexecute, L0deopt_mode); __ mov(G2_thread, O0); + __ mov(L0deopt_mode, O2); __ call(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap)); __ delayed()->nop(); oop_maps->add_gc_map( __ offset()-start, map->deep_copy()); @@ -3121,6 +3122,7 @@ void SharedRuntime::generate_deopt_blob() { // do the call by hand so we can get the oopmap __ mov(G2_thread, L7_thread_cache); + __ mov(L0deopt_mode, O1); __ call(CAST_FROM_FN_PTR(address, Deoptimization::fetch_unroll_info), relocInfo::runtime_call_type); __ delayed()->mov(G2_thread, O0); @@ -3146,6 +3148,7 @@ void SharedRuntime::generate_deopt_blob() { RegisterSaver::restore_result_registers(masm); + __ ld(O2UnrollBlock, Deoptimization::UnrollBlock::unpack_kind_offset_in_bytes(), G4deopt_mode); Label noException; __ cmp_and_br_short(G4deopt_mode, Deoptimization::Unpack_exception, Assembler::notEqual, Assembler::pt, noException); @@ -3269,7 +3272,8 @@ void SharedRuntime::generate_uncommon_trap_blob() { __ save_frame(0); __ set_last_Java_frame(SP, noreg); __ mov(I0, O2klass_index); - __ call_VM_leaf(L7_thread_cache, CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap), G2_thread, O2klass_index); + __ mov(Deoptimization::Unpack_uncommon_trap, O3); // exec mode + __ call_VM_leaf(L7_thread_cache, CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap), G2_thread, O2klass_index, O3); __ reset_last_Java_frame(); __ mov(O0, O2UnrollBlock->after_save()); __ restore(); @@ -3278,6 +3282,15 @@ void SharedRuntime::generate_uncommon_trap_blob() { __ mov(O2UnrollBlock, O2UnrollBlock->after_save()); __ restore(); +#ifdef ASSERT + { Label L; + __ ld(O2UnrollBlock, Deoptimization::UnrollBlock::unpack_kind_offset_in_bytes(), O1); + __ cmp_and_br_short(O1, Deoptimization::Unpack_uncommon_trap, Assembler::equal, Assembler::pt, L); + __ stop("SharedRuntime::generate_deopt_blob: expected Unpack_uncommon_trap"); + __ bind(L); + } +#endif + // Allocate new interpreter frame(s) and possible c2i adapter frame make_new_frames(masm, false); diff --git a/hotspot/src/cpu/sparc/vm/sparc.ad b/hotspot/src/cpu/sparc/vm/sparc.ad index 5d26b664175..15526706f78 100644 --- a/hotspot/src/cpu/sparc/vm/sparc.ad +++ b/hotspot/src/cpu/sparc/vm/sparc.ad @@ -1860,6 +1860,17 @@ const bool Matcher::match_rule_supported(int opcode) { return true; // Per default match rules are supported. } +const bool Matcher::match_rule_supported_vector(int opcode, int vlen) { + + // TODO + // identify extra cases that we might want to provide match rules for + // e.g. Op_ vector nodes and other intrinsics while guarding with vlen + bool ret_value = match_rule_supported(opcode); + // Add rules here. + + return ret_value; // Per default match rules are supported. +} + const int Matcher::float_pressure(int default_pressure_threshold) { return default_pressure_threshold; } @@ -1905,7 +1916,7 @@ const bool Matcher::misaligned_vectors_ok() { } // Current (2013) SPARC platforms need to read original key -// to construct decryption expanded key +// to construct decryption expanded key const bool Matcher::pass_original_key_for_aes() { return true; } @@ -2612,7 +2623,7 @@ encode %{ if (stub == NULL && !(TraceJumps && Compile::current()->in_scratch_emit_size())) { ciEnv::current()->record_failure("CodeCache is full"); return; - } + } } %} @@ -3132,10 +3143,10 @@ ins_attrib ins_size(32); // Required size attribute (in bits) // AVOID_NONE - instruction can be placed anywhere // AVOID_BEFORE - instruction cannot be placed after an // instruction with MachNode::AVOID_AFTER -// AVOID_AFTER - the next instruction cannot be the one +// AVOID_AFTER - the next instruction cannot be the one // with MachNode::AVOID_BEFORE -// AVOID_BEFORE_AND_AFTER - BEFORE and AFTER attributes at -// the same time +// AVOID_BEFORE_AND_AFTER - BEFORE and AFTER attributes at +// the same time ins_attrib ins_avoid_back_to_back(MachNode::AVOID_NONE); ins_attrib ins_short_branch(0); // Required flag: is this instruction a diff --git a/hotspot/src/cpu/sparc/vm/stubGenerator_sparc.cpp b/hotspot/src/cpu/sparc/vm/stubGenerator_sparc.cpp index e64ffa4f295..df1b5be8097 100644 --- a/hotspot/src/cpu/sparc/vm/stubGenerator_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/stubGenerator_sparc.cpp @@ -5292,6 +5292,38 @@ class StubGenerator: public StubCodeGenerator { return start; } +/** + * Arguments: + * + * Inputs: + * O0 - int crc + * O1 - byte* buf + * O2 - int len + * O3 - int* table + * + * Output: + * O0 - int crc result + */ + address generate_updateBytesCRC32() { + assert(UseCRC32Intrinsics, "need VIS3 instructions"); + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "updateBytesCRC32"); + address start = __ pc(); + + const Register crc = O0; // crc + const Register buf = O1; // source java byte array address + const Register len = O2; // length + const Register table = O3; // crc_table address (reuse register) + + __ kernel_crc32(crc, buf, len, table); + + __ retl(); + __ delayed()->nop(); + + return start; + } + void generate_initial() { // Generates all stubs and initializes the entry points @@ -5324,6 +5356,12 @@ class StubGenerator: public StubCodeGenerator { // Build this early so it's available for the interpreter. StubRoutines::_throw_StackOverflowError_entry = generate_throw_exception("StackOverflowError throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_StackOverflowError)); + + if (UseCRC32Intrinsics) { + // set table address before stub generation which use it + StubRoutines::_crc_table_adr = (address)StubRoutines::Sparc::_crc_table; + StubRoutines::_updateBytesCRC32 = generate_updateBytesCRC32(); + } } diff --git a/hotspot/src/cpu/sparc/vm/stubRoutines_sparc.cpp b/hotspot/src/cpu/sparc/vm/stubRoutines_sparc.cpp index 63fb86a1509..3a9ca6fffc9 100644 --- a/hotspot/src/cpu/sparc/vm/stubRoutines_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/stubRoutines_sparc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2015, 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 @@ -52,3 +52,98 @@ address StubRoutines::Sparc::_stop_subroutine_entry = NULL; address StubRoutines::Sparc::_flush_callers_register_windows_entry = CAST_FROM_FN_PTR(address, bootstrap_flush_windows); address StubRoutines::Sparc::_partial_subtype_check = NULL; + +uint64_t StubRoutines::Sparc::_crc_by128_masks[] = +{ + /* The fields in this structure are arranged so that they can be + * picked up two at a time with 128-bit loads. + * + * Because of flipped bit order for this CRC polynomials + * the constant for X**N is left-shifted by 1. This is because + * a 64 x 64 polynomial multiply produces a 127-bit result + * but the highest term is always aligned to bit 0 in the container. + * Pre-shifting by one fixes this, at the cost of potentially making + * the 32-bit constant no longer fit in a 32-bit container (thus the + * use of uint64_t, though this is also the size used by the carry- + * less multiply instruction. + * + * In addition, the flipped bit order and highest-term-at-least-bit + * multiply changes the constants used. The 96-bit result will be + * aligned to the high-term end of the target 128-bit container, + * not the low-term end; that is, instead of a 512-bit or 576-bit fold, + * instead it is a 480 (=512-32) or 544 (=512+64-32) bit fold. + * + * This cause additional problems in the 128-to-64-bit reduction; see the + * code for details. By storing a mask in the otherwise unused half of + * a 128-bit constant, bits can be cleared before multiplication without + * storing and reloading. Note that staying on a 128-bit datapath means + * that some data is uselessly stored and some unused data is intersected + * with an irrelevant constant. + */ + + ((uint64_t) 0xffffffffUL), /* low of K_M_64 */ + ((uint64_t) 0xb1e6b092U << 1), /* high of K_M_64 */ + ((uint64_t) 0xba8ccbe8U << 1), /* low of K_160_96 */ + ((uint64_t) 0x6655004fU << 1), /* high of K_160_96 */ + ((uint64_t) 0xaa2215eaU << 1), /* low of K_544_480 */ + ((uint64_t) 0xe3720acbU << 1) /* high of K_544_480 */ +}; + +/** + * crc_table[] from jdk/src/java.base/share/native/libzip/zlib-1.2.8/crc32.h + */ +juint StubRoutines::Sparc::_crc_table[] = +{ + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +}; diff --git a/hotspot/src/cpu/sparc/vm/stubRoutines_sparc.hpp b/hotspot/src/cpu/sparc/vm/stubRoutines_sparc.hpp index eeeb31d608d..e28ec2ffb31 100644 --- a/hotspot/src/cpu/sparc/vm/stubRoutines_sparc.hpp +++ b/hotspot/src/cpu/sparc/vm/stubRoutines_sparc.hpp @@ -53,6 +53,9 @@ class Sparc { static address _flush_callers_register_windows_entry; static address _partial_subtype_check; + // masks and table for CRC32 + static uint64_t _crc_by128_masks[]; + static juint _crc_table[]; public: // test assembler stop routine by setting registers @@ -65,6 +68,8 @@ class Sparc { static intptr_t* (*flush_callers_register_windows_func())() { return CAST_TO_FN_PTR(intptr_t* (*)(void), _flush_callers_register_windows_entry); } static address partial_subtype_check() { return _partial_subtype_check; } + + static address crc_by128_masks_addr() { return (address)_crc_by128_masks; } }; #endif // CPU_SPARC_VM_STUBROUTINES_SPARC_HPP diff --git a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp index 703a1b0f30c..a76004f786d 100644 --- a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp @@ -803,6 +803,106 @@ address InterpreterGenerator::generate_Reference_get_entry(void) { return NULL; } +/** + * Method entry for static native methods: + * int java.util.zip.CRC32.update(int crc, int b) + */ +address InterpreterGenerator::generate_CRC32_update_entry() { + + if (UseCRC32Intrinsics) { + address entry = __ pc(); + + Label L_slow_path; + // If we need a safepoint check, generate full interpreter entry. + ExternalAddress state(SafepointSynchronize::address_of_state()); + __ set(ExternalAddress(SafepointSynchronize::address_of_state()), O2); + __ set(SafepointSynchronize::_not_synchronized, O3); + __ cmp_and_br_short(O2, O3, Assembler::notEqual, Assembler::pt, L_slow_path); + + // Load parameters + const Register crc = O0; // initial crc + const Register val = O1; // byte to update with + const Register table = O2; // address of 256-entry lookup table + + __ ldub(Gargs, 3, val); + __ lduw(Gargs, 8, crc); + + __ set(ExternalAddress(StubRoutines::crc_table_addr()), table); + + __ not1(crc); // ~crc + __ clruwu(crc); + __ update_byte_crc32(crc, val, table); + __ not1(crc); // ~crc + + // result in O0 + __ retl(); + __ delayed()->nop(); + + // generate a vanilla native entry as the slow path + __ bind(L_slow_path); + __ jump_to_entry(Interpreter::entry_for_kind(Interpreter::native)); + return entry; + } + return NULL; +} + +/** + * Method entry for static native methods: + * int java.util.zip.CRC32.updateBytes(int crc, byte[] b, int off, int len) + * int java.util.zip.CRC32.updateByteBuffer(int crc, long buf, int off, int len) + */ +address InterpreterGenerator::generate_CRC32_updateBytes_entry(AbstractInterpreter::MethodKind kind) { + + if (UseCRC32Intrinsics) { + address entry = __ pc(); + + Label L_slow_path; + // If we need a safepoint check, generate full interpreter entry. + ExternalAddress state(SafepointSynchronize::address_of_state()); + __ set(ExternalAddress(SafepointSynchronize::address_of_state()), O2); + __ set(SafepointSynchronize::_not_synchronized, O3); + __ cmp_and_br_short(O2, O3, Assembler::notEqual, Assembler::pt, L_slow_path); + + // Load parameters from the stack + const Register crc = O0; // initial crc + const Register buf = O1; // source java byte array address + const Register len = O2; // len + const Register offset = O3; // offset + + // Arguments are reversed on java expression stack + // Calculate address of start element + if (kind == Interpreter::java_util_zip_CRC32_updateByteBuffer) { + __ lduw(Gargs, 0, len); + __ lduw(Gargs, 8, offset); + __ ldx( Gargs, 16, buf); + __ lduw(Gargs, 32, crc); + __ add(buf, offset, buf); + } else { + __ lduw(Gargs, 0, len); + __ lduw(Gargs, 8, offset); + __ ldx( Gargs, 16, buf); + __ lduw(Gargs, 24, crc); + __ add(buf, arrayOopDesc::base_offset_in_bytes(T_BYTE), buf); // account for the header size + __ add(buf ,offset, buf); + } + + // Call the crc32 kernel + __ MacroAssembler::save_thread(L7_thread_cache); + __ kernel_crc32(crc, buf, len, O3); + __ MacroAssembler::restore_thread(L7_thread_cache); + + // result in O0 + __ retl(); + __ delayed()->nop(); + + // generate a vanilla native entry as the slow path + __ bind(L_slow_path); + __ jump_to_entry(Interpreter::entry_for_kind(Interpreter::native)); + return entry; + } + return NULL; +} + // // Interpreter stub for calling a native method. (asm interpreter) // This sets up a somewhat different looking stack for calling the native method diff --git a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp index 0438025fed3..e6a79435447 100644 --- a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp @@ -229,35 +229,35 @@ void VM_Version::initialize() { // SPARC T4 and above should have support for AES instructions if (has_aes()) { - if (UseVIS > 2) { // AES intrinsics use MOVxTOd/MOVdTOx which are VIS3 - if (FLAG_IS_DEFAULT(UseAES)) { - FLAG_SET_DEFAULT(UseAES, true); + if (FLAG_IS_DEFAULT(UseAES)) { + FLAG_SET_DEFAULT(UseAES, true); + } + if (!UseAES) { + if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { + warning("AES intrinsics require UseAES flag to be enabled. Intrinsics will be disabled."); } - if (FLAG_IS_DEFAULT(UseAESIntrinsics)) { - FLAG_SET_DEFAULT(UseAESIntrinsics, true); - } - // we disable both the AES flags if either of them is disabled on the command line - if (!UseAES || !UseAESIntrinsics) { - FLAG_SET_DEFAULT(UseAES, false); + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } else { + // The AES intrinsic stubs require AES instruction support (of course) + // but also require VIS3 mode or higher for instructions it use. + if (UseVIS > 2) { + if (FLAG_IS_DEFAULT(UseAESIntrinsics)) { + FLAG_SET_DEFAULT(UseAESIntrinsics, true); + } + } else { + if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { + warning("SPARC AES intrinsics require VIS3 instructions. Intrinsics will be disabled."); + } FLAG_SET_DEFAULT(UseAESIntrinsics, false); } - } else { - if (UseAES || UseAESIntrinsics) { - warning("SPARC AES intrinsics require VIS3 instruction support. Intrinsics will be disabled."); - if (UseAES) { - FLAG_SET_DEFAULT(UseAES, false); - } - if (UseAESIntrinsics) { - FLAG_SET_DEFAULT(UseAESIntrinsics, false); - } - } } } else if (UseAES || UseAESIntrinsics) { - warning("AES instructions are not available on this CPU"); - if (UseAES) { + if (UseAES && !FLAG_IS_DEFAULT(UseAES)) { + warning("AES instructions are not available on this CPU"); FLAG_SET_DEFAULT(UseAES, false); } - if (UseAESIntrinsics) { + if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { + warning("AES intrinsics are not available on this CPU"); FLAG_SET_DEFAULT(UseAESIntrinsics, false); } } @@ -347,6 +347,15 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseAdler32Intrinsics, false); } + if (UseVIS > 2) { + if (FLAG_IS_DEFAULT(UseCRC32Intrinsics)) { + FLAG_SET_DEFAULT(UseCRC32Intrinsics, true); + } + } else if (UseCRC32Intrinsics) { + warning("SPARC CRC32 intrinsics require VIS3 insructions support. Intriniscs will be disabled"); + FLAG_SET_DEFAULT(UseCRC32Intrinsics, false); + } + if (FLAG_IS_DEFAULT(ContendedPaddingWidth) && (cache_line_size > ContendedPaddingWidth)) ContendedPaddingWidth = cache_line_size; @@ -358,7 +367,6 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseUnalignedAccesses, false); } -#ifndef PRODUCT if (PrintMiscellaneous && Verbose) { tty->print_cr("L1 data cache line size: %u", L1_data_cache_line_size()); tty->print_cr("L2 data cache line size: %u", L2_data_cache_line_size()); @@ -391,7 +399,6 @@ void VM_Version::initialize() { tty->print_cr("ContendedPaddingWidth %d", (int) ContendedPaddingWidth); } } -#endif // PRODUCT } void VM_Version::print_features() { @@ -400,7 +407,7 @@ void VM_Version::print_features() { int VM_Version::determine_features() { if (UseV8InstrsOnly) { - NOT_PRODUCT(if (PrintMiscellaneous && Verbose) tty->print_cr("Version is Forced-V8");) + if (PrintMiscellaneous && Verbose) { tty->print_cr("Version is Forced-V8"); } return generic_v8_m; } @@ -416,12 +423,12 @@ int VM_Version::determine_features() { if (is_T_family(features)) { // Happy to accomodate... } else { - NOT_PRODUCT(if (PrintMiscellaneous && Verbose) tty->print_cr("Version is Forced-Niagara");) + if (PrintMiscellaneous && Verbose) { tty->print_cr("Version is Forced-Niagara"); } features |= T_family_m; } } else { if (is_T_family(features) && !FLAG_IS_DEFAULT(UseNiagaraInstrs)) { - NOT_PRODUCT(if (PrintMiscellaneous && Verbose) tty->print_cr("Version is Forced-Not-Niagara");) + if (PrintMiscellaneous && Verbose) { tty->print_cr("Version is Forced-Not-Niagara"); } features &= ~(T_family_m | T1_model_m); } else { // Happy to accomodate... diff --git a/hotspot/src/cpu/x86/vm/assembler_x86.cpp b/hotspot/src/cpu/x86/vm/assembler_x86.cpp index 88db2698cda..015a66b7900 100644 --- a/hotspot/src/cpu/x86/vm/assembler_x86.cpp +++ b/hotspot/src/cpu/x86/vm/assembler_x86.cpp @@ -313,7 +313,7 @@ bool Assembler::query_compressed_disp_byte(int disp, bool is_evex_inst, int vect switch (cur_tuple_type) { case EVEX_FV: if ((cur_encoding & VEX_W) == VEX_W) { - mod_idx += 2 + ((cur_encoding & EVEX_Rb) == EVEX_Rb) ? 1 : 0; + mod_idx = ((cur_encoding & EVEX_Rb) == EVEX_Rb) ? 3 : 2; } else { mod_idx = ((cur_encoding & EVEX_Rb) == EVEX_Rb) ? 1 : 0; } @@ -394,25 +394,27 @@ bool Assembler::emit_compressed_disp_byte(int &disp) { int mod_idx = 0; // We will test if the displacement fits the compressed format and if so // apply the compression to the displacment iff the result is8bit. - if (VM_Version::supports_evex() && _is_evex_instruction) { - switch (_tuple_type) { + if (VM_Version::supports_evex() && (_attributes != NULL) && _attributes->is_evex_instruction()) { + int evex_encoding = _attributes->get_evex_encoding(); + int tuple_type = _attributes->get_tuple_type(); + switch (tuple_type) { case EVEX_FV: - if ((_evex_encoding & VEX_W) == VEX_W) { - mod_idx += 2 + ((_evex_encoding & EVEX_Rb) == EVEX_Rb) ? 1 : 0; + if ((evex_encoding & VEX_W) == VEX_W) { + mod_idx = ((evex_encoding & EVEX_Rb) == EVEX_Rb) ? 3 : 2; } else { - mod_idx = ((_evex_encoding & EVEX_Rb) == EVEX_Rb) ? 1 : 0; + mod_idx = ((evex_encoding & EVEX_Rb) == EVEX_Rb) ? 1 : 0; } break; case EVEX_HV: - mod_idx = ((_evex_encoding & EVEX_Rb) == EVEX_Rb) ? 1 : 0; + mod_idx = ((evex_encoding & EVEX_Rb) == EVEX_Rb) ? 1 : 0; break; case EVEX_FVM: break; case EVEX_T1S: - switch (_input_size_in_bits) { + switch (_attributes->get_input_size()) { case EVEX_8bit: break; @@ -433,7 +435,7 @@ bool Assembler::emit_compressed_disp_byte(int &disp) { case EVEX_T1F: case EVEX_T2: case EVEX_T4: - mod_idx = (_input_size_in_bits == EVEX_64bit) ? 1 : 0; + mod_idx = (_attributes->get_input_size() == EVEX_64bit) ? 1 : 0; break; case EVEX_T8: @@ -459,8 +461,9 @@ bool Assembler::emit_compressed_disp_byte(int &disp) { break; } - if (_avx_vector_len >= AVX_128bit && _avx_vector_len <= AVX_512bit) { - int disp_factor = tuple_table[_tuple_type + mod_idx][_avx_vector_len]; + int vector_len = _attributes->get_vector_len(); + if (vector_len >= AVX_128bit && vector_len <= AVX_512bit) { + int disp_factor = tuple_table[tuple_type + mod_idx][vector_len]; if ((disp % disp_factor) == 0) { int new_disp = disp / disp_factor; if (is8bit(new_disp)) { @@ -591,7 +594,6 @@ void Assembler::emit_operand(Register reg, Register base, Register index, emit_data(disp, rspec, disp32_operand); } } - _is_evex_instruction = false; } void Assembler::emit_operand(XMMRegister reg, Register base, Register index, @@ -770,7 +772,7 @@ address Assembler::locate_operand(address inst, WhichOperand which) { case 0x55: // andnps case 0x56: // orps case 0x57: // xorps - case 0x59: //mulpd + case 0x59: // mulpd case 0x6E: // movd case 0x7E: // movd case 0xAE: // ldmxcsr, stmxcsr, fxrstor, fxsave, clflush @@ -1234,51 +1236,53 @@ void Assembler::addr_nop_8() { void Assembler::addsd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x58, dst, src, VEX_SIMD_F2); - } else { - emit_simd_arith(0x58, dst, src, VEX_SIMD_F2); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x58); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::addsd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - emit_simd_arith_q(0x58, dst, src, VEX_SIMD_F2); - } else { - emit_simd_arith(0x58, dst, src, VEX_SIMD_F2); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x58); + emit_operand(dst, src); } void Assembler::addss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - emit_simd_arith(0x58, dst, src, VEX_SIMD_F3); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x58); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::addss(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } - emit_simd_arith(0x58, dst, src, VEX_SIMD_F3); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x58); + emit_operand(dst, src); } void Assembler::aesdec(XMMRegister dst, Address src) { assert(VM_Version::supports_aes(), ""); InstructionMark im(this); - simd_prefix(dst, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_38, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDE); emit_operand(dst, src); } void Assembler::aesdec(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_aes(), ""); - int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_38, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDE); emit_int8(0xC0 | encode); } @@ -1286,16 +1290,16 @@ void Assembler::aesdec(XMMRegister dst, XMMRegister src) { void Assembler::aesdeclast(XMMRegister dst, Address src) { assert(VM_Version::supports_aes(), ""); InstructionMark im(this); - simd_prefix(dst, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_38, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDF); emit_operand(dst, src); } void Assembler::aesdeclast(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_aes(), ""); - int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_38, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDF); emit_int8((unsigned char)(0xC0 | encode)); } @@ -1303,16 +1307,16 @@ void Assembler::aesdeclast(XMMRegister dst, XMMRegister src) { void Assembler::aesenc(XMMRegister dst, Address src) { assert(VM_Version::supports_aes(), ""); InstructionMark im(this); - simd_prefix(dst, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_38, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDC); emit_operand(dst, src); } void Assembler::aesenc(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_aes(), ""); - int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_38, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDC); emit_int8(0xC0 | encode); } @@ -1320,16 +1324,16 @@ void Assembler::aesenc(XMMRegister dst, XMMRegister src) { void Assembler::aesenclast(XMMRegister dst, Address src) { assert(VM_Version::supports_aes(), ""); InstructionMark im(this); - simd_prefix(dst, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_38, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDD); emit_operand(dst, src); } void Assembler::aesenclast(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_aes(), ""); - int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_38, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xDD); emit_int8((unsigned char)(0xC0 | encode)); } @@ -1361,15 +1365,17 @@ void Assembler::andl(Register dst, Register src) { void Assembler::andnl(Register dst, Register src1, Register src2) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - int encode = vex_prefix_0F38_and_encode_legacy(dst, src1, src2); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF2); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::andnl(Register dst, Register src1, Address src2) { - InstructionMark im(this); assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - vex_prefix_0F38_legacy(dst, src1, src2); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + vex_prefix(src2, src1->encoding(), dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF2); emit_operand(dst, src2); } @@ -1396,45 +1402,51 @@ void Assembler::bswapl(Register reg) { // bswap void Assembler::blsil(Register dst, Register src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - int encode = vex_prefix_0F38_and_encode_legacy(rbx, dst, src); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(rbx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::blsil(Register dst, Address src) { - InstructionMark im(this); assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - vex_prefix_0F38_legacy(rbx, dst, src); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + vex_prefix(src, dst->encoding(), rbx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_operand(rbx, src); } void Assembler::blsmskl(Register dst, Register src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - int encode = vex_prefix_0F38_and_encode_legacy(rdx, dst, src); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(rdx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::blsmskl(Register dst, Address src) { - InstructionMark im(this); assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - vex_prefix_0F38_legacy(rdx, dst, src); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + vex_prefix(src, dst->encoding(), rdx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_operand(rdx, src); } void Assembler::blsrl(Register dst, Register src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - int encode = vex_prefix_0F38_and_encode_legacy(rcx, dst, src); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(rcx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::blsrl(Register dst, Address src) { - InstructionMark im(this); assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - vex_prefix_0F38_legacy(rcx, dst, src); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + vex_prefix(src, dst->encoding(), rcx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_operand(rcx, src); } @@ -1581,36 +1593,38 @@ void Assembler::comisd(XMMRegister dst, Address src) { // NOTE: dbx seems to decode this as comiss even though the // 0x66 is there. Strangly ucomisd comes out correct NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - emit_simd_arith_nonds_q(0x2F, dst, src, VEX_SIMD_66, /* no_mask_reg */ true); - } else { - emit_simd_arith_nonds(0x2F, dst, src, VEX_SIMD_66); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);; + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x2F); + emit_operand(dst, src); } void Assembler::comisd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_nonds_q(0x2F, dst, src, VEX_SIMD_66, /* no_mask_reg */ true); - } else { - emit_simd_arith_nonds(0x2F, dst, src, VEX_SIMD_66); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x2F); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::comiss(XMMRegister dst, Address src) { - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } NOT_LP64(assert(VM_Version::supports_sse(), "")); - emit_simd_arith_nonds(0x2F, dst, src, VEX_SIMD_NONE, /* no_mask_reg */ true); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, xnoreg, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x2F); + emit_operand(dst, src); } void Assembler::comiss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - emit_simd_arith_nonds(0x2F, dst, src, VEX_SIMD_NONE, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x2F); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::cpuid() { @@ -1699,100 +1713,113 @@ void Assembler::crc32(Register crc, Address adr, int8_t sizeInBytes) { void Assembler::cvtdq2pd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith_nonds(0xE6, dst, src, VEX_SIMD_F3, /* no_mask_reg */ false, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xE6); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::cvtdq2ps(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith_nonds(0x5B, dst, src, VEX_SIMD_NONE, /* no_mask_reg */ false, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x5B); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::cvtsd2ss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x5A, dst, src, VEX_SIMD_F2); - } else { - emit_simd_arith(0x5A, dst, src, VEX_SIMD_F2); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x5A); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::cvtsd2ss(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1F; - _input_size_in_bits = EVEX_64bit; - emit_simd_arith_q(0x5A, dst, src, VEX_SIMD_F2); - } else { - emit_simd_arith(0x5A, dst, src, VEX_SIMD_F2); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x5A); + emit_operand(dst, src); } void Assembler::cvtsi2sdl(XMMRegister dst, Register src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VM_Version::supports_evex()); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x2A); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::cvtsi2sdl(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - emit_simd_arith(0x2A, dst, src, VEX_SIMD_F2, /* no_mask_reg */ true); - } else { - emit_simd_arith(0x2A, dst, src, VEX_SIMD_F2); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x2A); + emit_operand(dst, src); } void Assembler::cvtsi2ssl(XMMRegister dst, Register src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x2A); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::cvtsi2ssl(XMMRegister dst, Address src) { - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } NOT_LP64(assert(VM_Version::supports_sse(), "")); - emit_simd_arith(0x2A, dst, src, VEX_SIMD_F3, /* no_mask_reg */ true); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x2A); + emit_operand(dst, src); } void Assembler::cvtsi2ssq(XMMRegister dst, Register src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - int encode = simd_prefix_and_encode_q(dst, dst, src, VEX_SIMD_F3, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x2A); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0x5A, dst, src, VEX_SIMD_F3); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x5A); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::cvtss2sd(XMMRegister dst, Address src) { - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0x5A, dst, src, VEX_SIMD_F3); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x5A); + emit_operand(dst, src); } void Assembler::cvttsd2sil(Register dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - int encode = simd_prefix_and_encode(dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x2C); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::cvttss2sil(Register dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - int encode = simd_prefix_and_encode(dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x2C); emit_int8((unsigned char)(0xC0 | encode)); } @@ -1807,36 +1834,38 @@ void Assembler::decl(Address dst) { void Assembler::divsd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - emit_simd_arith_q(0x5E, dst, src, VEX_SIMD_F2); - } else { - emit_simd_arith(0x5E, dst, src, VEX_SIMD_F2); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x5E); + emit_operand(dst, src); } void Assembler::divsd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x5E, dst, src, VEX_SIMD_F2); - } else { - emit_simd_arith(0x5E, dst, src, VEX_SIMD_F2); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x5E); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::divss(XMMRegister dst, Address src) { - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } NOT_LP64(assert(VM_Version::supports_sse(), "")); - emit_simd_arith(0x5E, dst, src, VEX_SIMD_F3); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x5E); + emit_operand(dst, src); } void Assembler::divss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - emit_simd_arith(0x5E, dst, src, VEX_SIMD_F3); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x5E); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::emms() { @@ -2082,36 +2111,26 @@ void Assembler::mov(Register dst, Register src) { void Assembler::movapd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_avx512novl()) { - int vector_len = AVX_512bit; - int dst_enc = dst->encoding(); - int src_enc = src->encoding(); - int encode = vex_prefix_and_encode(dst_enc, 0, src_enc, VEX_SIMD_66, VEX_OPCODE_0F, - /* vex_w */ true, vector_len, /* legacy_mode */ false, /* no_mask_reg */ false); - emit_int8(0x28); - emit_int8((unsigned char)(0xC0 | encode)); - } else if (VM_Version::supports_evex()) { - emit_simd_arith_nonds_q(0x28, dst, src, VEX_SIMD_66); - } else { - emit_simd_arith_nonds(0x28, dst, src, VEX_SIMD_66); - } + int vector_len = VM_Version::supports_avx512novl() ? AVX_512bit : AVX_128bit; + InstructionAttr attributes(vector_len, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x28); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::movaps(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - if (VM_Version::supports_avx512novl()) { - int vector_len = AVX_512bit; - int encode = vex_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_NONE, vector_len); - emit_int8(0x28); - emit_int8((unsigned char)(0xC0 | encode)); - } else { - emit_simd_arith_nonds(0x28, dst, src, VEX_SIMD_NONE); - } + int vector_len = VM_Version::supports_avx512novl() ? AVX_512bit : AVX_128bit; + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x28); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::movlhps(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - int encode = simd_prefix_and_encode(dst, src, src, VEX_SIMD_NONE, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, src, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); emit_int8(0x16); emit_int8((unsigned char)(0xC0 | encode)); } @@ -2125,48 +2144,18 @@ void Assembler::movb(Register dst, Address src) { } void Assembler::movddup(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse3(), "")); - int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_F2, /* no_mask_reg */ false, VEX_OPCODE_0F, - /* rex_w */ VM_Version::supports_evex(), AVX_128bit, /* legacy_mode */ false); + int vector_len = VM_Version::supports_evex() ? AVX_512bit : AVX_128bit; + InstructionAttr attributes(vector_len, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x12); emit_int8(0xC0 | encode); - } -void Assembler::kmovql(KRegister dst, KRegister src) { +void Assembler::kmovwl(KRegister dst, Register src) { NOT_LP64(assert(VM_Version::supports_evex(), "")); - int encode = kreg_prefix_and_encode(dst, knoreg, src, VEX_SIMD_NONE, - /* no_mask_reg */ true, VEX_OPCODE_0F, /* rex_w */ true); - emit_int8((unsigned char)0x90); - emit_int8((unsigned char)(0xC0 | encode)); -} - -void Assembler::kmovql(KRegister dst, Address src) { - NOT_LP64(assert(VM_Version::supports_evex(), "")); - int dst_enc = dst->encoding(); - int nds_enc = 0; - vex_prefix(src, nds_enc, dst_enc, VEX_SIMD_NONE, - VEX_OPCODE_0F, /* vex_w */ true, AVX_128bit, /* legacy_mode */ true, /* no_reg_mask */ true); - emit_int8((unsigned char)0x90); - emit_operand((Register)dst, src); -} - -void Assembler::kmovql(Address dst, KRegister src) { - NOT_LP64(assert(VM_Version::supports_evex(), "")); - int src_enc = src->encoding(); - int nds_enc = 0; - vex_prefix(dst, nds_enc, src_enc, VEX_SIMD_NONE, - VEX_OPCODE_0F, /* vex_w */ true, AVX_128bit, /* legacy_mode */ true, /* no_reg_mask */ true); - emit_int8((unsigned char)0x90); - emit_operand((Register)src, dst); -} - -void Assembler::kmovql(KRegister dst, Register src) { - NOT_LP64(assert(VM_Version::supports_evex(), "")); - VexSimdPrefix pre = !_legacy_mode_bw ? VEX_SIMD_F2 : VEX_SIMD_NONE; - int encode = kreg_prefix_and_encode(dst, knoreg, src, pre, /* no_mask_reg */ true, - VEX_OPCODE_0F, /* legacy_mode */ !_legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = kreg_prefix_and_encode(dst, knoreg, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); emit_int8((unsigned char)0x92); emit_int8((unsigned char)(0xC0 | encode)); } @@ -2174,18 +2163,83 @@ void Assembler::kmovql(KRegister dst, Register src) { void Assembler::kmovdl(KRegister dst, Register src) { NOT_LP64(assert(VM_Version::supports_evex(), "")); VexSimdPrefix pre = !_legacy_mode_bw ? VEX_SIMD_F2 : VEX_SIMD_NONE; - int encode = kreg_prefix_and_encode(dst, knoreg, src, pre, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = kreg_prefix_and_encode(dst, knoreg, src, pre, VEX_OPCODE_0F, &attributes); emit_int8((unsigned char)0x92); emit_int8((unsigned char)(0xC0 | encode)); } -void Assembler::kmovwl(KRegister dst, Register src) { +void Assembler::kmovql(KRegister dst, KRegister src) { NOT_LP64(assert(VM_Version::supports_evex(), "")); - int encode = kreg_prefix_and_encode(dst, knoreg, src, VEX_SIMD_NONE, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = kreg_prefix_and_encode(dst, knoreg, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0x90); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::kmovql(KRegister dst, Address src) { + NOT_LP64(assert(VM_Version::supports_evex(), "")); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0x90); + emit_operand((Register)dst, src); +} + +void Assembler::kmovql(Address dst, KRegister src) { + NOT_LP64(assert(VM_Version::supports_evex(), "")); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0x90); + emit_operand((Register)src, dst); +} + +void Assembler::kmovql(KRegister dst, Register src) { + NOT_LP64(assert(VM_Version::supports_evex(), "")); + VexSimdPrefix pre = !_legacy_mode_bw ? VEX_SIMD_F2 : VEX_SIMD_NONE; + InstructionAttr attributes(AVX_128bit, /* rex_w */ !_legacy_mode_bw, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = kreg_prefix_and_encode(dst, knoreg, src, pre, VEX_OPCODE_0F, &attributes); emit_int8((unsigned char)0x92); emit_int8((unsigned char)(0xC0 | encode)); } +// This instruction produces ZF or CF flags +void Assembler::kortestbl(KRegister src1, KRegister src2) { + NOT_LP64(assert(VM_Version::supports_avx512dq(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = kreg_prefix_and_encode(src1, knoreg, src2, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0x98); + emit_int8((unsigned char)(0xC0 | encode)); +} + +// This instruction produces ZF or CF flags +void Assembler::kortestwl(KRegister src1, KRegister src2) { + NOT_LP64(assert(VM_Version::supports_evex(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = kreg_prefix_and_encode(src1, knoreg, src2, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0x98); + emit_int8((unsigned char)(0xC0 | encode)); +} + +// This instruction produces ZF or CF flags +void Assembler::kortestdl(KRegister src1, KRegister src2) { + NOT_LP64(assert(VM_Version::supports_avx512bw(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = kreg_prefix_and_encode(src1, knoreg, src2, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0x98); + emit_int8((unsigned char)(0xC0 | encode)); +} + +// This instruction produces ZF or CF flags +void Assembler::kortestql(KRegister src1, KRegister src2) { + NOT_LP64(assert(VM_Version::supports_avx512bw(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = kreg_prefix_and_encode(src1, knoreg, src2, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0x98); + emit_int8((unsigned char)(0xC0 | encode)); +} + void Assembler::movb(Address dst, int imm8) { InstructionMark im(this); prefix(dst); @@ -2205,190 +2259,231 @@ void Assembler::movb(Address dst, Register src) { void Assembler::movdl(XMMRegister dst, Register src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - int encode = simd_prefix_and_encode(dst, src, VEX_SIMD_66, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, as_XMMRegister(src->encoding()), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x6E); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::movdl(Register dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); // swap src/dst to get correct prefix - int encode = simd_prefix_and_encode(src, dst, VEX_SIMD_66, /* no_mask_reg */ true); + int encode = simd_prefix_and_encode(src, xnoreg, as_XMMRegister(dst->encoding()), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x7E); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::movdl(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } InstructionMark im(this); - simd_prefix(dst, src, VEX_SIMD_66, /* no_reg_mask */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x6E); emit_operand(dst, src); } void Assembler::movdl(Address dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } InstructionMark im(this); - simd_prefix(dst, src, VEX_SIMD_66, /* no_reg_mask */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(src, xnoreg, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x7E); emit_operand(src, dst); } void Assembler::movdqa(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith_nonds(0x6F, dst, src, VEX_SIMD_66); + int vector_len = VM_Version::supports_avx512novl() ? AVX_512bit : AVX_128bit; + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x6F); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::movdqa(XMMRegister dst, Address src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FVM; - } - emit_simd_arith_nonds(0x6F, dst, src, VEX_SIMD_66); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + simd_prefix(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x6F); + emit_operand(dst, src); } void Assembler::movdqu(XMMRegister dst, Address src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FVM; - } - emit_simd_arith_nonds(0x6F, dst, src, VEX_SIMD_F3); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + simd_prefix(dst, xnoreg, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x6F); + emit_operand(dst, src); } void Assembler::movdqu(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith_nonds(0x6F, dst, src, VEX_SIMD_F3); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x6F); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::movdqu(Address dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FVM; - } InstructionMark im(this); - simd_prefix(dst, src, VEX_SIMD_F3, /* no_mask_reg */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + simd_prefix(src, xnoreg, dst, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x7F); emit_operand(src, dst); } // Move Unaligned 256bit Vector void Assembler::vmovdqu(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; assert(UseAVX > 0, ""); - int vector_len = AVX_256bit; - int encode = vex_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_F3, vector_len); + InstructionAttr attributes(AVX_256bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x6F); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vmovdqu(XMMRegister dst, Address src) { - _instruction_uses_vl = true; assert(UseAVX > 0, ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FVM; - } InstructionMark im(this); - int vector_len = AVX_256bit; - vex_prefix(dst, xnoreg, src, VEX_SIMD_F3, vector_len); + InstructionAttr attributes(AVX_256bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x6F); emit_operand(dst, src); } void Assembler::vmovdqu(Address dst, XMMRegister src) { - _instruction_uses_vl = true; assert(UseAVX > 0, ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FVM; - } InstructionMark im(this); - int vector_len = AVX_256bit; + InstructionAttr attributes(AVX_256bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); // swap src<->dst for encoding assert(src != xnoreg, "sanity"); - vex_prefix(src, xnoreg, dst, VEX_SIMD_F3, vector_len); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x7F); emit_operand(src, dst); } // Move Unaligned EVEX enabled Vector (programmable : 8,16,32,64) +void Assembler::evmovdqub(XMMRegister dst, XMMRegister src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x6F); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::evmovdqub(XMMRegister dst, Address src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x6F); + emit_operand(dst, src); +} + +void Assembler::evmovdqub(Address dst, XMMRegister src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(src != xnoreg, "sanity"); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x7F); + emit_operand(src, dst); +} + +void Assembler::evmovdquw(XMMRegister dst, XMMRegister src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x6F); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::evmovdquw(XMMRegister dst, Address src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x6F); + emit_operand(dst, src); +} + +void Assembler::evmovdquw(Address dst, XMMRegister src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(src != xnoreg, "sanity"); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x7F); + emit_operand(src, dst); +} void Assembler::evmovdqul(XMMRegister dst, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 0, ""); - int src_enc = src->encoding(); - int dst_enc = dst->encoding(); - int encode = vex_prefix_and_encode(dst_enc, 0, src_enc, VEX_SIMD_F3, VEX_OPCODE_0F, - /* vex_w */ false, vector_len, /* legacy_mode */ false, /* no_mask_reg */ false); + assert(VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x6F); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::evmovdqul(XMMRegister dst, Address src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 0, ""); + assert(VM_Version::supports_evex(), ""); InstructionMark im(this); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FVM; - } - vex_prefix(dst, xnoreg, src, VEX_SIMD_F3, vector_len); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x6F); emit_operand(dst, src); } void Assembler::evmovdqul(Address dst, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 0, ""); - InstructionMark im(this); + assert(VM_Version::supports_evex(), ""); assert(src != xnoreg, "sanity"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FVM; - } - // swap src<->dst for encoding - vex_prefix(src, xnoreg, dst, VEX_SIMD_F3, vector_len); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x7F); emit_operand(src, dst); } void Assembler::evmovdquq(XMMRegister dst, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 0, ""); - int src_enc = src->encoding(); - int dst_enc = dst->encoding(); - int encode = vex_prefix_and_encode(dst_enc, 0, src_enc, VEX_SIMD_F3, VEX_OPCODE_0F, - /* vex_w */ true, vector_len, /* legacy_mode */ false, /* no_mask_reg */ false); + assert(VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x6F); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::evmovdquq(XMMRegister dst, Address src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 2, ""); + assert(VM_Version::supports_evex(), ""); InstructionMark im(this); - _tuple_type = EVEX_FVM; - vex_prefix_q(dst, xnoreg, src, VEX_SIMD_F3, vector_len); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x6F); emit_operand(dst, src); } void Assembler::evmovdquq(Address dst, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 2, ""); - InstructionMark im(this); + assert(VM_Version::supports_evex(), ""); assert(src != xnoreg, "sanity"); - _tuple_type = EVEX_FVM; - // swap src<->dst for encoding - vex_prefix_q(src, xnoreg, dst, VEX_SIMD_F3, vector_len); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x7F); emit_operand(src, dst); } @@ -2434,13 +2529,12 @@ void Assembler::movl(Address dst, Register src) { // The selection is done in MacroAssembler::movdbl() and movflt(). void Assembler::movlpd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - emit_simd_arith_q(0x12, dst, src, VEX_SIMD_66, /* no_mask_reg */ true); - } else { - emit_simd_arith(0x12, dst, src, VEX_SIMD_66, /* no_mask_reg */ true); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x12); + emit_operand(dst, src); } void Assembler::movq( MMXRegister dst, Address src ) { @@ -2466,13 +2560,9 @@ void Assembler::movq( Address dst, MMXRegister src ) { void Assembler::movq(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionMark im(this); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - simd_prefix_q(dst, xnoreg, src, VEX_SIMD_F3, /* no_mask_reg */ true); - } else { - simd_prefix(dst, src, VEX_SIMD_F3, /* no_mask_reg */ true); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(dst, xnoreg, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x7E); emit_operand(dst, src); } @@ -2480,14 +2570,9 @@ void Assembler::movq(XMMRegister dst, Address src) { void Assembler::movq(Address dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionMark im(this); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - simd_prefix(src, xnoreg, dst, VEX_SIMD_66, /* no_mask_reg */ true, - VEX_OPCODE_0F, /* rex_w */ true); - } else { - simd_prefix(dst, src, VEX_SIMD_66, /* no_mask_reg */ true); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(src, xnoreg, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8((unsigned char)0xD6); emit_operand(src, dst); } @@ -2510,60 +2595,56 @@ void Assembler::movsbl(Register dst, Register src) { // movsxb void Assembler::movsd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x10, dst, src, VEX_SIMD_F2, /* no_mask_reg */ true); - } else { - emit_simd_arith(0x10, dst, src, VEX_SIMD_F2); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x10); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::movsd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - emit_simd_arith_nonds_q(0x10, dst, src, VEX_SIMD_F2, /* no_mask_reg */ true); - } else { - emit_simd_arith_nonds(0x10, dst, src, VEX_SIMD_F2); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(dst, xnoreg, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x10); + emit_operand(dst, src); } void Assembler::movsd(Address dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionMark im(this); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - simd_prefix_q(src, xnoreg, dst, VEX_SIMD_F2); - } else { - simd_prefix(src, xnoreg, dst, VEX_SIMD_F2, /* no_mask_reg */ false); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(src, xnoreg, dst, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x11); emit_operand(src, dst); } void Assembler::movss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - emit_simd_arith(0x10, dst, src, VEX_SIMD_F3, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x10); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::movss(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } - emit_simd_arith_nonds(0x10, dst, src, VEX_SIMD_F3, /* no_mask_reg */ true); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, xnoreg, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x10); + emit_operand(dst, src); } void Assembler::movss(Address dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } InstructionMark im(this); - simd_prefix(dst, src, VEX_SIMD_F3, /* no_mask_reg */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(src, xnoreg, dst, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x11); emit_operand(src, dst); } @@ -2655,36 +2736,38 @@ void Assembler::mull(Register src) { void Assembler::mulsd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - emit_simd_arith_q(0x59, dst, src, VEX_SIMD_F2); - } else { - emit_simd_arith(0x59, dst, src, VEX_SIMD_F2); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x59); + emit_operand(dst, src); } void Assembler::mulsd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x59, dst, src, VEX_SIMD_F2); - } else { - emit_simd_arith(0x59, dst, src, VEX_SIMD_F2); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x59); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::mulss(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } - emit_simd_arith(0x59, dst, src, VEX_SIMD_F3); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x59); + emit_operand(dst, src); } void Assembler::mulss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - emit_simd_arith(0x59, dst, src, VEX_SIMD_F3); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x59); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::negl(Register dst) { @@ -2985,28 +3068,35 @@ void Assembler::orl(Address dst, Register src) { void Assembler::packuswb(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); assert((UseAVX > 0), "SSE mode requires address alignment 16 bytes"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } - emit_simd_arith(0x67, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x67); + emit_operand(dst, src); } void Assembler::packuswb(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0x67, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x67); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpackuswb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(UseAVX > 0, "some form of AVX must be enabled"); - emit_vex_arith(0x67, dst, nds, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x67); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpermq(XMMRegister dst, XMMRegister src, int imm8, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx2(), ""); - int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_3A, /* rex_w */ true, vector_len); + InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x00); emit_int8(0xC0 | encode); emit_int8(imm8); @@ -3020,8 +3110,8 @@ void Assembler::pause() { void Assembler::pcmpestri(XMMRegister dst, Address src, int imm8) { assert(VM_Version::supports_sse4_2(), ""); InstructionMark im(this); - simd_prefix(dst, xnoreg, src, VEX_SIMD_66, /* no_mask_reg */ false, VEX_OPCODE_0F_3A, - /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + simd_prefix(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x61); emit_operand(dst, src); emit_int8(imm8); @@ -3029,46 +3119,162 @@ void Assembler::pcmpestri(XMMRegister dst, Address src, int imm8) { void Assembler::pcmpestri(XMMRegister dst, XMMRegister src, int imm8) { assert(VM_Version::supports_sse4_2(), ""); - int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_3A, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x61); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(imm8); } -void Assembler::pcmpeqw(XMMRegister dst, XMMRegister src) { +// In this context, the dst vector contains the components that are equal, non equal components are zeroed in dst +void Assembler::pcmpeqb(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0x75, dst, src, VEX_SIMD_66, - false, (VM_Version::supports_avx512dq() == false)); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x74); + emit_int8((unsigned char)(0xC0 | encode)); } +// In this context, the dst vector contains the components that are equal, non equal components are zeroed in dst +void Assembler::vpcmpeqb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x74); + emit_int8((unsigned char)(0xC0 | encode)); +} + +// In this context, kdst is written the mask used to process the equal components +void Assembler::evpcmpeqb(KRegister kdst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx512bw(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(kdst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x74); + emit_int8((unsigned char)(0xC0 | encode)); +} + +// In this context, the dst vector contains the components that are equal, non equal components are zeroed in dst +void Assembler::pcmpeqw(XMMRegister dst, XMMRegister src) { + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x75); + emit_int8((unsigned char)(0xC0 | encode)); +} + +// In this context, the dst vector contains the components that are equal, non equal components are zeroed in dst void Assembler::vpcmpeqw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - assert(UseAVX > 0, "some form of AVX must be enabled"); - emit_vex_arith(0x75, dst, nds, src, VEX_SIMD_66, vector_len, - false, (VM_Version::supports_avx512dq() == false)); + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x75); + emit_int8((unsigned char)(0xC0 | encode)); +} + +// In this context, kdst is written the mask used to process the equal components +void Assembler::evpcmpeqw(KRegister kdst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx512bw(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(kdst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x75); + emit_int8((unsigned char)(0xC0 | encode)); +} + +// In this context, the dst vector contains the components that are equal, non equal components are zeroed in dst +void Assembler::pcmpeqd(XMMRegister dst, XMMRegister src) { + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x76); + emit_int8((unsigned char)(0xC0 | encode)); +} + +// In this context, the dst vector contains the components that are equal, non equal components are zeroed in dst +void Assembler::vpcmpeqd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x76); + emit_int8((unsigned char)(0xC0 | encode)); +} + +// In this context, kdst is written the mask used to process the equal components +void Assembler::evpcmpeqd(KRegister kdst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(kdst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x76); + emit_int8((unsigned char)(0xC0 | encode)); +} + +// In this context, the dst vector contains the components that are equal, non equal components are zeroed in dst +void Assembler::pcmpeqq(XMMRegister dst, XMMRegister src) { + NOT_LP64(assert(VM_Version::supports_sse4_1(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8(0x29); + emit_int8((unsigned char)(0xC0 | encode)); +} + +// In this context, the dst vector contains the components that are equal, non equal components are zeroed in dst +void Assembler::vpcmpeqq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_avx(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8(0x29); + emit_int8((unsigned char)(0xC0 | encode)); +} + +// In this context, kdst is written the mask used to process the equal components +void Assembler::evpcmpeqq(KRegister kdst, XMMRegister nds, XMMRegister src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(kdst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8(0x29); + emit_int8((unsigned char)(0xC0 | encode)); +} + +// In this context, kdst is written the mask used to process the equal components +void Assembler::evpcmpeqq(KRegister kdst, XMMRegister nds, Address src, int vector_len) { + assert(VM_Version::supports_evex(), ""); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int dst_enc = kdst->encoding(); + vex_prefix(src, nds_enc, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); + emit_int8(0x29); + emit_operand(as_Register(dst_enc), src); } void Assembler::pmovmskb(Register dst, XMMRegister src) { assert(VM_Version::supports_sse2(), ""); - int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, true, VEX_OPCODE_0F, - false, AVX_128bit, (VM_Version::supports_avx512dq() == false)); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8((unsigned char)0xD7); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpmovmskb(Register dst, XMMRegister src) { assert(VM_Version::supports_avx2(), ""); - int vector_len = AVX_256bit; - int encode = vex_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, - vector_len, VEX_OPCODE_0F, true, false); + InstructionAttr attributes(AVX_256bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8((unsigned char)0xD7); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::pextrd(Register dst, XMMRegister src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, /* no_mask_reg */ true, - VEX_OPCODE_0F_3A, /* rex_w */ false, AVX_128bit, /* legacy_mode */ _legacy_mode_dq); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x16); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(imm8); @@ -3076,8 +3282,8 @@ void Assembler::pextrd(Register dst, XMMRegister src, int imm8) { void Assembler::pextrq(Register dst, XMMRegister src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, /* no_mask_reg */ true, - VEX_OPCODE_0F_3A, /* rex_w */ true, AVX_128bit, /* legacy_mode */ _legacy_mode_dq); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x16); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(imm8); @@ -3085,8 +3291,8 @@ void Assembler::pextrq(Register dst, XMMRegister src, int imm8) { void Assembler::pextrw(Register dst, XMMRegister src, int imm8) { assert(VM_Version::supports_sse2(), ""); - int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, /* no_mask_reg */ true, - VEX_OPCODE_0F, /* rex_w */ false, AVX_128bit, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8((unsigned char)0xC5); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(imm8); @@ -3094,8 +3300,8 @@ void Assembler::pextrw(Register dst, XMMRegister src, int imm8) { void Assembler::pinsrd(XMMRegister dst, Register src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_66, /* no_mask_reg */ true, - VEX_OPCODE_0F_3A, /* rex_w */ false, AVX_128bit, /* legacy_mode */ _legacy_mode_dq); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x22); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(imm8); @@ -3103,8 +3309,8 @@ void Assembler::pinsrd(XMMRegister dst, Register src, int imm8) { void Assembler::pinsrq(XMMRegister dst, Register src, int imm8) { assert(VM_Version::supports_sse4_1(), ""); - int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_66, /* no_mask_reg */ true, - VEX_OPCODE_0F_3A, /* rex_w */ true, AVX_128bit, /* legacy_mode */ _legacy_mode_dq); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x22); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(imm8); @@ -3112,8 +3318,8 @@ void Assembler::pinsrq(XMMRegister dst, Register src, int imm8) { void Assembler::pinsrw(XMMRegister dst, Register src, int imm8) { assert(VM_Version::supports_sse2(), ""); - int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_66, /* no_mask_reg */ true, - VEX_OPCODE_0F, /* rex_w */ false, AVX_128bit, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8((unsigned char)0xC4); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(imm8); @@ -3121,29 +3327,29 @@ void Assembler::pinsrw(XMMRegister dst, Register src, int imm8) { void Assembler::pmovzxbw(XMMRegister dst, Address src) { assert(VM_Version::supports_sse4_1(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_HVM; - } InstructionMark im(this); - simd_prefix(dst, src, VEX_SIMD_66, /* no_mask_reg */ false, VEX_OPCODE_0F_38); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_HVM, /* input_size_in_bits */ EVEX_NObit); + simd_prefix(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x30); emit_operand(dst, src); } void Assembler::pmovzxbw(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_sse4_1(), ""); - int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, /* no_mask_reg */ false, VEX_OPCODE_0F_38); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x30); emit_int8((unsigned char)(0xC0 | encode)); } -void Assembler::vpmovzxbw(XMMRegister dst, Address src) { +void Assembler::vpmovzxbw(XMMRegister dst, Address src, int vector_len) { assert(VM_Version::supports_avx(), ""); InstructionMark im(this); - bool vector256 = true; assert(dst != xnoreg, "sanity"); - int dst_enc = dst->encoding(); - vex_prefix(src, 0, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_38, false, vector256); + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_HVM, /* input_size_in_bits */ EVEX_NObit); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x30); emit_operand(dst, src); } @@ -3246,43 +3452,41 @@ void Assembler::prefix(Prefix p) { void Assembler::pshufb(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_ssse3(), ""); - int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_38, /* rex_w */ false, AVX_128bit, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x00); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::pshufb(XMMRegister dst, Address src) { assert(VM_Version::supports_ssse3(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FVM; - } InstructionMark im(this); - simd_prefix(dst, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_38, /* rex_w */ false, AVX_128bit, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x00); emit_operand(dst, src); } void Assembler::pshufd(XMMRegister dst, XMMRegister src, int mode) { - _instruction_uses_vl = true; assert(isByte(mode), "invalid value"); NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith_nonds(0x70, dst, src, VEX_SIMD_66); + int vector_len = VM_Version::supports_evex() ? AVX_512bit : AVX_128bit; + InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x70); + emit_int8((unsigned char)(0xC0 | encode)); emit_int8(mode & 0xFF); } void Assembler::pshufd(XMMRegister dst, Address src, int mode) { - _instruction_uses_vl = true; assert(isByte(mode), "invalid value"); NOT_LP64(assert(VM_Version::supports_sse2(), "")); assert((UseAVX > 0), "SSE mode requires address alignment 16 bytes"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } InstructionMark im(this); - simd_prefix(dst, src, VEX_SIMD_66, /* no_mask_reg */ false); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x70); emit_operand(dst, src); emit_int8(mode & 0xFF); @@ -3291,7 +3495,10 @@ void Assembler::pshufd(XMMRegister dst, Address src, int mode) { void Assembler::pshuflw(XMMRegister dst, XMMRegister src, int mode) { assert(isByte(mode), "invalid value"); NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith_nonds(0x70, dst, src, VEX_SIMD_F2, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x70); + emit_int8((unsigned char)(0xC0 | encode)); emit_int8(mode & 0xFF); } @@ -3299,12 +3506,10 @@ void Assembler::pshuflw(XMMRegister dst, Address src, int mode) { assert(isByte(mode), "invalid value"); NOT_LP64(assert(VM_Version::supports_sse2(), "")); assert((UseAVX > 0), "SSE mode requires address alignment 16 bytes"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FVM; - } InstructionMark im(this); - simd_prefix(dst, xnoreg, src, VEX_SIMD_F2, /* no_mask_reg */ false, - VEX_OPCODE_0F, /* rex_w */ false, AVX_128bit, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + simd_prefix(dst, xnoreg, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x70); emit_operand(dst, src); emit_int8(mode & 0xFF); @@ -3313,9 +3518,9 @@ void Assembler::pshuflw(XMMRegister dst, Address src, int mode) { void Assembler::psrldq(XMMRegister dst, int shift) { // Shift left 128 bit value in dst XMMRegister by shift number of bytes. NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); // XMM3 is for /3 encoding: 66 0F 73 /3 ib - int encode = simd_prefix_and_encode(xmm3, dst, dst, VEX_SIMD_66, /* no_mask_reg */ true, - VEX_OPCODE_0F, /* rex_w */ false, AVX_128bit, /* legacy_mode */ _legacy_mode_bw); + int encode = simd_prefix_and_encode(xmm3, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x73); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift); @@ -3324,9 +3529,9 @@ void Assembler::psrldq(XMMRegister dst, int shift) { void Assembler::pslldq(XMMRegister dst, int shift) { // Shift left 128 bit value in dst XMMRegister by shift number of bytes. NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ false); // XMM7 is for /7 encoding: 66 0F 73 /7 ib - int encode = simd_prefix_and_encode(xmm7, dst, dst, VEX_SIMD_66, /* no_mask_reg */ true, - VEX_OPCODE_0F, /* rex_w */ false, AVX_128bit, /* legacy_mode */ _legacy_mode_bw); + int encode = simd_prefix_and_encode(xmm7, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x73); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift); @@ -3336,16 +3541,16 @@ void Assembler::ptest(XMMRegister dst, Address src) { assert(VM_Version::supports_sse4_1(), ""); assert((UseAVX > 0), "SSE mode requires address alignment 16 bytes"); InstructionMark im(this); - simd_prefix(dst, xnoreg, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_38, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + simd_prefix(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x17); emit_operand(dst, src); } void Assembler::ptest(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_sse4_1(), ""); - int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_38, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x17); emit_int8((unsigned char)(0xC0 | encode)); } @@ -3353,20 +3558,18 @@ void Assembler::ptest(XMMRegister dst, XMMRegister src) { void Assembler::vptest(XMMRegister dst, Address src) { assert(VM_Version::supports_avx(), ""); InstructionMark im(this); - int vector_len = AVX_256bit; + InstructionAttr attributes(AVX_256bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); assert(dst != xnoreg, "sanity"); - int dst_enc = dst->encoding(); // swap src<->dst for encoding - vex_prefix(src, 0, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_38, /* rex_w */ false, - vector_len, /* legacy_mode */ true, /* no_mask_reg */ false); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x17); emit_operand(dst, src); } void Assembler::vptest(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - int vector_len = AVX_256bit; - int encode = vex_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, vector_len, VEX_OPCODE_0F_38, /* legacy_mode */ true); + InstructionAttr attributes(AVX_256bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x17); emit_int8((unsigned char)(0xC0 | encode)); } @@ -3374,42 +3577,47 @@ void Assembler::vptest(XMMRegister dst, XMMRegister src) { void Assembler::punpcklbw(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); assert((UseAVX > 0), "SSE mode requires address alignment 16 bytes"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FVM; - } - emit_simd_arith(0x60, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_vlbw); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_vlbw, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x60); + emit_operand(dst, src); } void Assembler::punpcklbw(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0x60, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_vlbw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_vlbw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x60); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::punpckldq(XMMRegister dst, Address src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); assert((UseAVX > 0), "SSE mode requires address alignment 16 bytes"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } - emit_simd_arith(0x62, dst, src, VEX_SIMD_66); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x62); + emit_operand(dst, src); } void Assembler::punpckldq(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0x62, dst, src, VEX_SIMD_66); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x62); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::punpcklqdq(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x6C, dst, src, VEX_SIMD_66); - } else { - emit_simd_arith(0x6C, dst, src, VEX_SIMD_66); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x6C); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::push(int32_t imm32) { @@ -3454,16 +3662,18 @@ void Assembler::rcll(Register dst, int imm8) { void Assembler::rcpps(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_NONE, /* no_mask_reg */ false, VEX_OPCODE_0F, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); emit_int8(0x53); - emit_int8(0xC0 | encode); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::rcpss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, /* no_mask_reg */ false, VEX_OPCODE_0F, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x53); - emit_int8(0xC0 | encode); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::rdtsc() { @@ -3622,27 +3832,28 @@ void Assembler::smovl() { void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x51, dst, src, VEX_SIMD_F2); - } else { - emit_simd_arith(0x51, dst, src, VEX_SIMD_F2); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x51); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::sqrtsd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - emit_simd_arith_q(0x51, dst, src, VEX_SIMD_F2); - } else { - emit_simd_arith(0x51, dst, src, VEX_SIMD_F2); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x51); + emit_operand(dst, src); } void Assembler::sqrtss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - emit_simd_arith(0x51, dst, src, VEX_SIMD_F3); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x51); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::std() { @@ -3651,11 +3862,12 @@ void Assembler::std() { void Assembler::sqrtss(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } - emit_simd_arith(0x51, dst, src, VEX_SIMD_F3); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x51); + emit_operand(dst, src); } void Assembler::stmxcsr( Address dst) { @@ -3705,38 +3917,38 @@ void Assembler::subl(Register dst, Register src) { void Assembler::subsd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x5C, dst, src, VEX_SIMD_F2); - } else { - emit_simd_arith(0x5C, dst, src, VEX_SIMD_F2); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x5C); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::subsd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - } - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x5C, dst, src, VEX_SIMD_F2); - } else { - emit_simd_arith(0x5C, dst, src, VEX_SIMD_F2); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x5C); + emit_operand(dst, src); } void Assembler::subss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - emit_simd_arith(0x5C, dst, src, VEX_SIMD_F3); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x5C); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::subss(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } - emit_simd_arith(0x5C, dst, src, VEX_SIMD_F3); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x5C); + emit_operand(dst, src); } void Assembler::testb(Register dst, int imm8) { @@ -3765,7 +3977,7 @@ void Assembler::testl(Register dst, Register src) { emit_arith(0x85, 0xC0, dst, src); } -void Assembler::testl(Register dst, Address src) { +void Assembler::testl(Register dst, Address src) { InstructionMark im(this); prefix(src, dst); emit_int8((unsigned char)0x85); @@ -3792,36 +4004,38 @@ void Assembler::tzcntq(Register dst, Register src) { void Assembler::ucomisd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - emit_simd_arith_nonds_q(0x2E, dst, src, VEX_SIMD_66, /* no_mask_reg */ true); - } else { - emit_simd_arith_nonds(0x2E, dst, src, VEX_SIMD_66); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x2E); + emit_operand(dst, src); } void Assembler::ucomisd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_nonds_q(0x2E, dst, src, VEX_SIMD_66, /* no_mask_reg */ true); - } else { - emit_simd_arith_nonds(0x2E, dst, src, VEX_SIMD_66); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x2E); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::ucomiss(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } - emit_simd_arith_nonds(0x2E, dst, src, VEX_SIMD_NONE, /* no_mask_reg */ true); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, xnoreg, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x2E); + emit_operand(dst, src); } void Assembler::ucomiss(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - emit_simd_arith_nonds(0x2E, dst, src, VEX_SIMD_NONE, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x2E); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::xabort(int8_t imm8) { @@ -3903,138 +4117,162 @@ void Assembler::xorl(Register dst, Register src) { void Assembler::vaddsd(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - emit_vex_arith_q(0x58, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } else { - emit_vex_arith(0x58, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x58); + emit_operand(dst, src); } void Assembler::vaddsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - emit_vex_arith_q(0x58, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } else { - emit_vex_arith(0x58, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x58); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vaddss(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } - emit_vex_arith(0x58, dst, nds, src, VEX_SIMD_F3, AVX_128bit); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x58); + emit_operand(dst, src); } void Assembler::vaddss(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - emit_vex_arith(0x58, dst, nds, src, VEX_SIMD_F3, AVX_128bit); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x58); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vdivsd(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - emit_vex_arith_q(0x5E, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } else { - emit_vex_arith(0x5E, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x5E); + emit_operand(dst, src); } void Assembler::vdivsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - emit_vex_arith_q(0x5E, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } else { - emit_vex_arith(0x5E, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x5E); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vdivss(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } - emit_vex_arith(0x5E, dst, nds, src, VEX_SIMD_F3, AVX_128bit); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x5E); + emit_operand(dst, src); } void Assembler::vdivss(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - emit_vex_arith(0x5E, dst, nds, src, VEX_SIMD_F3, AVX_128bit); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x5E); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vmulsd(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - emit_vex_arith_q(0x59, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } else { - emit_vex_arith(0x59, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x59); + emit_operand(dst, src); } void Assembler::vmulsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - emit_vex_arith_q(0x59, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } else { - emit_vex_arith(0x59, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x59); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vmulss(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } - emit_vex_arith(0x59, dst, nds, src, VEX_SIMD_F3, AVX_128bit); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x59); + emit_operand(dst, src); } void Assembler::vmulss(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - emit_vex_arith(0x59, dst, nds, src, VEX_SIMD_F3, AVX_128bit); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x59); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vsubsd(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - emit_vex_arith_q(0x5C, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } else { - emit_vex_arith(0x5C, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x5C); + emit_operand(dst, src); } void Assembler::vsubsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - emit_vex_arith_q(0x5C, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } else { - emit_vex_arith(0x5C, dst, nds, src, VEX_SIMD_F2, AVX_128bit); - } + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); + emit_int8(0x5C); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vsubss(XMMRegister dst, XMMRegister nds, Address src) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } - emit_vex_arith(0x5C, dst, nds, src, VEX_SIMD_F3, AVX_128bit); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x5C); + emit_operand(dst, src); } void Assembler::vsubss(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - emit_vex_arith(0x5C, dst, nds, src, VEX_SIMD_F3, AVX_128bit); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); + emit_int8(0x5C); + emit_int8((unsigned char)(0xC0 | encode)); } //====================VECTOR ARITHMETIC===================================== @@ -4042,414 +4280,433 @@ void Assembler::vsubss(XMMRegister dst, XMMRegister nds, XMMRegister src) { // Float-point vector arithmetic void Assembler::addpd(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x58, dst, src, VEX_SIMD_66); - } else { - emit_simd_arith(0x58, dst, src, VEX_SIMD_66); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x58); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::addps(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0x58, dst, src, VEX_SIMD_NONE); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x58); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vaddpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - emit_vex_arith_q(0x58, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x58, dst, nds, src, VEX_SIMD_66, vector_len); - } + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x58); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vaddps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - emit_vex_arith(0x58, dst, nds, src, VEX_SIMD_NONE, vector_len); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x58); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vaddpd(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_64bit; - emit_vex_arith_q(0x58, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x58, dst, nds, src, VEX_SIMD_66, vector_len); - } + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x58); + emit_operand(dst, src); } void Assembler::vaddps(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } - emit_vex_arith(0x58, dst, nds, src, VEX_SIMD_NONE, vector_len); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x58); + emit_operand(dst, src); } void Assembler::subpd(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x5C, dst, src, VEX_SIMD_66); - } else { - emit_simd_arith(0x5C, dst, src, VEX_SIMD_66); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x5C); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::subps(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0x5C, dst, src, VEX_SIMD_NONE); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x5C); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vsubpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - emit_vex_arith_q(0x5C, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x5C, dst, nds, src, VEX_SIMD_66, vector_len); - } + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x5C); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vsubps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - emit_vex_arith(0x5C, dst, nds, src, VEX_SIMD_NONE, vector_len); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x5C); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vsubpd(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_64bit; - emit_vex_arith_q(0x5C, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x5C, dst, nds, src, VEX_SIMD_66, vector_len); - } + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x5C); + emit_operand(dst, src); } void Assembler::vsubps(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } - emit_vex_arith(0x5C, dst, nds, src, VEX_SIMD_NONE, vector_len); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x5C); + emit_operand(dst, src); } void Assembler::mulpd(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x59, dst, src, VEX_SIMD_66); - } else { - emit_simd_arith(0x59, dst, src, VEX_SIMD_66); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x59); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::mulpd(XMMRegister dst, Address src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x59, dst, src, VEX_SIMD_66); - } else { - emit_simd_arith(0x59, dst, src, VEX_SIMD_66); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x59); + emit_operand(dst, src); } void Assembler::mulps(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0x59, dst, src, VEX_SIMD_NONE); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x59); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vmulpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - emit_vex_arith_q(0x59, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x59, dst, nds, src, VEX_SIMD_66, vector_len); - } + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x59); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vmulps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - emit_vex_arith(0x59, dst, nds, src, VEX_SIMD_NONE, vector_len); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x59); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vmulpd(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_64bit; - emit_vex_arith_q(0x59, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x59, dst, nds, src, VEX_SIMD_66, vector_len); - } + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x59); + emit_operand(dst, src); } void Assembler::vmulps(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } - emit_vex_arith(0x59, dst, nds, src, VEX_SIMD_NONE, vector_len); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x59); + emit_operand(dst, src); } void Assembler::divpd(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x5E, dst, src, VEX_SIMD_66); - } else { - emit_simd_arith(0x5E, dst, src, VEX_SIMD_66); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x5E); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::divps(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0x5E, dst, src, VEX_SIMD_NONE); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x5E); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vdivpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - emit_vex_arith_q(0x5E, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x5E, dst, nds, src, VEX_SIMD_66, vector_len); - } + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x5E); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vdivps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - emit_vex_arith(0x5E, dst, nds, src, VEX_SIMD_NONE, vector_len); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x5E); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vdivpd(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_64bit; - emit_vex_arith_q(0x5E, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x5E, dst, nds, src, VEX_SIMD_66, vector_len); - } + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x5E); + emit_operand(dst, src); } void Assembler::vdivps(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } - emit_vex_arith(0x5E, dst, nds, src, VEX_SIMD_NONE, vector_len); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x5E); + emit_operand(dst, src); } void Assembler::vsqrtpd(XMMRegister dst, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - emit_vex_arith_q(0x51, dst, xnoreg, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x51, dst, xnoreg, src, VEX_SIMD_66, vector_len); - } + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x51); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vsqrtpd(XMMRegister dst, Address src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_64bit; - emit_vex_arith_q(0x51, dst, xnoreg, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x51, dst, xnoreg, src, VEX_SIMD_66, vector_len); - } + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x51); + emit_operand(dst, src); } void Assembler::andpd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_avx512dq()) { - emit_simd_arith_q(0x54, dst, src, VEX_SIMD_66); - } else { - emit_simd_arith(0x54, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ true); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ !_legacy_mode_dq, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x54); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::andps(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - emit_simd_arith(0x54, dst, src, VEX_SIMD_NONE, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_dq); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x54); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::andps(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } - emit_simd_arith(0x54, dst, src, VEX_SIMD_NONE, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_dq); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, dst, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x54); + emit_operand(dst, src); } void Assembler::andpd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_avx512dq()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_64bit; - emit_simd_arith_q(0x54, dst, src, VEX_SIMD_66); - } else { - emit_simd_arith(0x54, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ true); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ !_legacy_mode_dq, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x54); + emit_operand(dst, src); } void Assembler::vandpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_avx512dq()) { - emit_vex_arith_q(0x54, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x54, dst, nds, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ true); - } + InstructionAttr attributes(vector_len, /* vex_w */ !_legacy_mode_dq, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x54); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vandps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(VM_Version::supports_avx(), ""); - emit_vex_arith(0x54, dst, nds, src, VEX_SIMD_NONE, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_dq); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x54); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vandpd(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_avx512dq()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_64bit; - emit_vex_arith_q(0x54, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x54, dst, nds, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ true); - } + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ !_legacy_mode_dq, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x54); + emit_operand(dst, src); } void Assembler::vandps(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } - emit_vex_arith(0x54, dst, nds, src, VEX_SIMD_NONE, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_dq); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x54); + emit_operand(dst, src); } void Assembler::unpckhpd(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x15, dst, src, VEX_SIMD_66); - } else { - emit_simd_arith(0x15, dst, src, VEX_SIMD_66); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x15); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::unpcklpd(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0x14, dst, src, VEX_SIMD_66); - } else { - emit_simd_arith(0x14, dst, src, VEX_SIMD_66); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x14); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::xorpd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_avx512dq()) { - emit_simd_arith_q(0x57, dst, src, VEX_SIMD_66); - } else { - emit_simd_arith(0x57, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ true); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ !_legacy_mode_dq, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x57); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::xorps(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - emit_simd_arith(0x57, dst, src, VEX_SIMD_NONE, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_dq); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x57); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::xorpd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_avx512dq()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_64bit; - emit_simd_arith_q(0x57, dst, src, VEX_SIMD_66); - } else { - emit_simd_arith(0x57, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ true); - } + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ !_legacy_mode_dq, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x57); + emit_operand(dst, src); } void Assembler::xorps(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } - emit_simd_arith(0x57, dst, src, VEX_SIMD_NONE, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_dq); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + simd_prefix(dst, dst, src, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x57); + emit_operand(dst, src); } void Assembler::vxorpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_avx512dq()) { - emit_vex_arith_q(0x57, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x57, dst, nds, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ true); - } + InstructionAttr attributes(vector_len, /* vex_w */ !_legacy_mode_dq, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x57); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vxorps(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(VM_Version::supports_avx(), ""); - emit_vex_arith(0x57, dst, nds, src, VEX_SIMD_NONE, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_dq); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x57); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vxorpd(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_avx512dq()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_64bit; - emit_vex_arith_q(0x57, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x57, dst, nds, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ true); - } + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ !_legacy_mode_dq, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x57); + emit_operand(dst, src); } void Assembler::vxorps(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { assert(VM_Version::supports_avx(), ""); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } - emit_vex_arith(0x57, dst, nds, src, VEX_SIMD_NONE, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_dq); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x57); + emit_operand(dst, src); } // Integer vector arithmetic void Assembler::vphaddw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(VM_Version::supports_avx() && (vector_len == 0) || VM_Version::supports_avx2(), "256 bit integer vectors requires AVX2"); - int encode = vex_prefix_and_encode(dst, nds, src, VEX_SIMD_66, vector_len, VEX_OPCODE_0F_38, /* legacy_mode */ true); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x01); emit_int8((unsigned char)(0xC0 | encode)); } @@ -4457,280 +4714,324 @@ void Assembler::vphaddw(XMMRegister dst, XMMRegister nds, XMMRegister src, int v void Assembler::vphaddd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(VM_Version::supports_avx() && (vector_len == 0) || VM_Version::supports_avx2(), "256 bit integer vectors requires AVX2"); - int encode = vex_prefix_and_encode(dst, nds, src, VEX_SIMD_66, vector_len, VEX_OPCODE_0F_38, /* legacy_mode */ true); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x02); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::paddb(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xFC, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xFC); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::paddw(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xFD, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xFD); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::paddd(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xFE, dst, src, VEX_SIMD_66); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xFE); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::paddq(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0xD4, dst, src, VEX_SIMD_66); - } else { - emit_simd_arith(0xD4, dst, src, VEX_SIMD_66); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xD4); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::phaddw(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse3(), "")); - int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_38, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x01); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::phaddd(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse3(), "")); - int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_38, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x02); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpaddb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xFC, dst, nds, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xFC); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpaddw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xFD, dst, nds, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xFD); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpaddd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xFE, dst, nds, src, VEX_SIMD_66, vector_len); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xFE); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpaddq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - emit_vex_arith_q(0xD4, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0xD4, dst, nds, src, VEX_SIMD_66, vector_len); - } + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xD4); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpaddb(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FVM; - } - emit_vex_arith(0xFC, dst, nds, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xFC); + emit_operand(dst, src); } void Assembler::vpaddw(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FVM; - } - emit_vex_arith(0xFD, dst, nds, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xFD); + emit_operand(dst, src); } void Assembler::vpaddd(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } - emit_vex_arith(0xFE, dst, nds, src, VEX_SIMD_66, vector_len); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xFE); + emit_operand(dst, src); } void Assembler::vpaddq(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_64bit; - emit_vex_arith_q(0xD4, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0xD4, dst, nds, src, VEX_SIMD_66, vector_len); - } + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xD4); + emit_operand(dst, src); } void Assembler::psubb(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xF8, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xF8); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::psubw(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xF9, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xF9); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::psubd(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; - NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xFA, dst, src, VEX_SIMD_66); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xFA); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::psubq(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0xFB, dst, src, VEX_SIMD_66); - } else { - emit_simd_arith(0xFB, dst, src, VEX_SIMD_66); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xFB); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpsubb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xF8, dst, nds, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xF8); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpsubw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xF9, dst, nds, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xF9); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpsubd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xFA, dst, nds, src, VEX_SIMD_66, vector_len); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xFA); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpsubq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - emit_vex_arith_q(0xFB, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0xFB, dst, nds, src, VEX_SIMD_66, vector_len); - } + InstructionAttr attributes(vector_len, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xFB); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpsubb(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FVM; - } - emit_vex_arith(0xF8, dst, nds, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xF8); + emit_operand(dst, src); } void Assembler::vpsubw(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FVM; - } - emit_vex_arith(0xF9, dst, nds, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xF9); + emit_operand(dst, src); } void Assembler::vpsubd(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } - emit_vex_arith(0xFA, dst, nds, src, VEX_SIMD_66, vector_len); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xFA); + emit_operand(dst, src); } void Assembler::vpsubq(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_64bit; - emit_vex_arith_q(0xFB, dst, nds, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0xFB, dst, nds, src, VEX_SIMD_66, vector_len); - } + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xFB); + emit_operand(dst, src); } void Assembler::pmullw(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xD5, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xD5); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::pmulld(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; assert(VM_Version::supports_sse4_1(), ""); - int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, - /* no_mask_reg */ false, VEX_OPCODE_0F_38); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x40); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpmullw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xD5, dst, nds, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xD5); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpmulld(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - int encode = vex_prefix_and_encode(dst, nds, src, VEX_SIMD_66, vector_len, VEX_OPCODE_0F_38); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x40); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpmullq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { assert(UseAVX > 2, "requires some form of AVX"); - int src_enc = src->encoding(); - int dst_enc = dst->encoding(); + InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); int nds_enc = nds->is_valid() ? nds->encoding() : 0; - int encode = vex_prefix_and_encode(dst_enc, nds_enc, src_enc, VEX_SIMD_66, VEX_OPCODE_0F_38, - /* vex_w */ true, vector_len, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x40); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpmullw(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FVM; - } - emit_vex_arith(0xD5, dst, nds, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FVM, /* input_size_in_bits */ EVEX_NObit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xD5); + emit_operand(dst, src); } void Assembler::vpmulld(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } InstructionMark im(this); - int dst_enc = dst->encoding(); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); int nds_enc = nds->is_valid() ? nds->encoding() : 0; - vex_prefix(src, nds_enc, dst_enc, VEX_SIMD_66, - VEX_OPCODE_0F_38, /* vex_w */ false, vector_len); + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x40); emit_operand(dst, src); } void Assembler::vpmullq(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_64bit; - } InstructionMark im(this); - int dst_enc = dst->encoding(); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit); int nds_enc = nds->is_valid() ? nds->encoding() : 0; - vex_prefix(src, nds_enc, dst_enc, VEX_SIMD_66, - VEX_OPCODE_0F_38, /* vex_w */ true, vector_len, /* legacy_mode */ _legacy_mode_dq); + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x40); emit_operand(dst, src); } @@ -4738,29 +5039,29 @@ void Assembler::vpmullq(XMMRegister dst, XMMRegister nds, Address src, int vecto // Shift packed integers left by specified number of bits. void Assembler::psllw(XMMRegister dst, int shift) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); // XMM6 is for /6 encoding: 66 0F 71 /6 ib - int encode = simd_prefix_and_encode(xmm6, dst, dst, VEX_SIMD_66, /* no_mask_reg */ false, VEX_OPCODE_0F, - /* rex_w */ false, AVX_128bit, /* legacy_mode */ _legacy_mode_bw); + int encode = simd_prefix_and_encode(xmm6, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x71); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); } void Assembler::pslld(XMMRegister dst, int shift) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); // XMM6 is for /6 encoding: 66 0F 72 /6 ib - int encode = simd_prefix_and_encode(xmm6, dst, dst, VEX_SIMD_66, /* no_mask_reg */ false); + int encode = simd_prefix_and_encode(xmm6, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x72); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); } void Assembler::psllq(XMMRegister dst, int shift) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); // XMM6 is for /6 encoding: 66 0F 73 /6 ib - int encode = simd_prefix_and_encode(xmm6, dst, dst, VEX_SIMD_66, /* no_mask_reg */ false, VEX_OPCODE_0F, /* rex_w */ true); + int encode = simd_prefix_and_encode(xmm6, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x73); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); @@ -4768,102 +5069,111 @@ void Assembler::psllq(XMMRegister dst, int shift) { void Assembler::psllw(XMMRegister dst, XMMRegister shift) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xF1, dst, shift, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, shift, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xF1); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::pslld(XMMRegister dst, XMMRegister shift) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xF2, dst, shift, VEX_SIMD_66); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, shift, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xF2); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::psllq(XMMRegister dst, XMMRegister shift) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0xF3, dst, shift, VEX_SIMD_66); - } else { - emit_simd_arith(0xF3, dst, shift, VEX_SIMD_66); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, shift, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xF3); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpsllw(XMMRegister dst, XMMRegister src, int shift, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); // XMM6 is for /6 encoding: 66 0F 71 /6 ib - emit_vex_arith(0x71, xmm6, dst, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + int encode = vex_prefix_and_encode(xmm6->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x71); + emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); } void Assembler::vpslld(XMMRegister dst, XMMRegister src, int shift, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); + NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); // XMM6 is for /6 encoding: 66 0F 72 /6 ib - emit_vex_arith(0x72, xmm6, dst, src, VEX_SIMD_66, vector_len); + int encode = vex_prefix_and_encode(xmm6->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x72); + emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); } void Assembler::vpsllq(XMMRegister dst, XMMRegister src, int shift, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); // XMM6 is for /6 encoding: 66 0F 73 /6 ib - if (VM_Version::supports_evex()) { - emit_vex_arith_q(0x73, xmm6, dst, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x73, xmm6, dst, src, VEX_SIMD_66, vector_len); - } + int encode = vex_prefix_and_encode(xmm6->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x73); + emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); } void Assembler::vpsllw(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xF1, dst, src, shift, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), src->encoding(), shift->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xF1); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpslld(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xF2, dst, src, shift, VEX_SIMD_66, vector_len); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), src->encoding(), shift->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xF2); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpsllq(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - emit_vex_arith_q(0xF3, dst, src, shift, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0xF3, dst, src, shift, VEX_SIMD_66, vector_len); - } + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), src->encoding(), shift->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xF3); + emit_int8((unsigned char)(0xC0 | encode)); } // Shift packed integers logically right by specified number of bits. void Assembler::psrlw(XMMRegister dst, int shift) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); // XMM2 is for /2 encoding: 66 0F 71 /2 ib - int encode = simd_prefix_and_encode(xmm2, dst, dst, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F, /* rex_w */ false, AVX_128bit, /* legacy_mode */ _legacy_mode_bw); + int encode = simd_prefix_and_encode(xmm2, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x71); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); } void Assembler::psrld(XMMRegister dst, int shift) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); // XMM2 is for /2 encoding: 66 0F 72 /2 ib - int encode = simd_prefix_and_encode(xmm2, dst, dst, VEX_SIMD_66, /* no_mask_reg */ false); + int encode = simd_prefix_and_encode(xmm2, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x72); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); } void Assembler::psrlq(XMMRegister dst, int shift) { - _instruction_uses_vl = true; // Do not confuse it with psrldq SSE2 instruction which // shifts 128 bit value in xmm register by number of bytes. NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); // XMM2 is for /2 encoding: 66 0F 73 /2 ib - int encode = simd_prefix_and_encode(xmm2, dst, dst, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F, /* rex_w */ VM_Version::supports_evex()); + int encode = simd_prefix_and_encode(xmm2, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x73); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); @@ -4871,89 +5181,98 @@ void Assembler::psrlq(XMMRegister dst, int shift) { void Assembler::psrlw(XMMRegister dst, XMMRegister shift) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xD1, dst, shift, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, shift, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xD1); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::psrld(XMMRegister dst, XMMRegister shift) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xD2, dst, shift, VEX_SIMD_66); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, shift, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xD2); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::psrlq(XMMRegister dst, XMMRegister shift) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0xD3, dst, shift, VEX_SIMD_66); - } else { - emit_simd_arith(0xD3, dst, shift, VEX_SIMD_66); - } + InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, shift, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xD3); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpsrlw(XMMRegister dst, XMMRegister src, int shift, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); // XMM2 is for /2 encoding: 66 0F 71 /2 ib - emit_vex_arith(0x71, xmm2, dst, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + int encode = vex_prefix_and_encode(xmm2->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x71); + emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); } void Assembler::vpsrld(XMMRegister dst, XMMRegister src, int shift, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); // XMM2 is for /2 encoding: 66 0F 72 /2 ib - emit_vex_arith(0x72, xmm2, dst, src, VEX_SIMD_66, vector_len); + int encode = vex_prefix_and_encode(xmm2->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x72); + emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); } void Assembler::vpsrlq(XMMRegister dst, XMMRegister src, int shift, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); // XMM2 is for /2 encoding: 66 0F 73 /2 ib - if (VM_Version::supports_evex()) { - emit_vex_arith_q(0x73, xmm2, dst, src, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0x73, xmm2, dst, src, VEX_SIMD_66, vector_len); - } + int encode = vex_prefix_and_encode(xmm2->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x73); + emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); } void Assembler::vpsrlw(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xD1, dst, src, shift, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), src->encoding(), shift->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xD1); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpsrld(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xD2, dst, src, shift, VEX_SIMD_66, vector_len); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), src->encoding(), shift->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xD2); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpsrlq(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - emit_vex_arith_q(0xD3, dst, src, shift, VEX_SIMD_66, vector_len); - } else { - emit_vex_arith(0xD3, dst, src, shift, VEX_SIMD_66, vector_len); - } + InstructionAttr attributes(vector_len, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), src->encoding(), shift->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xD3); + emit_int8((unsigned char)(0xC0 | encode)); } // Shift packed integers arithmetically right by specified number of bits. void Assembler::psraw(XMMRegister dst, int shift) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); // XMM4 is for /4 encoding: 66 0F 71 /4 ib - int encode = simd_prefix_and_encode(xmm4, dst, dst, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F, /* rex_w */ false, AVX_128bit, /* legacy_mode */ _legacy_mode_bw); + int encode = simd_prefix_and_encode(xmm4, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x71); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); } void Assembler::psrad(XMMRegister dst, int shift) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); // XMM4 is for /4 encoding: 66 0F 72 /4 ib - int encode = simd_prefix_and_encode(xmm4, dst, dst, VEX_SIMD_66, /* no_mask_reg */ false); + int encode = simd_prefix_and_encode(xmm4, dst, dst, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x72); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); @@ -4961,128 +5280,157 @@ void Assembler::psrad(XMMRegister dst, int shift) { void Assembler::psraw(XMMRegister dst, XMMRegister shift) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xE1, dst, shift, VEX_SIMD_66, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, shift, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xE1); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::psrad(XMMRegister dst, XMMRegister shift) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xE2, dst, shift, VEX_SIMD_66); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, shift, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xE2); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpsraw(XMMRegister dst, XMMRegister src, int shift, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); // XMM4 is for /4 encoding: 66 0F 71 /4 ib - emit_vex_arith(0x71, xmm4, dst, src, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + int encode = vex_prefix_and_encode(xmm4->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x71); + emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); } void Assembler::vpsrad(XMMRegister dst, XMMRegister src, int shift, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); // XMM4 is for /4 encoding: 66 0F 71 /4 ib - emit_vex_arith(0x72, xmm4, dst, src, VEX_SIMD_66, vector_len); + int encode = vex_prefix_and_encode(xmm4->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8(0x72); + emit_int8((unsigned char)(0xC0 | encode)); emit_int8(shift & 0xFF); } void Assembler::vpsraw(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xE1, dst, src, shift, VEX_SIMD_66, vector_len, /* no_mask_reg */ false, /* legacy_mode */ _legacy_mode_bw); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), src->encoding(), shift->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xE1); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpsrad(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xE2, dst, src, shift, VEX_SIMD_66, vector_len); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), src->encoding(), shift->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xE2); + emit_int8((unsigned char)(0xC0 | encode)); } // logical operations packed integers void Assembler::pand(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xDB, dst, src, VEX_SIMD_66); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xDB); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpand(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xDB, dst, nds, src, VEX_SIMD_66, vector_len); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xDB); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpand(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } - emit_vex_arith(0xDB, dst, nds, src, VEX_SIMD_66, vector_len); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xDB); + emit_operand(dst, src); } void Assembler::pandn(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - emit_simd_arith_q(0xDF, dst, src, VEX_SIMD_66); - } - else { - emit_simd_arith(0xDF, dst, src, VEX_SIMD_66); - } + InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xDF); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::por(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xEB, dst, src, VEX_SIMD_66); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xEB); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpor(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xEB, dst, nds, src, VEX_SIMD_66, vector_len); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xEB); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpor(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } - emit_vex_arith(0xEB, dst, nds, src, VEX_SIMD_66, vector_len); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xEB); + emit_operand(dst, src); } void Assembler::pxor(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; NOT_LP64(assert(VM_Version::supports_sse2(), "")); - emit_simd_arith(0xEF, dst, src, VEX_SIMD_66); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xEF); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpxor(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - emit_vex_arith(0xEF, dst, nds, src, VEX_SIMD_66, vector_len); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xEF); + emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::vpxor(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { - _instruction_uses_vl = true; assert(UseAVX > 0, "requires some form of AVX"); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_FV; - _input_size_in_bits = EVEX_32bit; - } - emit_vex_arith(0xEF, dst, nds, src, VEX_SIMD_66, vector_len); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_32bit); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(src, nds_enc, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int8((unsigned char)0xEF); + emit_operand(dst, src); } void Assembler::vinsertf128h(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - int vector_len = AVX_256bit; - if (VM_Version::supports_evex()) { - vector_len = AVX_512bit; - } - int encode = vex_prefix_and_encode(dst, nds, src, VEX_SIMD_66, vector_len, VEX_OPCODE_0F_3A); + int vector_len = VM_Version::supports_evex() ? AVX_512bit : AVX_256bit; + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x18); emit_int8((unsigned char)(0xC0 | encode)); // 0x00 - insert into lower 128 bits @@ -5090,45 +5438,38 @@ void Assembler::vinsertf128h(XMMRegister dst, XMMRegister nds, XMMRegister src) emit_int8(0x01); } -void Assembler::vinsertf64x4h(XMMRegister dst, XMMRegister nds, XMMRegister src) { +void Assembler::vinsertf64x4h(XMMRegister dst, XMMRegister nds, XMMRegister src, int value) { assert(VM_Version::supports_evex(), ""); - int vector_len = AVX_512bit; - int src_enc = src->encoding(); - int dst_enc = dst->encoding(); + InstructionAttr attributes(AVX_512bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); int nds_enc = nds->is_valid() ? nds->encoding() : 0; - int encode = vex_prefix_and_encode(dst_enc, nds_enc, src_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, - /* vex_w */ true, vector_len, /* legacy_mode */ false, /* no_mask_reg */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x1A); emit_int8((unsigned char)(0xC0 | encode)); // 0x00 - insert into lower 256 bits // 0x01 - insert into upper 256 bits - emit_int8(0x01); + emit_int8(value & 0x01); } -void Assembler::vinsertf64x4h(XMMRegister dst, Address src) { +void Assembler::vinsertf64x4h(XMMRegister dst, Address src, int value) { assert(VM_Version::supports_evex(), ""); - _tuple_type = EVEX_T4; - _input_size_in_bits = EVEX_64bit; - InstructionMark im(this); - int vector_len = AVX_512bit; assert(dst != xnoreg, "sanity"); - int dst_enc = dst->encoding(); + InstructionMark im(this); + InstructionAttr attributes(AVX_512bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T4, /* input_size_in_bits */ EVEX_64bit); // swap src<->dst for encoding - vex_prefix(src, dst_enc, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, /* vex_w */ true, vector_len); + vex_prefix(src, dst->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x1A); emit_operand(dst, src); + // 0x00 - insert into lower 256 bits // 0x01 - insert into upper 128 bits - emit_int8(0x01); + emit_int8(value & 0x01); } void Assembler::vinsertf32x4h(XMMRegister dst, XMMRegister nds, XMMRegister src, int value) { assert(VM_Version::supports_evex(), ""); - int vector_len = AVX_512bit; - int src_enc = src->encoding(); - int dst_enc = dst->encoding(); + InstructionAttr attributes(AVX_512bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); int nds_enc = nds->is_valid() ? nds->encoding() : 0; - int encode = vex_prefix_and_encode(dst_enc, nds_enc, src_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, - /* vex_w */ false, vector_len, /* legacy_mode */ false, /* no_mask_reg */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x18); emit_int8((unsigned char)(0xC0 | encode)); // 0x00 - insert into q0 128 bits (0..127) @@ -5139,15 +5480,14 @@ void Assembler::vinsertf32x4h(XMMRegister dst, XMMRegister nds, XMMRegister src, } void Assembler::vinsertf32x4h(XMMRegister dst, Address src, int value) { - assert(VM_Version::supports_evex(), ""); - _tuple_type = EVEX_T4; - _input_size_in_bits = EVEX_32bit; - InstructionMark im(this); - int vector_len = AVX_512bit; + assert(VM_Version::supports_avx(), ""); assert(dst != xnoreg, "sanity"); - int dst_enc = dst->encoding(); + int vector_len = VM_Version::supports_evex() ? AVX_512bit : AVX_256bit; + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T4, /* input_size_in_bits */ EVEX_32bit); // swap src<->dst for encoding - vex_prefix(src, dst_enc, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, /* vex_w */ false, vector_len); + vex_prefix(src, dst->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x18); emit_operand(dst, src); // 0x00 - insert into q0 128 bits (0..127) @@ -5159,17 +5499,13 @@ void Assembler::vinsertf32x4h(XMMRegister dst, Address src, int value) { void Assembler::vinsertf128h(XMMRegister dst, Address src) { assert(VM_Version::supports_avx(), ""); - int vector_len = AVX_256bit; - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T4; - _input_size_in_bits = EVEX_32bit; - vector_len = AVX_512bit; - } - InstructionMark im(this); assert(dst != xnoreg, "sanity"); - int dst_enc = dst->encoding(); + int vector_len = VM_Version::supports_evex() ? AVX_512bit : AVX_256bit; + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T4, /* input_size_in_bits */ EVEX_32bit); // swap src<->dst for encoding - vex_prefix(src, dst_enc, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, /* vex_w */ false, vector_len); + vex_prefix(src, dst->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x18); emit_operand(dst, src); // 0x01 - insert into upper 128 bits @@ -5178,11 +5514,9 @@ void Assembler::vinsertf128h(XMMRegister dst, Address src) { void Assembler::vextractf128h(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - int vector_len = AVX_256bit; - if (VM_Version::supports_evex()) { - vector_len = AVX_512bit; - } - int encode = vex_prefix_and_encode(src, xnoreg, dst, VEX_SIMD_66, vector_len, VEX_OPCODE_0F_3A); + int vector_len = VM_Version::supports_evex() ? AVX_512bit : AVX_256bit; + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(src->encoding(), 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x19); emit_int8((unsigned char)(0xC0 | encode)); // 0x00 - insert into lower 128 bits @@ -5192,16 +5526,12 @@ void Assembler::vextractf128h(XMMRegister dst, XMMRegister src) { void Assembler::vextractf128h(Address dst, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - int vector_len = AVX_256bit; - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T4; - _input_size_in_bits = EVEX_32bit; - vector_len = AVX_512bit; - } - InstructionMark im(this); assert(src != xnoreg, "sanity"); - int src_enc = src->encoding(); - vex_prefix(dst, 0, src_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, /* vex_w */ false, vector_len); + int vector_len = VM_Version::supports_evex() ? AVX_512bit : AVX_256bit; + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T4, /* input_size_in_bits */ EVEX_32bit); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x19); emit_operand(src, dst); // 0x01 - extract from upper 128 bits @@ -5210,11 +5540,10 @@ void Assembler::vextractf128h(Address dst, XMMRegister src) { void Assembler::vinserti128h(XMMRegister dst, XMMRegister nds, XMMRegister src) { assert(VM_Version::supports_avx2(), ""); - int vector_len = AVX_256bit; - if (VM_Version::supports_evex()) { - vector_len = AVX_512bit; - } - int encode = vex_prefix_and_encode(dst, nds, src, VEX_SIMD_66, vector_len, VEX_OPCODE_0F_3A); + int vector_len = VM_Version::supports_evex() ? AVX_512bit : AVX_256bit; + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x38); emit_int8((unsigned char)(0xC0 | encode)); // 0x00 - insert into lower 128 bits @@ -5222,34 +5551,27 @@ void Assembler::vinserti128h(XMMRegister dst, XMMRegister nds, XMMRegister src) emit_int8(0x01); } -void Assembler::vinserti64x4h(XMMRegister dst, XMMRegister nds, XMMRegister src) { +void Assembler::vinserti64x4h(XMMRegister dst, XMMRegister nds, XMMRegister src, int value) { assert(VM_Version::supports_evex(), ""); - int vector_len = AVX_512bit; - int src_enc = src->encoding(); - int dst_enc = dst->encoding(); + InstructionAttr attributes(AVX_512bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); int nds_enc = nds->is_valid() ? nds->encoding() : 0; - int encode = vex_prefix_and_encode(dst_enc, nds_enc, src_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, - /* vex_w */ true, vector_len, /* legacy_mode */ false, /* no_reg_mask */ false); + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x38); emit_int8((unsigned char)(0xC0 | encode)); // 0x00 - insert into lower 256 bits // 0x01 - insert into upper 256 bits - emit_int8(0x01); + emit_int8(value & 0x01); } void Assembler::vinserti128h(XMMRegister dst, Address src) { assert(VM_Version::supports_avx2(), ""); - int vector_len = AVX_256bit; - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T4; - _input_size_in_bits = EVEX_32bit; - vector_len = AVX_512bit; - } - InstructionMark im(this); assert(dst != xnoreg, "sanity"); - int dst_enc = dst->encoding(); + int vector_len = VM_Version::supports_evex() ? AVX_512bit : AVX_256bit; + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T4, /* input_size_in_bits */ EVEX_32bit); // swap src<->dst for encoding - vex_prefix(src, dst_enc, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, /* vex_w */ false, vector_len); + vex_prefix(src, dst->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x38); emit_operand(dst, src); // 0x01 - insert into upper 128 bits @@ -5258,11 +5580,9 @@ void Assembler::vinserti128h(XMMRegister dst, Address src) { void Assembler::vextracti128h(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_avx(), ""); - int vector_len = AVX_256bit; - if (VM_Version::supports_evex()) { - vector_len = AVX_512bit; - } - int encode = vex_prefix_and_encode(src, xnoreg, dst, VEX_SIMD_66, vector_len, VEX_OPCODE_0F_3A); + int vector_len = VM_Version::supports_evex() ? AVX_512bit : AVX_256bit; + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(src->encoding(), 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x39); emit_int8((unsigned char)(0xC0 | encode)); // 0x00 - insert into lower 128 bits @@ -5272,48 +5592,33 @@ void Assembler::vextracti128h(XMMRegister dst, XMMRegister src) { void Assembler::vextracti128h(Address dst, XMMRegister src) { assert(VM_Version::supports_avx2(), ""); - int vector_len = AVX_256bit; - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T4; - _input_size_in_bits = EVEX_32bit; - vector_len = AVX_512bit; - } - InstructionMark im(this); assert(src != xnoreg, "sanity"); - int src_enc = src->encoding(); - vex_prefix(dst, 0, src_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, /* vex_w */ false, vector_len); + int vector_len = VM_Version::supports_evex() ? AVX_512bit : AVX_256bit; + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T4, /* input_size_in_bits */ EVEX_32bit); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x39); emit_operand(src, dst); // 0x01 - extract from upper 128 bits emit_int8(0x01); } -void Assembler::vextracti64x4h(XMMRegister dst, XMMRegister src) { +void Assembler::vextracti64x4h(XMMRegister dst, XMMRegister src, int value) { assert(VM_Version::supports_evex(), ""); - int vector_len = AVX_512bit; - int src_enc = src->encoding(); - int dst_enc = dst->encoding(); - int encode = vex_prefix_and_encode(src_enc, 0, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, - /* vex_w */ true, vector_len, /* legacy_mode */ false, /* no_mask_reg */ false); + InstructionAttr attributes(AVX_512bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(src->encoding(), 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x3B); emit_int8((unsigned char)(0xC0 | encode)); + // 0x00 - extract from lower 256 bits // 0x01 - extract from upper 256 bits - emit_int8(0x01); + emit_int8(value & 0x01); } void Assembler::vextracti64x2h(XMMRegister dst, XMMRegister src, int value) { assert(VM_Version::supports_evex(), ""); - int vector_len = AVX_512bit; - int src_enc = src->encoding(); - int dst_enc = dst->encoding(); - int encode; - if (VM_Version::supports_avx512dq()) { - encode = vex_prefix_and_encode(src_enc, 0, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, - /* vex_w */ true, vector_len, /* legacy_mode */ false, /* no_mask_reg */ false); - } else { - encode = vex_prefix_and_encode(src_enc, 0, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, - /* vex_w */ false, vector_len, /* legacy_mode */ true, /* no_mask_reg */ false); - } + InstructionAttr attributes(AVX_512bit, /* vex_w */ !_legacy_mode_dq, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(src->encoding(), 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x39); emit_int8((unsigned char)(0xC0 | encode)); // 0x01 - extract from bits 255:128 @@ -5322,42 +5627,36 @@ void Assembler::vextracti64x2h(XMMRegister dst, XMMRegister src, int value) { emit_int8(value & 0x3); } -void Assembler::vextractf64x4h(XMMRegister dst, XMMRegister src) { +void Assembler::vextractf64x4h(XMMRegister dst, XMMRegister src, int value) { assert(VM_Version::supports_evex(), ""); - int vector_len = AVX_512bit; - int src_enc = src->encoding(); - int dst_enc = dst->encoding(); - int encode = vex_prefix_and_encode(src_enc, 0, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, - /* vex_w */ true, vector_len, /* legacy_mode */ false, /* no_mask_reg */ false); + InstructionAttr attributes(AVX_512bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(src->encoding(), 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x1B); emit_int8((unsigned char)(0xC0 | encode)); + // 0x00 - extract from lower 256 bits // 0x01 - extract from upper 256 bits - emit_int8(0x01); + emit_int8(value & 0x1); } -void Assembler::vextractf64x4h(Address dst, XMMRegister src) { +void Assembler::vextractf64x4h(Address dst, XMMRegister src, int value) { assert(VM_Version::supports_evex(), ""); - _tuple_type = EVEX_T4; - _input_size_in_bits = EVEX_64bit; - InstructionMark im(this); - int vector_len = AVX_512bit; assert(src != xnoreg, "sanity"); - int src_enc = src->encoding(); - vex_prefix(dst, 0, src_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, - /* vex_w */ true, vector_len); + InstructionMark im(this); + InstructionAttr attributes(AVX_512bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T4,/* input_size_in_bits */ EVEX_64bit); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x1B); emit_operand(src, dst); + // 0x00 - extract from lower 256 bits // 0x01 - extract from upper 256 bits - emit_int8(0x01); + emit_int8(value & 0x01); } void Assembler::vextractf32x4h(XMMRegister dst, XMMRegister src, int value) { - assert(VM_Version::supports_evex(), ""); - int vector_len = AVX_512bit; - int src_enc = src->encoding(); - int dst_enc = dst->encoding(); - int encode = vex_prefix_and_encode(src_enc, 0, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, - /* vex_w */ false, vector_len, /* legacy_mode */ false, /* no_mask_reg */ false); + assert(VM_Version::supports_avx(), ""); + int vector_len = VM_Version::supports_evex() ? AVX_512bit : AVX_256bit; + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(src->encoding(), 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x19); emit_int8((unsigned char)(0xC0 | encode)); // 0x00 - extract from bits 127:0 @@ -5369,13 +5668,11 @@ void Assembler::vextractf32x4h(XMMRegister dst, XMMRegister src, int value) { void Assembler::vextractf32x4h(Address dst, XMMRegister src, int value) { assert(VM_Version::supports_evex(), ""); - _tuple_type = EVEX_T4; - _input_size_in_bits = EVEX_32bit; - InstructionMark im(this); - int vector_len = AVX_512bit; assert(src != xnoreg, "sanity"); - int src_enc = src->encoding(); - vex_prefix(dst, 0, src_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, /* vex_w */ false, vector_len); + InstructionMark im(this); + InstructionAttr attributes(AVX_512bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T4, /* input_size_in_bits */ EVEX_32bit); + vex_prefix(dst, 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x19); emit_operand(src, dst); // 0x00 - extract from bits 127:0 @@ -5387,11 +5684,8 @@ void Assembler::vextractf32x4h(Address dst, XMMRegister src, int value) { void Assembler::vextractf64x2h(XMMRegister dst, XMMRegister src, int value) { assert(VM_Version::supports_evex(), ""); - int vector_len = AVX_512bit; - int src_enc = src->encoding(); - int dst_enc = dst->encoding(); - int encode = vex_prefix_and_encode(src_enc, 0, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_3A, - /* vex_w */ !_legacy_mode_dq, vector_len, /* legacy_mode */ false, /* no_mask_reg */ false); + InstructionAttr attributes(AVX_512bit, /* vex_w */ !_legacy_mode_dq, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(src->encoding(), 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x19); emit_int8((unsigned char)(0xC0 | encode)); // 0x01 - extract from bits 255:128 @@ -5402,10 +5696,9 @@ void Assembler::vextractf64x2h(XMMRegister dst, XMMRegister src, int value) { // duplicate 4-bytes integer data from src into 8 locations in dest void Assembler::vpbroadcastd(XMMRegister dst, XMMRegister src) { - _instruction_uses_vl = true; - assert(UseAVX > 1, ""); - int vector_len = AVX_256bit; - int encode = vex_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, vector_len, VEX_OPCODE_0F_38); + assert(VM_Version::supports_avx2(), ""); + InstructionAttr attributes(AVX_256bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x58); emit_int8((unsigned char)(0xC0 | encode)); } @@ -5413,189 +5706,170 @@ void Assembler::vpbroadcastd(XMMRegister dst, XMMRegister src) { // duplicate 2-bytes integer data from src into 16 locations in dest void Assembler::vpbroadcastw(XMMRegister dst, XMMRegister src) { assert(VM_Version::supports_avx2(), ""); - bool vector_len = AVX_256bit; - int encode = vex_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, - vector_len, VEX_OPCODE_0F_38, false); + InstructionAttr attributes(AVX_256bit, /* vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x79); emit_int8((unsigned char)(0xC0 | encode)); } // duplicate 1-byte integer data from src into 16||32|64 locations in dest : requires AVX512BW and AVX512VL void Assembler::evpbroadcastb(XMMRegister dst, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 1, ""); - int encode = vex_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, vector_len, VEX_OPCODE_0F_38); + assert(VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x78); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::evpbroadcastb(XMMRegister dst, Address src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 1, ""); - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_8bit; - InstructionMark im(this); + assert(VM_Version::supports_evex(), ""); assert(dst != xnoreg, "sanity"); - int dst_enc = dst->encoding(); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_8bit); // swap src<->dst for encoding - vex_prefix(src, dst_enc, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_38, /* vex_w */ false, vector_len); + vex_prefix(src, dst->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x78); emit_operand(dst, src); } // duplicate 2-byte integer data from src into 8|16||32 locations in dest : requires AVX512BW and AVX512VL void Assembler::evpbroadcastw(XMMRegister dst, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 1, ""); - int encode = vex_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, vector_len, VEX_OPCODE_0F_38); + assert(VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x79); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::evpbroadcastw(XMMRegister dst, Address src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 1, ""); - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_16bit; - InstructionMark im(this); + assert(VM_Version::supports_evex(), ""); assert(dst != xnoreg, "sanity"); - int dst_enc = dst->encoding(); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_16bit); // swap src<->dst for encoding - vex_prefix(src, dst_enc, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_38, /* vex_w */ false, vector_len); + vex_prefix(src, dst->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x79); emit_operand(dst, src); } // duplicate 4-byte integer data from src into 4|8|16 locations in dest : requires AVX512VL void Assembler::evpbroadcastd(XMMRegister dst, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 1, ""); - int encode = vex_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, vector_len, VEX_OPCODE_0F_38); + assert(VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x58); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::evpbroadcastd(XMMRegister dst, Address src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 1, ""); - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - InstructionMark im(this); + assert(VM_Version::supports_evex(), ""); assert(dst != xnoreg, "sanity"); - int dst_enc = dst->encoding(); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); // swap src<->dst for encoding - vex_prefix(src, dst_enc, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_38, /* vex_w */ false, vector_len); + vex_prefix(src, dst->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x58); emit_operand(dst, src); } // duplicate 8-byte integer data from src into 4|8|16 locations in dest : requires AVX512VL void Assembler::evpbroadcastq(XMMRegister dst, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 1, ""); - int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, - /* vex_w */ true, vector_len, /* legacy_mode */ false, /* no_mask_reg */ false); + assert(VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x59); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::evpbroadcastq(XMMRegister dst, Address src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 1, ""); - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - InstructionMark im(this); + assert(VM_Version::supports_evex(), ""); assert(dst != xnoreg, "sanity"); - int dst_enc = dst->encoding(); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); // swap src<->dst for encoding - vex_prefix(src, dst_enc, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_38, /* vex_w */ true, vector_len); + vex_prefix(src, dst->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x59); emit_operand(dst, src); } // duplicate single precision fp from src into 4|8|16 locations in dest : requires AVX512VL void Assembler::evpbroadcastss(XMMRegister dst, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 1, ""); - int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, - /* vex_w */ false, vector_len, /* legacy_mode */ false, /*no_mask_reg */ false); + assert(VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x18); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::evpbroadcastss(XMMRegister dst, Address src, int vector_len) { - assert(UseAVX > 1, ""); - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - InstructionMark im(this); + assert(VM_Version::supports_evex(), ""); assert(dst != xnoreg, "sanity"); - int dst_enc = dst->encoding(); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit); // swap src<->dst for encoding - vex_prefix(src, 0, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_38, /* vex_w */ false, vector_len); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x18); emit_operand(dst, src); } // duplicate double precision fp from src into 2|4|8 locations in dest : requires AVX512VL void Assembler::evpbroadcastsd(XMMRegister dst, XMMRegister src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 1, ""); - int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, - /*vex_w */ true, vector_len, /* legacy_mode */ false, /*no_mask_reg */ false); + assert(VM_Version::supports_evex(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x19); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::evpbroadcastsd(XMMRegister dst, Address src, int vector_len) { - _instruction_uses_vl = true; - assert(UseAVX > 1, ""); - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_64bit; - InstructionMark im(this); + assert(VM_Version::supports_evex(), ""); assert(dst != xnoreg, "sanity"); - int dst_enc = dst->encoding(); + InstructionMark im(this); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); // swap src<->dst for encoding - vex_prefix(src, 0, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_38, /* vex_w */ true, vector_len); + vex_prefix(src, 0, dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x19); emit_operand(dst, src); } // duplicate 1-byte integer data from src into 16||32|64 locations in dest : requires AVX512BW and AVX512VL void Assembler::evpbroadcastb(XMMRegister dst, Register src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_evex(), ""); - int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, - /*vex_w */ false, vector_len, /* legacy_mode */ false, /*no_mask_reg */ false); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x7A); emit_int8((unsigned char)(0xC0 | encode)); } // duplicate 2-byte integer data from src into 8|16||32 locations in dest : requires AVX512BW and AVX512VL void Assembler::evpbroadcastw(XMMRegister dst, Register src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_evex(), ""); - int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, - /* vex_w */ false, vector_len, /* legacy_mode */ false, /*no_mask_reg */ false); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x7B); emit_int8((unsigned char)(0xC0 | encode)); } // duplicate 4-byte integer data from src into 4|8|16 locations in dest : requires AVX512VL void Assembler::evpbroadcastd(XMMRegister dst, Register src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_evex(), ""); - int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, - /* vex_w */ false, vector_len, /* legacy_mode */ false, /*no_mask_reg */ false); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x7C); emit_int8((unsigned char)(0xC0 | encode)); } // duplicate 8-byte integer data from src into 4|8|16 locations in dest : requires AVX512VL void Assembler::evpbroadcastq(XMMRegister dst, Register src, int vector_len) { - _instruction_uses_vl = true; assert(VM_Version::supports_evex(), ""); - int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, - /* vex_w */ true, vector_len, /* legacy_mode */ false, /*no_mask_reg */ false); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes); emit_int8(0x7C); emit_int8((unsigned char)(0xC0 | encode)); } @@ -5603,8 +5877,8 @@ void Assembler::evpbroadcastq(XMMRegister dst, Register src, int vector_len) { // Carry-Less Multiplication Quadword void Assembler::pclmulqdq(XMMRegister dst, XMMRegister src, int mask) { assert(VM_Version::supports_clmul(), ""); - int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, /* no_mask_reg */ false, - VEX_OPCODE_0F_3A, /* rex_w */ false, AVX_128bit, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x44); emit_int8((unsigned char)(0xC0 | encode)); emit_int8((unsigned char)mask); @@ -5613,8 +5887,9 @@ void Assembler::pclmulqdq(XMMRegister dst, XMMRegister src, int mask) { // Carry-Less Multiplication Quadword void Assembler::vpclmulqdq(XMMRegister dst, XMMRegister nds, XMMRegister src, int mask) { assert(VM_Version::supports_avx() && VM_Version::supports_clmul(), ""); - int vector_len = AVX_128bit; - int encode = vex_prefix_and_encode(dst, nds, src, VEX_SIMD_66, vector_len, VEX_OPCODE_0F_3A, /* legacy_mode */ true); + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8(0x44); emit_int8((unsigned char)(0xC0 | encode)); emit_int8((unsigned char)mask); @@ -5622,11 +5897,9 @@ void Assembler::vpclmulqdq(XMMRegister dst, XMMRegister nds, XMMRegister src, in void Assembler::vzeroupper() { assert(VM_Version::supports_avx(), ""); - if (UseAVX < 3) - { - (void)vex_prefix_and_encode(xmm0, xmm0, xmm0, VEX_SIMD_NONE); - emit_int8(0x77); - } + InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + (void)vex_prefix_and_encode(0, 0, 0, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes); + emit_int8(0x77); } @@ -6130,8 +6403,7 @@ int Assembler::rex_prefix_and_encode(int dst_enc, int src_enc, VexSimdPrefix pre if (pre > 0) { emit_int8(simd_pre[pre]); } - int encode = (rex_w) ? prefixq_and_encode(dst_enc, src_enc) : - prefix_and_encode(dst_enc, src_enc); + int encode = (rex_w) ? prefixq_and_encode(dst_enc, src_enc) : prefix_and_encode(dst_enc, src_enc); if (opc > 0) { emit_int8(0x0F); int opc2 = simd_opc[opc]; @@ -6143,7 +6415,9 @@ int Assembler::rex_prefix_and_encode(int dst_enc, int src_enc, VexSimdPrefix pre } -void Assembler::vex_prefix(bool vex_r, bool vex_b, bool vex_x, bool vex_w, int nds_enc, VexSimdPrefix pre, VexOpcode opc, int vector_len) { +void Assembler::vex_prefix(bool vex_r, bool vex_b, bool vex_x, int nds_enc, VexSimdPrefix pre, VexOpcode opc) { + int vector_len = _attributes->get_vector_len(); + bool vex_w = _attributes->is_rex_vex_w(); if (vex_b || vex_x || vex_w || (opc == VEX_OPCODE_0F_38) || (opc == VEX_OPCODE_0F_3A)) { prefix(VEX_3bytes); @@ -6167,13 +6441,13 @@ void Assembler::vex_prefix(bool vex_r, bool vex_b, bool vex_x, bool vex_w, int n } // This is a 4 byte encoding -void Assembler::evex_prefix(bool vex_r, bool vex_b, bool vex_x, bool vex_w, bool evex_r, bool evex_v, - int nds_enc, VexSimdPrefix pre, VexOpcode opc, - bool is_extended_context, bool is_merge_context, - int vector_len, bool no_mask_reg ){ +void Assembler::evex_prefix(bool vex_r, bool vex_b, bool vex_x, bool evex_r, bool evex_v, int nds_enc, VexSimdPrefix pre, VexOpcode opc){ // EVEX 0x62 prefix prefix(EVEX_4bytes); - _evex_encoding = (vex_w ? VEX_W : 0) | (evex_r ? EVEX_Rb : 0); + bool vex_w = _attributes->is_rex_vex_w(); + int evex_encoding = (vex_w ? VEX_W : 0); + // EVEX.b is not currently used for broadcast of single element or data rounding modes + _attributes->set_evex_encoding(evex_encoding); // P0: byte 2, initialized to RXBR`00mm // instead of not'd @@ -6195,214 +6469,127 @@ void Assembler::evex_prefix(bool vex_r, bool vex_b, bool vex_x, bool vex_w, bool emit_int8(byte3); // P2: byte 4 as zL'Lbv'aaa - int byte4 = (no_mask_reg) ? 0 : 1; // kregs are implemented in the low 3 bits as aaa (hard code k1, it will be initialized for now) + int byte4 = (_attributes->is_no_reg_mask()) ? 0 : 1; // kregs are implemented in the low 3 bits as aaa (hard code k1, it will be initialized for now) // EVEX.v` for extending EVEX.vvvv or VIDX byte4 |= (evex_v ? 0: EVEX_V); // third EXEC.b for broadcast actions - byte4 |= (is_extended_context ? EVEX_Rb : 0); + byte4 |= (_attributes->is_extended_context() ? EVEX_Rb : 0); // fourth EVEX.L'L for vector length : 0 is 128, 1 is 256, 2 is 512, currently we do not support 1024 - byte4 |= ((vector_len) & 0x3) << 5; + byte4 |= ((_attributes->get_vector_len())& 0x3) << 5; // last is EVEX.z for zero/merge actions - byte4 |= (is_merge_context ? EVEX_Z : 0); + byte4 |= (_attributes->is_clear_context() ? EVEX_Z : 0); emit_int8(byte4); } -void Assembler::vex_prefix(Address adr, int nds_enc, int xreg_enc, VexSimdPrefix pre, - VexOpcode opc, bool vex_w, int vector_len, bool legacy_mode, bool no_mask_reg) { +void Assembler::vex_prefix(Address adr, int nds_enc, int xreg_enc, VexSimdPrefix pre, VexOpcode opc, InstructionAttr *attributes) { bool vex_r = ((xreg_enc & 8) == 8) ? 1 : 0; bool vex_b = adr.base_needs_rex(); bool vex_x = adr.index_needs_rex(); - _avx_vector_len = vector_len; + set_attributes(attributes); + attributes->set_current_assembler(this); // if vector length is turned off, revert to AVX for vectors smaller than 512-bit - if (_legacy_mode_vl && _instruction_uses_vl) { - switch (vector_len) { + if ((UseAVX > 2) && _legacy_mode_vl && attributes->uses_vl()) { + switch (attributes->get_vector_len()) { case AVX_128bit: case AVX_256bit: - legacy_mode = true; + attributes->set_is_legacy_mode(); break; } } - if ((UseAVX > 2) && (legacy_mode == false)) + if ((UseAVX > 2) && !attributes->is_legacy_mode()) { bool evex_r = (xreg_enc >= 16); bool evex_v = (nds_enc >= 16); - _is_evex_instruction = true; - evex_prefix(vex_r, vex_b, vex_x, vex_w, evex_r, evex_v, nds_enc, pre, opc, false, false, vector_len, no_mask_reg); + attributes->set_is_evex_instruction(); + evex_prefix(vex_r, vex_b, vex_x, evex_r, evex_v, nds_enc, pre, opc); } else { - vex_prefix(vex_r, vex_b, vex_x, vex_w, nds_enc, pre, opc, vector_len); + vex_prefix(vex_r, vex_b, vex_x, nds_enc, pre, opc); } - _instruction_uses_vl = false; } -int Assembler::vex_prefix_and_encode(int dst_enc, int nds_enc, int src_enc, VexSimdPrefix pre, VexOpcode opc, - bool vex_w, int vector_len, bool legacy_mode, bool no_mask_reg ) { +int Assembler::vex_prefix_and_encode(int dst_enc, int nds_enc, int src_enc, VexSimdPrefix pre, VexOpcode opc, InstructionAttr *attributes) { bool vex_r = ((dst_enc & 8) == 8) ? 1 : 0; bool vex_b = ((src_enc & 8) == 8) ? 1 : 0; bool vex_x = false; - _avx_vector_len = vector_len; + set_attributes(attributes); + attributes->set_current_assembler(this); // if vector length is turned off, revert to AVX for vectors smaller than 512-bit - if (_legacy_mode_vl && _instruction_uses_vl) { - switch (vector_len) { + if ((UseAVX > 2) && _legacy_mode_vl && attributes->uses_vl()) { + switch (attributes->get_vector_len()) { case AVX_128bit: case AVX_256bit: - legacy_mode = true; + if ((dst_enc >= 16) | (nds_enc >= 16) | (src_enc >= 16)) { + // up propagate arithmetic instructions to meet RA requirements + attributes->set_vector_len(AVX_512bit); + } else { + attributes->set_is_legacy_mode(); + } break; } } - if ((UseAVX > 2) && (legacy_mode == false)) + if ((UseAVX > 2) && !attributes->is_legacy_mode()) { bool evex_r = (dst_enc >= 16); bool evex_v = (nds_enc >= 16); // can use vex_x as bank extender on rm encoding vex_x = (src_enc >= 16); - evex_prefix(vex_r, vex_b, vex_x, vex_w, evex_r, evex_v, nds_enc, pre, opc, false, false, vector_len, no_mask_reg); + attributes->set_is_evex_instruction(); + evex_prefix(vex_r, vex_b, vex_x, evex_r, evex_v, nds_enc, pre, opc); } else { - vex_prefix(vex_r, vex_b, vex_x, vex_w, nds_enc, pre, opc, vector_len); + vex_prefix(vex_r, vex_b, vex_x, nds_enc, pre, opc); } - _instruction_uses_vl = false; - // return modrm byte components for operands return (((dst_enc & 7) << 3) | (src_enc & 7)); } void Assembler::simd_prefix(XMMRegister xreg, XMMRegister nds, Address adr, VexSimdPrefix pre, - bool no_mask_reg, VexOpcode opc, bool rex_w, int vector_len, bool legacy_mode) { + VexOpcode opc, InstructionAttr *attributes) { if (UseAVX > 0) { int xreg_enc = xreg->encoding(); - int nds_enc = nds->is_valid() ? nds->encoding() : 0; - vex_prefix(adr, nds_enc, xreg_enc, pre, opc, rex_w, vector_len, legacy_mode, no_mask_reg); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + vex_prefix(adr, nds_enc, xreg_enc, pre, opc, attributes); } else { assert((nds == xreg) || (nds == xnoreg), "wrong sse encoding"); - rex_prefix(adr, xreg, pre, opc, rex_w); + rex_prefix(adr, xreg, pre, opc, attributes->is_rex_vex_w()); } } int Assembler::simd_prefix_and_encode(XMMRegister dst, XMMRegister nds, XMMRegister src, VexSimdPrefix pre, - bool no_mask_reg, VexOpcode opc, bool rex_w, int vector_len, bool legacy_mode) { + VexOpcode opc, InstructionAttr *attributes) { int dst_enc = dst->encoding(); int src_enc = src->encoding(); if (UseAVX > 0) { int nds_enc = nds->is_valid() ? nds->encoding() : 0; - return vex_prefix_and_encode(dst_enc, nds_enc, src_enc, pre, opc, rex_w, vector_len, legacy_mode, no_mask_reg); + return vex_prefix_and_encode(dst_enc, nds_enc, src_enc, pre, opc, attributes); } else { assert((nds == dst) || (nds == src) || (nds == xnoreg), "wrong sse encoding"); - return rex_prefix_and_encode(dst_enc, src_enc, pre, opc, rex_w); + return rex_prefix_and_encode(dst_enc, src_enc, pre, opc, attributes->is_rex_vex_w()); } } int Assembler::kreg_prefix_and_encode(KRegister dst, KRegister nds, KRegister src, VexSimdPrefix pre, - bool no_mask_reg, VexOpcode opc, bool rex_w, int vector_len) { - int dst_enc = dst->encoding(); - int src_enc = src->encoding(); + VexOpcode opc, InstructionAttr *attributes) { int nds_enc = nds->is_valid() ? nds->encoding() : 0; - return vex_prefix_and_encode(dst_enc, nds_enc, src_enc, pre, opc, rex_w, vector_len, true, no_mask_reg); + return vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), pre, opc, attributes); } int Assembler::kreg_prefix_and_encode(KRegister dst, KRegister nds, Register src, VexSimdPrefix pre, - bool no_mask_reg, VexOpcode opc, bool rex_w, int vector_len) { - int dst_enc = dst->encoding(); - int src_enc = src->encoding(); + VexOpcode opc, InstructionAttr *attributes) { int nds_enc = nds->is_valid() ? nds->encoding() : 0; - return vex_prefix_and_encode(dst_enc, nds_enc, src_enc, pre, opc, rex_w, vector_len, true, no_mask_reg); -} - -void Assembler::emit_simd_arith(int opcode, XMMRegister dst, Address src, VexSimdPrefix pre, bool no_mask_reg, bool legacy_mode) { - InstructionMark im(this); - simd_prefix(dst, dst, src, pre, no_mask_reg, VEX_OPCODE_0F, false, AVX_128bit, legacy_mode); - emit_int8(opcode); - emit_operand(dst, src); -} - -void Assembler::emit_simd_arith_q(int opcode, XMMRegister dst, Address src, VexSimdPrefix pre, bool no_mask_reg) { - InstructionMark im(this); - simd_prefix_q(dst, dst, src, pre, no_mask_reg); - emit_int8(opcode); - emit_operand(dst, src); -} - -void Assembler::emit_simd_arith(int opcode, XMMRegister dst, XMMRegister src, VexSimdPrefix pre, bool no_mask_reg, bool legacy_mode) { - int encode = simd_prefix_and_encode(dst, dst, src, pre, no_mask_reg, VEX_OPCODE_0F, false, AVX_128bit, legacy_mode); - emit_int8(opcode); - emit_int8((unsigned char)(0xC0 | encode)); -} - -void Assembler::emit_simd_arith_q(int opcode, XMMRegister dst, XMMRegister src, VexSimdPrefix pre, bool no_mask_reg) { - int encode = simd_prefix_and_encode(dst, dst, src, pre, no_mask_reg, VEX_OPCODE_0F, true, AVX_128bit); - emit_int8(opcode); - emit_int8((unsigned char)(0xC0 | encode)); -} - -// Versions with no second source register (non-destructive source). -void Assembler::emit_simd_arith_nonds(int opcode, XMMRegister dst, Address src, VexSimdPrefix pre, bool opNoRegMask) { - InstructionMark im(this); - simd_prefix(dst, xnoreg, src, pre, opNoRegMask); - emit_int8(opcode); - emit_operand(dst, src); -} - -void Assembler::emit_simd_arith_nonds_q(int opcode, XMMRegister dst, Address src, VexSimdPrefix pre, bool opNoRegMask) { - InstructionMark im(this); - simd_prefix_q(dst, xnoreg, src, pre, opNoRegMask); - emit_int8(opcode); - emit_operand(dst, src); -} - -void Assembler::emit_simd_arith_nonds(int opcode, XMMRegister dst, XMMRegister src, VexSimdPrefix pre, bool no_mask_reg, bool legacy_mode) { - int encode = simd_prefix_and_encode(dst, xnoreg, src, pre, no_mask_reg, VEX_OPCODE_0F, false, AVX_128bit, legacy_mode); - emit_int8(opcode); - emit_int8((unsigned char)(0xC0 | encode)); -} - -void Assembler::emit_simd_arith_nonds_q(int opcode, XMMRegister dst, XMMRegister src, VexSimdPrefix pre, bool no_mask_reg) { - int encode = simd_prefix_and_encode(dst, xnoreg, src, pre, no_mask_reg, VEX_OPCODE_0F, true); - emit_int8(opcode); - emit_int8((unsigned char)(0xC0 | encode)); -} - -// 3-operands AVX instructions -void Assembler::emit_vex_arith(int opcode, XMMRegister dst, XMMRegister nds, Address src, - VexSimdPrefix pre, int vector_len, bool no_mask_reg, bool legacy_mode) { - InstructionMark im(this); - vex_prefix(dst, nds, src, pre, vector_len, no_mask_reg, legacy_mode); - emit_int8(opcode); - emit_operand(dst, src); -} - -void Assembler::emit_vex_arith_q(int opcode, XMMRegister dst, XMMRegister nds, - Address src, VexSimdPrefix pre, int vector_len, bool no_mask_reg) { - InstructionMark im(this); - vex_prefix_q(dst, nds, src, pre, vector_len, no_mask_reg); - emit_int8(opcode); - emit_operand(dst, src); -} - -void Assembler::emit_vex_arith(int opcode, XMMRegister dst, XMMRegister nds, XMMRegister src, - VexSimdPrefix pre, int vector_len, bool no_mask_reg, bool legacy_mode) { - int encode = vex_prefix_and_encode(dst, nds, src, pre, vector_len, VEX_OPCODE_0F, legacy_mode, no_mask_reg); - emit_int8(opcode); - emit_int8((unsigned char)(0xC0 | encode)); -} - -void Assembler::emit_vex_arith_q(int opcode, XMMRegister dst, XMMRegister nds, XMMRegister src, - VexSimdPrefix pre, int vector_len, bool no_mask_reg) { - int src_enc = src->encoding(); - int dst_enc = dst->encoding(); - int nds_enc = nds->is_valid() ? nds->encoding() : 0; - int encode = vex_prefix_and_encode(dst_enc, nds_enc, src_enc, pre, VEX_OPCODE_0F, true, vector_len, false, no_mask_reg); - emit_int8(opcode); - emit_int8((unsigned char)(0xC0 | encode)); + return vex_prefix_and_encode(dst->encoding(), nds_enc, src->encoding(), pre, opc, attributes); } void Assembler::cmppd(XMMRegister dst, XMMRegister nds, XMMRegister src, int cop, int vector_len) { assert(VM_Version::supports_avx(), ""); assert(!VM_Version::supports_evex(), ""); - int encode = vex_prefix_and_encode(dst, nds, src, VEX_SIMD_66, vector_len, VEX_OPCODE_0F, /* no_mask_reg */ false); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, nds, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8((unsigned char)0xC2); emit_int8((unsigned char)(0xC0 | encode)); emit_int8((unsigned char)(0xF & cop)); @@ -6411,7 +6598,9 @@ void Assembler::cmppd(XMMRegister dst, XMMRegister nds, XMMRegister src, int cop void Assembler::vpblendd(XMMRegister dst, XMMRegister nds, XMMRegister src1, XMMRegister src2, int vector_len) { assert(VM_Version::supports_avx(), ""); assert(!VM_Version::supports_evex(), ""); - int encode = vex_prefix_and_encode(dst, nds, src1, VEX_SIMD_66, vector_len, VEX_OPCODE_0F_3A, /* no_mask_reg */ false); + InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int nds_enc = nds->is_valid() ? nds->encoding() : 0; + int encode = vex_prefix_and_encode(dst->encoding(), nds_enc, src1->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); emit_int8((unsigned char)0x4B); emit_int8((unsigned char)(0xC0 | encode)); int src2_enc = src2->encoding(); @@ -6430,7 +6619,7 @@ void Assembler::lea(Register dst, Address src) { leal(dst, src); } -void Assembler::mov_literal32(Address dst, int32_t imm32, RelocationHolder const& rspec) { +void Assembler::mov_literal32(Address dst, int32_t imm32, RelocationHolder const& rspec) { InstructionMark im(this); emit_int8((unsigned char)0xC7); emit_operand(rax, dst); @@ -6948,15 +7137,17 @@ void Assembler::andq(Register dst, Register src) { void Assembler::andnq(Register dst, Register src1, Register src2) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - int encode = vex_prefix_0F38_and_encode_q_legacy(dst, src1, src2); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF2); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::andnq(Register dst, Register src1, Address src2) { - InstructionMark im(this); assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - vex_prefix_0F38_q_legacy(dst, src1, src2); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + vex_prefix(src2, src1->encoding(), dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF2); emit_operand(dst, src2); } @@ -6983,45 +7174,51 @@ void Assembler::bswapq(Register reg) { void Assembler::blsiq(Register dst, Register src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - int encode = vex_prefix_0F38_and_encode_q_legacy(rbx, dst, src); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(rbx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::blsiq(Register dst, Address src) { - InstructionMark im(this); assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - vex_prefix_0F38_q_legacy(rbx, dst, src); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + vex_prefix(src, dst->encoding(), rbx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_operand(rbx, src); } void Assembler::blsmskq(Register dst, Register src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - int encode = vex_prefix_0F38_and_encode_q_legacy(rdx, dst, src); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(rdx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::blsmskq(Register dst, Address src) { - InstructionMark im(this); assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - vex_prefix_0F38_q_legacy(rdx, dst, src); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + vex_prefix(src, dst->encoding(), rdx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_operand(rdx, src); } void Assembler::blsrq(Register dst, Register src) { assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - int encode = vex_prefix_0F38_and_encode_q_legacy(rcx, dst, src); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(rcx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::blsrq(Register dst, Address src) { - InstructionMark im(this); assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); - vex_prefix_0F38_q_legacy(rcx, dst, src); + InstructionMark im(this); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + vex_prefix(src, dst->encoding(), rcx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF3); emit_operand(rcx, src); } @@ -7095,45 +7292,44 @@ void Assembler::cmpxchgq(Register reg, Address adr) { void Assembler::cvtsi2sdq(XMMRegister dst, Register src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - int encode = simd_prefix_and_encode_q(dst, dst, src, VEX_SIMD_F2, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, dst, as_XMMRegister(src->encoding()), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x2A); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::cvtsi2sdq(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } InstructionMark im(this); - simd_prefix_q(dst, dst, src, VEX_SIMD_F2, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x2A); emit_operand(dst, src); } void Assembler::cvtsi2ssq(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - if (VM_Version::supports_evex()) { - _tuple_type = EVEX_T1S; - _input_size_in_bits = EVEX_32bit; - } InstructionMark im(this); - simd_prefix_q(dst, dst, src, VEX_SIMD_F3, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit); + simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x2A); emit_operand(dst, src); } void Assembler::cvttsd2siq(Register dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); - int encode = simd_prefix_and_encode_q(dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes); emit_int8(0x2C); emit_int8((unsigned char)(0xC0 | encode)); } void Assembler::cvttss2siq(Register dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse(), "")); - int encode = simd_prefix_and_encode_q(dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes); emit_int8(0x2C); emit_int8((unsigned char)(0xC0 | encode)); } @@ -7316,7 +7512,8 @@ void Assembler::lzcntq(Register dst, Register src) { void Assembler::movdq(XMMRegister dst, Register src) { // table D-1 says MMX/SSE2 NOT_LP64(assert(VM_Version::supports_sse2(), "")); - int encode = simd_prefix_and_encode_q(dst, src, VEX_SIMD_66, /* no_mask_reg */ true); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = simd_prefix_and_encode(dst, xnoreg, as_XMMRegister(src->encoding()), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x6E); emit_int8((unsigned char)(0xC0 | encode)); } @@ -7324,8 +7521,9 @@ void Assembler::movdq(XMMRegister dst, Register src) { void Assembler::movdq(Register dst, XMMRegister src) { // table D-1 says MMX/SSE2 NOT_LP64(assert(VM_Version::supports_sse2(), "")); + InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false); // swap src/dst to get correct prefix - int encode = simd_prefix_and_encode_q(src, dst, VEX_SIMD_66, /* no_mask_reg */ true); + int encode = simd_prefix_and_encode(src, xnoreg, as_XMMRegister(dst->encoding()), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); emit_int8(0x7E); emit_int8((unsigned char)(0xC0 | encode)); } @@ -7458,8 +7656,8 @@ void Assembler::mulq(Register src) { void Assembler::mulxq(Register dst1, Register dst2, Register src) { assert(VM_Version::supports_bmi2(), "bit manipulation instructions not supported"); - int encode = vex_prefix_and_encode(dst1->encoding(), dst2->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_38, - /* vex_w */ true, AVX_128bit, /* legacy_mode */ true, /* no_mask_reg */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst1->encoding(), dst2->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_38, &attributes); emit_int8((unsigned char)0xF6); emit_int8((unsigned char)(0xC0 | encode)); } @@ -7621,8 +7819,8 @@ void Assembler::rorq(Register dst, int imm8) { void Assembler::rorxq(Register dst, Register src, int imm8) { assert(VM_Version::supports_bmi2(), "bit manipulation instructions not supported"); - int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_3A, - /* vex_w */ true, AVX_128bit, /* legacy_mode */ true, /* no_mask_reg */ false); + InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_3A, &attributes); emit_int8((unsigned char)0xF0); emit_int8((unsigned char)(0xC0 | encode)); emit_int8(imm8); diff --git a/hotspot/src/cpu/x86/vm/assembler_x86.hpp b/hotspot/src/cpu/x86/vm/assembler_x86.hpp index 28210a7af47..a5f493905fc 100644 --- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp +++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp @@ -438,6 +438,8 @@ class ArrayAddress VALUE_OBJ_CLASS_SPEC { }; +class InstructionAttr; + // 64-bit refect the fxsave size which is 512 bytes and the new xsave area on EVEX which is another 2176 bytes // See fxsave and xsave(EVEX enabled) documentation for layout const int FPUStateSizeInWords = NOT_LP64(27) LP64_ONLY(2688 / wordSize); @@ -568,7 +570,8 @@ class Assembler : public AbstractAssembler { EVEX_8bit = 0, EVEX_16bit = 1, EVEX_32bit = 2, - EVEX_64bit = 3 + EVEX_64bit = 3, + EVEX_NObit = 4 }; enum WhichOperand { @@ -598,16 +601,12 @@ class Assembler : public AbstractAssembler { private: - int _evex_encoding; - int _input_size_in_bits; - int _avx_vector_len; - int _tuple_type; - bool _is_evex_instruction; bool _legacy_mode_bw; bool _legacy_mode_dq; bool _legacy_mode_vl; bool _legacy_mode_vlbw; - bool _instruction_uses_vl; + + class InstructionAttr *_attributes; // 64bit prefixes int prefix_and_encode(int reg_enc, bool byteinst = false); @@ -637,181 +636,30 @@ private: int rex_prefix_and_encode(int dst_enc, int src_enc, VexSimdPrefix pre, VexOpcode opc, bool rex_w); - void vex_prefix(bool vex_r, bool vex_b, bool vex_x, bool vex_w, - int nds_enc, VexSimdPrefix pre, VexOpcode opc, - int vector_len); + void vex_prefix(bool vex_r, bool vex_b, bool vex_x, int nds_enc, VexSimdPrefix pre, VexOpcode opc); - void evex_prefix(bool vex_r, bool vex_b, bool vex_x, bool vex_w, bool evex_r, bool evex_v, - int nds_enc, VexSimdPrefix pre, VexOpcode opc, - bool is_extended_context, bool is_merge_context, - int vector_len, bool no_mask_reg ); + void evex_prefix(bool vex_r, bool vex_b, bool vex_x, bool evex_r, bool evex_v, + int nds_enc, VexSimdPrefix pre, VexOpcode opc); void vex_prefix(Address adr, int nds_enc, int xreg_enc, VexSimdPrefix pre, VexOpcode opc, - bool vex_w, int vector_len, - bool legacy_mode = false, bool no_mask_reg = false); - - void vex_prefix(XMMRegister dst, XMMRegister nds, Address src, - VexSimdPrefix pre, int vector_len = AVX_128bit, - bool no_mask_reg = false, bool legacy_mode = false) { - int dst_enc = dst->encoding(); - int nds_enc = nds->is_valid() ? nds->encoding() : 0; - vex_prefix(src, nds_enc, dst_enc, pre, VEX_OPCODE_0F, false, vector_len, legacy_mode, no_mask_reg); - } - - void vex_prefix_q(XMMRegister dst, XMMRegister nds, Address src, - VexSimdPrefix pre, int vector_len = AVX_128bit, - bool no_mask_reg = false) { - int dst_enc = dst->encoding(); - int nds_enc = nds->is_valid() ? nds->encoding() : 0; - vex_prefix(src, nds_enc, dst_enc, pre, VEX_OPCODE_0F, true, vector_len, false, no_mask_reg); - } - - void vex_prefix_0F38(Register dst, Register nds, Address src, bool no_mask_reg = false) { - bool vex_w = false; - int vector_len = AVX_128bit; - vex_prefix(src, nds->encoding(), dst->encoding(), - VEX_SIMD_NONE, VEX_OPCODE_0F_38, vex_w, - vector_len, no_mask_reg); - } - - void vex_prefix_0F38_legacy(Register dst, Register nds, Address src, bool no_mask_reg = false) { - bool vex_w = false; - int vector_len = AVX_128bit; - vex_prefix(src, nds->encoding(), dst->encoding(), - VEX_SIMD_NONE, VEX_OPCODE_0F_38, vex_w, - vector_len, true, no_mask_reg); - } - - void vex_prefix_0F38_q(Register dst, Register nds, Address src, bool no_mask_reg = false) { - bool vex_w = true; - int vector_len = AVX_128bit; - vex_prefix(src, nds->encoding(), dst->encoding(), - VEX_SIMD_NONE, VEX_OPCODE_0F_38, vex_w, - vector_len, no_mask_reg); - } - - void vex_prefix_0F38_q_legacy(Register dst, Register nds, Address src, bool no_mask_reg = false) { - bool vex_w = true; - int vector_len = AVX_128bit; - vex_prefix(src, nds->encoding(), dst->encoding(), - VEX_SIMD_NONE, VEX_OPCODE_0F_38, vex_w, - vector_len, true, no_mask_reg); - } + InstructionAttr *attributes); int vex_prefix_and_encode(int dst_enc, int nds_enc, int src_enc, VexSimdPrefix pre, VexOpcode opc, - bool vex_w, int vector_len, - bool legacy_mode, bool no_mask_reg); + InstructionAttr *attributes); - int vex_prefix_0F38_and_encode(Register dst, Register nds, Register src, bool no_mask_reg = false) { - bool vex_w = false; - int vector_len = AVX_128bit; - return vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), - VEX_SIMD_NONE, VEX_OPCODE_0F_38, vex_w, vector_len, - false, no_mask_reg); - } + void simd_prefix(XMMRegister xreg, XMMRegister nds, Address adr, VexSimdPrefix pre, + VexOpcode opc, InstructionAttr *attributes); - int vex_prefix_0F38_and_encode_legacy(Register dst, Register nds, Register src, bool no_mask_reg = false) { - bool vex_w = false; - int vector_len = AVX_128bit; - return vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), - VEX_SIMD_NONE, VEX_OPCODE_0F_38, vex_w, vector_len, - true, no_mask_reg); - } + int simd_prefix_and_encode(XMMRegister dst, XMMRegister nds, XMMRegister src, VexSimdPrefix pre, + VexOpcode opc, InstructionAttr *attributes); - int vex_prefix_0F38_and_encode_q(Register dst, Register nds, Register src, bool no_mask_reg = false) { - bool vex_w = true; - int vector_len = AVX_128bit; - return vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), - VEX_SIMD_NONE, VEX_OPCODE_0F_38, vex_w, vector_len, - false, no_mask_reg); - } + int kreg_prefix_and_encode(KRegister dst, KRegister nds, KRegister src, VexSimdPrefix pre, + VexOpcode opc, InstructionAttr *attributes); - int vex_prefix_0F38_and_encode_q_legacy(Register dst, Register nds, Register src, bool no_mask_reg = false) { - bool vex_w = true; - int vector_len = AVX_128bit; - return vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), - VEX_SIMD_NONE, VEX_OPCODE_0F_38, vex_w, vector_len, - true, no_mask_reg); - } - - int vex_prefix_and_encode(XMMRegister dst, XMMRegister nds, XMMRegister src, - VexSimdPrefix pre, int vector_len = AVX_128bit, - VexOpcode opc = VEX_OPCODE_0F, bool legacy_mode = false, - bool no_mask_reg = false) { - int src_enc = src->encoding(); - int dst_enc = dst->encoding(); - int nds_enc = nds->is_valid() ? nds->encoding() : 0; - return vex_prefix_and_encode(dst_enc, nds_enc, src_enc, pre, opc, false, vector_len, legacy_mode, no_mask_reg); - } - - void simd_prefix(XMMRegister xreg, XMMRegister nds, Address adr, - VexSimdPrefix pre, bool no_mask_reg, VexOpcode opc = VEX_OPCODE_0F, - bool rex_w = false, int vector_len = AVX_128bit, bool legacy_mode = false); - - void simd_prefix(XMMRegister dst, Address src, VexSimdPrefix pre, - bool no_mask_reg, VexOpcode opc = VEX_OPCODE_0F) { - simd_prefix(dst, xnoreg, src, pre, no_mask_reg, opc); - } - - void simd_prefix(Address dst, XMMRegister src, VexSimdPrefix pre, bool no_mask_reg) { - simd_prefix(src, dst, pre, no_mask_reg); - } - void simd_prefix_q(XMMRegister dst, XMMRegister nds, Address src, - VexSimdPrefix pre, bool no_mask_reg = false) { - bool rex_w = true; - simd_prefix(dst, nds, src, pre, no_mask_reg, VEX_OPCODE_0F, rex_w); - } - - int simd_prefix_and_encode(XMMRegister dst, XMMRegister nds, XMMRegister src, - VexSimdPrefix pre, bool no_mask_reg, - VexOpcode opc = VEX_OPCODE_0F, - bool rex_w = false, int vector_len = AVX_128bit, - bool legacy_mode = false); - - int kreg_prefix_and_encode(KRegister dst, KRegister nds, KRegister src, - VexSimdPrefix pre, bool no_mask_reg, - VexOpcode opc = VEX_OPCODE_0F, - bool rex_w = false, int vector_len = AVX_128bit); - - int kreg_prefix_and_encode(KRegister dst, KRegister nds, Register src, - VexSimdPrefix pre, bool no_mask_reg, - VexOpcode opc = VEX_OPCODE_0F, - bool rex_w = false, int vector_len = AVX_128bit); - - // Move/convert 32-bit integer value. - int simd_prefix_and_encode(XMMRegister dst, XMMRegister nds, Register src, - VexSimdPrefix pre, bool no_mask_reg) { - // It is OK to cast from Register to XMMRegister to pass argument here - // since only encoding is used in simd_prefix_and_encode() and number of - // Gen and Xmm registers are the same. - return simd_prefix_and_encode(dst, nds, as_XMMRegister(src->encoding()), pre, no_mask_reg, VEX_OPCODE_0F); - } - int simd_prefix_and_encode(XMMRegister dst, Register src, VexSimdPrefix pre, bool no_mask_reg) { - return simd_prefix_and_encode(dst, xnoreg, src, pre, no_mask_reg); - } - int simd_prefix_and_encode(Register dst, XMMRegister src, - VexSimdPrefix pre, VexOpcode opc = VEX_OPCODE_0F, - bool no_mask_reg = false) { - return simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, pre, no_mask_reg, opc); - } - - // Move/convert 64-bit integer value. - int simd_prefix_and_encode_q(XMMRegister dst, XMMRegister nds, Register src, - VexSimdPrefix pre, bool no_mask_reg = false) { - bool rex_w = true; - return simd_prefix_and_encode(dst, nds, as_XMMRegister(src->encoding()), pre, no_mask_reg, VEX_OPCODE_0F, rex_w); - } - int simd_prefix_and_encode_q(XMMRegister dst, Register src, VexSimdPrefix pre, bool no_mask_reg) { - return simd_prefix_and_encode_q(dst, xnoreg, src, pre, no_mask_reg); - } - int simd_prefix_and_encode_q(Register dst, XMMRegister src, - VexSimdPrefix pre, VexOpcode opc = VEX_OPCODE_0F, - bool no_mask_reg = false) { - bool rex_w = true; - return simd_prefix_and_encode(as_XMMRegister(dst->encoding()), xnoreg, src, pre, no_mask_reg, opc, rex_w); - } + int kreg_prefix_and_encode(KRegister dst, KRegister nds, Register src, VexSimdPrefix pre, + VexOpcode opc, InstructionAttr *attributes); // Helper functions for groups of instructions void emit_arith_b(int op1, int op2, Register dst, int imm8); @@ -821,27 +669,6 @@ private: void emit_arith_imm32(int op1, int op2, Register dst, int32_t imm32); void emit_arith(int op1, int op2, Register dst, Register src); - void emit_simd_arith(int opcode, XMMRegister dst, Address src, VexSimdPrefix pre, bool no_mask_reg = false, bool legacy_mode = false); - void emit_simd_arith_q(int opcode, XMMRegister dst, Address src, VexSimdPrefix pre, bool no_mask_reg = false); - void emit_simd_arith(int opcode, XMMRegister dst, XMMRegister src, VexSimdPrefix pre, bool no_mask_reg = false, bool legacy_mode = false); - void emit_simd_arith_q(int opcode, XMMRegister dst, XMMRegister src, VexSimdPrefix pre, bool no_mask_reg = false); - void emit_simd_arith_nonds(int opcode, XMMRegister dst, Address src, VexSimdPrefix pre, bool no_mask_reg = false); - void emit_simd_arith_nonds_q(int opcode, XMMRegister dst, Address src, VexSimdPrefix pre, bool no_mask_reg = false); - void emit_simd_arith_nonds(int opcode, XMMRegister dst, XMMRegister src, VexSimdPrefix pre, bool no_mask_reg = false, bool legacy_mode = false); - void emit_simd_arith_nonds_q(int opcode, XMMRegister dst, XMMRegister src, VexSimdPrefix pre, bool no_mask_reg = false); - void emit_vex_arith(int opcode, XMMRegister dst, XMMRegister nds, - Address src, VexSimdPrefix pre, int vector_len, - bool no_mask_reg = false, bool legacy_mode = false); - void emit_vex_arith_q(int opcode, XMMRegister dst, XMMRegister nds, - Address src, VexSimdPrefix pre, int vector_len, - bool no_mask_reg = false); - void emit_vex_arith(int opcode, XMMRegister dst, XMMRegister nds, - XMMRegister src, VexSimdPrefix pre, int vector_len, - bool no_mask_reg = false, bool legacy_mode = false); - void emit_vex_arith_q(int opcode, XMMRegister dst, XMMRegister nds, - XMMRegister src, VexSimdPrefix pre, int vector_len, - bool no_mask_reg = false); - bool emit_compressed_disp_byte(int &disp); void emit_operand(Register reg, @@ -986,18 +813,16 @@ private: // belong in macro assembler but there is no need for both varieties to exist void init_attributes(void) { - _evex_encoding = 0; - _input_size_in_bits = 0; - _avx_vector_len = AVX_NoVec; - _tuple_type = EVEX_ETUP; - _is_evex_instruction = false; _legacy_mode_bw = (VM_Version::supports_avx512bw() == false); _legacy_mode_dq = (VM_Version::supports_avx512dq() == false); _legacy_mode_vl = (VM_Version::supports_avx512vl() == false); _legacy_mode_vlbw = (VM_Version::supports_avx512vlbw() == false); - _instruction_uses_vl = false; + _attributes = NULL; } + void set_attributes(InstructionAttr *attributes) { _attributes = attributes; } + void clear_attributes(void) { _attributes = NULL; } + void lea(Register dst, Address src); void mov(Register dst, Register src); @@ -1506,13 +1331,18 @@ private: void movddup(XMMRegister dst, XMMRegister src); + void kmovwl(KRegister dst, Register src); + void kmovdl(KRegister dst, Register src); void kmovql(KRegister dst, KRegister src); void kmovql(KRegister dst, Register src); - void kmovdl(KRegister dst, Register src); - void kmovwl(KRegister dst, Register src); void kmovql(Address dst, KRegister src); void kmovql(KRegister dst, Address src); + void kortestbl(KRegister dst, KRegister src); + void kortestwl(KRegister dst, KRegister src); + void kortestdl(KRegister dst, KRegister src); + void kortestql(KRegister dst, KRegister src); + void movdl(XMMRegister dst, Register src); void movdl(Register dst, XMMRegister src); void movdl(XMMRegister dst, Address src); @@ -1537,6 +1367,12 @@ private: void vmovdqu(XMMRegister dst, XMMRegister src); // Move Unaligned 512bit Vector + void evmovdqub(Address dst, XMMRegister src, int vector_len); + void evmovdqub(XMMRegister dst, Address src, int vector_len); + void evmovdqub(XMMRegister dst, XMMRegister src, int vector_len); + void evmovdquw(Address dst, XMMRegister src, int vector_len); + void evmovdquw(XMMRegister dst, Address src, int vector_len); + void evmovdquw(XMMRegister dst, XMMRegister src, int vector_len); void evmovdqul(Address dst, XMMRegister src, int vector_len); void evmovdqul(XMMRegister dst, Address src, int vector_len); void evmovdqul(XMMRegister dst, XMMRegister src, int vector_len); @@ -1682,8 +1518,22 @@ private: void pcmpestri(XMMRegister xmm1, XMMRegister xmm2, int imm8); void pcmpestri(XMMRegister xmm1, Address src, int imm8); + void pcmpeqb(XMMRegister dst, XMMRegister src); + void vpcmpeqb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void evpcmpeqb(KRegister kdst, XMMRegister nds, XMMRegister src, int vector_len); + void pcmpeqw(XMMRegister dst, XMMRegister src); void vpcmpeqw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void evpcmpeqw(KRegister kdst, XMMRegister nds, XMMRegister src, int vector_len); + + void pcmpeqd(XMMRegister dst, XMMRegister src); + void vpcmpeqd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void evpcmpeqd(KRegister kdst, XMMRegister nds, XMMRegister src, int vector_len); + + void pcmpeqq(XMMRegister dst, XMMRegister src); + void vpcmpeqq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void evpcmpeqq(KRegister kdst, XMMRegister nds, XMMRegister src, int vector_len); + void evpcmpeqq(KRegister kdst, XMMRegister nds, Address src, int vector_len); void pmovmskb(Register dst, XMMRegister src); void vpmovmskb(Register dst, XMMRegister src); @@ -1704,7 +1554,7 @@ private: void pmovzxbw(XMMRegister dst, XMMRegister src); void pmovzxbw(XMMRegister dst, Address src); - void vpmovzxbw(XMMRegister dst, Address src); + void vpmovzxbw(XMMRegister dst, Address src, int vector_len); #ifndef _LP64 // no 32bit push/pop on amd64 void popl(Address dst); @@ -2106,12 +1956,12 @@ private: void vextracti128h(Address dst, XMMRegister src); // Copy low 256bit into high 256bit of ZMM registers. - void vinserti64x4h(XMMRegister dst, XMMRegister nds, XMMRegister src); - void vinsertf64x4h(XMMRegister dst, XMMRegister nds, XMMRegister src); - void vextracti64x4h(XMMRegister dst, XMMRegister src); - void vextractf64x4h(XMMRegister dst, XMMRegister src); - void vextractf64x4h(Address dst, XMMRegister src); - void vinsertf64x4h(XMMRegister dst, Address src); + void vinserti64x4h(XMMRegister dst, XMMRegister nds, XMMRegister src, int value); + void vinsertf64x4h(XMMRegister dst, XMMRegister nds, XMMRegister src, int value); + void vextracti64x4h(XMMRegister dst, XMMRegister src, int value); + void vextractf64x4h(XMMRegister dst, XMMRegister src, int value); + void vextractf64x4h(Address dst, XMMRegister src, int value); + void vinsertf64x4h(XMMRegister dst, Address src, int value); // Copy targeted 128bit segments of the ZMM registers void vextracti64x2h(XMMRegister dst, XMMRegister src, int value); @@ -2173,4 +2023,95 @@ private: }; +// The Intel x86/Amd64 Assembler attributes: All fields enclosed here are to guide encoding level decisions. +// Specific set functions are for specialized use, else defaults or whatever was supplied to object construction +// are applied. +class InstructionAttr { +public: + InstructionAttr( + int vector_len, + bool rex_vex_w, + bool legacy_mode, + bool no_reg_mask, + bool uses_vl) + : + _avx_vector_len(vector_len), + _rex_vex_w(rex_vex_w), + _legacy_mode(legacy_mode), + _no_reg_mask(no_reg_mask), + _uses_vl(uses_vl), + _tuple_type(Assembler::EVEX_ETUP), + _input_size_in_bits(Assembler::EVEX_NObit), + _is_evex_instruction(false), + _evex_encoding(0), + _is_clear_context(false), + _is_extended_context(false), + _current_assembler(NULL) { + if (UseAVX < 3) _legacy_mode = true; + } + + ~InstructionAttr() { + if (_current_assembler != NULL) { + _current_assembler->clear_attributes(); + } + _current_assembler = NULL; + } + +private: + int _avx_vector_len; + bool _rex_vex_w; + bool _legacy_mode; + bool _no_reg_mask; + bool _uses_vl; + int _tuple_type; + int _input_size_in_bits; + bool _is_evex_instruction; + int _evex_encoding; + bool _is_clear_context; + bool _is_extended_context; + + Assembler *_current_assembler; + +public: + // query functions for field accessors + int get_vector_len(void) const { return _avx_vector_len; } + bool is_rex_vex_w(void) const { return _rex_vex_w; } + bool is_legacy_mode(void) const { return _legacy_mode; } + bool is_no_reg_mask(void) const { return _no_reg_mask; } + bool uses_vl(void) const { return _uses_vl; } + int get_tuple_type(void) const { return _tuple_type; } + int get_input_size(void) const { return _input_size_in_bits; } + int is_evex_instruction(void) const { return _is_evex_instruction; } + int get_evex_encoding(void) const { return _evex_encoding; } + bool is_clear_context(void) const { return _is_clear_context; } + bool is_extended_context(void) const { return _is_extended_context; } + + // Set the vector len manually + void set_vector_len(int vector_len) { _avx_vector_len = vector_len; } + + // Set the instruction to be encoded in AVX mode + void set_is_legacy_mode(void) { _legacy_mode = true; } + + // Set the current instuction to be encoded as an EVEX instuction + void set_is_evex_instruction(void) { _is_evex_instruction = true; } + + // Internal encoding data used in compressed immediate offset programming + void set_evex_encoding(int value) { _evex_encoding = value; } + + // Set the Evex.Z field to be used to clear all non directed XMM/YMM/ZMM components + void set_is_clear_context(void) { _is_clear_context = true; } + + // Map back to current asembler so that we can manage object level assocation + void set_current_assembler(Assembler *current_assembler) { _current_assembler = current_assembler; } + + // Address modifiers used for compressed displacement calculation + void set_address_attributes(int tuple_type, int input_size_in_bits) { + if (VM_Version::supports_evex()) { + _tuple_type = tuple_type; + _input_size_in_bits = input_size_in_bits; + } + } + +}; + #endif // CPU_X86_VM_ASSEMBLER_X86_HPP diff --git a/hotspot/src/cpu/x86/vm/c1_CodeStubs_x86.cpp b/hotspot/src/cpu/x86/vm/c1_CodeStubs_x86.cpp index 8a25fd636b3..4f6690fa540 100644 --- a/hotspot/src/cpu/x86/vm/c1_CodeStubs_x86.cpp +++ b/hotspot/src/cpu/x86/vm/c1_CodeStubs_x86.cpp @@ -81,7 +81,8 @@ void ConversionStub::emit_code(LIR_Assembler* ce) { void CounterOverflowStub::emit_code(LIR_Assembler* ce) { __ bind(_entry); - ce->store_parameter(_method->as_register(), 1); + Metadata *m = _method->as_constant_ptr()->as_metadata(); + ce->store_parameter(m, 1); ce->store_parameter(_bci, 0); __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::counter_overflow_id))); ce->add_call_info_here(_info); diff --git a/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp index bd7d145e991..6e62429a7ed 100644 --- a/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp +++ b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp @@ -2971,6 +2971,14 @@ void LIR_Assembler::store_parameter(jobject o, int offset_from_rsp_in_words) { } +void LIR_Assembler::store_parameter(Metadata* m, int offset_from_rsp_in_words) { + assert(offset_from_rsp_in_words >= 0, "invalid offset from rsp"); + int offset_from_rsp_in_bytes = offset_from_rsp_in_words * BytesPerWord; + assert(offset_from_rsp_in_bytes < frame_map()->reserved_argument_area_size(), "invalid offset"); + __ mov_metadata(Address(rsp, offset_from_rsp_in_bytes), m); +} + + // This code replaces a call to arraycopy; no exception may // be thrown in this code, they must be thrown in the System.arraycopy // activation frame; we could save some checks if this would not be the case @@ -3711,7 +3719,7 @@ void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest) { if (left->as_xmm_float_reg() != dest->as_xmm_float_reg()) { __ movflt(dest->as_xmm_float_reg(), left->as_xmm_float_reg()); } - if (UseAVX > 1) { + if (UseAVX > 0) { __ vnegatess(dest->as_xmm_float_reg(), dest->as_xmm_float_reg(), ExternalAddress((address)float_signflip_pool)); } else { @@ -3722,7 +3730,7 @@ void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest) { if (left->as_xmm_double_reg() != dest->as_xmm_double_reg()) { __ movdbl(dest->as_xmm_double_reg(), left->as_xmm_double_reg()); } - if (UseAVX > 1) { + if (UseAVX > 0) { __ vnegatesd(dest->as_xmm_double_reg(), dest->as_xmm_double_reg(), ExternalAddress((address)double_signflip_pool)); } else { diff --git a/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.hpp b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.hpp index 6ed351033bc..f9514edef45 100644 --- a/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.hpp +++ b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2015, 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 @@ -49,9 +49,10 @@ Register recv, Label* update_done); public: - void store_parameter(Register r, int offset_from_esp_in_words); - void store_parameter(jint c, int offset_from_esp_in_words); - void store_parameter(jobject c, int offset_from_esp_in_words); + void store_parameter(Register r, int offset_from_esp_in_words); + void store_parameter(jint c, int offset_from_esp_in_words); + void store_parameter(jobject c, int offset_from_esp_in_words); + void store_parameter(Metadata* c, int offset_from_esp_in_words); enum { call_stub_size = NOT_LP64(15) LP64_ONLY(28), exception_handler_size = DEBUG_ONLY(1*K) NOT_DEBUG(175), diff --git a/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp b/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp index 9933655cdba..8303fe989c8 100644 --- a/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp +++ b/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2015, 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 @@ -80,6 +80,7 @@ LIR_Opr LIRGenerator::divInOpr() { return FrameMap::rax_opr; } LIR_Opr LIRGenerator::divOutOpr() { return FrameMap::rax_opr; } LIR_Opr LIRGenerator::remOutOpr() { return FrameMap::rdx_opr; } LIR_Opr LIRGenerator::shiftCountOpr() { return FrameMap::rcx_opr; } +LIR_Opr LIRGenerator::syncLockOpr() { return new_register(T_INT); } LIR_Opr LIRGenerator::syncTempOpr() { return FrameMap::rax_opr; } LIR_Opr LIRGenerator::getThreadTemp() { return LIR_OprFact::illegalOpr; } diff --git a/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.hpp b/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.hpp index c53937c78a0..5695dad2251 100644 --- a/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.hpp +++ b/hotspot/src/cpu/x86/vm/c1_MacroAssembler_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, 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 @@ -117,4 +117,7 @@ void invalidate_registers(bool inv_rax, bool inv_rbx, bool inv_rcx, bool inv_rdx, bool inv_rsi, bool inv_rdi) PRODUCT_RETURN; + // This platform only uses signal-based null checks. The Label is not needed. + void null_check(Register r, Label *Lnull = NULL) { MacroAssembler::null_check(r); } + #endif // CPU_X86_VM_C1_MACROASSEMBLER_X86_HPP diff --git a/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp b/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp index 02c0bed270d..c977465cfc5 100644 --- a/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp +++ b/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp @@ -1622,9 +1622,9 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { NOT_LP64(__ get_thread(thread);) Address queue_index(thread, in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_index())); + SATBMarkQueue::byte_offset_of_index())); Address buffer(thread, in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_buf())); + SATBMarkQueue::byte_offset_of_buf())); Label done; Label runtime; @@ -1698,9 +1698,9 @@ OopMapSet* Runtime1::generate_code_for(StubID id, StubAssembler* sasm) { const Register thread = NOT_LP64(rax) LP64_ONLY(r15_thread); Address queue_index(thread, in_bytes(JavaThread::dirty_card_queue_offset() + - PtrQueue::byte_offset_of_index())); + DirtyCardQueue::byte_offset_of_index())); Address buffer(thread, in_bytes(JavaThread::dirty_card_queue_offset() + - PtrQueue::byte_offset_of_buf())); + DirtyCardQueue::byte_offset_of_buf())); __ push(rax); __ push(rcx); diff --git a/hotspot/src/cpu/x86/vm/c2_globals_x86.hpp b/hotspot/src/cpu/x86/vm/c2_globals_x86.hpp index db3692f8ccc..ec0b81cc541 100644 --- a/hotspot/src/cpu/x86/vm/c2_globals_x86.hpp +++ b/hotspot/src/cpu/x86/vm/c2_globals_x86.hpp @@ -84,6 +84,7 @@ define_pd_global(bool, UseCISCSpill, true); define_pd_global(bool, OptoScheduling, false); define_pd_global(bool, OptoBundling, false); define_pd_global(bool, OptoRegScheduling, true); +define_pd_global(bool, SuperWordLoopUnrollAnalysis, true); define_pd_global(intx, ReservedCodeCacheSize, 48*M); define_pd_global(intx, NonProfiledCodeHeapSize, 21*M); diff --git a/hotspot/src/cpu/x86/vm/c2_init_x86.cpp b/hotspot/src/cpu/x86/vm/c2_init_x86.cpp index 32afe488f35..522af0038ae 100644 --- a/hotspot/src/cpu/x86/vm/c2_init_x86.cpp +++ b/hotspot/src/cpu/x86/vm/c2_init_x86.cpp @@ -58,6 +58,4 @@ void Compile::pd_compiler2_init() { OptoReg::invalidate(i); } } - - SuperWordLoopUnrollAnalysis = true; } diff --git a/hotspot/src/cpu/x86/vm/jvmciCodeInstaller_x86.cpp b/hotspot/src/cpu/x86/vm/jvmciCodeInstaller_x86.cpp index a7d5c62ef38..d70dbaccb73 100644 --- a/hotspot/src/cpu/x86/vm/jvmciCodeInstaller_x86.cpp +++ b/hotspot/src/cpu/x86/vm/jvmciCodeInstaller_x86.cpp @@ -36,7 +36,7 @@ #include "code/vmreg.hpp" #include "vmreg_x86.inline.hpp" -jint CodeInstaller::pd_next_offset(NativeInstruction* inst, jint pc_offset, oop method) { +jint CodeInstaller::pd_next_offset(NativeInstruction* inst, jint pc_offset, Handle method, TRAPS) { if (inst->is_call() || inst->is_jump()) { assert(NativeCall::instruction_size == (int)NativeJump::instruction_size, "unexpected size"); return (pc_offset + NativeCall::instruction_size); @@ -53,18 +53,17 @@ jint CodeInstaller::pd_next_offset(NativeInstruction* inst, jint pc_offset, oop return (offset); } else if (inst->is_call_reg()) { // the inlined vtable stub contains a "call register" instruction - assert(method != NULL, "only valid for virtual calls"); + assert(method.not_null(), "only valid for virtual calls"); return (pc_offset + ((NativeCallReg *) inst)->next_instruction_offset()); } else if (inst->is_cond_jump()) { address pc = (address) (inst); return pc_offset + (jint) (Assembler::locate_next_instruction(pc) - pc); } else { - fatal("unsupported type of instruction for call site"); - return 0; + JVMCI_ERROR_0("unsupported type of instruction for call site"); } } -void CodeInstaller::pd_patch_OopConstant(int pc_offset, Handle& constant) { +void CodeInstaller::pd_patch_OopConstant(int pc_offset, Handle constant, TRAPS) { address pc = _instructions->start() + pc_offset; Handle obj = HotSpotObjectConstantImpl::object(constant); jobject value = JNIHandles::make_local(obj()); @@ -75,7 +74,7 @@ void CodeInstaller::pd_patch_OopConstant(int pc_offset, Handle& constant) { _instructions->relocate(pc, oop_Relocation::spec(oop_index), Assembler::narrow_oop_operand); TRACE_jvmci_3("relocating (narrow oop constant) at " PTR_FORMAT "/" PTR_FORMAT, p2i(pc), p2i(operand)); #else - fatal("compressed oop on 32bit"); + JVMCI_ERROR("compressed oop on 32bit"); #endif } else { address operand = Assembler::locate_operand(pc, Assembler::imm_operand); @@ -85,19 +84,19 @@ void CodeInstaller::pd_patch_OopConstant(int pc_offset, Handle& constant) { } } -void CodeInstaller::pd_patch_MetaspaceConstant(int pc_offset, Handle& constant) { +void CodeInstaller::pd_patch_MetaspaceConstant(int pc_offset, Handle constant, TRAPS) { address pc = _instructions->start() + pc_offset; if (HotSpotMetaspaceConstantImpl::compressed(constant)) { #ifdef _LP64 address operand = Assembler::locate_operand(pc, Assembler::narrow_oop_operand); - *((narrowKlass*) operand) = record_narrow_metadata_reference(constant); + *((narrowKlass*) operand) = record_narrow_metadata_reference(constant, CHECK); TRACE_jvmci_3("relocating (narrow metaspace constant) at " PTR_FORMAT "/" PTR_FORMAT, p2i(pc), p2i(operand)); #else - fatal("compressed Klass* on 32bit"); + JVMCI_ERROR("compressed Klass* on 32bit"); #endif } else { address operand = Assembler::locate_operand(pc, Assembler::imm_operand); - *((Metadata**) operand) = record_metadata_reference(constant); + *((Metadata**) operand) = record_metadata_reference(constant, CHECK); TRACE_jvmci_3("relocating (metaspace constant) at " PTR_FORMAT "/" PTR_FORMAT, p2i(pc), p2i(operand)); } } @@ -117,7 +116,7 @@ void CodeInstaller::pd_patch_DataSectionReference(int pc_offset, int data_offset TRACE_jvmci_3("relocating at " PTR_FORMAT "/" PTR_FORMAT " with destination at " PTR_FORMAT " (%d)", p2i(pc), p2i(operand), p2i(dest), data_offset); } -void CodeInstaller::pd_relocate_ForeignCall(NativeInstruction* inst, jlong foreign_call_destination) { +void CodeInstaller::pd_relocate_ForeignCall(NativeInstruction* inst, jlong foreign_call_destination, TRAPS) { address pc = (address) inst; if (inst->is_call()) { // NOTE: for call without a mov, the offset must fit a 32-bit immediate @@ -139,18 +138,18 @@ void CodeInstaller::pd_relocate_ForeignCall(NativeInstruction* inst, jlong forei *(jint*) disp += ((address) foreign_call_destination) - old_dest; _instructions->relocate(pc, runtime_call_Relocation::spec(), Assembler::call32_operand); } else { - fatal("unsupported relocation for foreign call"); + JVMCI_ERROR("unsupported relocation for foreign call"); } TRACE_jvmci_3("relocating (foreign call) at " PTR_FORMAT, p2i(inst)); } -void CodeInstaller::pd_relocate_JavaMethod(oop hotspot_method, jint pc_offset) { +void CodeInstaller::pd_relocate_JavaMethod(Handle hotspot_method, jint pc_offset, TRAPS) { #ifdef ASSERT Method* method = NULL; // we need to check, this might also be an unresolved method if (hotspot_method->is_a(HotSpotResolvedJavaMethodImpl::klass())) { - method = getMethodFromHotSpotMethod(hotspot_method); + method = getMethodFromHotSpotMethod(hotspot_method()); } #endif switch (_next_call_type) { @@ -185,6 +184,7 @@ void CodeInstaller::pd_relocate_JavaMethod(oop hotspot_method, jint pc_offset) { break; } default: + JVMCI_ERROR("invalid _next_call_type value"); break; } } @@ -198,7 +198,7 @@ static void relocate_poll_near(address pc) { } -void CodeInstaller::pd_relocate_poll(address pc, jint mark) { +void CodeInstaller::pd_relocate_poll(address pc, jint mark, TRAPS) { switch (mark) { case POLL_NEAR: { relocate_poll_near(pc); @@ -222,13 +222,13 @@ void CodeInstaller::pd_relocate_poll(address pc, jint mark) { _instructions->relocate(pc, relocInfo::poll_return_type, Assembler::imm_operand); break; default: - fatal("invalid mark value"); + JVMCI_ERROR("invalid mark value: %d", mark); break; } } // convert JVMCI register indices (as used in oop maps) to HotSpot registers -VMReg CodeInstaller::get_hotspot_reg(jint jvmci_reg) { +VMReg CodeInstaller::get_hotspot_reg(jint jvmci_reg, TRAPS) { if (jvmci_reg < RegisterImpl::number_of_registers) { return as_Register(jvmci_reg)->as_VMReg(); } else { @@ -236,8 +236,7 @@ VMReg CodeInstaller::get_hotspot_reg(jint jvmci_reg) { if (floatRegisterNumber < XMMRegisterImpl::number_of_registers) { return as_XMMRegister(floatRegisterNumber)->as_VMReg(); } - ShouldNotReachHere(); - return NULL; + JVMCI_ERROR_NULL("invalid register number: %d", jvmci_reg); } } diff --git a/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp b/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp index 100ea5962c6..21646e1bc0d 100644 --- a/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp +++ b/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp @@ -3651,12 +3651,71 @@ void MacroAssembler::movptr(Address dst, Register src) { LP64_ONLY(movq(dst, src)) NOT_LP64(movl(dst, src)); } +void MacroAssembler::movdqu(Address dst, XMMRegister src) { + if (UseAVX > 2 && !VM_Version::supports_avx512vl() && (src->encoding() > 15)) { + Assembler::vextractf32x4h(dst, src, 0); + } else { + Assembler::movdqu(dst, src); + } +} + +void MacroAssembler::movdqu(XMMRegister dst, Address src) { + if (UseAVX > 2 && !VM_Version::supports_avx512vl() && (dst->encoding() > 15)) { + Assembler::vinsertf32x4h(dst, src, 0); + } else { + Assembler::movdqu(dst, src); + } +} + +void MacroAssembler::movdqu(XMMRegister dst, XMMRegister src) { + if (UseAVX > 2 && !VM_Version::supports_avx512vl()) { + Assembler::evmovdqul(dst, src, Assembler::AVX_512bit); + } else { + Assembler::movdqu(dst, src); + } +} + void MacroAssembler::movdqu(XMMRegister dst, AddressLiteral src) { if (reachable(src)) { - Assembler::movdqu(dst, as_Address(src)); + movdqu(dst, as_Address(src)); } else { lea(rscratch1, src); - Assembler::movdqu(dst, Address(rscratch1, 0)); + movdqu(dst, Address(rscratch1, 0)); + } +} + +void MacroAssembler::vmovdqu(Address dst, XMMRegister src) { + if (UseAVX > 2 && !VM_Version::supports_avx512vl() && (src->encoding() > 15)) { + Assembler::vextractf64x4h(dst, src, 0); + } else { + Assembler::vmovdqu(dst, src); + } +} + +void MacroAssembler::vmovdqu(XMMRegister dst, Address src) { + if (UseAVX > 2 && !VM_Version::supports_avx512vl() && (dst->encoding() > 15)) { + Assembler::vinsertf64x4h(dst, src, 0); + } else { + Assembler::vmovdqu(dst, src); + } +} + +void MacroAssembler::vmovdqu(XMMRegister dst, XMMRegister src) { + if (UseAVX > 2 && !VM_Version::supports_avx512vl()) { + Assembler::evmovdqul(dst, src, Assembler::AVX_512bit); + } + else { + Assembler::vmovdqu(dst, src); + } +} + +void MacroAssembler::vmovdqu(XMMRegister dst, AddressLiteral src) { + if (reachable(src)) { + vmovdqu(dst, as_Address(src)); + } + else { + lea(rscratch1, src); + vmovdqu(dst, Address(rscratch1, 0)); } } @@ -3726,6 +3785,10 @@ void MacroAssembler::os_breakpoint() { call(RuntimeAddress(CAST_FROM_FN_PTR(address, os::breakpoint))); } +#ifdef _LP64 +#define XSTATE_BV 0x200 +#endif + void MacroAssembler::pop_CPU_state() { pop_FPU_state(); pop_IU_state(); @@ -3735,27 +3798,7 @@ void MacroAssembler::pop_FPU_state() { #ifndef _LP64 frstor(Address(rsp, 0)); #else - // AVX will continue to use the fxsave area. - // EVEX needs to utilize the xsave area, which is under different - // management. - if(VM_Version::supports_evex()) { - // EDX:EAX describe the XSAVE header and - // are obtained while fetching info for XCR0 via cpuid. - // These two registers make up 64-bits in the header for which bits - // 62:10 are currently reserved for future implementations and unused. Bit 63 - // is unused for our implementation as we do not utilize - // compressed XSAVE areas. Bits 9..8 are currently ignored as we do not use - // the functionality for PKRU state and MSR tracing. - // Ergo we are primarily concerned with bits 7..0, which define - // which ISA extensions and features are enabled for a given machine and are - // defined in XemXcr0Eax and is used to map the XSAVE area - // for restoring registers as described via XCR0. - movl(rdx,VM_Version::get_xsave_header_upper_segment()); - movl(rax,VM_Version::get_xsave_header_lower_segment()); - xrstor(Address(rsp, 0)); - } else { - fxrstor(Address(rsp, 0)); - } + fxrstor(Address(rsp, 0)); #endif addptr(rsp, FPUStateSizeInWords * wordSize); } @@ -3773,49 +3816,13 @@ void MacroAssembler::push_CPU_state() { push_FPU_state(); } -#ifdef _LP64 -#define XSTATE_BV 0x200 -#endif - void MacroAssembler::push_FPU_state() { subptr(rsp, FPUStateSizeInWords * wordSize); #ifndef _LP64 fnsave(Address(rsp, 0)); fwait(); #else - // AVX will continue to use the fxsave area. - // EVEX needs to utilize the xsave area, which is under different - // management. - if(VM_Version::supports_evex()) { - // Save a copy of EAX and EDX - push(rax); - push(rdx); - // EDX:EAX describe the XSAVE header and - // are obtained while fetching info for XCR0 via cpuid. - // These two registers make up 64-bits in the header for which bits - // 62:10 are currently reserved for future implementations and unused. Bit 63 - // is unused for our implementation as we do not utilize - // compressed XSAVE areas. Bits 9..8 are currently ignored as we do not use - // the functionality for PKRU state and MSR tracing. - // Ergo we are primarily concerned with bits 7..0, which define - // which ISA extensions and features are enabled for a given machine and are - // defined in XemXcr0Eax and is used to program XSAVE area - // for saving the required registers as defined in XCR0. - int xcr0_edx = VM_Version::get_xsave_header_upper_segment(); - int xcr0_eax = VM_Version::get_xsave_header_lower_segment(); - movl(rdx,xcr0_edx); - movl(rax,xcr0_eax); - xsave(Address(rsp, wordSize*2)); - // now Apply control bits and clear bytes 8..23 in the header - pop(rdx); - pop(rax); - movl(Address(rsp, XSTATE_BV), xcr0_eax); - movl(Address(rsp, XSTATE_BV+4), xcr0_edx); - andq(Address(rsp, XSTATE_BV+8), 0); - andq(Address(rsp, XSTATE_BV+16), 0); - } else { - fxsave(Address(rsp, 0)); - } + fxsave(Address(rsp, 0)); #endif // LP64 } @@ -3942,6 +3949,236 @@ void MacroAssembler::testl(Register dst, AddressLiteral src) { testl(dst, as_Address(src)); } +void MacroAssembler::pcmpeqb(XMMRegister dst, XMMRegister src) { + int dst_enc = dst->encoding(); + int src_enc = src->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::pcmpeqb(dst, src); + } else if ((dst_enc < 16) && (src_enc < 16)) { + Assembler::pcmpeqb(dst, src); + } else if (src_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::pcmpeqb(xmm0, src); + movdqu(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else if (dst_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::pcmpeqb(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + movdqu(xmm0, src); + movdqu(xmm1, dst); + Assembler::pcmpeqb(xmm1, xmm0); + movdqu(dst, xmm1); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::pcmpeqw(XMMRegister dst, XMMRegister src) { + int dst_enc = dst->encoding(); + int src_enc = src->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::pcmpeqw(dst, src); + } else if ((dst_enc < 16) && (src_enc < 16)) { + Assembler::pcmpeqw(dst, src); + } else if (src_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::pcmpeqw(xmm0, src); + movdqu(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else if (dst_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::pcmpeqw(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + movdqu(xmm0, src); + movdqu(xmm1, dst); + Assembler::pcmpeqw(xmm1, xmm0); + movdqu(dst, xmm1); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::pcmpestri(XMMRegister dst, Address src, int imm8) { + int dst_enc = dst->encoding(); + if (dst_enc < 16) { + Assembler::pcmpestri(dst, src, imm8); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::pcmpestri(xmm0, src, imm8); + movdqu(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::pcmpestri(XMMRegister dst, XMMRegister src, int imm8) { + int dst_enc = dst->encoding(); + int src_enc = src->encoding(); + if ((dst_enc < 16) && (src_enc < 16)) { + Assembler::pcmpestri(dst, src, imm8); + } else if (src_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::pcmpestri(xmm0, src, imm8); + movdqu(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else if (dst_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::pcmpestri(dst, xmm0, imm8); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + movdqu(xmm0, src); + movdqu(xmm1, dst); + Assembler::pcmpestri(xmm1, xmm0, imm8); + movdqu(dst, xmm1); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::pmovzxbw(XMMRegister dst, XMMRegister src) { + int dst_enc = dst->encoding(); + int src_enc = src->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::pmovzxbw(dst, src); + } else if ((dst_enc < 16) && (src_enc < 16)) { + Assembler::pmovzxbw(dst, src); + } else if (src_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::pmovzxbw(xmm0, src); + movdqu(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else if (dst_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::pmovzxbw(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + movdqu(xmm0, src); + movdqu(xmm1, dst); + Assembler::pmovzxbw(xmm1, xmm0); + movdqu(dst, xmm1); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::pmovzxbw(XMMRegister dst, Address src) { + int dst_enc = dst->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::pmovzxbw(dst, src); + } else if (dst_enc < 16) { + Assembler::pmovzxbw(dst, src); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::pmovzxbw(xmm0, src); + movdqu(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::pmovmskb(Register dst, XMMRegister src) { + int src_enc = src->encoding(); + if (src_enc < 16) { + Assembler::pmovmskb(dst, src); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::pmovmskb(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::ptest(XMMRegister dst, XMMRegister src) { + int dst_enc = dst->encoding(); + int src_enc = src->encoding(); + if ((dst_enc < 16) && (src_enc < 16)) { + Assembler::ptest(dst, src); + } else if (src_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::ptest(xmm0, src); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else if (dst_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::ptest(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + movdqu(xmm0, src); + movdqu(xmm1, dst); + Assembler::ptest(xmm1, xmm0); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + void MacroAssembler::sqrtsd(XMMRegister dst, AddressLiteral src) { if (reachable(src)) { Assembler::sqrtsd(dst, as_Address(src)); @@ -4007,6 +4244,23 @@ void MacroAssembler::xorpd(XMMRegister dst, AddressLiteral src) { } } +void MacroAssembler::xorpd(XMMRegister dst, XMMRegister src) { + if (UseAVX > 2 && !VM_Version::supports_avx512dq() && (dst->encoding() == src->encoding())) { + Assembler::vpxor(dst, dst, src, Assembler::AVX_512bit); + } + else { + Assembler::xorpd(dst, src); + } +} + +void MacroAssembler::xorps(XMMRegister dst, XMMRegister src) { + if (UseAVX > 2 && !VM_Version::supports_avx512dq() && (dst->encoding() == src->encoding())) { + Assembler::vpxor(dst, dst, src, Assembler::AVX_512bit); + } else { + Assembler::xorps(dst, src); + } +} + void MacroAssembler::xorps(XMMRegister dst, AddressLiteral src) { // Used in sign-bit flipping with aligned address. assert((UseAVX > 0) || (((intptr_t)src.target() & 15) == 0), "SSE mode requires address alignment 16 bytes"); @@ -4050,6 +4304,864 @@ void MacroAssembler::vaddss(XMMRegister dst, XMMRegister nds, AddressLiteral src } } +void MacroAssembler::vabsss(XMMRegister dst, XMMRegister nds, XMMRegister src, AddressLiteral negate_field, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + int src_enc = src->encoding(); + if ((dst_enc < 16) && (nds_enc < 16)) { + vandps(dst, nds, negate_field, vector_len); + } else if ((src_enc < 16) && (dst_enc < 16)) { + movss(src, nds); + vandps(dst, src, negate_field, vector_len); + } else if (src_enc < 16) { + movss(src, nds); + vandps(src, src, negate_field, vector_len); + movss(dst, src); + } else if (dst_enc < 16) { + movdqu(src, xmm0); + movss(xmm0, nds); + vandps(dst, xmm0, negate_field, vector_len); + movdqu(xmm0, src); + } else if (nds_enc < 16) { + movdqu(src, xmm0); + vandps(xmm0, nds, negate_field, vector_len); + movss(dst, xmm0); + movdqu(xmm0, src); + } else { + movdqu(src, xmm0); + movss(xmm0, nds); + vandps(xmm0, xmm0, negate_field, vector_len); + movss(dst, xmm0); + movdqu(xmm0, src); + } +} + +void MacroAssembler::vabssd(XMMRegister dst, XMMRegister nds, XMMRegister src, AddressLiteral negate_field, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + int src_enc = src->encoding(); + if ((dst_enc < 16) && (nds_enc < 16)) { + vandpd(dst, nds, negate_field, vector_len); + } else if ((src_enc < 16) && (dst_enc < 16)) { + movsd(src, nds); + vandpd(dst, src, negate_field, vector_len); + } else if (src_enc < 16) { + movsd(src, nds); + vandpd(src, src, negate_field, vector_len); + movsd(dst, src); + } else if (dst_enc < 16) { + movdqu(src, xmm0); + movsd(xmm0, nds); + vandpd(dst, xmm0, negate_field, vector_len); + movdqu(xmm0, src); + } else if (nds_enc < 16) { + movdqu(src, xmm0); + vandpd(xmm0, nds, negate_field, vector_len); + movsd(dst, xmm0); + movdqu(xmm0, src); + } else { + movdqu(src, xmm0); + movsd(xmm0, nds); + vandpd(xmm0, xmm0, negate_field, vector_len); + movsd(dst, xmm0); + movdqu(xmm0, src); + } +} + +void MacroAssembler::vpaddb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + int src_enc = src->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpaddb(dst, nds, src, vector_len); + } else if ((dst_enc < 16) && (src_enc < 16)) { + Assembler::vpaddb(dst, dst, src, vector_len); + } else if ((dst_enc < 16) && (nds_enc < 16)) { + // use nds as scratch for src + evmovdqul(nds, src, Assembler::AVX_512bit); + Assembler::vpaddb(dst, dst, nds, vector_len); + } else if ((src_enc < 16) && (nds_enc < 16)) { + // use nds as scratch for dst + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpaddb(nds, nds, src, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else if (dst_enc < 16) { + // use nds as scatch for xmm0 to hold src + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::vpaddb(dst, dst, xmm0, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } else { + // worse case scenario, all regs are in the upper bank + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm1, src, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpaddb(xmm0, xmm0, xmm1, vector_len); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::vpaddb(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpaddb(dst, nds, src, vector_len); + } else if (dst_enc < 16) { + Assembler::vpaddb(dst, dst, src, vector_len); + } else if (nds_enc < 16) { + // implies dst_enc in upper bank with src as scratch + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpaddb(nds, nds, src, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else { + // worse case scenario, all regs in upper bank + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpaddb(xmm0, xmm0, src, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } +} + +void MacroAssembler::vpaddw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + int src_enc = src->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpaddw(dst, nds, src, vector_len); + } else if ((dst_enc < 16) && (src_enc < 16)) { + Assembler::vpaddw(dst, dst, src, vector_len); + } else if ((dst_enc < 16) && (nds_enc < 16)) { + // use nds as scratch for src + evmovdqul(nds, src, Assembler::AVX_512bit); + Assembler::vpaddw(dst, dst, nds, vector_len); + } else if ((src_enc < 16) && (nds_enc < 16)) { + // use nds as scratch for dst + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpaddw(nds, nds, src, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else if (dst_enc < 16) { + // use nds as scatch for xmm0 to hold src + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::vpaddw(dst, dst, xmm0, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } else { + // worse case scenario, all regs are in the upper bank + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm1, src, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpaddw(xmm0, xmm0, xmm1, vector_len); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::vpaddw(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpaddw(dst, nds, src, vector_len); + } else if (dst_enc < 16) { + Assembler::vpaddw(dst, dst, src, vector_len); + } else if (nds_enc < 16) { + // implies dst_enc in upper bank with src as scratch + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpaddw(nds, nds, src, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else { + // worse case scenario, all regs in upper bank + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpaddw(xmm0, xmm0, src, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } +} + +void MacroAssembler::vpbroadcastw(XMMRegister dst, XMMRegister src) { + int dst_enc = dst->encoding(); + int src_enc = src->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpbroadcastw(dst, src); + } else if ((dst_enc < 16) && (src_enc < 16)) { + Assembler::vpbroadcastw(dst, src); + } else if (src_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpbroadcastw(xmm0, src); + movdqu(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else if (dst_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::vpbroadcastw(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + movdqu(xmm0, src); + movdqu(xmm1, dst); + Assembler::vpbroadcastw(xmm1, xmm0); + movdqu(dst, xmm1); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::vpcmpeqb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + int src_enc = src->encoding(); + assert(dst_enc == nds_enc, ""); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpcmpeqb(dst, nds, src, vector_len); + } else if ((dst_enc < 16) && (src_enc < 16)) { + Assembler::vpcmpeqb(dst, nds, src, vector_len); + } else if (src_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpcmpeqb(xmm0, xmm0, src, vector_len); + movdqu(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else if (dst_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::vpcmpeqb(dst, dst, xmm0, vector_len); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + movdqu(xmm0, src); + movdqu(xmm1, dst); + Assembler::vpcmpeqb(xmm1, xmm1, xmm0, vector_len); + movdqu(dst, xmm1); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::vpcmpeqw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + int src_enc = src->encoding(); + assert(dst_enc == nds_enc, ""); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpcmpeqw(dst, nds, src, vector_len); + } else if ((dst_enc < 16) && (src_enc < 16)) { + Assembler::vpcmpeqw(dst, nds, src, vector_len); + } else if (src_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpcmpeqw(xmm0, xmm0, src, vector_len); + movdqu(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else if (dst_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::vpcmpeqw(dst, dst, xmm0, vector_len); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + movdqu(xmm0, src); + movdqu(xmm1, dst); + Assembler::vpcmpeqw(xmm1, xmm1, xmm0, vector_len); + movdqu(dst, xmm1); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::vpmovzxbw(XMMRegister dst, Address src, int vector_len) { + int dst_enc = dst->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpmovzxbw(dst, src, vector_len); + } else if (dst_enc < 16) { + Assembler::vpmovzxbw(dst, src, vector_len); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpmovzxbw(xmm0, src, vector_len); + movdqu(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::vpmovmskb(Register dst, XMMRegister src) { + int src_enc = src->encoding(); + if (src_enc < 16) { + Assembler::vpmovmskb(dst, src); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::vpmovmskb(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::vpmullw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + int src_enc = src->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpmullw(dst, nds, src, vector_len); + } else if ((dst_enc < 16) && (src_enc < 16)) { + Assembler::vpmullw(dst, dst, src, vector_len); + } else if ((dst_enc < 16) && (nds_enc < 16)) { + // use nds as scratch for src + evmovdqul(nds, src, Assembler::AVX_512bit); + Assembler::vpmullw(dst, dst, nds, vector_len); + } else if ((src_enc < 16) && (nds_enc < 16)) { + // use nds as scratch for dst + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpmullw(nds, nds, src, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else if (dst_enc < 16) { + // use nds as scatch for xmm0 to hold src + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::vpmullw(dst, dst, xmm0, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } else { + // worse case scenario, all regs are in the upper bank + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm1, src, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpmullw(xmm0, xmm0, xmm1, vector_len); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::vpmullw(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpmullw(dst, nds, src, vector_len); + } else if (dst_enc < 16) { + Assembler::vpmullw(dst, dst, src, vector_len); + } else if (nds_enc < 16) { + // implies dst_enc in upper bank with src as scratch + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpmullw(nds, nds, src, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else { + // worse case scenario, all regs in upper bank + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpmullw(xmm0, xmm0, src, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } +} + +void MacroAssembler::vpsubb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + int src_enc = src->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpsubb(dst, nds, src, vector_len); + } else if ((dst_enc < 16) && (src_enc < 16)) { + Assembler::vpsubb(dst, dst, src, vector_len); + } else if ((dst_enc < 16) && (nds_enc < 16)) { + // use nds as scratch for src + evmovdqul(nds, src, Assembler::AVX_512bit); + Assembler::vpsubb(dst, dst, nds, vector_len); + } else if ((src_enc < 16) && (nds_enc < 16)) { + // use nds as scratch for dst + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpsubb(nds, nds, src, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else if (dst_enc < 16) { + // use nds as scatch for xmm0 to hold src + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::vpsubb(dst, dst, xmm0, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } else { + // worse case scenario, all regs are in the upper bank + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm1, src, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpsubb(xmm0, xmm0, xmm1, vector_len); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::vpsubb(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpsubb(dst, nds, src, vector_len); + } else if (dst_enc < 16) { + Assembler::vpsubb(dst, dst, src, vector_len); + } else if (nds_enc < 16) { + // implies dst_enc in upper bank with src as scratch + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpsubb(nds, nds, src, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else { + // worse case scenario, all regs in upper bank + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpsubw(xmm0, xmm0, src, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } +} + +void MacroAssembler::vpsubw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + int src_enc = src->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpsubw(dst, nds, src, vector_len); + } else if ((dst_enc < 16) && (src_enc < 16)) { + Assembler::vpsubw(dst, dst, src, vector_len); + } else if ((dst_enc < 16) && (nds_enc < 16)) { + // use nds as scratch for src + evmovdqul(nds, src, Assembler::AVX_512bit); + Assembler::vpsubw(dst, dst, nds, vector_len); + } else if ((src_enc < 16) && (nds_enc < 16)) { + // use nds as scratch for dst + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpsubw(nds, nds, src, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else if (dst_enc < 16) { + // use nds as scatch for xmm0 to hold src + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::vpsubw(dst, dst, xmm0, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } else { + // worse case scenario, all regs are in the upper bank + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm1, src, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpsubw(xmm0, xmm0, xmm1, vector_len); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::vpsubw(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpsubw(dst, nds, src, vector_len); + } else if (dst_enc < 16) { + Assembler::vpsubw(dst, dst, src, vector_len); + } else if (nds_enc < 16) { + // implies dst_enc in upper bank with src as scratch + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpsubw(nds, nds, src, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else { + // worse case scenario, all regs in upper bank + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpsubw(xmm0, xmm0, src, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } +} + +void MacroAssembler::vpsraw(XMMRegister dst, XMMRegister nds, XMMRegister shift, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + int shift_enc = shift->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpsraw(dst, nds, shift, vector_len); + } else if ((dst_enc < 16) && (shift_enc < 16)) { + Assembler::vpsraw(dst, dst, shift, vector_len); + } else if ((dst_enc < 16) && (nds_enc < 16)) { + // use nds_enc as scratch with shift + evmovdqul(nds, shift, Assembler::AVX_512bit); + Assembler::vpsraw(dst, dst, nds, vector_len); + } else if ((shift_enc < 16) && (nds_enc < 16)) { + // use nds as scratch with dst + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpsraw(nds, nds, shift, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else if (dst_enc < 16) { + // use nds to save a copy of xmm0 and hold shift + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, shift, Assembler::AVX_512bit); + Assembler::vpsraw(dst, dst, xmm0, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } else if (nds_enc < 16) { + // use nds as dest as temps + evmovdqul(nds, dst, Assembler::AVX_512bit); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, shift, Assembler::AVX_512bit); + Assembler::vpsraw(nds, nds, xmm0, vector_len); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else { + // worse case scenario, all regs are in the upper bank + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm1, shift, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpsllw(xmm0, xmm0, xmm1, vector_len); + evmovdqul(xmm1, dst, Assembler::AVX_512bit); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::vpsraw(XMMRegister dst, XMMRegister nds, int shift, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpsraw(dst, nds, shift, vector_len); + } else if (dst_enc < 16) { + Assembler::vpsraw(dst, dst, shift, vector_len); + } else if (nds_enc < 16) { + // use nds as scratch + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpsraw(nds, nds, shift, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else { + // use nds as scratch for xmm0 + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpsraw(xmm0, xmm0, shift, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } +} + +void MacroAssembler::vpsrlw(XMMRegister dst, XMMRegister nds, XMMRegister shift, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + int shift_enc = shift->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpsrlw(dst, nds, shift, vector_len); + } else if ((dst_enc < 16) && (shift_enc < 16)) { + Assembler::vpsrlw(dst, dst, shift, vector_len); + } else if ((dst_enc < 16) && (nds_enc < 16)) { + // use nds_enc as scratch with shift + evmovdqul(nds, shift, Assembler::AVX_512bit); + Assembler::vpsrlw(dst, dst, nds, vector_len); + } else if ((shift_enc < 16) && (nds_enc < 16)) { + // use nds as scratch with dst + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpsrlw(nds, nds, shift, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else if (dst_enc < 16) { + // use nds to save a copy of xmm0 and hold shift + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, shift, Assembler::AVX_512bit); + Assembler::vpsrlw(dst, dst, xmm0, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } else if (nds_enc < 16) { + // use nds as dest as temps + evmovdqul(nds, dst, Assembler::AVX_512bit); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, shift, Assembler::AVX_512bit); + Assembler::vpsrlw(nds, nds, xmm0, vector_len); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else { + // worse case scenario, all regs are in the upper bank + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm1, shift, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpsllw(xmm0, xmm0, xmm1, vector_len); + evmovdqul(xmm1, dst, Assembler::AVX_512bit); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::vpsrlw(XMMRegister dst, XMMRegister nds, int shift, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpsrlw(dst, nds, shift, vector_len); + } else if (dst_enc < 16) { + Assembler::vpsrlw(dst, dst, shift, vector_len); + } else if (nds_enc < 16) { + // use nds as scratch + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpsrlw(nds, nds, shift, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else { + // use nds as scratch for xmm0 + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpsrlw(xmm0, xmm0, shift, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } +} + +void MacroAssembler::vpsllw(XMMRegister dst, XMMRegister nds, XMMRegister shift, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + int shift_enc = shift->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpsllw(dst, nds, shift, vector_len); + } else if ((dst_enc < 16) && (shift_enc < 16)) { + Assembler::vpsllw(dst, dst, shift, vector_len); + } else if ((dst_enc < 16) && (nds_enc < 16)) { + // use nds_enc as scratch with shift + evmovdqul(nds, shift, Assembler::AVX_512bit); + Assembler::vpsllw(dst, dst, nds, vector_len); + } else if ((shift_enc < 16) && (nds_enc < 16)) { + // use nds as scratch with dst + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpsllw(nds, nds, shift, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else if (dst_enc < 16) { + // use nds to save a copy of xmm0 and hold shift + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, shift, Assembler::AVX_512bit); + Assembler::vpsllw(dst, dst, xmm0, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } else if (nds_enc < 16) { + // use nds as dest as temps + evmovdqul(nds, dst, Assembler::AVX_512bit); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, shift, Assembler::AVX_512bit); + Assembler::vpsllw(nds, nds, xmm0, vector_len); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else { + // worse case scenario, all regs are in the upper bank + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm1, shift, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpsllw(xmm0, xmm0, xmm1, vector_len); + evmovdqul(xmm1, dst, Assembler::AVX_512bit); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +void MacroAssembler::vpsllw(XMMRegister dst, XMMRegister nds, int shift, int vector_len) { + int dst_enc = dst->encoding(); + int nds_enc = nds->encoding(); + if (VM_Version::supports_avxonly() || VM_Version::supports_avx512bw()) { + Assembler::vpsllw(dst, nds, shift, vector_len); + } else if (dst_enc < 16) { + Assembler::vpsllw(dst, dst, shift, vector_len); + } else if (nds_enc < 16) { + // use nds as scratch + evmovdqul(nds, dst, Assembler::AVX_512bit); + Assembler::vpsllw(nds, nds, shift, vector_len); + evmovdqul(dst, nds, Assembler::AVX_512bit); + } else { + // use nds as scratch for xmm0 + evmovdqul(nds, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vpsllw(xmm0, xmm0, shift, vector_len); + evmovdqul(xmm0, nds, Assembler::AVX_512bit); + } +} + +void MacroAssembler::vptest(XMMRegister dst, XMMRegister src) { + int dst_enc = dst->encoding(); + int src_enc = src->encoding(); + if ((dst_enc < 16) && (src_enc < 16)) { + Assembler::vptest(dst, src); + } else if (src_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::vptest(xmm0, src); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else if (dst_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::vptest(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + movdqu(xmm0, src); + movdqu(xmm1, dst); + Assembler::vptest(xmm1, xmm0); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } +} + +// This instruction exists within macros, ergo we cannot control its input +// when emitted through those patterns. +void MacroAssembler::punpcklbw(XMMRegister dst, XMMRegister src) { + if (VM_Version::supports_avx512nobw()) { + int dst_enc = dst->encoding(); + int src_enc = src->encoding(); + if (dst_enc == src_enc) { + if (dst_enc < 16) { + Assembler::punpcklbw(dst, src); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::punpcklbw(xmm0, xmm0); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } + } else { + if ((src_enc < 16) && (dst_enc < 16)) { + Assembler::punpcklbw(dst, src); + } else if (src_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::punpcklbw(xmm0, src); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else if (dst_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::punpcklbw(dst, xmm0); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + evmovdqul(xmm1, src, Assembler::AVX_512bit); + Assembler::punpcklbw(xmm0, xmm1); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } + } + } else { + Assembler::punpcklbw(dst, src); + } +} + +// This instruction exists within macros, ergo we cannot control its input +// when emitted through those patterns. +void MacroAssembler::pshuflw(XMMRegister dst, XMMRegister src, int mode) { + if (VM_Version::supports_avx512nobw()) { + int dst_enc = dst->encoding(); + int src_enc = src->encoding(); + if (dst_enc == src_enc) { + if (dst_enc < 16) { + Assembler::pshuflw(dst, src, mode); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::pshuflw(xmm0, xmm0, mode); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } + } else { + if ((src_enc < 16) && (dst_enc < 16)) { + Assembler::pshuflw(dst, src, mode); + } else if (src_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + Assembler::pshuflw(xmm0, src, mode); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else if (dst_enc < 16) { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + evmovdqul(xmm0, src, Assembler::AVX_512bit); + Assembler::pshuflw(dst, xmm0, mode); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } else { + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); + subptr(rsp, 64); + evmovdqul(Address(rsp, 0), xmm1, Assembler::AVX_512bit); + evmovdqul(xmm0, dst, Assembler::AVX_512bit); + evmovdqul(xmm1, src, Assembler::AVX_512bit); + Assembler::pshuflw(xmm0, xmm1, mode); + evmovdqul(dst, xmm0, Assembler::AVX_512bit); + evmovdqul(xmm1, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); + addptr(rsp, 64); + } + } + } else { + Assembler::pshuflw(dst, src, mode); + } +} + void MacroAssembler::vandpd(XMMRegister dst, XMMRegister nds, AddressLiteral src, int vector_len) { if (reachable(src)) { vandpd(dst, nds, as_Address(src), vector_len); @@ -4133,31 +5245,16 @@ void MacroAssembler::vnegatess(XMMRegister dst, XMMRegister nds, AddressLiteral subptr(rsp, 64); evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); movflt(xmm0, nds); - if (reachable(src)) { - vxorps(xmm0, xmm0, as_Address(src), Assembler::AVX_128bit); - } else { - lea(rscratch1, src); - vxorps(xmm0, xmm0, Address(rscratch1, 0), Assembler::AVX_128bit); - } + vxorps(xmm0, xmm0, src, Assembler::AVX_128bit); movflt(dst, xmm0); evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); addptr(rsp, 64); } else { movflt(dst, nds); - if (reachable(src)) { - vxorps(dst, dst, as_Address(src), Assembler::AVX_128bit); - } else { - lea(rscratch1, src); - vxorps(dst, dst, Address(rscratch1, 0), Assembler::AVX_128bit); - } + vxorps(dst, dst, src, Assembler::AVX_128bit); } } else { - if (reachable(src)) { - vxorps(dst, nds, as_Address(src), Assembler::AVX_128bit); - } else { - lea(rscratch1, src); - vxorps(dst, nds, Address(rscratch1, 0), Assembler::AVX_128bit); - } + vxorps(dst, nds, src, Assembler::AVX_128bit); } } @@ -4172,31 +5269,16 @@ void MacroAssembler::vnegatesd(XMMRegister dst, XMMRegister nds, AddressLiteral subptr(rsp, 64); evmovdqul(Address(rsp, 0), xmm0, Assembler::AVX_512bit); movdbl(xmm0, nds); - if (reachable(src)) { - vxorps(xmm0, xmm0, as_Address(src), Assembler::AVX_128bit); - } else { - lea(rscratch1, src); - vxorps(xmm0, xmm0, Address(rscratch1, 0), Assembler::AVX_128bit); - } + vxorpd(xmm0, xmm0, src, Assembler::AVX_128bit); movdbl(dst, xmm0); evmovdqul(xmm0, Address(rsp, 0), Assembler::AVX_512bit); addptr(rsp, 64); } else { movdbl(dst, nds); - if (reachable(src)) { - vxorps(dst, dst, as_Address(src), Assembler::AVX_128bit); - } else { - lea(rscratch1, src); - vxorps(dst, dst, Address(rscratch1, 0), Assembler::AVX_128bit); - } + vxorpd(dst, dst, src, Assembler::AVX_128bit); } } else { - if (reachable(src)) { - vxorpd(dst, nds, as_Address(src), Assembler::AVX_128bit); - } else { - lea(rscratch1, src); - vxorpd(dst, nds, Address(rscratch1, 0), Assembler::AVX_128bit); - } + vxorpd(dst, nds, src, Assembler::AVX_128bit); } } @@ -4248,18 +5330,18 @@ void MacroAssembler::g1_write_barrier_pre(Register obj, } Address in_progress(thread, in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_active())); + SATBMarkQueue::byte_offset_of_active())); Address index(thread, in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_index())); + SATBMarkQueue::byte_offset_of_index())); Address buffer(thread, in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_buf())); + SATBMarkQueue::byte_offset_of_buf())); // Is marking active? - if (in_bytes(PtrQueue::byte_width_of_active()) == 4) { + if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { cmpl(in_progress, 0); } else { - assert(in_bytes(PtrQueue::byte_width_of_active()) == 1, "Assumption"); + assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); cmpb(in_progress, 0); } jcc(Assembler::equal, done); @@ -4346,9 +5428,9 @@ void MacroAssembler::g1_write_barrier_post(Register store_addr, #endif // _LP64 Address queue_index(thread, in_bytes(JavaThread::dirty_card_queue_offset() + - PtrQueue::byte_offset_of_index())); + DirtyCardQueue::byte_offset_of_index())); Address buffer(thread, in_bytes(JavaThread::dirty_card_queue_offset() + - PtrQueue::byte_offset_of_buf())); + DirtyCardQueue::byte_offset_of_buf())); CardTableModRefBS* ct = barrier_set_cast(Universe::heap()->barrier_set()); @@ -4688,7 +5770,6 @@ void MacroAssembler::fp_runtime_fallback(address runtime_entry, int nb_args, int pusha(); // if we are coming from c1, xmm registers may be live - int off = 0; int num_xmm_regs = LP64_ONLY(16) NOT_LP64(8); if (UseAVX > 2) { num_xmm_regs = LP64_ONLY(32) NOT_LP64(8); @@ -4697,7 +5778,7 @@ void MacroAssembler::fp_runtime_fallback(address runtime_entry, int nb_args, int if (UseSSE == 1) { subptr(rsp, sizeof(jdouble)*8); for (int n = 0; n < 8; n++) { - movflt(Address(rsp, off++*sizeof(jdouble)), as_XMMRegister(n)); + movflt(Address(rsp, n*sizeof(jdouble)), as_XMMRegister(n)); } } else if (UseSSE >= 2) { if (UseAVX > 2) { @@ -4709,37 +5790,35 @@ void MacroAssembler::fp_runtime_fallback(address runtime_entry, int nb_args, int #ifdef COMPILER2 if (MaxVectorSize > 16) { if(UseAVX > 2) { - // Save upper half of ZMM registes + // Save upper half of ZMM registers subptr(rsp, 32*num_xmm_regs); for (int n = 0; n < num_xmm_regs; n++) { - vextractf64x4h(Address(rsp, off++*32), as_XMMRegister(n)); + vextractf64x4h(Address(rsp, n*32), as_XMMRegister(n), 1); } - off = 0; } assert(UseAVX > 0, "256 bit vectors are supported only with AVX"); - // Save upper half of YMM registes + // Save upper half of YMM registers subptr(rsp, 16*num_xmm_regs); for (int n = 0; n < num_xmm_regs; n++) { - vextractf128h(Address(rsp, off++*16), as_XMMRegister(n)); + vextractf128h(Address(rsp, n*16), as_XMMRegister(n)); } } #endif // Save whole 128bit (16 bytes) XMM registers subptr(rsp, 16*num_xmm_regs); - off = 0; #ifdef _LP64 - if (VM_Version::supports_avx512novl()) { + if (VM_Version::supports_evex()) { for (int n = 0; n < num_xmm_regs; n++) { - vextractf32x4h(Address(rsp, off++*16), as_XMMRegister(n), 0); + vextractf32x4h(Address(rsp, n*16), as_XMMRegister(n), 0); } } else { for (int n = 0; n < num_xmm_regs; n++) { - movdqu(Address(rsp, off++*16), as_XMMRegister(n)); + movdqu(Address(rsp, n*16), as_XMMRegister(n)); } } #else for (int n = 0; n < num_xmm_regs; n++) { - movdqu(Address(rsp, off++*16), as_XMMRegister(n)); + movdqu(Address(rsp, n*16), as_XMMRegister(n)); } #endif } @@ -4808,44 +5887,40 @@ void MacroAssembler::fp_runtime_fallback(address runtime_entry, int nb_args, int addptr(rsp, sizeof(jdouble)*nb_args); } - off = 0; if (UseSSE == 1) { for (int n = 0; n < 8; n++) { - movflt(as_XMMRegister(n), Address(rsp, off++*sizeof(jdouble))); + movflt(as_XMMRegister(n), Address(rsp, n*sizeof(jdouble))); } addptr(rsp, sizeof(jdouble)*8); } else if (UseSSE >= 2) { - // Restore whole 128bit (16 bytes) XMM regiters + // Restore whole 128bit (16 bytes) XMM registers #ifdef _LP64 - if (VM_Version::supports_avx512novl()) { - for (int n = 0; n < num_xmm_regs; n++) { - vinsertf32x4h(as_XMMRegister(n), Address(rsp, off++*16), 0); - } - } - else { - for (int n = 0; n < num_xmm_regs; n++) { - movdqu(as_XMMRegister(n), Address(rsp, off++*16)); - } - } -#else + if (VM_Version::supports_evex()) { for (int n = 0; n < num_xmm_regs; n++) { - movdqu(as_XMMRegister(n), Address(rsp, off++ * 16)); + vinsertf32x4h(as_XMMRegister(n), Address(rsp, n*16), 0); } + } else { + for (int n = 0; n < num_xmm_regs; n++) { + movdqu(as_XMMRegister(n), Address(rsp, n*16)); + } + } +#else + for (int n = 0; n < num_xmm_regs; n++) { + movdqu(as_XMMRegister(n), Address(rsp, n*16)); + } #endif addptr(rsp, 16*num_xmm_regs); #ifdef COMPILER2 if (MaxVectorSize > 16) { - // Restore upper half of YMM registes. - off = 0; + // Restore upper half of YMM registers. for (int n = 0; n < num_xmm_regs; n++) { - vinsertf128h(as_XMMRegister(n), Address(rsp, off++*16)); + vinsertf128h(as_XMMRegister(n), Address(rsp, n*16)); } addptr(rsp, 16*num_xmm_regs); if(UseAVX > 2) { - off = 0; for (int n = 0; n < num_xmm_regs; n++) { - vinsertf64x4h(as_XMMRegister(n), Address(rsp, off++*32)); + vinsertf64x4h(as_XMMRegister(n), Address(rsp, n*32), 1); } addptr(rsp, 32*num_xmm_regs); } @@ -6312,7 +7387,8 @@ void MacroAssembler::string_indexofC8(Register str1, Register str2, XMMRegister vec, Register tmp, int ae) { ShortBranchVerifier sbv(this); - assert(UseSSE42Intrinsics, "SSE4.2 is required"); + assert(UseSSE42Intrinsics, "SSE4.2 intrinsics are required"); + assert(UseSSE >= 4, "SSE4 must be enabled for SSE4.2 intrinsics to be available"); assert(ae != StrIntrinsicNode::LU, "Invalid encoding"); // This method uses the pcmpestri instruction with bound registers @@ -6490,7 +7566,8 @@ void MacroAssembler::string_indexof(Register str1, Register str2, XMMRegister vec, Register tmp, int ae) { ShortBranchVerifier sbv(this); - assert(UseSSE42Intrinsics, "SSE4.2 is required"); + assert(UseSSE42Intrinsics, "SSE4.2 intrinsics are required"); + assert(UseSSE >= 4, "SSE4 must be enabled for SSE4.2 intrinsics to be available"); assert(ae != StrIntrinsicNode::LU, "Invalid encoding"); // @@ -6807,7 +7884,8 @@ void MacroAssembler::string_indexof(Register str1, Register str2, void MacroAssembler::string_indexof_char(Register str1, Register cnt1, Register ch, Register result, XMMRegister vec1, XMMRegister vec2, XMMRegister vec3, Register tmp) { ShortBranchVerifier sbv(this); - assert(UseSSE42Intrinsics, "SSE4.2 is required"); + assert(UseSSE42Intrinsics, "SSE4.2 intrinsics are required"); + assert(UseSSE >= 4, "SSE4 must be enabled for SSE4.2 intrinsics to be available"); int stride = 8; @@ -6831,7 +7909,7 @@ void MacroAssembler::string_indexof_char(Register str1, Register cnt1, Register bind(SCAN_TO_16_CHAR_LOOP); vmovdqu(vec3, Address(result, 0)); - vpcmpeqw(vec3, vec3, vec1, true); + vpcmpeqw(vec3, vec3, vec1, 1); vptest(vec2, vec3); jcc(Assembler::carryClear, FOUND_CHAR); addptr(result, 32); @@ -6844,36 +7922,32 @@ void MacroAssembler::string_indexof_char(Register str1, Register cnt1, Register pshufd(vec1, vec1, 0); pxor(vec2, vec2); } - if (UseAVX >= 2 || UseSSE42Intrinsics) { - bind(SCAN_TO_8_CHAR); - cmpl(cnt1, stride); - if (UseAVX >= 2) { - jccb(Assembler::less, SCAN_TO_CHAR); - } - if (!(UseAVX >= 2)) { - jccb(Assembler::less, SCAN_TO_CHAR_LOOP); - movdl(vec1, ch); - pshuflw(vec1, vec1, 0x00); - pshufd(vec1, vec1, 0); - pxor(vec2, vec2); - } - movl(tmp, cnt1); - andl(tmp, 0xFFFFFFF8); //vector count (in chars) - andl(cnt1,0x00000007); //tail count (in chars) - - bind(SCAN_TO_8_CHAR_LOOP); - movdqu(vec3, Address(result, 0)); - pcmpeqw(vec3, vec1); - ptest(vec2, vec3); - jcc(Assembler::carryClear, FOUND_CHAR); - addptr(result, 16); - subl(tmp, stride); - jccb(Assembler::notZero, SCAN_TO_8_CHAR_LOOP); + bind(SCAN_TO_8_CHAR); + cmpl(cnt1, stride); + if (UseAVX >= 2) { + jccb(Assembler::less, SCAN_TO_CHAR); + } else { + jccb(Assembler::less, SCAN_TO_CHAR_LOOP); + movdl(vec1, ch); + pshuflw(vec1, vec1, 0x00); + pshufd(vec1, vec1, 0); + pxor(vec2, vec2); } + movl(tmp, cnt1); + andl(tmp, 0xFFFFFFF8); //vector count (in chars) + andl(cnt1,0x00000007); //tail count (in chars) + + bind(SCAN_TO_8_CHAR_LOOP); + movdqu(vec3, Address(result, 0)); + pcmpeqw(vec3, vec1); + ptest(vec2, vec3); + jcc(Assembler::carryClear, FOUND_CHAR); + addptr(result, 16); + subl(tmp, stride); + jccb(Assembler::notZero, SCAN_TO_8_CHAR_LOOP); bind(SCAN_TO_CHAR); testl(cnt1, cnt1); jcc(Assembler::zero, RET_NOT_FOUND); - bind(SCAN_TO_CHAR_LOOP); load_unsigned_short(tmp, Address(result, 0)); cmpl(ch, tmp); @@ -6887,16 +7961,14 @@ void MacroAssembler::string_indexof_char(Register str1, Register cnt1, Register movl(result, -1); jmpb(DONE_LABEL); - if (UseAVX >= 2 || UseSSE42Intrinsics) { - bind(FOUND_CHAR); - if (UseAVX >= 2) { - vpmovmskb(tmp, vec3); - } else { - pmovmskb(tmp, vec3); - } - bsfl(ch, tmp); - addl(result, ch); + bind(FOUND_CHAR); + if (UseAVX >= 2) { + vpmovmskb(tmp, vec3); + } else { + pmovmskb(tmp, vec3); } + bsfl(ch, tmp); + addl(result, ch); bind(FOUND_SEQ_CHAR); subptr(result, str1); @@ -6985,6 +8057,7 @@ void MacroAssembler::string_compare(Register str1, Register str2, } if (UseAVX >= 2 && UseSSE42Intrinsics) { + assert(UseSSE >= 4, "SSE4 must be enabled for SSE4.2 intrinsics to be available"); Label COMPARE_WIDE_VECTORS, VECTOR_NOT_EQUAL, COMPARE_WIDE_TAIL, COMPARE_SMALL_STR; Label COMPARE_WIDE_VECTORS_LOOP, COMPARE_16_CHARS, COMPARE_INDEX_CHAR; Label COMPARE_TAIL_LONG; @@ -7059,7 +8132,7 @@ void MacroAssembler::string_compare(Register str1, Register str2, vmovdqu(vec1, Address(str1, result, scale)); vpxor(vec1, Address(str2, result, scale)); } else { - vpmovzxbw(vec1, Address(str1, result, scale1)); + vpmovzxbw(vec1, Address(str1, result, scale1), Assembler::AVX_256bit); vpxor(vec1, Address(str2, result, scale2)); } vptest(vec1, vec1); @@ -7120,6 +8193,7 @@ void MacroAssembler::string_compare(Register str1, Register str2, bind(COMPARE_SMALL_STR); } else if (UseSSE42Intrinsics) { + assert(UseSSE >= 4, "SSE4 must be enabled for SSE4.2 intrinsics to be available"); Label COMPARE_WIDE_VECTORS, VECTOR_NOT_EQUAL, COMPARE_TAIL; int pcmpmask = 0x19; // Setup to compare 8-char (16-byte) vectors, @@ -7252,7 +8326,7 @@ void MacroAssembler::has_negatives(Register ary1, Register len, movl(result, len); // copy - if (UseAVX >= 2) { + if (UseAVX >= 2 && UseSSE >= 2) { // With AVX2, use 32-byte vector compare Label COMPARE_WIDE_VECTORS, COMPARE_TAIL; @@ -7287,6 +8361,7 @@ void MacroAssembler::has_negatives(Register ary1, Register len, movl(len, result); // Fallthru to tail compare } else if (UseSSE42Intrinsics) { + assert(UseSSE >= 4, "SSE4 must be for SSE4.2 intrinsics to be available"); // With SSE4.2, use double quad vector compare Label COMPARE_WIDE_VECTORS, COMPARE_TAIL; @@ -7363,7 +8438,7 @@ void MacroAssembler::has_negatives(Register ary1, Register len, // That's it bind(DONE); - if (UseAVX >= 2) { + if (UseAVX >= 2 && UseSSE >= 2) { // clean upper bits of YMM registers vpxor(vec1, vec1); vpxor(vec2, vec2); @@ -7451,6 +8526,7 @@ void MacroAssembler::arrays_equals(bool is_array_equ, Register ary1, Register ar movl(limit, result); // Fallthru to tail compare } else if (UseSSE42Intrinsics) { + assert(UseSSE >= 4, "SSE4 must be enabled for SSE4.2 intrinsics to be available"); // With SSE4.2, use double quad vector compare Label COMPARE_WIDE_VECTORS, COMPARE_TAIL; @@ -7672,7 +8748,7 @@ void MacroAssembler::generate_fill(BasicType t, bool aligned, BIND(L_check_fill_32_bytes); addl(count, 8 << shift); jccb(Assembler::less, L_check_fill_8_bytes); - evmovdqul(Address(to, 0), xtmp, Assembler::AVX_256bit); + vmovdqu(Address(to, 0), xtmp); addptr(to, 32); subl(count, 8 << shift); @@ -7800,6 +8876,7 @@ void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, negptr(len); if (UseSSE42Intrinsics || UseAVX >= 2) { + assert(UseSSE42Intrinsics ? UseSSE >= 4 : true, "SSE4 must be enabled for SSE4.2 intrinsics to be available"); Label L_chars_8_check, L_copy_8_chars, L_copy_8_chars_exit; Label L_chars_16_check, L_copy_16_chars, L_copy_16_chars_exit; @@ -9572,6 +10649,7 @@ void MacroAssembler::char_array_compress(Register src, Register dst, Register le push(len); if (UseSSE42Intrinsics) { + assert(UseSSE >= 4, "SSE4 must be enabled for SSE4.2 intrinsics to be available"); Label copy_32_loop, copy_16, copy_tail; movl(result, len); @@ -9671,6 +10749,7 @@ void MacroAssembler::byte_array_inflate(Register src, Register dst, Register len assert_different_registers(src, dst, len, tmp2); if (UseSSE42Intrinsics) { + assert(UseSSE >= 4, "SSE4 must be enabled for SSE4.2 intrinsics to be available"); Label copy_8_loop, copy_bytes, copy_tail; movl(tmp2, len); diff --git a/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp b/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp index 1b0185110d7..b4e440f4383 100644 --- a/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp +++ b/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp @@ -962,10 +962,15 @@ public: void divss(XMMRegister dst, AddressLiteral src); // Move Unaligned Double Quadword - void movdqu(Address dst, XMMRegister src) { Assembler::movdqu(dst, src); } - void movdqu(XMMRegister dst, Address src) { Assembler::movdqu(dst, src); } - void movdqu(XMMRegister dst, XMMRegister src) { Assembler::movdqu(dst, src); } + void movdqu(Address dst, XMMRegister src); + void movdqu(XMMRegister dst, Address src); + void movdqu(XMMRegister dst, XMMRegister src); void movdqu(XMMRegister dst, AddressLiteral src); + // AVX Unaligned forms + void vmovdqu(Address dst, XMMRegister src); + void vmovdqu(XMMRegister dst, Address src); + void vmovdqu(XMMRegister dst, XMMRegister src); + void vmovdqu(XMMRegister dst, AddressLiteral src); // Move Aligned Double Quadword void movdqa(XMMRegister dst, Address src) { Assembler::movdqa(dst, src); } @@ -999,6 +1004,19 @@ public: Assembler::pclmulqdq(dst, src, 0x11); } + void pcmpeqb(XMMRegister dst, XMMRegister src); + void pcmpeqw(XMMRegister dst, XMMRegister src); + + void pcmpestri(XMMRegister dst, Address src, int imm8); + void pcmpestri(XMMRegister dst, XMMRegister src, int imm8); + + void pmovzxbw(XMMRegister dst, XMMRegister src); + void pmovzxbw(XMMRegister dst, Address src); + + void pmovmskb(Register dst, XMMRegister src); + + void ptest(XMMRegister dst, XMMRegister src); + void sqrtsd(XMMRegister dst, XMMRegister src) { Assembler::sqrtsd(dst, src); } void sqrtsd(XMMRegister dst, Address src) { Assembler::sqrtsd(dst, src); } void sqrtsd(XMMRegister dst, AddressLiteral src); @@ -1024,12 +1042,12 @@ public: void ucomisd(XMMRegister dst, AddressLiteral src); // Bitwise Logical XOR of Packed Double-Precision Floating-Point Values - void xorpd(XMMRegister dst, XMMRegister src) { Assembler::xorpd(dst, src); } + void xorpd(XMMRegister dst, XMMRegister src); void xorpd(XMMRegister dst, Address src) { Assembler::xorpd(dst, src); } void xorpd(XMMRegister dst, AddressLiteral src); // Bitwise Logical XOR of Packed Single-Precision Floating-Point Values - void xorps(XMMRegister dst, XMMRegister src) { Assembler::xorps(dst, src); } + void xorps(XMMRegister dst, XMMRegister src); void xorps(XMMRegister dst, Address src) { Assembler::xorps(dst, src); } void xorps(XMMRegister dst, AddressLiteral src); @@ -1047,6 +1065,49 @@ public: void vaddss(XMMRegister dst, XMMRegister nds, Address src) { Assembler::vaddss(dst, nds, src); } void vaddss(XMMRegister dst, XMMRegister nds, AddressLiteral src); + void vabsss(XMMRegister dst, XMMRegister nds, XMMRegister src, AddressLiteral negate_field, int vector_len); + void vabssd(XMMRegister dst, XMMRegister nds, XMMRegister src, AddressLiteral negate_field, int vector_len); + + void vpaddb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vpaddb(XMMRegister dst, XMMRegister nds, Address src, int vector_len); + + void vpaddw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vpaddw(XMMRegister dst, XMMRegister nds, Address src, int vector_len); + + void vpbroadcastw(XMMRegister dst, XMMRegister src); + + void vpcmpeqb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vpcmpeqw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + + void vpmovzxbw(XMMRegister dst, Address src, int vector_len); + void vpmovmskb(Register dst, XMMRegister src); + + void vpmullw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vpmullw(XMMRegister dst, XMMRegister nds, Address src, int vector_len); + + void vpsubb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vpsubb(XMMRegister dst, XMMRegister nds, Address src, int vector_len); + + void vpsubw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); + void vpsubw(XMMRegister dst, XMMRegister nds, Address src, int vector_len); + + void vpsraw(XMMRegister dst, XMMRegister nds, XMMRegister shift, int vector_len); + void vpsraw(XMMRegister dst, XMMRegister nds, int shift, int vector_len); + + void vpsrlw(XMMRegister dst, XMMRegister nds, XMMRegister shift, int vector_len); + void vpsrlw(XMMRegister dst, XMMRegister nds, int shift, int vector_len); + + void vpsllw(XMMRegister dst, XMMRegister nds, XMMRegister shift, int vector_len); + void vpsllw(XMMRegister dst, XMMRegister nds, int shift, int vector_len); + + void vptest(XMMRegister dst, XMMRegister src); + + void punpcklbw(XMMRegister dst, XMMRegister src); + void punpcklbw(XMMRegister dst, Address src) { Assembler::punpcklbw(dst, src); } + + void pshuflw(XMMRegister dst, XMMRegister src, int mode); + void pshuflw(XMMRegister dst, Address src, int mode) { Assembler::pshuflw(dst, src, mode); } + void vandpd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) { Assembler::vandpd(dst, nds, src, vector_len); } void vandpd(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { Assembler::vandpd(dst, nds, src, vector_len); } void vandpd(XMMRegister dst, XMMRegister nds, AddressLiteral src, int vector_len); diff --git a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp index 564ce75421a..96cfbe56ea1 100644 --- a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp +++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp @@ -192,31 +192,22 @@ OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm, int additional_ } } else if(UseSSE >= 2) { // Save whole 128bit (16 bytes) XMM regiters - if (VM_Version::supports_avx512novl()) { - for (int n = 0; n < num_xmm_regs; n++) { - __ vextractf32x4h(Address(rsp, off*wordSize), as_XMMRegister(n), 0); - off += delta; - } - } else { - for (int n = 0; n < num_xmm_regs; n++) { - __ movdqu(Address(rsp, off*wordSize), as_XMMRegister(n)); - off += delta; - } + for (int n = 0; n < num_xmm_regs; n++) { + __ movdqu(Address(rsp, off*wordSize), as_XMMRegister(n)); + off += delta; } } - if (vect_words > 0) { + if (save_vectors) { assert(vect_words*wordSize == 128, ""); __ subptr(rsp, 128); // Save upper half of YMM registes - off = 0; for (int n = 0; n < num_xmm_regs; n++) { - __ vextractf128h(Address(rsp, off++*16), as_XMMRegister(n)); + __ vextractf128h(Address(rsp, n*16), as_XMMRegister(n)); } if (UseAVX > 2) { __ subptr(rsp, 256); // Save upper half of ZMM registes - off = 0; for (int n = 0; n < num_xmm_regs; n++) { - __ vextractf64x4h(Address(rsp, off++*32), as_XMMRegister(n)); + __ vextractf64x4h(Address(rsp, n*32), as_XMMRegister(n), 1); } } } @@ -275,44 +266,39 @@ void RegisterSaver::restore_live_registers(MacroAssembler* masm, bool restore_ve #else assert(!restore_vectors, "vectors are generated only by C2"); #endif + + if (restore_vectors) { + assert(additional_frame_bytes == 128, ""); + if (UseAVX > 2) { + // Restore upper half of ZMM registers. + for (int n = 0; n < num_xmm_regs; n++) { + __ vinsertf64x4h(as_XMMRegister(n), Address(rsp, n*32), 1); + } + __ addptr(rsp, additional_frame_bytes*2); // Save upper half of ZMM registes + } + // Restore upper half of YMM registes. + for (int n = 0; n < num_xmm_regs; n++) { + __ vinsertf128h(as_XMMRegister(n), Address(rsp, n*16)); + } + __ addptr(rsp, additional_frame_bytes); // Save upper half of YMM registes + } + int off = xmm0_off; int delta = xmm1_off - off; if (UseSSE == 1) { - assert(additional_frame_bytes == 0, ""); for (int n = 0; n < num_xmm_regs; n++) { __ movflt(as_XMMRegister(n), Address(rsp, off*wordSize)); off += delta; } } else if (UseSSE >= 2) { - if (VM_Version::supports_avx512novl()) { - for (int n = 0; n < num_xmm_regs; n++) { - __ vinsertf32x4h(as_XMMRegister(n), Address(rsp, off*wordSize+additional_frame_bytes), 0); - off += delta; - } - } else { - for (int n = 0; n < num_xmm_regs; n++) { - __ movdqu(as_XMMRegister(n), Address(rsp, off*wordSize+additional_frame_bytes)); - off += delta; - } - } - } - if (restore_vectors) { - if (UseAVX > 2) { - off = 0; - for (int n = 0; n < num_xmm_regs; n++) { - __ vinsertf64x4h(as_XMMRegister(n), Address(rsp, off++*32)); - } - __ addptr(rsp, additional_frame_bytes*2); // Save upper half of ZMM registes - } - // Restore upper half of YMM registes. - assert(additional_frame_bytes == 128, ""); - off = 0; + // additional_frame_bytes only populated for the restore_vector case, else it is 0 for (int n = 0; n < num_xmm_regs; n++) { - __ vinsertf128h(as_XMMRegister(n), Address(rsp, off++*16)); + __ movdqu(as_XMMRegister(n), Address(rsp, off*wordSize+additional_frame_bytes)); + off += delta; } - __ addptr(rsp, additional_frame_bytes); // Save upper half of YMM registes } + __ pop_FPU_state(); __ addptr(rsp, FPU_regs_live*wordSize); // Pop FPU registers @@ -2562,7 +2548,8 @@ void SharedRuntime::generate_deopt_blob() { oop_maps->add_gc_map( __ pc()-start, map); - // Discard arg to fetch_unroll_info + // Discard args to fetch_unroll_info + __ pop(rcx); __ pop(rcx); __ get_thread(rcx); @@ -2575,9 +2562,8 @@ void SharedRuntime::generate_deopt_blob() { // we are very short of registers Address unpack_kind(rdi, Deoptimization::UnrollBlock::unpack_kind_offset_in_bytes()); - // retrieve the deopt kind from where we left it. - __ pop(rax); - __ movl(unpack_kind, rax); // save the unpack_kind value + // retrieve the deopt kind from the UnrollBlock. + __ movl(rax, unpack_kind); Label noException; __ cmpl(rax, Deoptimization::Unpack_exception); // Was exception pending? @@ -2787,11 +2773,12 @@ void SharedRuntime::generate_uncommon_trap_blob() { enum frame_layout { arg0_off, // thread sp + 0 // Arg location for arg1_off, // unloaded_class_index sp + 1 // calling C + arg2_off, // exec_mode sp + 2 // The frame sender code expects that rbp will be in the "natural" place and // will override any oopMap setting for it. We must therefore force the layout // so that it agrees with the frame sender code. - rbp_off, // callee saved register sp + 2 - return_off, // slot for return address sp + 3 + rbp_off, // callee saved register sp + 3 + return_off, // slot for return address sp + 4 framesize }; @@ -2823,6 +2810,7 @@ void SharedRuntime::generate_uncommon_trap_blob() { __ movptr(Address(rsp, arg0_off*wordSize), rdx); // argument already in ECX __ movl(Address(rsp, arg1_off*wordSize),rcx); + __ movl(Address(rsp, arg2_off*wordSize), Deoptimization::Unpack_uncommon_trap); __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap))); // Set an oopmap for the call site @@ -2839,6 +2827,16 @@ void SharedRuntime::generate_uncommon_trap_blob() { // Load UnrollBlock into EDI __ movptr(rdi, rax); +#ifdef ASSERT + { Label L; + __ cmpptr(Address(rdi, Deoptimization::UnrollBlock::unpack_kind_offset_in_bytes()), + (int32_t)Deoptimization::Unpack_uncommon_trap); + __ jcc(Assembler::equal, L); + __ stop("SharedRuntime::generate_deopt_blob: expected Unpack_uncommon_trap"); + __ bind(L); + } +#endif + // Pop all the frames we must move/replace. // // Frame picture (youngest to oldest) diff --git a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp index 42d7823405c..79b2a0f1db6 100644 --- a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp +++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp @@ -72,45 +72,28 @@ class SimpleRuntimeFrame { class RegisterSaver { // Capture info about frame layout. Layout offsets are in jint // units because compiler frame slots are jints. -#define HALF_ZMM_BANK_WORDS 128 +#define XSAVE_AREA_BEGIN 160 +#define XSAVE_AREA_YMM_BEGIN 576 +#define XSAVE_AREA_ZMM_BEGIN 1152 +#define XSAVE_AREA_UPPERBANK 1664 #define DEF_XMM_OFFS(regnum) xmm ## regnum ## _off = xmm_off + (regnum)*16/BytesPerInt, xmm ## regnum ## H_off +#define DEF_YMM_OFFS(regnum) ymm ## regnum ## _off = ymm_off + (regnum)*16/BytesPerInt, ymm ## regnum ## H_off #define DEF_ZMM_OFFS(regnum) zmm ## regnum ## _off = zmm_off + (regnum-16)*64/BytesPerInt, zmm ## regnum ## H_off enum layout { fpu_state_off = frame::arg_reg_save_area_bytes/BytesPerInt, // fxsave save area - xmm_off = fpu_state_off + 160/BytesPerInt, // offset in fxsave save area + xmm_off = fpu_state_off + XSAVE_AREA_BEGIN/BytesPerInt, // offset in fxsave save area DEF_XMM_OFFS(0), DEF_XMM_OFFS(1), - DEF_XMM_OFFS(2), - DEF_XMM_OFFS(3), - DEF_XMM_OFFS(4), - DEF_XMM_OFFS(5), - DEF_XMM_OFFS(6), - DEF_XMM_OFFS(7), - DEF_XMM_OFFS(8), - DEF_XMM_OFFS(9), - DEF_XMM_OFFS(10), - DEF_XMM_OFFS(11), - DEF_XMM_OFFS(12), - DEF_XMM_OFFS(13), - DEF_XMM_OFFS(14), - DEF_XMM_OFFS(15), - zmm_off = fpu_state_off + ((FPUStateSizeInWords - (HALF_ZMM_BANK_WORDS + 1))*wordSize / BytesPerInt), + // 2..15 are implied in range usage + ymm_off = xmm_off + (XSAVE_AREA_YMM_BEGIN - XSAVE_AREA_BEGIN)/BytesPerInt, + DEF_YMM_OFFS(0), + DEF_YMM_OFFS(1), + // 2..15 are implied in range usage + zmm_high = xmm_off + (XSAVE_AREA_ZMM_BEGIN - XSAVE_AREA_BEGIN)/BytesPerInt, + zmm_off = xmm_off + (XSAVE_AREA_UPPERBANK - XSAVE_AREA_BEGIN)/BytesPerInt, DEF_ZMM_OFFS(16), DEF_ZMM_OFFS(17), - DEF_ZMM_OFFS(18), - DEF_ZMM_OFFS(19), - DEF_ZMM_OFFS(20), - DEF_ZMM_OFFS(21), - DEF_ZMM_OFFS(22), - DEF_ZMM_OFFS(23), - DEF_ZMM_OFFS(24), - DEF_ZMM_OFFS(25), - DEF_ZMM_OFFS(26), - DEF_ZMM_OFFS(27), - DEF_ZMM_OFFS(28), - DEF_ZMM_OFFS(29), - DEF_ZMM_OFFS(30), - DEF_ZMM_OFFS(31), + // 18..31 are implied in range usage fpu_state_end = fpu_state_off + ((FPUStateSizeInWords-1)*wordSize / BytesPerInt), fpu_stateH_end, r15_off, r15H_off, @@ -160,8 +143,6 @@ class RegisterSaver { }; OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm, int additional_frame_words, int* total_frame_words, bool save_vectors) { - int vect_words = 0; - int ymmhi_offset = -1; int off = 0; int num_xmm_regs = XMMRegisterImpl::number_of_registers; if (UseAVX < 3) { @@ -171,24 +152,15 @@ OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm, int additional_ if (save_vectors) { assert(UseAVX > 0, "512bit vectors are supported only with EVEX"); assert(MaxVectorSize == 64, "only 512bit vectors are supported now"); - // Save upper half of YMM registers - vect_words = 16 * num_xmm_regs / wordSize; - if (UseAVX < 3) { - ymmhi_offset = additional_frame_words; - additional_frame_words += vect_words; - } } #else assert(!save_vectors, "vectors are generated only by C2 and JVMCI"); #endif - // Always make the frame size 16-byte aligned - int frame_size_in_bytes = round_to(additional_frame_words*wordSize + - reg_save_size*BytesPerInt, num_xmm_regs); + // Always make the frame size 16-byte aligned, both vector and non vector stacks are always allocated + int frame_size_in_bytes = round_to(reg_save_size*BytesPerInt, num_xmm_regs); // OopMap frame size is in compiler stack slots (jint's) not bytes or words int frame_size_in_slots = frame_size_in_bytes / BytesPerInt; - // The caller will allocate additional_frame_words - int additional_frame_slots = additional_frame_words*wordSize / BytesPerInt; // CodeBlob frame size is in words. int frame_size_in_words = frame_size_in_bytes / wordSize; *total_frame_words = frame_size_in_words; @@ -203,12 +175,34 @@ OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm, int additional_ __ push_CPU_state(); // Push a multiple of 16 bytes // push cpu state handles this on EVEX enabled targets - if ((vect_words > 0) && (UseAVX < 3)) { - assert(vect_words*wordSize >= 256, ""); - // Save upper half of YMM registes(0..num_xmm_regs) - __ subptr(rsp, num_xmm_regs*16); - for (int n = 0; n < num_xmm_regs; n++) { - __ vextractf128h(Address(rsp, off++*16), as_XMMRegister(n)); + if (save_vectors) { + // Save upper half of YMM registes(0..15) + int base_addr = XSAVE_AREA_YMM_BEGIN; + for (int n = 0; n < 16; n++) { + __ vextractf128h(Address(rsp, base_addr+n*16), as_XMMRegister(n)); + } + if (VM_Version::supports_evex()) { + // Save upper half of ZMM registes(0..15) + base_addr = XSAVE_AREA_ZMM_BEGIN; + for (int n = 0; n < 16; n++) { + __ vextractf64x4h(Address(rsp, base_addr+n*32), as_XMMRegister(n), 1); + } + // Save full ZMM registes(16..num_xmm_regs) + base_addr = XSAVE_AREA_UPPERBANK; + int off = 0; + int vector_len = Assembler::AVX_512bit; + for (int n = 16; n < num_xmm_regs; n++) { + __ evmovdqul(Address(rsp, base_addr+(off++*64)), as_XMMRegister(n), vector_len); + } + } + } else { + if (VM_Version::supports_evex()) { + // Save upper bank of ZMM registers(16..31) for double/float usage + int base_addr = XSAVE_AREA_UPPERBANK; + int off = 0; + for (int n = 16; n < num_xmm_regs; n++) { + __ movsd(Address(rsp, base_addr+(off++*64)), as_XMMRegister(n)); + } } } if (frame::arg_reg_save_area_bytes != 0) { @@ -224,8 +218,7 @@ OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm, int additional_ OopMapSet *oop_maps = new OopMapSet(); OopMap* map = new OopMap(frame_size_in_slots, 0); -#define STACK_OFFSET(x) VMRegImpl::stack2reg((x) + additional_frame_slots) -#define YMMHI_STACK_OFFSET(x) VMRegImpl::stack2reg((x / VMRegImpl::stack_slot_size) + ymmhi_offset) +#define STACK_OFFSET(x) VMRegImpl::stack2reg((x)) map->set_callee_saved(STACK_OFFSET( rax_off ), rax->as_VMReg()); map->set_callee_saved(STACK_OFFSET( rcx_off ), rcx->as_VMReg()); @@ -257,31 +250,21 @@ OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm, int additional_ off = zmm16_off; delta = zmm17_off - off; for (int n = 16; n < num_xmm_regs; n++) { - XMMRegister xmm_name = as_XMMRegister(n); - map->set_callee_saved(STACK_OFFSET(off), xmm_name->as_VMReg()); + XMMRegister zmm_name = as_XMMRegister(n); + map->set_callee_saved(STACK_OFFSET(off), zmm_name->as_VMReg()); off += delta; } } #if defined(COMPILER2) || INCLUDE_JVMCI if (save_vectors) { - assert(ymmhi_offset != -1, "save area must exist"); - map->set_callee_saved(YMMHI_STACK_OFFSET( 0), xmm0->as_VMReg()->next(4)); - map->set_callee_saved(YMMHI_STACK_OFFSET( 16), xmm1->as_VMReg()->next(4)); - map->set_callee_saved(YMMHI_STACK_OFFSET( 32), xmm2->as_VMReg()->next(4)); - map->set_callee_saved(YMMHI_STACK_OFFSET( 48), xmm3->as_VMReg()->next(4)); - map->set_callee_saved(YMMHI_STACK_OFFSET( 64), xmm4->as_VMReg()->next(4)); - map->set_callee_saved(YMMHI_STACK_OFFSET( 80), xmm5->as_VMReg()->next(4)); - map->set_callee_saved(YMMHI_STACK_OFFSET( 96), xmm6->as_VMReg()->next(4)); - map->set_callee_saved(YMMHI_STACK_OFFSET(112), xmm7->as_VMReg()->next(4)); - map->set_callee_saved(YMMHI_STACK_OFFSET(128), xmm8->as_VMReg()->next(4)); - map->set_callee_saved(YMMHI_STACK_OFFSET(144), xmm9->as_VMReg()->next(4)); - map->set_callee_saved(YMMHI_STACK_OFFSET(160), xmm10->as_VMReg()->next(4)); - map->set_callee_saved(YMMHI_STACK_OFFSET(176), xmm11->as_VMReg()->next(4)); - map->set_callee_saved(YMMHI_STACK_OFFSET(192), xmm12->as_VMReg()->next(4)); - map->set_callee_saved(YMMHI_STACK_OFFSET(208), xmm13->as_VMReg()->next(4)); - map->set_callee_saved(YMMHI_STACK_OFFSET(224), xmm14->as_VMReg()->next(4)); - map->set_callee_saved(YMMHI_STACK_OFFSET(240), xmm15->as_VMReg()->next(4)); + off = ymm0_off; + int delta = ymm1_off - off; + for (int n = 0; n < 16; n++) { + XMMRegister ymm_name = as_XMMRegister(n); + map->set_callee_saved(STACK_OFFSET(off), ymm_name->as_VMReg()->next(4)); + off += delta; + } } #endif // COMPILER2 || INCLUDE_JVMCI @@ -316,8 +299,8 @@ OopMap* RegisterSaver::save_live_registers(MacroAssembler* masm, int additional_ off = zmm16H_off; delta = zmm17H_off - off; for (int n = 16; n < num_xmm_regs; n++) { - XMMRegister xmm_name = as_XMMRegister(n); - map->set_callee_saved(STACK_OFFSET(off), xmm_name->as_VMReg()->next()); + XMMRegister zmm_name = as_XMMRegister(n); + map->set_callee_saved(STACK_OFFSET(off), zmm_name->as_VMReg()->next()); off += delta; } } @@ -335,21 +318,48 @@ void RegisterSaver::restore_live_registers(MacroAssembler* masm, bool restore_ve // Pop arg register save area __ addptr(rsp, frame::arg_reg_save_area_bytes); } + #if defined(COMPILER2) || INCLUDE_JVMCI - // On EVEX enabled targets everything is handled in pop fpu state - if ((restore_vectors) && (UseAVX < 3)) { - assert(UseAVX > 0, "256/512-bit vectors are supported only with AVX"); - assert(MaxVectorSize == 64, "up to 512bit vectors are supported now"); - int off = 0; - // Restore upper half of YMM registes (0..num_xmm_regs) - for (int n = 0; n < num_xmm_regs; n++) { - __ vinsertf128h(as_XMMRegister(n), Address(rsp, off++*16)); - } - __ addptr(rsp, num_xmm_regs*16); + if (restore_vectors) { + assert(UseAVX > 0, "512bit vectors are supported only with EVEX"); + assert(MaxVectorSize == 64, "only 512bit vectors are supported now"); } #else - assert(!restore_vectors, "vectors are generated only by C2 and JVMCI"); + assert(!save_vectors, "vectors are generated only by C2"); #endif + + // On EVEX enabled targets everything is handled in pop fpu state + if (restore_vectors) { + // Restore upper half of YMM registes (0..15) + int base_addr = XSAVE_AREA_YMM_BEGIN; + for (int n = 0; n < 16; n++) { + __ vinsertf128h(as_XMMRegister(n), Address(rsp, base_addr+n*16)); + } + if (VM_Version::supports_evex()) { + // Restore upper half of ZMM registes (0..15) + base_addr = XSAVE_AREA_ZMM_BEGIN; + for (int n = 0; n < 16; n++) { + __ vinsertf64x4h(as_XMMRegister(n), Address(rsp, base_addr+n*32), 1); + } + // Restore full ZMM registes(16..num_xmm_regs) + base_addr = XSAVE_AREA_UPPERBANK; + int vector_len = Assembler::AVX_512bit; + int off = 0; + for (int n = 16; n < num_xmm_regs; n++) { + __ evmovdqul(as_XMMRegister(n), Address(rsp, base_addr+(off++*64)), vector_len); + } + } + } else { + if (VM_Version::supports_evex()) { + // Restore upper bank of ZMM registes(16..31) for double/float usage + int base_addr = XSAVE_AREA_UPPERBANK; + int off = 0; + for (int n = 16; n < num_xmm_regs; n++) { + __ movsd(as_XMMRegister(n), Address(rsp, base_addr+(off++*64))); + } + } + } + // Recover CPU state __ pop_CPU_state(); // Get the rbp described implicitly by the calling convention (no oopMap) @@ -2819,6 +2829,7 @@ void SharedRuntime::generate_deopt_blob() { __ movl(r14, (int32_t)Deoptimization::Unpack_reexecute); __ mov(c_rarg0, r15_thread); + __ movl(c_rarg2, r14); // exec mode __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap))); oop_maps->add_gc_map( __ pc()-start, map->deep_copy()); @@ -2905,6 +2916,7 @@ void SharedRuntime::generate_deopt_blob() { } #endif // ASSERT __ mov(c_rarg0, r15_thread); + __ movl(c_rarg1, r14); // exec_mode __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::fetch_unroll_info))); // Need to have an oopmap that tells fetch_unroll_info where to @@ -2922,6 +2934,7 @@ void SharedRuntime::generate_deopt_blob() { // Load UnrollBlock* into rdi __ mov(rdi, rax); + __ movl(r14, Address(rdi, Deoptimization::UnrollBlock::unpack_kind_offset_in_bytes())); Label noException; __ cmpl(r14, Deoptimization::Unpack_exception); // Was exception pending? __ jcc(Assembler::notEqual, noException); @@ -3140,6 +3153,7 @@ void SharedRuntime::generate_uncommon_trap_blob() { // UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index); __ mov(c_rarg0, r15_thread); + __ movl(c_rarg2, Deoptimization::Unpack_uncommon_trap); __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap))); // Set an oopmap for the call site @@ -3155,6 +3169,16 @@ void SharedRuntime::generate_uncommon_trap_blob() { // Load UnrollBlock* into rdi __ mov(rdi, rax); +#ifdef ASSERT + { Label L; + __ cmpptr(Address(rdi, Deoptimization::UnrollBlock::unpack_kind_offset_in_bytes()), + (int32_t)Deoptimization::Unpack_uncommon_trap); + __ jcc(Assembler::equal, L); + __ stop("SharedRuntime::generate_deopt_blob: expected Unpack_uncommon_trap"); + __ bind(L); + } +#endif + // Pop all the frames we must move/replace. // // Frame picture (youngest to oldest) diff --git a/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp b/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp index 49b78565677..b68bedf1ad6 100644 --- a/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp +++ b/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp @@ -273,7 +273,7 @@ class StubGenerator: public StubCodeGenerator { if (UseAVX > 2) { last_reg = 31; } - if (VM_Version::supports_avx512novl()) { + if (VM_Version::supports_evex()) { for (int i = xmm_save_first; i <= last_reg; i++) { __ vextractf32x4h(xmm_save(i), as_XMMRegister(i), 0); } @@ -391,7 +391,7 @@ class StubGenerator: public StubCodeGenerator { // restore regs belonging to calling function #ifdef _WIN64 // emit the restores for xmm regs - if (VM_Version::supports_avx512novl()) { + if (VM_Version::supports_evex()) { for (int i = xmm_save_first; i <= last_reg; i++) { __ vinsertf32x4h(as_XMMRegister(i), xmm_save(i), 0); } @@ -1439,8 +1439,8 @@ class StubGenerator: public StubCodeGenerator { // Copy 64-bytes per iteration __ BIND(L_loop); if (UseAVX > 2) { - __ evmovdqul(xmm0, Address(from, qword_count, Address::times_8, 32), Assembler::AVX_512bit); - __ evmovdqul(Address(dest, qword_count, Address::times_8, 32), xmm0, Assembler::AVX_512bit); + __ evmovdqul(xmm0, Address(from, qword_count, Address::times_8, 0), Assembler::AVX_512bit); + __ evmovdqul(Address(dest, qword_count, Address::times_8, 0), xmm0, Assembler::AVX_512bit); } else if (UseAVX == 2) { __ vmovdqu(xmm0, Address(from, qword_count, Address::times_8, 32)); __ vmovdqu(Address(dest, qword_count, Address::times_8, 32), xmm0); diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp index 4e2e1942cef..eed88e81342 100644 --- a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp +++ b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp @@ -632,12 +632,36 @@ void VM_Version::get_processor_features() { // Use AES instructions if available. if (supports_aes()) { if (FLAG_IS_DEFAULT(UseAES)) { - UseAES = true; + FLAG_SET_DEFAULT(UseAES, true); } - } else if (UseAES) { - if (!FLAG_IS_DEFAULT(UseAES)) + if (!UseAES) { + if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { + warning("AES intrinsics require UseAES flag to be enabled. Intrinsics will be disabled."); + } + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } else { + if (UseSSE > 2) { + if (FLAG_IS_DEFAULT(UseAESIntrinsics)) { + FLAG_SET_DEFAULT(UseAESIntrinsics, true); + } + } else { + // The AES intrinsic stubs require AES instruction support (of course) + // but also require sse3 mode or higher for instructions it use. + if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { + warning("X86 AES intrinsics require SSE3 instructions or higher. Intrinsics will be disabled."); + } + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } + } + } else if (UseAES || UseAESIntrinsics) { + if (UseAES && !FLAG_IS_DEFAULT(UseAES)) { warning("AES instructions are not available on this CPU"); - FLAG_SET_DEFAULT(UseAES, false); + FLAG_SET_DEFAULT(UseAES, false); + } + if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { + warning("AES intrinsics are not available on this CPU"); + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } } // Use CLMUL instructions if available. @@ -673,18 +697,6 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseCRC32CIntrinsics, false); } - // The AES intrinsic stubs require AES instruction support (of course) - // but also require sse3 mode for instructions it use. - if (UseAES && (UseSSE > 2)) { - if (FLAG_IS_DEFAULT(UseAESIntrinsics)) { - UseAESIntrinsics = true; - } - } else if (UseAESIntrinsics) { - if (!FLAG_IS_DEFAULT(UseAESIntrinsics)) - warning("AES intrinsics are not available on this CPU"); - FLAG_SET_DEFAULT(UseAESIntrinsics, false); - } - // GHASH/GCM intrinsics if (UseCLMUL && (UseSSE > 2)) { if (FLAG_IS_DEFAULT(UseGHASHIntrinsics)) { @@ -891,7 +903,7 @@ void VM_Version::get_processor_features() { UseNewLongLShift = true; } if( FLAG_IS_DEFAULT(UseXmmLoadAndClearUpper) ) { - if( supports_sse4a() ) { + if (supports_sse4a()) { UseXmmLoadAndClearUpper = true; // use movsd only on '10h' Opteron } else { UseXmmLoadAndClearUpper = false; @@ -918,10 +930,15 @@ void VM_Version::get_processor_features() { UseXmmI2D = false; } } - if( FLAG_IS_DEFAULT(UseSSE42Intrinsics) ) { - if( supports_sse4_2() && UseSSE >= 4 ) { - UseSSE42Intrinsics = true; + if (supports_sse4_2() && UseSSE >= 4) { + if (FLAG_IS_DEFAULT(UseSSE42Intrinsics)) { + FLAG_SET_DEFAULT(UseSSE42Intrinsics, true); } + } else { + if (UseSSE42Intrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { + warning("SSE4.2 intrinsics require SSE4.2 instructions or higher. Intrinsics will be disabled."); + } + FLAG_SET_DEFAULT(UseSSE42Intrinsics, false); } // some defaults for AMD family 15h @@ -995,8 +1012,13 @@ void VM_Version::get_processor_features() { } if (supports_sse4_2() && UseSSE >= 4) { if (FLAG_IS_DEFAULT(UseSSE42Intrinsics)) { - UseSSE42Intrinsics = true; + FLAG_SET_DEFAULT(UseSSE42Intrinsics, true); } + } else { + if (UseSSE42Intrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { + warning("SSE4.2 intrinsics require SSE4.2 instructions or higher. Intrinsics will be disabled."); + } + FLAG_SET_DEFAULT(UseSSE42Intrinsics, false); } } if ((cpu_family() == 0x06) && diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86.hpp b/hotspot/src/cpu/x86/vm/vm_version_x86.hpp index 94d8a595b52..784e8475da9 100644 --- a/hotspot/src/cpu/x86/vm/vm_version_x86.hpp +++ b/hotspot/src/cpu/x86/vm/vm_version_x86.hpp @@ -552,6 +552,19 @@ protected: break; } } + // zmm_save will be set on a EVEX enabled machine even if we choose AVX code gen + if (retVal == false) { + // Verify that OS save/restore all bits of EVEX registers + // during signal processing. + int nreg = 2 LP64_ONLY(+2); + retVal = true; + for (int i = 0; i < 16 * nreg; i++) { // 64 bytes per zmm register + if (_cpuid_info.zmm_save[i] != ymm_test_value()) { + retVal = false; + break; + } + } + } } return retVal; } @@ -706,6 +719,9 @@ public: static bool supports_avx512vl() { return (_cpuFeatures & CPU_AVX512VL) != 0; } static bool supports_avx512vlbw() { return (supports_avx512bw() && supports_avx512vl()); } static bool supports_avx512novl() { return (supports_evex() && !supports_avx512vl()); } + static bool supports_avx512nobw() { return (supports_evex() && !supports_avx512bw()); } + static bool supports_avx256only() { return (supports_avx2() && !supports_evex()); } + static bool supports_avxonly() { return ((supports_avx2() || supports_avx()) && !supports_evex()); } // Intel features static bool is_intel_family_core() { return is_intel() && extended_cpu_family() == CPU_FAMILY_INTEL_CORE; } diff --git a/hotspot/src/cpu/x86/vm/x86.ad b/hotspot/src/cpu/x86/vm/x86.ad index b637b9bddac..28d25cca76b 100644 --- a/hotspot/src/cpu/x86/vm/x86.ad +++ b/hotspot/src/cpu/x86/vm/x86.ad @@ -1716,6 +1716,36 @@ const bool Matcher::match_rule_supported(int opcode) { return ret_value; // Per default match rules are supported. } +const bool Matcher::match_rule_supported_vector(int opcode, int vlen) { + // identify extra cases that we might want to provide match rules for + // e.g. Op_ vector nodes and other intrinsics while guarding with vlen + bool ret_value = match_rule_supported(opcode); + if (ret_value) { + switch (opcode) { + case Op_AddVB: + case Op_SubVB: + if ((vlen == 64) && (VM_Version::supports_avx512bw() == false)) + ret_value = false; + break; + case Op_URShiftVS: + case Op_RShiftVS: + case Op_LShiftVS: + case Op_MulVS: + case Op_AddVS: + case Op_SubVS: + if ((vlen == 32) && (VM_Version::supports_avx512bw() == false)) + ret_value = false; + break; + case Op_CMoveVD: + if (vlen != 4) + ret_value = false; + break; + } + } + + return ret_value; // Per default match rules are supported. +} + const int Matcher::float_pressure(int default_pressure_threshold) { int float_pressure_threshold = default_pressure_threshold; #ifdef _LP64 @@ -1759,11 +1789,9 @@ const int Matcher::vector_width_in_bytes(BasicType bt) { break; case T_BYTE: if (size < 4) return 0; - if ((size > 32) && !VM_Version::supports_avx512bw()) return 0; break; case T_SHORT: if (size < 4) return 0; - if ((size > 16) && !VM_Version::supports_avx512bw()) return 0; break; default: ShouldNotReachHere(); @@ -1967,27 +1995,34 @@ static int vec_spill_helper(CodeBuffer *cbuf, bool do_size, bool is_load, bool is_single_byte = false; int vec_len = 0; if ((UseAVX > 2) && (stack_offset != 0)) { + int tuple_type = Assembler::EVEX_FVM; + int input_size = Assembler::EVEX_32bit; switch (ireg) { - case Op_VecS: + case Op_VecS: + tuple_type = Assembler::EVEX_T1S; + break; case Op_VecD: + tuple_type = Assembler::EVEX_T1S; + input_size = Assembler::EVEX_64bit; + break; case Op_VecX: - break; - case Op_VecY: - vec_len = 1; - break; + break; + case Op_VecY: + vec_len = 1; + break; case Op_VecZ: - vec_len = 2; - break; + vec_len = 2; + break; } - is_single_byte = Assembler::query_compressed_disp_byte(stack_offset, true, vec_len, Assembler::EVEX_FVM, Assembler::EVEX_32bit, 0); + is_single_byte = Assembler::query_compressed_disp_byte(stack_offset, true, vec_len, tuple_type, input_size, 0); } int offset_size = 0; int size = 5; if (UseAVX > 2 ) { - if ((VM_Version::supports_avx512vl() == false) && (vec_len == 2)) { + if (VM_Version::supports_avx512novl() && (vec_len == 2)) { offset_size = (stack_offset == 0) ? 0 : ((is_single_byte) ? 1 : 4); size += 2; // Need an additional two bytes for EVEX encoding - } else if ((VM_Version::supports_avx512vl() == false) && (vec_len < 2)) { + } else if (VM_Version::supports_avx512novl() && (vec_len < 2)) { offset_size = (stack_offset == 0) ? 0 : ((stack_offset <= 127) ? 1 : 4); } else { offset_size = (stack_offset == 0) ? 0 : ((is_single_byte) ? 1 : 4); @@ -2711,7 +2746,7 @@ instruct absF_reg(regF dst) %{ %} instruct absF_reg_reg(regF dst, regF src) %{ - predicate(UseAVX > 0); + predicate(VM_Version::supports_avxonly()); match(Set dst (AbsF src)); ins_cost(150); format %{ "vandps $dst, $src, [0x7fffffff]\t# abs float by sign masking" %} @@ -2723,6 +2758,48 @@ instruct absF_reg_reg(regF dst, regF src) %{ ins_pipe(pipe_slow); %} +#ifdef _LP64 +instruct absF_reg_reg_evex(regF dst, regF src) %{ + predicate(UseAVX > 2 && VM_Version::supports_avx512vl()); + match(Set dst (AbsF src)); + ins_cost(150); + format %{ "vandps $dst, $src, [0x7fffffff]\t# abs float by sign masking" %} + ins_encode %{ + int vector_len = 0; + __ vandps($dst$$XMMRegister, $src$$XMMRegister, + ExternalAddress(float_signmask()), vector_len); + %} + ins_pipe(pipe_slow); +%} + +instruct absF_reg_reg_evex_special(regF dst, regF src1, regF src2) %{ + predicate(VM_Version::supports_avx512novl()); + match(Set dst (AbsF src1)); + effect(TEMP src2); + ins_cost(150); + format %{ "vabsss $dst, $src1, $src2, [0x7fffffff]\t# abs float by sign masking" %} + ins_encode %{ + int vector_len = 0; + __ vabsss($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, + ExternalAddress(float_signmask()), vector_len); + %} + ins_pipe(pipe_slow); +%} +#else // _LP64 +instruct absF_reg_reg_evex(regF dst, regF src) %{ + predicate(UseAVX > 2); + match(Set dst (AbsF src)); + ins_cost(150); + format %{ "vandps $dst, $src, [0x7fffffff]\t# abs float by sign masking" %} + ins_encode %{ + int vector_len = 0; + __ vandps($dst$$XMMRegister, $src$$XMMRegister, + ExternalAddress(float_signmask()), vector_len); + %} + ins_pipe(pipe_slow); +%} +#endif + instruct absD_reg(regD dst) %{ predicate((UseSSE>=2) && (UseAVX == 0)); match(Set dst (AbsD dst)); @@ -2736,7 +2813,7 @@ instruct absD_reg(regD dst) %{ %} instruct absD_reg_reg(regD dst, regD src) %{ - predicate(UseAVX > 0); + predicate(VM_Version::supports_avxonly()); match(Set dst (AbsD src)); ins_cost(150); format %{ "vandpd $dst, $src, [0x7fffffffffffffff]\t" @@ -2749,6 +2826,50 @@ instruct absD_reg_reg(regD dst, regD src) %{ ins_pipe(pipe_slow); %} +#ifdef _LP64 +instruct absD_reg_reg_evex(regD dst, regD src) %{ + predicate(UseAVX > 2 && VM_Version::supports_avx512vl()); + match(Set dst (AbsD src)); + ins_cost(150); + format %{ "vandpd $dst, $src, [0x7fffffffffffffff]\t" + "# abs double by sign masking" %} + ins_encode %{ + int vector_len = 0; + __ vandpd($dst$$XMMRegister, $src$$XMMRegister, + ExternalAddress(double_signmask()), vector_len); + %} + ins_pipe(pipe_slow); +%} + +instruct absD_reg_reg_evex_special(regD dst, regD src1, regD src2) %{ + predicate(VM_Version::supports_avx512novl()); + match(Set dst (AbsD src1)); + effect(TEMP src2); + ins_cost(150); + format %{ "vabssd $dst, $src1, $src2, [0x7fffffffffffffff]\t# abs float by sign masking" %} + ins_encode %{ + int vector_len = 0; + __ vabssd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, + ExternalAddress(double_signmask()), vector_len); + %} + ins_pipe(pipe_slow); +%} +#else // _LP64 +instruct absD_reg_reg_evex(regD dst, regD src) %{ + predicate(UseAVX > 2); + match(Set dst (AbsD src)); + ins_cost(150); + format %{ "vandpd $dst, $src, [0x7fffffffffffffff]\t" + "# abs double by sign masking" %} + ins_encode %{ + int vector_len = 0; + __ vandpd($dst$$XMMRegister, $src$$XMMRegister, + ExternalAddress(double_signmask()), vector_len); + %} + ins_pipe(pipe_slow); +%} +#endif + instruct negF_reg(regF dst) %{ predicate((UseSSE>=1) && (UseAVX == 0)); match(Set dst (NegF dst)); @@ -4554,7 +4675,7 @@ instruct rsadd2I_reduction_reg(rRegI dst, rRegI src1, vecD src2, regF tmp, regF %} instruct rvadd2I_reduction_reg(rRegI dst, rRegI src1, vecD src2, regF tmp, regF tmp2) %{ - predicate(UseAVX > 0 && UseAVX < 3); + predicate(VM_Version::supports_avxonly()); match(Set dst (AddReductionVI src1 src2)); effect(TEMP tmp, TEMP tmp2); format %{ "vphaddd $tmp,$src2,$src2\n\t" @@ -4594,37 +4715,37 @@ instruct rvadd2I_reduction_reg_evex(rRegI dst, rRegI src1, vecD src2, regF tmp, instruct rsadd4I_reduction_reg(rRegI dst, rRegI src1, vecX src2, regF tmp, regF tmp2) %{ predicate(UseSSE > 2 && UseAVX == 0); match(Set dst (AddReductionVI src1 src2)); - effect(TEMP tmp2, TEMP tmp); - format %{ "movdqu $tmp2,$src2\n\t" - "phaddd $tmp2,$tmp2\n\t" - "phaddd $tmp2,$tmp2\n\t" - "movd $tmp,$src1\n\t" - "paddd $tmp,$tmp2\n\t" - "movd $dst,$tmp\t! add reduction4I" %} + effect(TEMP tmp, TEMP tmp2); + format %{ "movdqu $tmp,$src2\n\t" + "phaddd $tmp,$tmp\n\t" + "phaddd $tmp,$tmp\n\t" + "movd $tmp2,$src1\n\t" + "paddd $tmp2,$tmp\n\t" + "movd $dst,$tmp2\t! add reduction4I" %} ins_encode %{ - __ movdqu($tmp2$$XMMRegister, $src2$$XMMRegister); - __ phaddd($tmp2$$XMMRegister, $tmp2$$XMMRegister); - __ phaddd($tmp2$$XMMRegister, $tmp2$$XMMRegister); - __ movdl($tmp$$XMMRegister, $src1$$Register); - __ paddd($tmp$$XMMRegister, $tmp2$$XMMRegister); - __ movdl($dst$$Register, $tmp$$XMMRegister); + __ movdqu($tmp$$XMMRegister, $src2$$XMMRegister); + __ phaddd($tmp$$XMMRegister, $tmp$$XMMRegister); + __ phaddd($tmp$$XMMRegister, $tmp$$XMMRegister); + __ movdl($tmp2$$XMMRegister, $src1$$Register); + __ paddd($tmp2$$XMMRegister, $tmp$$XMMRegister); + __ movdl($dst$$Register, $tmp2$$XMMRegister); %} ins_pipe( pipe_slow ); %} instruct rvadd4I_reduction_reg(rRegI dst, rRegI src1, vecX src2, regF tmp, regF tmp2) %{ - predicate(UseAVX > 0 && UseAVX < 3); + predicate(VM_Version::supports_avxonly()); match(Set dst (AddReductionVI src1 src2)); effect(TEMP tmp, TEMP tmp2); format %{ "vphaddd $tmp,$src2,$src2\n\t" - "vphaddd $tmp,$tmp,$tmp2\n\t" + "vphaddd $tmp,$tmp,$tmp\n\t" "movd $tmp2,$src1\n\t" "vpaddd $tmp2,$tmp2,$tmp\n\t" "movd $dst,$tmp2\t! add reduction4I" %} ins_encode %{ int vector_len = 0; __ vphaddd($tmp$$XMMRegister, $src2$$XMMRegister, $src2$$XMMRegister, vector_len); - __ vphaddd($tmp$$XMMRegister, $tmp$$XMMRegister, $tmp2$$XMMRegister, vector_len); + __ vphaddd($tmp$$XMMRegister, $tmp$$XMMRegister, $tmp$$XMMRegister, vector_len); __ movdl($tmp2$$XMMRegister, $src1$$Register); __ vpaddd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister, vector_len); __ movdl($dst$$Register, $tmp2$$XMMRegister); @@ -4657,7 +4778,7 @@ instruct rvadd4I_reduction_reg_evex(rRegI dst, rRegI src1, vecX src2, regF tmp, %} instruct rvadd8I_reduction_reg(rRegI dst, rRegI src1, vecY src2, regF tmp, regF tmp2) %{ - predicate(UseAVX > 0 && UseAVX < 3); + predicate(VM_Version::supports_avxonly()); match(Set dst (AddReductionVI src1 src2)); effect(TEMP tmp, TEMP tmp2); format %{ "vphaddd $tmp,$src2,$src2\n\t" @@ -4712,7 +4833,7 @@ instruct rvadd16I_reduction_reg_evex(rRegI dst, rRegI src1, vecZ src2, regF tmp, predicate(UseAVX > 2); match(Set dst (AddReductionVI src1 src2)); effect(TEMP tmp, TEMP tmp2, TEMP tmp3); - format %{ "vextracti64x4 $tmp3,$src2\n\t" + format %{ "vextracti64x4 $tmp3,$src2,0x1\n\t" "vpaddd $tmp3,$tmp3,$src2\n\t" "vextracti128 $tmp,$tmp3\n\t" "vpaddd $tmp,$tmp,$tmp3\n\t" @@ -4724,7 +4845,7 @@ instruct rvadd16I_reduction_reg_evex(rRegI dst, rRegI src1, vecZ src2, regF tmp, "vpaddd $tmp2,$tmp,$tmp2\n\t" "movd $dst,$tmp2\t! mul reduction16I" %} ins_encode %{ - __ vextracti64x4h($tmp3$$XMMRegister, $src2$$XMMRegister); + __ vextracti64x4h($tmp3$$XMMRegister, $src2$$XMMRegister, 1); __ vpaddd($tmp3$$XMMRegister, $tmp3$$XMMRegister, $src2$$XMMRegister, 1); __ vextracti128h($tmp$$XMMRegister, $tmp3$$XMMRegister); __ vpaddd($tmp$$XMMRegister, $tmp$$XMMRegister, $tmp3$$XMMRegister, 0); @@ -4763,7 +4884,7 @@ instruct rvadd4L_reduction_reg(rRegL dst, rRegL src1, vecY src2, regF tmp, regF predicate(UseAVX > 2); match(Set dst (AddReductionVL src1 src2)); effect(TEMP tmp, TEMP tmp2); - format %{ "vextracti64x2 $tmp,$src2, 0x1\n\t" + format %{ "vextracti128 $tmp,$src2\n\t" "vpaddq $tmp2,$tmp,$src2\n\t" "pshufd $tmp,$tmp2,0xE\n\t" "vpaddq $tmp2,$tmp2,$tmp\n\t" @@ -4771,7 +4892,7 @@ instruct rvadd4L_reduction_reg(rRegL dst, rRegL src1, vecY src2, regF tmp, regF "vpaddq $tmp2,$tmp2,$tmp\n\t" "movdq $dst,$tmp2\t! add reduction4L" %} ins_encode %{ - __ vextracti64x2h($tmp$$XMMRegister, $src2$$XMMRegister, 0x1); + __ vextracti128h($tmp$$XMMRegister, $src2$$XMMRegister); __ vpaddq($tmp2$$XMMRegister, $tmp$$XMMRegister, $src2$$XMMRegister, 0); __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0xE); __ vpaddq($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister, 0); @@ -4786,7 +4907,7 @@ instruct rvadd8L_reduction_reg(rRegL dst, rRegL src1, vecZ src2, regF tmp, regF predicate(UseAVX > 2); match(Set dst (AddReductionVL src1 src2)); effect(TEMP tmp, TEMP tmp2); - format %{ "vextracti64x4 $tmp2,$src2\n\t" + format %{ "vextracti64x4 $tmp2,$src2,0x1\n\t" "vpaddq $tmp2,$tmp2,$src2\n\t" "vextracti128 $tmp,$tmp2\n\t" "vpaddq $tmp2,$tmp2,$tmp\n\t" @@ -4796,7 +4917,7 @@ instruct rvadd8L_reduction_reg(rRegL dst, rRegL src1, vecZ src2, regF tmp, regF "vpaddq $tmp2,$tmp2,$tmp\n\t" "movdq $dst,$tmp2\t! add reduction8L" %} ins_encode %{ - __ vextracti64x4h($tmp2$$XMMRegister, $src2$$XMMRegister); + __ vextracti64x4h($tmp2$$XMMRegister, $src2$$XMMRegister, 1); __ vpaddq($tmp2$$XMMRegister, $tmp2$$XMMRegister, $src2$$XMMRegister, 1); __ vextracti128h($tmp$$XMMRegister, $tmp2$$XMMRegister); __ vpaddq($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister, 0); @@ -4810,290 +4931,280 @@ instruct rvadd8L_reduction_reg(rRegL dst, rRegL src1, vecZ src2, regF tmp, regF %} #endif -instruct rsadd2F_reduction_reg(regF dst, regF src1, vecD src2, regF tmp, regF tmp2) %{ +instruct rsadd2F_reduction_reg(regF dst, vecD src2, regF tmp) %{ predicate(UseSSE >= 1 && UseAVX == 0); - match(Set dst (AddReductionVF src1 src2)); - effect(TEMP tmp, TEMP tmp2); - format %{ "movdqu $tmp,$src1\n\t" - "addss $tmp,$src2\n\t" - "pshufd $tmp2,$src2,0x01\n\t" - "addss $tmp,$tmp2\n\t" - "movdqu $dst,$tmp\t! add reduction2F" %} - ins_encode %{ - __ movdqu($tmp$$XMMRegister, $src1$$XMMRegister); - __ addss($tmp$$XMMRegister, $src2$$XMMRegister); - __ pshufd($tmp2$$XMMRegister, $src2$$XMMRegister, 0x01); - __ addss($tmp$$XMMRegister, $tmp2$$XMMRegister); - __ movdqu($dst$$XMMRegister, $tmp$$XMMRegister); - %} - ins_pipe( pipe_slow ); -%} - -instruct rvadd2F_reduction_reg(regF dst, regF src1, vecD src2, regF tmp, regF tmp2) %{ - predicate(UseAVX > 0); - match(Set dst (AddReductionVF src1 src2)); - effect(TEMP tmp2, TEMP tmp); - format %{ "vaddss $tmp2,$src1,$src2\n\t" + match(Set dst (AddReductionVF dst src2)); + effect(TEMP dst, TEMP tmp); + format %{ "addss $dst,$src2\n\t" "pshufd $tmp,$src2,0x01\n\t" - "vaddss $dst,$tmp2,$tmp\t! add reduction2F" %} + "addss $dst,$tmp\t! add reduction2F" %} ins_encode %{ - __ vaddss($tmp2$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister); + __ addss($dst$$XMMRegister, $src2$$XMMRegister); __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); - __ vaddss($dst$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); + __ addss($dst$$XMMRegister, $tmp$$XMMRegister); %} ins_pipe( pipe_slow ); %} -instruct rsadd4F_reduction_reg(regF dst, regF src1, vecX src2, regF tmp, regF tmp2) %{ +instruct rvadd2F_reduction_reg(regF dst, vecD src2, regF tmp) %{ + predicate(UseAVX > 0); + match(Set dst (AddReductionVF dst src2)); + effect(TEMP dst, TEMP tmp); + format %{ "vaddss $dst,$dst,$src2\n\t" + "pshufd $tmp,$src2,0x01\n\t" + "vaddss $dst,$dst,$tmp\t! add reduction2F" %} + ins_encode %{ + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $src2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct rsadd4F_reduction_reg(regF dst, vecX src2, regF tmp) %{ predicate(UseSSE >= 1 && UseAVX == 0); - match(Set dst (AddReductionVF src1 src2)); - effect(TEMP tmp, TEMP tmp2); - format %{ "movdqu $tmp,$src1\n\t" - "addss $tmp,$src2\n\t" - "pshufd $tmp2,$src2,0x01\n\t" - "addss $tmp,$tmp2\n\t" - "pshufd $tmp2,$src2,0x02\n\t" - "addss $tmp,$tmp2\n\t" - "pshufd $tmp2,$src2,0x03\n\t" - "addss $tmp,$tmp2\n\t" - "movdqu $dst,$tmp\t! add reduction4F" %} + match(Set dst (AddReductionVF dst src2)); + effect(TEMP dst, TEMP tmp); + format %{ "addss $dst,$src2\n\t" + "pshufd $tmp,$src2,0x01\n\t" + "addss $dst,$tmp\n\t" + "pshufd $tmp,$src2,0x02\n\t" + "addss $dst,$tmp\n\t" + "pshufd $tmp,$src2,0x03\n\t" + "addss $dst,$tmp\t! add reduction4F" %} ins_encode %{ - __ movdqu($tmp$$XMMRegister, $src1$$XMMRegister); - __ addss($tmp$$XMMRegister, $src2$$XMMRegister); - __ pshufd($tmp2$$XMMRegister, $src2$$XMMRegister, 0x01); - __ addss($tmp$$XMMRegister, $tmp2$$XMMRegister); - __ pshufd($tmp2$$XMMRegister, $src2$$XMMRegister, 0x02); - __ addss($tmp$$XMMRegister, $tmp2$$XMMRegister); - __ pshufd($tmp2$$XMMRegister, $src2$$XMMRegister, 0x03); - __ addss($tmp$$XMMRegister, $tmp2$$XMMRegister); - __ movdqu($dst$$XMMRegister, $tmp$$XMMRegister); + __ addss($dst$$XMMRegister, $src2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); + __ addss($dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x02); + __ addss($dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x03); + __ addss($dst$$XMMRegister, $tmp$$XMMRegister); %} ins_pipe( pipe_slow ); %} -instruct rvadd4F_reduction_reg(regF dst, regF src1, vecX src2, regF tmp, regF tmp2) %{ +instruct rvadd4F_reduction_reg(regF dst, vecX src2, regF tmp) %{ predicate(UseAVX > 0); - match(Set dst (AddReductionVF src1 src2)); - effect(TEMP tmp, TEMP tmp2); - format %{ "vaddss $tmp2,$src1,$src2\n\t" - "pshufd $tmp,$src2,0x01\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$src2,0x02\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$src2,0x03\n\t" - "vaddss $dst,$tmp2,$tmp\t! add reduction4F" %} - ins_encode %{ - __ vaddss($tmp2$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x02); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x03); - __ vaddss($dst$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - %} - ins_pipe( pipe_slow ); -%} - -instruct radd8F_reduction_reg(regF dst, regF src1, vecY src2, regF tmp, regF tmp2, regF tmp3) %{ - predicate(UseAVX > 0); - match(Set dst (AddReductionVF src1 src2)); - effect(TEMP tmp, TEMP tmp2, TEMP tmp3); - format %{ "vaddss $tmp2,$src1,$src2\n\t" - "pshufd $tmp,$src2,0x01\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$src2,0x02\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$src2,0x03\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "vextractf128 $tmp3,$src2\n\t" - "vaddss $tmp2,$tmp2,$tmp3\n\t" - "pshufd $tmp,$tmp3,0x01\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x02\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x03\n\t" - "vaddss $dst,$tmp2,$tmp\t! add reduction8F" %} - ins_encode %{ - __ vaddss($tmp2$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x02); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x03); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf128h($tmp3$$XMMRegister, $src2$$XMMRegister); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x01); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x02); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x03); - __ vaddss($dst$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - %} - ins_pipe( pipe_slow ); -%} - -instruct radd16F_reduction_reg(regF dst, regF src1, vecZ src2, regF tmp, regF tmp2, regF tmp3) %{ - predicate(UseAVX > 2); - match(Set dst (AddReductionVF src1 src2)); - effect(TEMP tmp, TEMP tmp2, TEMP tmp3); - format %{ "vaddss $tmp2,$src1,$src2\n\t" - "pshufd $tmp,$src2,0x01\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$src2,0x02\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$src2,0x03\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "vextractf64x2 $tmp3,$src2, 0x1\n\t" - "vaddss $tmp2,$tmp2,$tmp3\n\t" - "pshufd $tmp,$tmp3,0x01\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x02\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x03\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "vextractf64x2 $tmp3,$src2, 0x2\n\t" - "vaddss $tmp2,$tmp2,$tmp3\n\t" - "pshufd $tmp,$tmp3,0x01\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x02\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x03\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "vextractf64x2 $tmp3,$src2, 0x3\n\t" - "vaddss $tmp2,$tmp2,$tmp3\n\t" - "pshufd $tmp,$tmp3,0x01\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x02\n\t" - "vaddss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x03\n\t" - "vaddss $dst,$tmp2,$tmp\t! add reduction16F" %} - ins_encode %{ - __ vaddss($tmp2$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x02); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x03); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf32x4h($tmp3$$XMMRegister, $src2$$XMMRegister, 0x1); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x01); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x02); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x03); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf32x4h($tmp3$$XMMRegister, $src2$$XMMRegister, 0x2); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x01); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x02); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x03); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf32x4h($tmp3$$XMMRegister, $src2$$XMMRegister, 0x3); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x01); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x02); - __ vaddss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x03); - __ vaddss($dst$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - %} - ins_pipe( pipe_slow ); -%} - -instruct rsadd2D_reduction_reg(regD dst, regD src1, vecX src2, regD tmp) %{ - predicate(UseSSE >= 1 && UseAVX == 0); - match(Set dst (AddReductionVD src1 src2)); + match(Set dst (AddReductionVF dst src2)); effect(TEMP tmp, TEMP dst); - format %{ "movdqu $tmp,$src1\n\t" - "addsd $tmp,$src2\n\t" - "pshufd $dst,$src2,0xE\n\t" + format %{ "vaddss $dst,dst,$src2\n\t" + "pshufd $tmp,$src2,0x01\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$src2,0x02\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$src2,0x03\n\t" + "vaddss $dst,$dst,$tmp\t! add reduction4F" %} + ins_encode %{ + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $src2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x02); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x03); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct radd8F_reduction_reg(regF dst, vecY src2, regF tmp, regF tmp2) %{ + predicate(UseAVX > 0); + match(Set dst (AddReductionVF dst src2)); + effect(TEMP tmp, TEMP dst, TEMP tmp2); + format %{ "vaddss $dst,$dst,$src2\n\t" + "pshufd $tmp,$src2,0x01\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$src2,0x02\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$src2,0x03\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "vextractf128 $tmp2,$src2\n\t" + "vaddss $dst,$dst,$tmp2\n\t" + "pshufd $tmp,$tmp2,0x01\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x02\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x03\n\t" + "vaddss $dst,$dst,$tmp\t! add reduction8F" %} + ins_encode %{ + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $src2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x02); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x03); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf128h($tmp2$$XMMRegister, $src2$$XMMRegister); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x01); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x02); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x03); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct radd16F_reduction_reg(regF dst, vecZ src2, regF tmp, regF tmp2) %{ + predicate(UseAVX > 2); + match(Set dst (AddReductionVF dst src2)); + effect(TEMP tmp, TEMP dst, TEMP tmp2); + format %{ "vaddss $dst,$dst,$src2\n\t" + "pshufd $tmp,$src2,0x01\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$src2,0x02\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$src2,0x03\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "vextractf32x4 $tmp2,$src2, 0x1\n\t" + "vaddss $dst,$dst,$tmp2\n\t" + "pshufd $tmp,$tmp2,0x01\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x02\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x03\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "vextractf32x4 $tmp2,$src2, 0x2\n\t" + "vaddss $dst,$dst,$tmp2\n\t" + "pshufd $tmp,$tmp2,0x01\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x02\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x03\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "vextractf32x4 $tmp2,$src2, 0x3\n\t" + "vaddss $dst,$dst,$tmp2\n\t" + "pshufd $tmp,$tmp2,0x01\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x02\n\t" + "vaddss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x03\n\t" + "vaddss $dst,$dst,$tmp\t! add reduction16F" %} + ins_encode %{ + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $src2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x02); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x03); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf32x4h($tmp2$$XMMRegister, $src2$$XMMRegister, 0x1); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x01); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x02); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x03); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf32x4h($tmp2$$XMMRegister, $src2$$XMMRegister, 0x2); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x01); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x02); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x03); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf32x4h($tmp2$$XMMRegister, $src2$$XMMRegister, 0x3); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x01); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x02); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x03); + __ vaddss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct rsadd2D_reduction_reg(regD dst, vecX src2, regD tmp) %{ + predicate(UseSSE >= 1 && UseAVX == 0); + match(Set dst (AddReductionVD dst src2)); + effect(TEMP tmp, TEMP dst); + format %{ "addsd $dst,$src2\n\t" + "pshufd $tmp,$src2,0xE\n\t" "addsd $dst,$tmp\t! add reduction2D" %} ins_encode %{ - __ movdqu($tmp$$XMMRegister, $src1$$XMMRegister); - __ addsd($tmp$$XMMRegister, $src2$$XMMRegister); - __ pshufd($dst$$XMMRegister, $src2$$XMMRegister, 0xE); + __ addsd($dst$$XMMRegister, $src2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0xE); __ addsd($dst$$XMMRegister, $tmp$$XMMRegister); %} ins_pipe( pipe_slow ); %} -instruct rvadd2D_reduction_reg(regD dst, regD src1, vecX src2, regD tmp, regD tmp2) %{ +instruct rvadd2D_reduction_reg(regD dst, vecX src2, regD tmp) %{ predicate(UseAVX > 0); - match(Set dst (AddReductionVD src1 src2)); - effect(TEMP tmp, TEMP tmp2); - format %{ "vaddsd $tmp2,$src1,$src2\n\t" + match(Set dst (AddReductionVD dst src2)); + effect(TEMP tmp, TEMP dst); + format %{ "vaddsd $dst,$dst,$src2\n\t" "pshufd $tmp,$src2,0xE\n\t" - "vaddsd $dst,$tmp2,$tmp\t! add reduction2D" %} + "vaddsd $dst,$dst,$tmp\t! add reduction2D" %} ins_encode %{ - __ vaddsd($tmp2$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister); + __ vaddsd($dst$$XMMRegister, $dst$$XMMRegister, $src2$$XMMRegister); __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0xE); - __ vaddsd($dst$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); + __ vaddsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); %} ins_pipe( pipe_slow ); %} -instruct rvadd4D_reduction_reg(regD dst, regD src1, vecY src2, regD tmp, regD tmp2, regD tmp3) %{ +instruct rvadd4D_reduction_reg(regD dst, vecY src2, regD tmp, regD tmp2) %{ predicate(UseAVX > 0); - match(Set dst (AddReductionVD src1 src2)); - effect(TEMP tmp, TEMP tmp2, TEMP tmp3); - format %{ "vaddsd $tmp2,$src1,$src2\n\t" + match(Set dst (AddReductionVD dst src2)); + effect(TEMP tmp, TEMP dst, TEMP tmp2); + format %{ "vaddsd $dst,$dst,$src2\n\t" "pshufd $tmp,$src2,0xE\n\t" - "vaddsd $tmp2,$tmp2,$tmp\n\t" - "vextractf128 $tmp3,$src2\n\t" - "vaddsd $tmp2,$tmp2,$tmp3\n\t" - "pshufd $tmp,$tmp3,0xE\n\t" - "vaddsd $dst,$tmp2,$tmp\t! add reduction4D" %} + "vaddsd $dst,$dst,$tmp\n\t" + "vextractf32x4h $tmp2,$src2, 0x1\n\t" + "vaddsd $dst,$dst,$tmp2\n\t" + "pshufd $tmp,$tmp2,0xE\n\t" + "vaddsd $dst,$dst,$tmp\t! add reduction4D" %} ins_encode %{ - __ vaddsd($tmp2$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister); + __ vaddsd($dst$$XMMRegister, $dst$$XMMRegister, $src2$$XMMRegister); __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0xE); - __ vaddsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf128h($tmp3$$XMMRegister, $src2$$XMMRegister); - __ vaddsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0xE); - __ vaddsd($dst$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); + __ vaddsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf32x4h($tmp2$$XMMRegister, $src2$$XMMRegister, 0x1); + __ vaddsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0xE); + __ vaddsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); %} ins_pipe( pipe_slow ); %} -instruct rvadd8D_reduction_reg(regD dst, regD src1, vecZ src2, regD tmp, regD tmp2, regD tmp3) %{ +instruct rvadd8D_reduction_reg(regD dst, vecZ src2, regD tmp, regD tmp2) %{ predicate(UseAVX > 2); - match(Set dst (AddReductionVD src1 src2)); - effect(TEMP tmp, TEMP tmp2, TEMP tmp3); - format %{ "vaddsd $tmp2,$src1,$src2\n\t" + match(Set dst (AddReductionVD dst src2)); + effect(TEMP tmp, TEMP dst, TEMP tmp2); + format %{ "vaddsd $dst,$dst,$src2\n\t" "pshufd $tmp,$src2,0xE\n\t" - "vaddsd $tmp2,$tmp2,$tmp\n\t" - "vextractf64x2 $tmp3,$src2, 0x1\n\t" - "vaddsd $tmp2,$tmp2,$tmp3\n\t" - "pshufd $tmp,$tmp3,0xE\n\t" - "vaddsd $tmp2,$tmp2,$tmp\n\t" - "vextractf64x2 $tmp3,$src2, 0x2\n\t" - "vaddsd $tmp2,$tmp2,$tmp3\n\t" - "pshufd $tmp,$tmp3,0xE\n\t" - "vaddsd $tmp2,$tmp2,$tmp\n\t" - "vextractf64x2 $tmp3,$src2, 0x3\n\t" - "vaddsd $tmp2,$tmp2,$tmp3\n\t" - "pshufd $tmp,$tmp3,0xE\n\t" - "vaddsd $dst,$tmp2,$tmp\t! add reduction8D" %} + "vaddsd $dst,$dst,$tmp\n\t" + "vextractf32x4 $tmp2,$src2, 0x1\n\t" + "vaddsd $dst,$dst,$tmp2\n\t" + "pshufd $tmp,$tmp2,0xE\n\t" + "vaddsd $dst,$dst,$tmp\n\t" + "vextractf32x4 $tmp2,$src2, 0x2\n\t" + "vaddsd $dst,$dst,$tmp2\n\t" + "pshufd $tmp,$tmp2,0xE\n\t" + "vaddsd $dst,$dst,$tmp\n\t" + "vextractf32x4 $tmp2,$src2, 0x3\n\t" + "vaddsd $dst,$dst,$tmp2\n\t" + "pshufd $tmp,$tmp2,0xE\n\t" + "vaddsd $dst,$dst,$tmp\t! add reduction8D" %} ins_encode %{ - __ vaddsd($tmp2$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister); + __ vaddsd($dst$$XMMRegister, $dst$$XMMRegister, $src2$$XMMRegister); __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0xE); - __ vaddsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf64x2h($tmp3$$XMMRegister, $src2$$XMMRegister, 0x1); - __ vaddsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0xE); - __ vaddsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf64x2h($tmp3$$XMMRegister, $src2$$XMMRegister, 0x2); - __ vaddsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0xE); - __ vaddsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf64x2h($tmp3$$XMMRegister, $src2$$XMMRegister, 0x3); - __ vaddsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0xE); - __ vaddsd($dst$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); + __ vaddsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf32x4h($tmp2$$XMMRegister, $src2$$XMMRegister, 0x1); + __ vaddsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0xE); + __ vaddsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf32x4h($tmp2$$XMMRegister, $src2$$XMMRegister, 0x2); + __ vaddsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0xE); + __ vaddsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf32x4h($tmp2$$XMMRegister, $src2$$XMMRegister, 0x3); + __ vaddsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0xE); + __ vaddsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); %} ins_pipe( pipe_slow ); %} @@ -5216,7 +5327,7 @@ instruct rvmul16I_reduction_reg(rRegI dst, rRegI src1, vecZ src2, regF tmp, regF predicate(UseAVX > 2); match(Set dst (MulReductionVI src1 src2)); effect(TEMP tmp, TEMP tmp2, TEMP tmp3); - format %{ "vextracti64x4 $tmp3,$src2\n\t" + format %{ "vextracti64x4 $tmp3,$src2,0x1\n\t" "vpmulld $tmp3,$tmp3,$src2\n\t" "vextracti128 $tmp,$tmp3\n\t" "vpmulld $tmp,$tmp,$src2\n\t" @@ -5228,7 +5339,7 @@ instruct rvmul16I_reduction_reg(rRegI dst, rRegI src1, vecZ src2, regF tmp, regF "vpmulld $tmp2,$tmp,$tmp2\n\t" "movd $dst,$tmp2\t! mul reduction16I" %} ins_encode %{ - __ vextracti64x4h($tmp3$$XMMRegister, $src2$$XMMRegister); + __ vextracti64x4h($tmp3$$XMMRegister, $src2$$XMMRegister, 1); __ vpmulld($tmp3$$XMMRegister, $tmp3$$XMMRegister, $src2$$XMMRegister, 1); __ vextracti128h($tmp$$XMMRegister, $tmp3$$XMMRegister); __ vpmulld($tmp$$XMMRegister, $tmp$$XMMRegister, $tmp3$$XMMRegister, 0); @@ -5267,7 +5378,7 @@ instruct rvmul4L_reduction_reg(rRegL dst, rRegL src1, vecY src2, regF tmp, regF predicate(UseAVX > 2 && VM_Version::supports_avx512dq()); match(Set dst (MulReductionVL src1 src2)); effect(TEMP tmp, TEMP tmp2); - format %{ "vextracti64x2 $tmp,$src2, 0x1\n\t" + format %{ "vextracti128 $tmp,$src2\n\t" "vpmullq $tmp2,$tmp,$src2\n\t" "pshufd $tmp,$tmp2,0xE\n\t" "vpmullq $tmp2,$tmp2,$tmp\n\t" @@ -5275,7 +5386,7 @@ instruct rvmul4L_reduction_reg(rRegL dst, rRegL src1, vecY src2, regF tmp, regF "vpmullq $tmp2,$tmp2,$tmp\n\t" "movdq $dst,$tmp2\t! mul reduction4L" %} ins_encode %{ - __ vextracti64x2h($tmp$$XMMRegister, $src2$$XMMRegister, 0x1); + __ vextracti128h($tmp$$XMMRegister, $src2$$XMMRegister); __ vpmullq($tmp2$$XMMRegister, $tmp$$XMMRegister, $src2$$XMMRegister, 0); __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0xE); __ vpmullq($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister, 0); @@ -5290,7 +5401,7 @@ instruct rvmul8L_reduction_reg(rRegL dst, rRegL src1, vecZ src2, regF tmp, regF predicate(UseAVX > 2 && VM_Version::supports_avx512dq()); match(Set dst (MulReductionVL src1 src2)); effect(TEMP tmp, TEMP tmp2); - format %{ "vextracti64x4 $tmp2,$src2\n\t" + format %{ "vextracti64x4 $tmp2,$src2,0x1\n\t" "vpmullq $tmp2,$tmp2,$src2\n\t" "vextracti128 $tmp,$tmp2\n\t" "vpmullq $tmp2,$tmp2,$tmp\n\t" @@ -5300,7 +5411,7 @@ instruct rvmul8L_reduction_reg(rRegL dst, rRegL src1, vecZ src2, regF tmp, regF "vpmullq $tmp2,$tmp2,$tmp\n\t" "movdq $dst,$tmp2\t! mul reduction8L" %} ins_encode %{ - __ vextracti64x4h($tmp2$$XMMRegister, $src2$$XMMRegister); + __ vextracti64x4h($tmp2$$XMMRegister, $src2$$XMMRegister, 1); __ vpmullq($tmp2$$XMMRegister, $tmp2$$XMMRegister, $src2$$XMMRegister, 1); __ vextracti128h($tmp$$XMMRegister, $tmp2$$XMMRegister); __ vpmullq($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister, 0); @@ -5314,290 +5425,280 @@ instruct rvmul8L_reduction_reg(rRegL dst, rRegL src1, vecZ src2, regF tmp, regF %} #endif -instruct rsmul2F_reduction(regF dst, regF src1, vecD src2, regF tmp, regF tmp2) %{ +instruct rsmul2F_reduction(regF dst, vecD src2, regF tmp) %{ predicate(UseSSE >= 1 && UseAVX == 0); - match(Set dst (MulReductionVF src1 src2)); - effect(TEMP tmp, TEMP tmp2); - format %{ "movdqu $tmp,$src1\n\t" - "mulss $tmp,$src2\n\t" - "pshufd $tmp2,$src2,0x01\n\t" - "mulss $tmp,$tmp2\n\t" - "movdqu $dst,$tmp\t! mul reduction2F" %} + match(Set dst (MulReductionVF dst src2)); + effect(TEMP dst, TEMP tmp); + format %{ "mulss $dst,$src2\n\t" + "pshufd $tmp,$src2,0x01\n\t" + "mulss $dst,$tmp\t! mul reduction2F" %} ins_encode %{ - __ movdqu($tmp$$XMMRegister, $src1$$XMMRegister); - __ mulss($tmp$$XMMRegister, $src2$$XMMRegister); - __ pshufd($tmp2$$XMMRegister, $src2$$XMMRegister, 0x01); - __ mulss($tmp$$XMMRegister, $tmp2$$XMMRegister); - __ movdqu($dst$$XMMRegister, $tmp$$XMMRegister); + __ mulss($dst$$XMMRegister, $src2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); + __ mulss($dst$$XMMRegister, $tmp$$XMMRegister); %} ins_pipe( pipe_slow ); %} -instruct rvmul2F_reduction_reg(regF dst, regF src1, vecD src2, regF tmp, regF tmp2) %{ +instruct rvmul2F_reduction_reg(regF dst, vecD src2, regF tmp) %{ predicate(UseAVX > 0); - match(Set dst (MulReductionVF src1 src2)); - effect(TEMP tmp, TEMP tmp2); - format %{ "vmulss $tmp2,$src1,$src2\n\t" - "pshufd $tmp,$src2,0x01\n\t" - "vmulss $dst,$tmp2,$tmp\t! mul reduction2F" %} - ins_encode %{ - __ vmulss($tmp2$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); - __ vmulss($dst$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - %} - ins_pipe( pipe_slow ); -%} - -instruct rsmul4F_reduction_reg(regF dst, regF src1, vecX src2, regF tmp, regF tmp2) %{ - predicate(UseSSE >= 1 && UseAVX == 0); - match(Set dst (MulReductionVF src1 src2)); - effect(TEMP tmp, TEMP tmp2); - format %{ "movdqu $tmp,$src1\n\t" - "mulss $tmp,$src2\n\t" - "pshufd $tmp2,$src2,0x01\n\t" - "mulss $tmp,$tmp2\n\t" - "pshufd $tmp2,$src2,0x02\n\t" - "mulss $tmp,$tmp2\n\t" - "pshufd $tmp2,$src2,0x03\n\t" - "mulss $tmp,$tmp2\n\t" - "movdqu $dst,$tmp\t! mul reduction4F" %} - ins_encode %{ - __ movdqu($tmp$$XMMRegister, $src1$$XMMRegister); - __ mulss($tmp$$XMMRegister, $src2$$XMMRegister); - __ pshufd($tmp2$$XMMRegister, $src2$$XMMRegister, 0x01); - __ mulss($tmp$$XMMRegister, $tmp2$$XMMRegister); - __ pshufd($tmp2$$XMMRegister, $src2$$XMMRegister, 0x02); - __ mulss($tmp$$XMMRegister, $tmp2$$XMMRegister); - __ pshufd($tmp2$$XMMRegister, $src2$$XMMRegister, 0x03); - __ mulss($tmp$$XMMRegister, $tmp2$$XMMRegister); - __ movdqu($dst$$XMMRegister, $tmp$$XMMRegister); - %} - ins_pipe( pipe_slow ); -%} - -instruct rvmul4F_reduction_reg(regF dst, regF src1, vecX src2, regF tmp, regF tmp2) %{ - predicate(UseAVX > 0); - match(Set dst (MulReductionVF src1 src2)); - effect(TEMP tmp, TEMP tmp2); - format %{ "vmulss $tmp2,$src1,$src2\n\t" - "pshufd $tmp,$src2,0x01\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$src2,0x02\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$src2,0x03\n\t" - "vmulss $dst,$tmp2,$tmp\t! mul reduction4F" %} - ins_encode %{ - __ vmulss($tmp2$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x02); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x03); - __ vmulss($dst$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - %} - ins_pipe( pipe_slow ); -%} - -instruct rvmul8F_reduction_reg(regF dst, regF src1, vecY src2, regF tmp, regF tmp2, regF tmp3) %{ - predicate(UseAVX > 0); - match(Set dst (MulReductionVF src1 src2)); - effect(TEMP tmp, TEMP tmp2, TEMP tmp3); - format %{ "vmulss $tmp2,$src1,$src2\n\t" - "pshufd $tmp,$src2,0x01\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$src2,0x02\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$src2,0x03\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "vextractf128 $tmp3,$src2\n\t" - "vmulss $tmp2,$tmp2,$tmp3\n\t" - "pshufd $tmp,$tmp3,0x01\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x02\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x03\n\t" - "vmulss $dst,$tmp2,$tmp\t! mul reduction8F" %} - ins_encode %{ - __ vmulss($tmp2$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x02); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x03); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf128h($tmp3$$XMMRegister, $src2$$XMMRegister); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x01); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x02); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x03); - __ vmulss($dst$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - %} - ins_pipe( pipe_slow ); -%} - -instruct rvmul16F_reduction_reg(regF dst, regF src1, vecZ src2, regF tmp, regF tmp2, regF tmp3) %{ - predicate(UseAVX > 2); - match(Set dst (MulReductionVF src1 src2)); - effect(TEMP tmp, TEMP tmp2, TEMP tmp3); - format %{ "vmulss $tmp2,$src1,$src2\n\t" - "pshufd $tmp,$src2,0x01\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$src2,0x02\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$src2,0x03\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "vextractf32x4 $tmp3,$src2, 0x1\n\t" - "vmulss $tmp2,$tmp2,$tmp3\n\t" - "pshufd $tmp,$tmp3,0x01\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x02\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x03\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "vextractf32x4 $tmp3,$src2, 0x2\n\t" - "vmulss $tmp2,$tmp2,$tmp3\n\t" - "pshufd $tmp,$tmp3,0x01\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x02\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x03\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "vextractf32x4 $tmp3,$src2, 0x3\n\t" - "vmulss $tmp2,$tmp2,$tmp3\n\t" - "pshufd $tmp,$tmp3,0x01\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x02\n\t" - "vmulss $tmp2,$tmp2,$tmp\n\t" - "pshufd $tmp,$tmp3,0x03\n\t" - "vmulss $dst,$tmp2,$tmp\t! mul reduction16F" %} - ins_encode %{ - __ vmulss($tmp2$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x02); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x03); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf32x4h($tmp3$$XMMRegister, $src2$$XMMRegister, 0x1); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x01); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x02); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x03); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf32x4h($tmp3$$XMMRegister, $src2$$XMMRegister, 0x2); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x01); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x02); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x03); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf32x4h($tmp3$$XMMRegister, $src2$$XMMRegister, 0x3); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x01); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x02); - __ vmulss($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0x03); - __ vmulss($dst$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - %} - ins_pipe( pipe_slow ); -%} - -instruct rsmul2D_reduction_reg(regD dst, regD src1, vecX src2, regD tmp) %{ - predicate(UseSSE >= 1 && UseAVX == 0); - match(Set dst (MulReductionVD src1 src2)); + match(Set dst (MulReductionVF dst src2)); effect(TEMP tmp, TEMP dst); - format %{ "movdqu $tmp,$src1\n\t" - "mulsd $tmp,$src2\n\t" - "pshufd $dst,$src2,0xE\n\t" + format %{ "vmulss $dst,$dst,$src2\n\t" + "pshufd $tmp,$src2,0x01\n\t" + "vmulss $dst,$dst,$tmp\t! mul reduction2F" %} + ins_encode %{ + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $src2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct rsmul4F_reduction_reg(regF dst, vecX src2, regF tmp) %{ + predicate(UseSSE >= 1 && UseAVX == 0); + match(Set dst (MulReductionVF dst src2)); + effect(TEMP dst, TEMP tmp); + format %{ "mulss $dst,$src2\n\t" + "pshufd $tmp,$src2,0x01\n\t" + "mulss $dst,$tmp\n\t" + "pshufd $tmp,$src2,0x02\n\t" + "mulss $dst,$tmp\n\t" + "pshufd $tmp,$src2,0x03\n\t" + "mulss $dst,$tmp\t! mul reduction4F" %} + ins_encode %{ + __ mulss($dst$$XMMRegister, $src2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); + __ mulss($dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x02); + __ mulss($dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x03); + __ mulss($dst$$XMMRegister, $tmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct rvmul4F_reduction_reg(regF dst, vecX src2, regF tmp) %{ + predicate(UseAVX > 0); + match(Set dst (MulReductionVF dst src2)); + effect(TEMP tmp, TEMP dst); + format %{ "vmulss $dst,$dst,$src2\n\t" + "pshufd $tmp,$src2,0x01\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$src2,0x02\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$src2,0x03\n\t" + "vmulss $dst,$dst,$tmp\t! mul reduction4F" %} + ins_encode %{ + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $src2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x02); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x03); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct rvmul8F_reduction_reg(regF dst, vecY src2, regF tmp, regF tmp2) %{ + predicate(UseAVX > 0); + match(Set dst (MulReductionVF dst src2)); + effect(TEMP tmp, TEMP dst, TEMP tmp2); + format %{ "vmulss $dst,$dst,$src2\n\t" + "pshufd $tmp,$src2,0x01\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$src2,0x02\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$src2,0x03\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "vextractf128 $tmp2,$src2\n\t" + "vmulss $dst,$dst,$tmp2\n\t" + "pshufd $tmp,$tmp2,0x01\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x02\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x03\n\t" + "vmulss $dst,$dst,$tmp\t! mul reduction8F" %} + ins_encode %{ + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $src2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x02); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x03); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf128h($tmp2$$XMMRegister, $src2$$XMMRegister); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x01); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x02); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x03); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct rvmul16F_reduction_reg(regF dst, vecZ src2, regF tmp, regF tmp2) %{ + predicate(UseAVX > 2); + match(Set dst (MulReductionVF dst src2)); + effect(TEMP tmp, TEMP dst, TEMP tmp2); + format %{ "vmulss $dst,$dst,$src2\n\t" + "pshufd $tmp,$src2,0x01\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$src2,0x02\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$src2,0x03\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "vextractf32x4 $tmp2,$src2, 0x1\n\t" + "vmulss $dst,$dst,$tmp2\n\t" + "pshufd $tmp,$tmp2,0x01\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x02\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x03\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "vextractf32x4 $tmp2,$src2, 0x2\n\t" + "vmulss $dst,$dst,$tmp2\n\t" + "pshufd $tmp,$tmp2,0x01\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x02\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x03\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "vextractf32x4 $tmp2,$src2, 0x3\n\t" + "vmulss $dst,$dst,$tmp2\n\t" + "pshufd $tmp,$tmp2,0x01\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x02\n\t" + "vmulss $dst,$dst,$tmp\n\t" + "pshufd $tmp,$tmp2,0x03\n\t" + "vmulss $dst,$dst,$tmp\t! mul reduction16F" %} + ins_encode %{ + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $src2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x01); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x02); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0x03); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf32x4h($tmp2$$XMMRegister, $src2$$XMMRegister, 0x1); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x01); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x02); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x03); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf32x4h($tmp2$$XMMRegister, $src2$$XMMRegister, 0x2); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x01); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x02); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x03); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf32x4h($tmp2$$XMMRegister, $src2$$XMMRegister, 0x3); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x01); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x02); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0x03); + __ vmulss($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct rsmul2D_reduction_reg(regD dst, vecX src2, regD tmp) %{ + predicate(UseSSE >= 1 && UseAVX == 0); + match(Set dst (MulReductionVD dst src2)); + effect(TEMP dst, TEMP tmp); + format %{ "mulsd $dst,$src2\n\t" + "pshufd $tmp,$src2,0xE\n\t" "mulsd $dst,$tmp\t! mul reduction2D" %} ins_encode %{ - __ movdqu($tmp$$XMMRegister, $src1$$XMMRegister); - __ mulsd($tmp$$XMMRegister, $src2$$XMMRegister); - __ pshufd($dst$$XMMRegister, $src2$$XMMRegister, 0xE); + __ mulsd($dst$$XMMRegister, $src2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0xE); __ mulsd($dst$$XMMRegister, $tmp$$XMMRegister); %} ins_pipe( pipe_slow ); %} -instruct rvmul2D_reduction_reg(regD dst, regD src1, vecX src2, regD tmp, regD tmp2) %{ +instruct rvmul2D_reduction_reg(regD dst, vecX src2, regD tmp) %{ predicate(UseAVX > 0); - match(Set dst (MulReductionVD src1 src2)); - effect(TEMP tmp, TEMP tmp2); - format %{ "vmulsd $tmp2,$src1,$src2\n\t" + match(Set dst (MulReductionVD dst src2)); + effect(TEMP tmp, TEMP dst); + format %{ "vmulsd $dst,$dst,$src2\n\t" "pshufd $tmp,$src2,0xE\n\t" - "vmulsd $dst,$tmp2,$tmp\t! mul reduction2D" %} + "vmulsd $dst,$dst,$tmp\t! mul reduction2D" %} ins_encode %{ - __ vmulsd($tmp2$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister); + __ vmulsd($dst$$XMMRegister, $dst$$XMMRegister, $src2$$XMMRegister); __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0xE); - __ vmulsd($dst$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); + __ vmulsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); %} ins_pipe( pipe_slow ); %} -instruct rvmul4D_reduction_reg(regD dst, regD src1, vecY src2, regD tmp, regD tmp2, regD tmp3) %{ +instruct rvmul4D_reduction_reg(regD dst, vecY src2, regD tmp, regD tmp2) %{ predicate(UseAVX > 0); - match(Set dst (MulReductionVD src1 src2)); - effect(TEMP tmp, TEMP tmp2, TEMP tmp3); - format %{ "vmulsd $tmp2,$src1,$src2\n\t" + match(Set dst (MulReductionVD dst src2)); + effect(TEMP tmp, TEMP dst, TEMP tmp2); + format %{ "vmulsd $dst,$dst,$src2\n\t" "pshufd $tmp,$src2,0xE\n\t" - "vmulsd $tmp2,$tmp2,$tmp\n\t" - "vextractf128 $tmp3,$src2\n\t" - "vmulsd $tmp2,$tmp2,$tmp3\n\t" - "pshufd $tmp,$tmp3,0xE\n\t" - "vmulsd $dst,$tmp2,$tmp\t! mul reduction4D" %} + "vmulsd $dst,$dst,$tmp\n\t" + "vextractf128 $tmp2,$src2\n\t" + "vmulsd $dst,$dst,$tmp2\n\t" + "pshufd $tmp,$tmp2,0xE\n\t" + "vmulsd $dst,$dst,$tmp\t! mul reduction4D" %} ins_encode %{ - __ vmulsd($tmp2$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister); + __ vmulsd($dst$$XMMRegister, $dst$$XMMRegister, $src2$$XMMRegister); __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0xE); - __ vmulsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf128h($tmp3$$XMMRegister, $src2$$XMMRegister); - __ vmulsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0xE); - __ vmulsd($dst$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); + __ vmulsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf128h($tmp2$$XMMRegister, $src2$$XMMRegister); + __ vmulsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0xE); + __ vmulsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); %} ins_pipe( pipe_slow ); %} -instruct rvmul8D_reduction_reg(regD dst, regD src1, vecZ src2, regD tmp, regD tmp2, regD tmp3) %{ +instruct rvmul8D_reduction_reg(regD dst, vecZ src2, regD tmp, regD tmp2) %{ predicate(UseAVX > 2); - match(Set dst (MulReductionVD src1 src2)); - effect(TEMP tmp, TEMP tmp2, TEMP tmp3); - format %{ "vmulsd $tmp2,$src1,$src2\n\t" + match(Set dst (MulReductionVD dst src2)); + effect(TEMP tmp, TEMP dst, TEMP tmp2); + format %{ "vmulsd $dst,$dst,$src2\n\t" "pshufd $tmp,$src2,0xE\n\t" - "vmulsd $tmp2,$tmp2,$tmp\n\t" - "vextractf64x2 $tmp3,$src2, 0x1\n\t" - "vmulsd $tmp2,$tmp2,$tmp3\n\t" + "vmulsd $dst,$dst,$tmp\n\t" + "vextractf32x4 $tmp2,$src2, 0x1\n\t" + "vmulsd $dst,$dst,$tmp2\n\t" "pshufd $tmp,$src2,0xE\n\t" - "vmulsd $tmp2,$tmp2,$tmp\n\t" - "vextractf64x2 $tmp3,$src2, 0x2\n\t" - "vmulsd $tmp2,$tmp2,$tmp3\n\t" - "pshufd $tmp,$tmp3,0xE\n\t" - "vmulsd $tmp2,$tmp2,$tmp\n\t" - "vextractf64x2 $tmp3,$src2, 0x3\n\t" - "vmulsd $tmp2,$tmp2,$tmp3\n\t" - "pshufd $tmp,$tmp3,0xE\n\t" - "vmulsd $dst,$tmp2,$tmp\t! mul reduction8D" %} + "vmulsd $dst,$dst,$tmp\n\t" + "vextractf32x4 $tmp2,$src2, 0x2\n\t" + "vmulsd $dst,$dst,$tmp2\n\t" + "pshufd $tmp,$tmp2,0xE\n\t" + "vmulsd $dst,$dst,$tmp\n\t" + "vextractf32x4 $tmp2,$src2, 0x3\n\t" + "vmulsd $dst,$dst,$tmp2\n\t" + "pshufd $tmp,$tmp2,0xE\n\t" + "vmulsd $dst,$dst,$tmp\t! mul reduction8D" %} ins_encode %{ - __ vmulsd($tmp2$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister); + __ vmulsd($dst$$XMMRegister, $dst$$XMMRegister, $src2$$XMMRegister); __ pshufd($tmp$$XMMRegister, $src2$$XMMRegister, 0xE); - __ vmulsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf64x2h($tmp3$$XMMRegister, $src2$$XMMRegister, 0x1); - __ vmulsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0xE); - __ vmulsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf64x2h($tmp3$$XMMRegister, $src2$$XMMRegister, 0x2); - __ vmulsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0xE); - __ vmulsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); - __ vextractf64x2h($tmp3$$XMMRegister, $src2$$XMMRegister, 0x3); - __ vmulsd($tmp2$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister); - __ pshufd($tmp$$XMMRegister, $tmp3$$XMMRegister, 0xE); - __ vmulsd($dst$$XMMRegister, $tmp2$$XMMRegister, $tmp$$XMMRegister); + __ vmulsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf32x4h($tmp2$$XMMRegister, $src2$$XMMRegister, 0x1); + __ vmulsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0xE); + __ vmulsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf32x4h($tmp2$$XMMRegister, $src2$$XMMRegister, 0x2); + __ vmulsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0xE); + __ vmulsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); + __ vextractf32x4h($tmp2$$XMMRegister, $src2$$XMMRegister, 0x3); + __ vmulsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp2$$XMMRegister); + __ pshufd($tmp$$XMMRegister, $tmp2$$XMMRegister, 0xE); + __ vmulsd($dst$$XMMRegister, $dst$$XMMRegister, $tmp$$XMMRegister); %} ins_pipe( pipe_slow ); %} @@ -5608,7 +5709,7 @@ instruct rvmul8D_reduction_reg(regD dst, regD src1, vecZ src2, regD tmp, regD tm // Bytes vector add instruct vadd4B(vecS dst, vecS src) %{ - predicate(n->as_Vector()->length() == 4); + predicate(UseAVX == 0 && n->as_Vector()->length() == 4); match(Set dst (AddVB dst src)); format %{ "paddb $dst,$src\t! add packed4B" %} ins_encode %{ @@ -5617,8 +5718,8 @@ instruct vadd4B(vecS dst, vecS src) %{ ins_pipe( pipe_slow ); %} -instruct vadd4B_reg(vecS dst, vecS src1, vecS src2) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vadd4B_reg_avx(vecS dst, vecS src1, vecS src2) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); match(Set dst (AddVB src1 src2)); format %{ "vpaddb $dst,$src1,$src2\t! add packed4B" %} ins_encode %{ @@ -5628,8 +5729,31 @@ instruct vadd4B_reg(vecS dst, vecS src1, vecS src2) %{ ins_pipe( pipe_slow ); %} -instruct vadd4B_mem(vecS dst, vecS src, memory mem) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vadd4B_reg_evex(vecS dst, vecS src1, vecS src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); + match(Set dst (AddVB src1 src2)); + format %{ "vpaddb $dst,$src1,$src2\t! add packed4B" %} + ins_encode %{ + int vector_len = 0; + __ vpaddb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd4B_reg_evex_special(vecS dst, vecS src1, vecS src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 4); + match(Set dst (AddVB dst src2)); + effect(TEMP src1); + format %{ "vpaddb $dst,$dst,$src2\t! add packed4B" %} + ins_encode %{ + int vector_len = 0; + __ vpaddb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd4B_mem_avx(vecS dst, vecS src, memory mem) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); match(Set dst (AddVB src (LoadVector mem))); format %{ "vpaddb $dst,$src,$mem\t! add packed4B" %} ins_encode %{ @@ -5639,8 +5763,31 @@ instruct vadd4B_mem(vecS dst, vecS src, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vadd4B_mem_evex(vecS dst, vecS src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); + match(Set dst (AddVB src (LoadVector mem))); + format %{ "vpaddb $dst,$src,$mem\t! add packed4B" %} + ins_encode %{ + int vector_len = 0; + __ vpaddb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd4B_mem_evex_special(vecS dst, vecS src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); + match(Set dst (AddVB dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpaddb $dst,$src,$mem\t! add packed4B" %} + ins_encode %{ + int vector_len = 0; + __ vpaddb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + instruct vadd8B(vecD dst, vecD src) %{ - predicate(n->as_Vector()->length() == 8); + predicate(UseAVX == 0 && n->as_Vector()->length() == 8); match(Set dst (AddVB dst src)); format %{ "paddb $dst,$src\t! add packed8B" %} ins_encode %{ @@ -5649,8 +5796,8 @@ instruct vadd8B(vecD dst, vecD src) %{ ins_pipe( pipe_slow ); %} -instruct vadd8B_reg(vecD dst, vecD src1, vecD src2) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vadd8B_reg_avx(vecD dst, vecD src1, vecD src2) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (AddVB src1 src2)); format %{ "vpaddb $dst,$src1,$src2\t! add packed8B" %} ins_encode %{ @@ -5660,8 +5807,31 @@ instruct vadd8B_reg(vecD dst, vecD src1, vecD src2) %{ ins_pipe( pipe_slow ); %} -instruct vadd8B_mem(vecD dst, vecD src, memory mem) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vadd8B_reg_evex(vecD dst, vecD src1, vecD src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (AddVB src1 src2)); + format %{ "vpaddb $dst,$src1,$src2\t! add packed8B" %} + ins_encode %{ + int vector_len = 0; + __ vpaddb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd8B_reg_evex_special(vecD dst, vecD src1, vecD src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 8); + match(Set dst (AddVB dst src2)); + effect(TEMP src1); + format %{ "vpaddb $dst,$dst,$src2\t! add packed8B" %} + ins_encode %{ + int vector_len = 0; + __ vpaddb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd8B_mem_avx(vecD dst, vecD src, memory mem) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (AddVB src (LoadVector mem))); format %{ "vpaddb $dst,$src,$mem\t! add packed8B" %} ins_encode %{ @@ -5671,8 +5841,31 @@ instruct vadd8B_mem(vecD dst, vecD src, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vadd8B_mem_evex(vecD dst, vecD src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (AddVB src (LoadVector mem))); + format %{ "vpaddb $dst,$src,$mem\t! add packed8B" %} + ins_encode %{ + int vector_len = 0; + __ vpaddb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd8B_mem_evex_special(vecD dst, vecD src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (AddVB dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpaddb $dst,$src,$mem\t! add packed8B" %} + ins_encode %{ + int vector_len = 0; + __ vpaddb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + instruct vadd16B(vecX dst, vecX src) %{ - predicate(n->as_Vector()->length() == 16); + predicate(UseAVX == 0 && n->as_Vector()->length() == 16); match(Set dst (AddVB dst src)); format %{ "paddb $dst,$src\t! add packed16B" %} ins_encode %{ @@ -5681,8 +5874,8 @@ instruct vadd16B(vecX dst, vecX src) %{ ins_pipe( pipe_slow ); %} -instruct vadd16B_reg(vecX dst, vecX src1, vecX src2) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 16); +instruct vadd16B_reg_avx(vecX dst, vecX src1, vecX src2) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 16); match(Set dst (AddVB src1 src2)); format %{ "vpaddb $dst,$src1,$src2\t! add packed16B" %} ins_encode %{ @@ -5692,8 +5885,31 @@ instruct vadd16B_reg(vecX dst, vecX src1, vecX src2) %{ ins_pipe( pipe_slow ); %} -instruct vadd16B_mem(vecX dst, vecX src, memory mem) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 16); +instruct vadd16B_reg_evex(vecX dst, vecX src1, vecX src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); + match(Set dst (AddVB src1 src2)); + format %{ "vpaddb $dst,$src1,$src2\t! add packed16B" %} + ins_encode %{ + int vector_len = 0; + __ vpaddb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd16B_reg_evex_special(vecX dst, vecX src1, vecX src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 16); + match(Set dst (AddVB dst src2)); + effect(TEMP src1); + format %{ "vpaddb $dst,$dst,$src2\t! add packed16B" %} + ins_encode %{ + int vector_len = 0; + __ vpaddb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd16B_mem_avx(vecX dst, vecX src, memory mem) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 16); match(Set dst (AddVB src (LoadVector mem))); format %{ "vpaddb $dst,$src,$mem\t! add packed16B" %} ins_encode %{ @@ -5703,8 +5919,31 @@ instruct vadd16B_mem(vecX dst, vecX src, memory mem) %{ ins_pipe( pipe_slow ); %} -instruct vadd32B_reg(vecY dst, vecY src1, vecY src2) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 32); +instruct vadd16B_mem_evex(vecX dst, vecX src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); + match(Set dst (AddVB src (LoadVector mem))); + format %{ "vpaddb $dst,$src,$mem\t! add packed16B" %} + ins_encode %{ + int vector_len = 0; + __ vpaddb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd16B_mem_evex_special(vecX dst, vecX src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); + match(Set dst (AddVB dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpaddb $dst,$src,$mem\t! add packed16B" %} + ins_encode %{ + int vector_len = 0; + __ vpaddb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd32B_reg_avx(vecY dst, vecY src1, vecY src2) %{ + predicate(VM_Version::supports_avx256only() && n->as_Vector()->length() == 32); match(Set dst (AddVB src1 src2)); format %{ "vpaddb $dst,$src1,$src2\t! add packed32B" %} ins_encode %{ @@ -5714,8 +5953,31 @@ instruct vadd32B_reg(vecY dst, vecY src1, vecY src2) %{ ins_pipe( pipe_slow ); %} -instruct vadd32B_mem(vecY dst, vecY src, memory mem) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 32); +instruct vadd32B_reg_evex(vecY dst, vecY src1, vecY src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); + match(Set dst (AddVB src1 src2)); + format %{ "vpaddb $dst,$src1,$src2\t! add packed32B" %} + ins_encode %{ + int vector_len = 1; + __ vpaddb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd32B_reg_evex_special(vecY dst, vecY src1, vecY src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 32); + match(Set dst (AddVB dst src2)); + effect(TEMP src1); + format %{ "vpaddb $dst,$dst,$src2\t! add packed32B" %} + ins_encode %{ + int vector_len = 1; + __ vpaddb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd32B_mem_avx(vecY dst, vecY src, memory mem) %{ + predicate(VM_Version::supports_avx256only() && n->as_Vector()->length() == 32); match(Set dst (AddVB src (LoadVector mem))); format %{ "vpaddb $dst,$src,$mem\t! add packed32B" %} ins_encode %{ @@ -5725,8 +5987,31 @@ instruct vadd32B_mem(vecY dst, vecY src, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vadd32B_mem_evex(vecY dst, vecY src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); + match(Set dst (AddVB src (LoadVector mem))); + format %{ "vpaddb $dst,$src,$mem\t! add packed32B" %} + ins_encode %{ + int vector_len = 1; + __ vpaddb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd32B_mem_evex_special(vecY dst, vecY src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); + match(Set dst (AddVB dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpaddb $dst,$src,$mem\t! add packed32B" %} + ins_encode %{ + int vector_len = 1; + __ vpaddb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + instruct vadd64B_reg(vecZ dst, vecZ src1, vecZ src2) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 64); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 64); match(Set dst (AddVB src1 src2)); format %{ "vpaddb $dst,$src1,$src2\t! add packed64B" %} ins_encode %{ @@ -5737,7 +6022,7 @@ instruct vadd64B_reg(vecZ dst, vecZ src1, vecZ src2) %{ %} instruct vadd64B_mem(vecZ dst, vecZ src, memory mem) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 64); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 64); match(Set dst (AddVB src (LoadVector mem))); format %{ "vpaddb $dst,$src,$mem\t! add packed64B" %} ins_encode %{ @@ -5749,7 +6034,7 @@ instruct vadd64B_mem(vecZ dst, vecZ src, memory mem) %{ // Shorts/Chars vector add instruct vadd2S(vecS dst, vecS src) %{ - predicate(n->as_Vector()->length() == 2); + predicate(UseAVX == 0 && n->as_Vector()->length() == 2); match(Set dst (AddVS dst src)); format %{ "paddw $dst,$src\t! add packed2S" %} ins_encode %{ @@ -5758,8 +6043,8 @@ instruct vadd2S(vecS dst, vecS src) %{ ins_pipe( pipe_slow ); %} -instruct vadd2S_reg(vecS dst, vecS src1, vecS src2) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 2); +instruct vadd2S_reg_avx(vecS dst, vecS src1, vecS src2) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 2); match(Set dst (AddVS src1 src2)); format %{ "vpaddw $dst,$src1,$src2\t! add packed2S" %} ins_encode %{ @@ -5769,8 +6054,31 @@ instruct vadd2S_reg(vecS dst, vecS src1, vecS src2) %{ ins_pipe( pipe_slow ); %} -instruct vadd2S_mem(vecS dst, vecS src, memory mem) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 2); +instruct vadd2S_reg_evex(vecS dst, vecS src1, vecS src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 2); + match(Set dst (AddVS src1 src2)); + format %{ "vpaddw $dst,$src1,$src2\t! add packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpaddw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd2S_reg_evex_special(vecS dst, vecS src1, vecS src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 2); + match(Set dst (AddVS dst src2)); + effect(TEMP src1); + format %{ "vpaddw $dst,$dst,$src2\t! add packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpaddw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd2S_mem_avx(vecS dst, vecS src, memory mem) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 2); match(Set dst (AddVS src (LoadVector mem))); format %{ "vpaddw $dst,$src,$mem\t! add packed2S" %} ins_encode %{ @@ -5780,8 +6088,31 @@ instruct vadd2S_mem(vecS dst, vecS src, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vadd2S_mem_evex(vecS dst, vecS src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 2); + match(Set dst (AddVS src (LoadVector mem))); + format %{ "vpaddw $dst,$src,$mem\t! add packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpaddw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd2S_mem_evex_special(vecS dst, vecS src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 2); + match(Set dst (AddVS dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpaddw $dst,$src,$mem\t! add packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpaddw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + instruct vadd4S(vecD dst, vecD src) %{ - predicate(n->as_Vector()->length() == 4); + predicate(UseAVX == 0 && n->as_Vector()->length() == 4); match(Set dst (AddVS dst src)); format %{ "paddw $dst,$src\t! add packed4S" %} ins_encode %{ @@ -5790,8 +6121,8 @@ instruct vadd4S(vecD dst, vecD src) %{ ins_pipe( pipe_slow ); %} -instruct vadd4S_reg(vecD dst, vecD src1, vecD src2) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vadd4S_reg_avx(vecD dst, vecD src1, vecD src2) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); match(Set dst (AddVS src1 src2)); format %{ "vpaddw $dst,$src1,$src2\t! add packed4S" %} ins_encode %{ @@ -5801,8 +6132,31 @@ instruct vadd4S_reg(vecD dst, vecD src1, vecD src2) %{ ins_pipe( pipe_slow ); %} -instruct vadd4S_mem(vecD dst, vecD src, memory mem) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vadd4S_reg_evex(vecD dst, vecD src1, vecD src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); + match(Set dst (AddVS src1 src2)); + format %{ "vpaddw $dst,$src1,$src2\t! add packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpaddw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd4S_reg_evex_special(vecD dst, vecD src1, vecD src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 4); + match(Set dst (AddVS dst src2)); + effect(TEMP src1); + format %{ "vpaddw $dst,$dst,$src2\t! add packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpaddw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd4S_mem_avx(vecD dst, vecD src, memory mem) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); match(Set dst (AddVS src (LoadVector mem))); format %{ "vpaddw $dst,$src,$mem\t! add packed4S" %} ins_encode %{ @@ -5812,8 +6166,31 @@ instruct vadd4S_mem(vecD dst, vecD src, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vadd4S_mem_evex(vecD dst, vecD src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); + match(Set dst (AddVS src (LoadVector mem))); + format %{ "vpaddw $dst,$src,$mem\t! add packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpaddw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd4S_mem_evex_special(vecD dst, vecD src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); + match(Set dst (AddVS dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpaddw $dst,$src,$mem\t! add packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpaddw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + instruct vadd8S(vecX dst, vecX src) %{ - predicate(n->as_Vector()->length() == 8); + predicate(UseAVX == 0 && n->as_Vector()->length() == 8); match(Set dst (AddVS dst src)); format %{ "paddw $dst,$src\t! add packed8S" %} ins_encode %{ @@ -5822,8 +6199,8 @@ instruct vadd8S(vecX dst, vecX src) %{ ins_pipe( pipe_slow ); %} -instruct vadd8S_reg(vecX dst, vecX src1, vecX src2) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vadd8S_reg_avx(vecX dst, vecX src1, vecX src2) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (AddVS src1 src2)); format %{ "vpaddw $dst,$src1,$src2\t! add packed8S" %} ins_encode %{ @@ -5833,8 +6210,31 @@ instruct vadd8S_reg(vecX dst, vecX src1, vecX src2) %{ ins_pipe( pipe_slow ); %} -instruct vadd8S_mem(vecX dst, vecX src, memory mem) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vadd8S_reg_evex(vecX dst, vecX src1, vecX src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (AddVS src1 src2)); + format %{ "vpaddw $dst,$src1,$src2\t! add packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpaddw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd8S_reg_evex_special(vecX dst, vecX src1, vecX src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 8); + match(Set dst (AddVS dst src2)); + effect(TEMP src1); + format %{ "vpaddw $dst,$dst,$src2\t! add packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpaddw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd8S_mem_avx(vecX dst, vecX src, memory mem) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (AddVS src (LoadVector mem))); format %{ "vpaddw $dst,$src,$mem\t! add packed8S" %} ins_encode %{ @@ -5844,8 +6244,31 @@ instruct vadd8S_mem(vecX dst, vecX src, memory mem) %{ ins_pipe( pipe_slow ); %} -instruct vadd16S_reg(vecY dst, vecY src1, vecY src2) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 16); +instruct vadd8S_mem_evex(vecX dst, vecX src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (AddVS src (LoadVector mem))); + format %{ "vpaddw $dst,$src,$mem\t! add packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpaddw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd8S_mem_evex_special(vecX dst, vecX src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (AddVS dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpaddw $dst,$src,$mem\t! add packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpaddw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd16S_reg_avx(vecY dst, vecY src1, vecY src2) %{ + predicate(VM_Version::supports_avx256only() && n->as_Vector()->length() == 16); match(Set dst (AddVS src1 src2)); format %{ "vpaddw $dst,$src1,$src2\t! add packed16S" %} ins_encode %{ @@ -5855,8 +6278,31 @@ instruct vadd16S_reg(vecY dst, vecY src1, vecY src2) %{ ins_pipe( pipe_slow ); %} -instruct vadd16S_mem(vecY dst, vecY src, memory mem) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 16); +instruct vadd16S_reg_evex(vecY dst, vecY src1, vecY src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); + match(Set dst (AddVS src1 src2)); + format %{ "vpaddw $dst,$src1,$src2\t! add packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpaddw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd16S_reg_evex_special(vecY dst, vecY src1, vecY src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 16); + match(Set dst (AddVS dst src2)); + effect(TEMP src1); + format %{ "vpaddw $dst,$dst,$src2\t! add packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpaddw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd16S_mem_avx(vecY dst, vecY src, memory mem) %{ + predicate(VM_Version::supports_avx256only() && n->as_Vector()->length() == 16); match(Set dst (AddVS src (LoadVector mem))); format %{ "vpaddw $dst,$src,$mem\t! add packed16S" %} ins_encode %{ @@ -5866,8 +6312,31 @@ instruct vadd16S_mem(vecY dst, vecY src, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vadd16S_mem_evex(vecY dst, vecY src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); + match(Set dst (AddVS src (LoadVector mem))); + format %{ "vpaddw $dst,$src,$mem\t! add packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpaddw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vadd16S_mem_evex_special(vecY dst, vecY src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); + match(Set dst (AddVS dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpaddw $dst,$src,$mem\t! add packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpaddw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + instruct vadd32S_reg(vecZ dst, vecZ src1, vecZ src2) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 32); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); match(Set dst (AddVS src1 src2)); format %{ "vpaddw $dst,$src1,$src2\t! add packed32S" %} ins_encode %{ @@ -5878,7 +6347,7 @@ instruct vadd32S_reg(vecZ dst, vecZ src1, vecZ src2) %{ %} instruct vadd32S_mem(vecZ dst, vecZ src, memory mem) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 32); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); match(Set dst (AddVS src (LoadVector mem))); format %{ "vpaddw $dst,$src,$mem\t! add packed32S" %} ins_encode %{ @@ -6264,7 +6733,7 @@ instruct vadd8D_mem(vecZ dst, vecZ src, memory mem) %{ // Bytes vector sub instruct vsub4B(vecS dst, vecS src) %{ - predicate(n->as_Vector()->length() == 4); + predicate(UseAVX == 0 && n->as_Vector()->length() == 4); match(Set dst (SubVB dst src)); format %{ "psubb $dst,$src\t! sub packed4B" %} ins_encode %{ @@ -6273,8 +6742,8 @@ instruct vsub4B(vecS dst, vecS src) %{ ins_pipe( pipe_slow ); %} -instruct vsub4B_reg(vecS dst, vecS src1, vecS src2) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vsub4B_reg_avx(vecS dst, vecS src1, vecS src2) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); match(Set dst (SubVB src1 src2)); format %{ "vpsubb $dst,$src1,$src2\t! sub packed4B" %} ins_encode %{ @@ -6284,8 +6753,31 @@ instruct vsub4B_reg(vecS dst, vecS src1, vecS src2) %{ ins_pipe( pipe_slow ); %} -instruct vsub4B_mem(vecS dst, vecS src, memory mem) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vsub4B_reg_evex(vecS dst, vecS src1, vecS src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); + match(Set dst (SubVB src1 src2)); + format %{ "vpsubb $dst,$src1,$src2\t! sub packed4B" %} + ins_encode %{ + int vector_len = 0; + __ vpsubb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub4B_reg_exex_special(vecS dst, vecS src1, vecS src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 4); + match(Set dst (SubVB dst src2)); + effect(TEMP src1); + format %{ "vpsubb $dst,$src1,$src2\t! sub packed4B" %} + ins_encode %{ + int vector_len = 0; + __ vpsubb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub4B_mem_avx(vecS dst, vecS src, memory mem) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); match(Set dst (SubVB src (LoadVector mem))); format %{ "vpsubb $dst,$src,$mem\t! sub packed4B" %} ins_encode %{ @@ -6295,8 +6787,31 @@ instruct vsub4B_mem(vecS dst, vecS src, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vsub4B_mem_evex(vecS dst, vecS src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); + match(Set dst (SubVB src (LoadVector mem))); + format %{ "vpsubb $dst,$src,$mem\t! sub packed4B" %} + ins_encode %{ + int vector_len = 0; + __ vpsubb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub4B_mem_evex_special(vecS dst, vecS src, memory mem) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 4); + match(Set dst (SubVB dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpsubb $dst,$src,$mem\t! sub packed4B" %} + ins_encode %{ + int vector_len = 0; + __ vpsubb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + instruct vsub8B(vecD dst, vecD src) %{ - predicate(n->as_Vector()->length() == 8); + predicate(UseAVX == 0 && n->as_Vector()->length() == 8); match(Set dst (SubVB dst src)); format %{ "psubb $dst,$src\t! sub packed8B" %} ins_encode %{ @@ -6305,8 +6820,8 @@ instruct vsub8B(vecD dst, vecD src) %{ ins_pipe( pipe_slow ); %} -instruct vsub8B_reg(vecD dst, vecD src1, vecD src2) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vsub8B_reg_avx(vecD dst, vecD src1, vecD src2) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (SubVB src1 src2)); format %{ "vpsubb $dst,$src1,$src2\t! sub packed8B" %} ins_encode %{ @@ -6316,8 +6831,31 @@ instruct vsub8B_reg(vecD dst, vecD src1, vecD src2) %{ ins_pipe( pipe_slow ); %} -instruct vsub8B_mem(vecD dst, vecD src, memory mem) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vsub8B_reg_evex(vecD dst, vecD src1, vecD src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (SubVB src1 src2)); + format %{ "vpsubb $dst,$src1,$src2\t! sub packed8B" %} + ins_encode %{ + int vector_len = 0; + __ vpsubb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub8B_reg_evex_special(vecD dst, vecD src1, vecD src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 8); + match(Set dst (SubVB dst src2)); + effect(TEMP src1); + format %{ "vpsubb $dst,$src1,$src2\t! sub packed8B" %} + ins_encode %{ + int vector_len = 0; + __ vpsubb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub8B_mem_avx(vecD dst, vecD src, memory mem) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (SubVB src (LoadVector mem))); format %{ "vpsubb $dst,$src,$mem\t! sub packed8B" %} ins_encode %{ @@ -6327,8 +6865,31 @@ instruct vsub8B_mem(vecD dst, vecD src, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vsub8B_mem_evex(vecD dst, vecD src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (SubVB src (LoadVector mem))); + format %{ "vpsubb $dst,$src,$mem\t! sub packed8B" %} + ins_encode %{ + int vector_len = 0; + __ vpsubb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub8B_mem_evex_special(vecD dst, vecD src, memory mem) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 8); + match(Set dst (SubVB dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpsubb $dst,$src,$mem\t! sub packed8B" %} + ins_encode %{ + int vector_len = 0; + __ vpsubb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + instruct vsub16B(vecX dst, vecX src) %{ - predicate(n->as_Vector()->length() == 16); + predicate(UseAVX == 0 && n->as_Vector()->length() == 16); match(Set dst (SubVB dst src)); format %{ "psubb $dst,$src\t! sub packed16B" %} ins_encode %{ @@ -6337,8 +6898,8 @@ instruct vsub16B(vecX dst, vecX src) %{ ins_pipe( pipe_slow ); %} -instruct vsub16B_reg(vecX dst, vecX src1, vecX src2) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 16); +instruct vsub16B_reg_avx(vecX dst, vecX src1, vecX src2) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 16); match(Set dst (SubVB src1 src2)); format %{ "vpsubb $dst,$src1,$src2\t! sub packed16B" %} ins_encode %{ @@ -6348,8 +6909,31 @@ instruct vsub16B_reg(vecX dst, vecX src1, vecX src2) %{ ins_pipe( pipe_slow ); %} -instruct vsub16B_mem(vecX dst, vecX src, memory mem) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 16); +instruct vsub16B_reg_evex(vecX dst, vecX src1, vecX src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); + match(Set dst (SubVB src1 src2)); + format %{ "vpsubb $dst,$src1,$src2\t! sub packed16B" %} + ins_encode %{ + int vector_len = 0; + __ vpsubb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub16B_reg_evex_special(vecX dst, vecX src1, vecX src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 16); + match(Set dst (SubVB dst src2)); + effect(TEMP src1); + format %{ "vpsubb $dst,$src1,$src2\t! sub packed16B" %} + ins_encode %{ + int vector_len = 0; + __ vpsubb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub16B_mem_avx(vecX dst, vecX src, memory mem) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 16); match(Set dst (SubVB src (LoadVector mem))); format %{ "vpsubb $dst,$src,$mem\t! sub packed16B" %} ins_encode %{ @@ -6359,8 +6943,31 @@ instruct vsub16B_mem(vecX dst, vecX src, memory mem) %{ ins_pipe( pipe_slow ); %} -instruct vsub32B_reg(vecY dst, vecY src1, vecY src2) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 32); +instruct vsub16B_mem_evex(vecX dst, vecX src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); + match(Set dst (SubVB src (LoadVector mem))); + format %{ "vpsubb $dst,$src,$mem\t! sub packed16B" %} + ins_encode %{ + int vector_len = 0; + __ vpsubb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub16B_mem_evex_special(vecX dst, vecX src, memory mem) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 16); + match(Set dst (SubVB dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpsubb $dst,$src,$mem\t! sub packed16B" %} + ins_encode %{ + int vector_len = 0; + __ vpsubb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub32B_reg_avx(vecY dst, vecY src1, vecY src2) %{ + predicate(VM_Version::supports_avx256only() && n->as_Vector()->length() == 32); match(Set dst (SubVB src1 src2)); format %{ "vpsubb $dst,$src1,$src2\t! sub packed32B" %} ins_encode %{ @@ -6370,8 +6977,31 @@ instruct vsub32B_reg(vecY dst, vecY src1, vecY src2) %{ ins_pipe( pipe_slow ); %} -instruct vsub32B_mem(vecY dst, vecY src, memory mem) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 32); +instruct vsub32B_reg_evex(vecY dst, vecY src1, vecY src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); + match(Set dst (SubVB src1 src2)); + format %{ "vpsubb $dst,$src1,$src2\t! sub packed32B" %} + ins_encode %{ + int vector_len = 1; + __ vpsubb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub32B_reg_evex_special(vecY dst, vecY src1, vecY src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 32); + match(Set dst (SubVB dst src2)); + effect(TEMP src1); + format %{ "vpsubb $dst,$src1,$src2\t! sub packed32B" %} + ins_encode %{ + int vector_len = 1; + __ vpsubb($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub32B_mem_avx(vecY dst, vecY src, memory mem) %{ + predicate(VM_Version::supports_avx256only() && n->as_Vector()->length() == 32); match(Set dst (SubVB src (LoadVector mem))); format %{ "vpsubb $dst,$src,$mem\t! sub packed32B" %} ins_encode %{ @@ -6381,8 +7011,31 @@ instruct vsub32B_mem(vecY dst, vecY src, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vsub32B_mem_evex(vecY dst, vecY src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); + match(Set dst (SubVB src (LoadVector mem))); + format %{ "vpsubb $dst,$src,$mem\t! sub packed32B" %} + ins_encode %{ + int vector_len = 1; + __ vpsubb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub32B_mem_evex_special(vecY dst, vecY src, memory mem) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 32); + match(Set dst (SubVB dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpsubb $dst,$src,$mem\t! sub packed32B" %} + ins_encode %{ + int vector_len = 1; + __ vpsubb($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + instruct vsub64B_reg(vecZ dst, vecZ src1, vecZ src2) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 64); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 64); match(Set dst (SubVB src1 src2)); format %{ "vpsubb $dst,$src1,$src2\t! sub packed64B" %} ins_encode %{ @@ -6393,7 +7046,7 @@ instruct vsub64B_reg(vecZ dst, vecZ src1, vecZ src2) %{ %} instruct vsub64B_mem(vecZ dst, vecZ src, memory mem) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 64); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 64); match(Set dst (SubVB src (LoadVector mem))); format %{ "vpsubb $dst,$src,$mem\t! sub packed64B" %} ins_encode %{ @@ -6405,7 +7058,7 @@ instruct vsub64B_mem(vecZ dst, vecZ src, memory mem) %{ // Shorts/Chars vector sub instruct vsub2S(vecS dst, vecS src) %{ - predicate(n->as_Vector()->length() == 2); + predicate(UseAVX == 0 && n->as_Vector()->length() == 2); match(Set dst (SubVS dst src)); format %{ "psubw $dst,$src\t! sub packed2S" %} ins_encode %{ @@ -6414,8 +7067,8 @@ instruct vsub2S(vecS dst, vecS src) %{ ins_pipe( pipe_slow ); %} -instruct vsub2S_reg(vecS dst, vecS src1, vecS src2) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 2); +instruct vsub2S_reg_avx(vecS dst, vecS src1, vecS src2) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 2); match(Set dst (SubVS src1 src2)); format %{ "vpsubw $dst,$src1,$src2\t! sub packed2S" %} ins_encode %{ @@ -6425,8 +7078,31 @@ instruct vsub2S_reg(vecS dst, vecS src1, vecS src2) %{ ins_pipe( pipe_slow ); %} -instruct vsub2S_mem(vecS dst, vecS src, memory mem) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 2); +instruct vsub2S_reg_evex(vecS dst, vecS src1, vecS src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 2); + match(Set dst (SubVS src1 src2)); + format %{ "vpsubw $dst,$src1,$src2\t! sub packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsubw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub2S_reg_evex_special(vecS dst, vecS src1, vecS src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 2); + match(Set dst (SubVS dst src2)); + effect(TEMP src1); + format %{ "vpsubw $dst,$src1,$src2\t! sub packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsubw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub2S_mem_avx(vecS dst, vecS src, memory mem) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 2); match(Set dst (SubVS src (LoadVector mem))); format %{ "vpsubw $dst,$src,$mem\t! sub packed2S" %} ins_encode %{ @@ -6436,8 +7112,31 @@ instruct vsub2S_mem(vecS dst, vecS src, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vsub2S_mem_evex(vecS dst, vecS src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 2); + match(Set dst (SubVS src (LoadVector mem))); + format %{ "vpsubw $dst,$src,$mem\t! sub packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsubw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub2S_mem_evex_special(vecS dst, vecS src, memory mem) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 2); + match(Set dst (SubVS dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpsubw $dst,$src,$mem\t! sub packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsubw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + instruct vsub4S(vecD dst, vecD src) %{ - predicate(n->as_Vector()->length() == 4); + predicate(UseAVX == 0 && n->as_Vector()->length() == 4); match(Set dst (SubVS dst src)); format %{ "psubw $dst,$src\t! sub packed4S" %} ins_encode %{ @@ -6446,8 +7145,8 @@ instruct vsub4S(vecD dst, vecD src) %{ ins_pipe( pipe_slow ); %} -instruct vsub4S_reg(vecD dst, vecD src1, vecD src2) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vsub4S_reg_avx(vecD dst, vecD src1, vecD src2) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); match(Set dst (SubVS src1 src2)); format %{ "vpsubw $dst,$src1,$src2\t! sub packed4S" %} ins_encode %{ @@ -6457,8 +7156,31 @@ instruct vsub4S_reg(vecD dst, vecD src1, vecD src2) %{ ins_pipe( pipe_slow ); %} -instruct vsub4S_mem(vecD dst, vecD src, memory mem) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vsub4S_reg_evex(vecD dst, vecD src1, vecD src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); + match(Set dst (SubVS src1 src2)); + format %{ "vpsubw $dst,$src1,$src2\t! sub packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsubw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub4S_reg_evex_special(vecD dst, vecD src1, vecD src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 4); + match(Set dst (SubVS dst src2)); + effect(TEMP src1); + format %{ "vpsubw $dst,$src1,$src2\t! sub packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsubw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub4S_mem_avx(vecD dst, vecD src, memory mem) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); match(Set dst (SubVS src (LoadVector mem))); format %{ "vpsubw $dst,$src,$mem\t! sub packed4S" %} ins_encode %{ @@ -6468,8 +7190,31 @@ instruct vsub4S_mem(vecD dst, vecD src, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vsub4S_mem_evex(vecD dst, vecD src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); + match(Set dst (SubVS src (LoadVector mem))); + format %{ "vpsubw $dst,$src,$mem\t! sub packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsubw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub4S_mem_evex_special(vecD dst, vecD src, memory mem) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 4); + match(Set dst (SubVS dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpsubw $dst,$src,$mem\t! sub packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsubw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + instruct vsub8S(vecX dst, vecX src) %{ - predicate(n->as_Vector()->length() == 8); + predicate(UseAVX == 0 && n->as_Vector()->length() == 8); match(Set dst (SubVS dst src)); format %{ "psubw $dst,$src\t! sub packed8S" %} ins_encode %{ @@ -6478,8 +7223,8 @@ instruct vsub8S(vecX dst, vecX src) %{ ins_pipe( pipe_slow ); %} -instruct vsub8S_reg(vecX dst, vecX src1, vecX src2) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vsub8S_reg_avx(vecX dst, vecX src1, vecX src2) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (SubVS src1 src2)); format %{ "vpsubw $dst,$src1,$src2\t! sub packed8S" %} ins_encode %{ @@ -6489,8 +7234,31 @@ instruct vsub8S_reg(vecX dst, vecX src1, vecX src2) %{ ins_pipe( pipe_slow ); %} -instruct vsub8S_mem(vecX dst, vecX src, memory mem) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vsub8S_reg_evex(vecX dst, vecX src1, vecX src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (SubVS src1 src2)); + format %{ "vpsubw $dst,$src1,$src2\t! sub packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsubw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub8S_reg_evex_special(vecX dst, vecX src1, vecX src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 8); + match(Set dst (SubVS dst src2)); + effect(TEMP src1); + format %{ "vpsubw $dst,$src1,$src2\t! sub packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsubw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub8S_mem_avx(vecX dst, vecX src, memory mem) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (SubVS src (LoadVector mem))); format %{ "vpsubw $dst,$src,$mem\t! sub packed8S" %} ins_encode %{ @@ -6500,8 +7268,31 @@ instruct vsub8S_mem(vecX dst, vecX src, memory mem) %{ ins_pipe( pipe_slow ); %} -instruct vsub16S_reg(vecY dst, vecY src1, vecY src2) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 16); +instruct vsub8S_mem_evex(vecX dst, vecX src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (SubVS src (LoadVector mem))); + format %{ "vpsubw $dst,$src,$mem\t! sub packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsubw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub8S_mem_evex_special(vecX dst, vecX src, memory mem) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 8); + match(Set dst (SubVS dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpsubw $dst,$src,$mem\t! sub packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsubw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub16S_reg_avx(vecY dst, vecY src1, vecY src2) %{ + predicate(VM_Version::supports_avx256only() && n->as_Vector()->length() == 16); match(Set dst (SubVS src1 src2)); format %{ "vpsubw $dst,$src1,$src2\t! sub packed16S" %} ins_encode %{ @@ -6511,8 +7302,31 @@ instruct vsub16S_reg(vecY dst, vecY src1, vecY src2) %{ ins_pipe( pipe_slow ); %} -instruct vsub16S_mem(vecY dst, vecY src, memory mem) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 16); +instruct vsub16S_reg_evex(vecY dst, vecY src1, vecY src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); + match(Set dst (SubVS src1 src2)); + format %{ "vpsubw $dst,$src1,$src2\t! sub packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsubw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub16S_reg_evex_special(vecY dst, vecY src1, vecY src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 16); + match(Set dst (SubVS dst src2)); + effect(TEMP src1); + format %{ "vpsubw $dst,$src1,$src2\t! sub packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsubw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub16S_mem_avx(vecY dst, vecY src, memory mem) %{ + predicate(VM_Version::supports_avx256only() && n->as_Vector()->length() == 16); match(Set dst (SubVS src (LoadVector mem))); format %{ "vpsubw $dst,$src,$mem\t! sub packed16S" %} ins_encode %{ @@ -6522,8 +7336,31 @@ instruct vsub16S_mem(vecY dst, vecY src, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vsub16S_mem_evex(vecY dst, vecY src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); + match(Set dst (SubVS src (LoadVector mem))); + format %{ "vpsubw $dst,$src,$mem\t! sub packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsubw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsub16S_mem_evex_special(vecY dst, vecY src, memory mem) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 16); + match(Set dst (SubVS dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpsubw $dst,$src,$mem\t! sub packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsubw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + instruct vsub32S_reg(vecZ dst, vecZ src1, vecZ src2) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 32); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); match(Set dst (SubVS src1 src2)); format %{ "vpsubw $dst,$src1,$src2\t! sub packed32S" %} ins_encode %{ @@ -6534,7 +7371,7 @@ instruct vsub32S_reg(vecZ dst, vecZ src1, vecZ src2) %{ %} instruct vsub32S_mem(vecZ dst, vecZ src, memory mem) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 32); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); match(Set dst (SubVS src (LoadVector mem))); format %{ "vpsubw $dst,$src,$mem\t! sub packed32S" %} ins_encode %{ @@ -6920,7 +7757,7 @@ instruct vsub8D_mem(vecZ dst, vecZ src, memory mem) %{ // Shorts/Chars vector mul instruct vmul2S(vecS dst, vecS src) %{ - predicate(n->as_Vector()->length() == 2); + predicate(UseAVX == 0 && n->as_Vector()->length() == 2); match(Set dst (MulVS dst src)); format %{ "pmullw $dst,$src\t! mul packed2S" %} ins_encode %{ @@ -6929,8 +7766,8 @@ instruct vmul2S(vecS dst, vecS src) %{ ins_pipe( pipe_slow ); %} -instruct vmul2S_reg(vecS dst, vecS src1, vecS src2) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 2); +instruct vmul2S_reg_avx(vecS dst, vecS src1, vecS src2) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 2); match(Set dst (MulVS src1 src2)); format %{ "vpmullw $dst,$src1,$src2\t! mul packed2S" %} ins_encode %{ @@ -6940,8 +7777,31 @@ instruct vmul2S_reg(vecS dst, vecS src1, vecS src2) %{ ins_pipe( pipe_slow ); %} -instruct vmul2S_mem(vecS dst, vecS src, memory mem) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 2); +instruct vmul2S_reg_evex(vecS dst, vecS src1, vecS src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 2); + match(Set dst (MulVS src1 src2)); + format %{ "vpmullw $dst,$src1,$src2\t! mul packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpmullw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vmul2S_evex_special(vecS dst, vecS src1, vecS src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 2); + match(Set dst (MulVS dst src2)); + effect(TEMP src1); + format %{ "vpmullw $dst,$src1,$src2\t! mul packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpmullw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vmul2S_mem_avx(vecS dst, vecS src, memory mem) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 2); match(Set dst (MulVS src (LoadVector mem))); format %{ "vpmullw $dst,$src,$mem\t! mul packed2S" %} ins_encode %{ @@ -6951,8 +7811,31 @@ instruct vmul2S_mem(vecS dst, vecS src, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vmul2S_mem_evex(vecS dst, vecS src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 2); + match(Set dst (MulVS src (LoadVector mem))); + format %{ "vpmullw $dst,$src,$mem\t! mul packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpmullw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vmul2S_mem_evex_special(vecS dst, vecS src, memory mem) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 2); + match(Set dst (MulVS dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpmullw $dst,$src,$mem\t! mul packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpmullw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + instruct vmul4S(vecD dst, vecD src) %{ - predicate(n->as_Vector()->length() == 4); + predicate(UseAVX == 0 && n->as_Vector()->length() == 4); match(Set dst (MulVS dst src)); format %{ "pmullw $dst,$src\t! mul packed4S" %} ins_encode %{ @@ -6961,8 +7844,8 @@ instruct vmul4S(vecD dst, vecD src) %{ ins_pipe( pipe_slow ); %} -instruct vmul4S_reg(vecD dst, vecD src1, vecD src2) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vmul4S_reg_avx(vecD dst, vecD src1, vecD src2) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); match(Set dst (MulVS src1 src2)); format %{ "vpmullw $dst,$src1,$src2\t! mul packed4S" %} ins_encode %{ @@ -6972,8 +7855,31 @@ instruct vmul4S_reg(vecD dst, vecD src1, vecD src2) %{ ins_pipe( pipe_slow ); %} -instruct vmul4S_mem(vecD dst, vecD src, memory mem) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vmul4S_reg_evex(vecD dst, vecD src1, vecD src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); + match(Set dst (MulVS src1 src2)); + format %{ "vpmullw $dst,$src1,$src2\t! mul packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpmullw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vmul4S_reg_evex_special(vecD dst, vecD src1, vecD src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 4); + match(Set dst (MulVS dst src2)); + effect(TEMP src1); + format %{ "vpmullw $dst,$src1,$src2\t! mul packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpmullw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vmul4S_mem_avx(vecD dst, vecD src, memory mem) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); match(Set dst (MulVS src (LoadVector mem))); format %{ "vpmullw $dst,$src,$mem\t! mul packed4S" %} ins_encode %{ @@ -6983,8 +7889,31 @@ instruct vmul4S_mem(vecD dst, vecD src, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vmul4S_mem_evex(vecD dst, vecD src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); + match(Set dst (MulVS src (LoadVector mem))); + format %{ "vpmullw $dst,$src,$mem\t! mul packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpmullw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vmul4S_mem_evex_special(vecD dst, vecD src, memory mem) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 4); + match(Set dst (MulVS dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpmullw $dst,$src,$mem\t! mul packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpmullw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + instruct vmul8S(vecX dst, vecX src) %{ - predicate(n->as_Vector()->length() == 8); + predicate(UseAVX == 0 && n->as_Vector()->length() == 8); match(Set dst (MulVS dst src)); format %{ "pmullw $dst,$src\t! mul packed8S" %} ins_encode %{ @@ -6993,8 +7922,8 @@ instruct vmul8S(vecX dst, vecX src) %{ ins_pipe( pipe_slow ); %} -instruct vmul8S_reg(vecX dst, vecX src1, vecX src2) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vmul8S_reg_avx(vecX dst, vecX src1, vecX src2) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (MulVS src1 src2)); format %{ "vpmullw $dst,$src1,$src2\t! mul packed8S" %} ins_encode %{ @@ -7004,8 +7933,31 @@ instruct vmul8S_reg(vecX dst, vecX src1, vecX src2) %{ ins_pipe( pipe_slow ); %} -instruct vmul8S_mem(vecX dst, vecX src, memory mem) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vmul8S_reg_evex(vecX dst, vecX src1, vecX src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (MulVS src1 src2)); + format %{ "vpmullw $dst,$src1,$src2\t! mul packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpmullw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vmul8S_reg_evex_special(vecX dst, vecX src1, vecX src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 8); + match(Set dst (MulVS dst src2)); + effect(TEMP src1); + format %{ "vpmullw $dst,$src1,$src2\t! mul packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpmullw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vmul8S_mem_avx(vecX dst, vecX src, memory mem) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (MulVS src (LoadVector mem))); format %{ "vpmullw $dst,$src,$mem\t! mul packed8S" %} ins_encode %{ @@ -7015,8 +7967,31 @@ instruct vmul8S_mem(vecX dst, vecX src, memory mem) %{ ins_pipe( pipe_slow ); %} -instruct vmul16S_reg(vecY dst, vecY src1, vecY src2) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 16); +instruct vmul8S_mem_evex(vecX dst, vecX src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (MulVS src (LoadVector mem))); + format %{ "vpmullw $dst,$src,$mem\t! mul packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpmullw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vmul8S_mem_evex_special(vecX dst, vecX src, memory mem) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 8); + match(Set dst (MulVS dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpmullw $dst,$src,$mem\t! mul packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpmullw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vmul16S_reg_avx(vecY dst, vecY src1, vecY src2) %{ + predicate(VM_Version::supports_avx256only() && n->as_Vector()->length() == 16); match(Set dst (MulVS src1 src2)); format %{ "vpmullw $dst,$src1,$src2\t! mul packed16S" %} ins_encode %{ @@ -7026,8 +8001,31 @@ instruct vmul16S_reg(vecY dst, vecY src1, vecY src2) %{ ins_pipe( pipe_slow ); %} -instruct vmul16S_mem(vecY dst, vecY src, memory mem) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 16); +instruct vmul16S_reg_evex(vecY dst, vecY src1, vecY src2) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); + match(Set dst (MulVS src1 src2)); + format %{ "vpmullw $dst,$src1,$src2\t! mul packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpmullw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vmul16S_reg_evex_special(vecY dst, vecY src1, vecY src2) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 16); + match(Set dst (MulVS dst src2)); + effect(TEMP src1); + format %{ "vpmullw $dst,$src1,$src2\t! mul packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpmullw($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vmul16S_mem_avx(vecY dst, vecY src, memory mem) %{ + predicate(VM_Version::supports_avx256only() && n->as_Vector()->length() == 16); match(Set dst (MulVS src (LoadVector mem))); format %{ "vpmullw $dst,$src,$mem\t! mul packed16S" %} ins_encode %{ @@ -7037,8 +8035,31 @@ instruct vmul16S_mem(vecY dst, vecY src, memory mem) %{ ins_pipe( pipe_slow ); %} +instruct vmul16S_mem_evex(vecY dst, vecY src, memory mem) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); + match(Set dst (MulVS src (LoadVector mem))); + format %{ "vpmullw $dst,$src,$mem\t! mul packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpmullw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vmul16S_mem_evex_special(vecY dst, vecY src, memory mem) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 16); + match(Set dst (MulVS dst (LoadVector mem))); + effect(TEMP src); + format %{ "vpmullw $dst,$src,$mem\t! mul packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpmullw($dst$$XMMRegister, $src$$XMMRegister, $mem$$Address, vector_len); + %} + ins_pipe( pipe_slow ); +%} + instruct vmul32S_reg(vecZ dst, vecZ src1, vecZ src2) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 32); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); match(Set dst (MulVS src1 src2)); format %{ "vpmullw $dst,$src1,$src2\t! mul packed32S" %} ins_encode %{ @@ -7049,7 +8070,7 @@ instruct vmul32S_reg(vecZ dst, vecZ src1, vecZ src2) %{ %} instruct vmul32S_mem(vecZ dst, vecZ src, memory mem) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 32); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); match(Set dst (MulVS src (LoadVector mem))); format %{ "vpmullw $dst,$src,$mem\t! mul packed32S" %} ins_encode %{ @@ -7711,7 +8732,7 @@ instruct vsqrt8D_mem(vecZ dst, memory mem) %{ // Shorts/Chars vector left shift instruct vsll2S(vecS dst, vecS shift) %{ - predicate(n->as_Vector()->length() == 2); + predicate(UseAVX == 0 && n->as_Vector()->length() == 2); match(Set dst (LShiftVS dst shift)); format %{ "psllw $dst,$shift\t! left shift packed2S" %} ins_encode %{ @@ -7721,7 +8742,7 @@ instruct vsll2S(vecS dst, vecS shift) %{ %} instruct vsll2S_imm(vecS dst, immI8 shift) %{ - predicate(n->as_Vector()->length() == 2); + predicate(UseAVX == 0 && n->as_Vector()->length() == 2); match(Set dst (LShiftVS dst shift)); format %{ "psllw $dst,$shift\t! left shift packed2S" %} ins_encode %{ @@ -7730,8 +8751,8 @@ instruct vsll2S_imm(vecS dst, immI8 shift) %{ ins_pipe( pipe_slow ); %} -instruct vsll2S_reg(vecS dst, vecS src, vecS shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 2); +instruct vsll2S_reg_avx(vecS dst, vecS src, vecS shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 2); match(Set dst (LShiftVS src shift)); format %{ "vpsllw $dst,$src,$shift\t! left shift packed2S" %} ins_encode %{ @@ -7741,10 +8762,56 @@ instruct vsll2S_reg(vecS dst, vecS src, vecS shift) %{ ins_pipe( pipe_slow ); %} -instruct vsll2S_reg_imm(vecS dst, vecS src, immI8 shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 2); +instruct vsll2S_reg_evex(vecS dst, vecS src, vecS shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 2); match(Set dst (LShiftVS src shift)); format %{ "vpsllw $dst,$src,$shift\t! left shift packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll2S_reg_evex_special(vecS dst, vecS src, vecS shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 2); + match(Set dst (LShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll2S_reg_imm_avx(vecS dst, vecS src, immI8 shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 2); + match(Set dst (LShiftVS src shift)); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll2S_reg_imm_evex(vecS dst, vecS src, immI8 shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 2); + match(Set dst (LShiftVS src shift)); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll2S_reg_imm_evex_special(vecS dst, vecS src, immI8 shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 2); + match(Set dst (LShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed2S" %} ins_encode %{ int vector_len = 0; __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); @@ -7753,7 +8820,7 @@ instruct vsll2S_reg_imm(vecS dst, vecS src, immI8 shift) %{ %} instruct vsll4S(vecD dst, vecS shift) %{ - predicate(n->as_Vector()->length() == 4); + predicate(UseAVX == 0 && n->as_Vector()->length() == 4); match(Set dst (LShiftVS dst shift)); format %{ "psllw $dst,$shift\t! left shift packed4S" %} ins_encode %{ @@ -7763,7 +8830,7 @@ instruct vsll4S(vecD dst, vecS shift) %{ %} instruct vsll4S_imm(vecD dst, immI8 shift) %{ - predicate(n->as_Vector()->length() == 4); + predicate(UseAVX == 0 && n->as_Vector()->length() == 4); match(Set dst (LShiftVS dst shift)); format %{ "psllw $dst,$shift\t! left shift packed4S" %} ins_encode %{ @@ -7772,8 +8839,8 @@ instruct vsll4S_imm(vecD dst, immI8 shift) %{ ins_pipe( pipe_slow ); %} -instruct vsll4S_reg(vecD dst, vecD src, vecS shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vsll4S_reg_avx(vecD dst, vecD src, vecS shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); match(Set dst (LShiftVS src shift)); format %{ "vpsllw $dst,$src,$shift\t! left shift packed4S" %} ins_encode %{ @@ -7783,10 +8850,56 @@ instruct vsll4S_reg(vecD dst, vecD src, vecS shift) %{ ins_pipe( pipe_slow ); %} -instruct vsll4S_reg_imm(vecD dst, vecD src, immI8 shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vsll4S_reg_evex(vecD dst, vecD src, vecS shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); match(Set dst (LShiftVS src shift)); format %{ "vpsllw $dst,$src,$shift\t! left shift packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll4S_reg_evex_special(vecD dst, vecD src, vecS shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 4); + match(Set dst (LShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll4S_reg_imm_avx(vecD dst, vecD src, immI8 shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); + match(Set dst (LShiftVS src shift)); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll4S_reg_imm_evex(vecD dst, vecD src, immI8 shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); + match(Set dst (LShiftVS src shift)); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll4S_reg_imm_evex_special(vecD dst, vecD src, immI8 shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 4); + match(Set dst (LShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed4S" %} ins_encode %{ int vector_len = 0; __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); @@ -7795,7 +8908,7 @@ instruct vsll4S_reg_imm(vecD dst, vecD src, immI8 shift) %{ %} instruct vsll8S(vecX dst, vecS shift) %{ - predicate(n->as_Vector()->length() == 8); + predicate(UseAVX == 0 && n->as_Vector()->length() == 8); match(Set dst (LShiftVS dst shift)); format %{ "psllw $dst,$shift\t! left shift packed8S" %} ins_encode %{ @@ -7805,7 +8918,7 @@ instruct vsll8S(vecX dst, vecS shift) %{ %} instruct vsll8S_imm(vecX dst, immI8 shift) %{ - predicate(n->as_Vector()->length() == 8); + predicate(UseAVX == 0 && n->as_Vector()->length() == 8); match(Set dst (LShiftVS dst shift)); format %{ "psllw $dst,$shift\t! left shift packed8S" %} ins_encode %{ @@ -7814,8 +8927,8 @@ instruct vsll8S_imm(vecX dst, immI8 shift) %{ ins_pipe( pipe_slow ); %} -instruct vsll8S_reg(vecX dst, vecX src, vecS shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vsll8S_reg_avx(vecX dst, vecX src, vecS shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (LShiftVS src shift)); format %{ "vpsllw $dst,$src,$shift\t! left shift packed8S" %} ins_encode %{ @@ -7825,8 +8938,31 @@ instruct vsll8S_reg(vecX dst, vecX src, vecS shift) %{ ins_pipe( pipe_slow ); %} -instruct vsll8S_reg_imm(vecX dst, vecX src, immI8 shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vsll8S_reg_evex(vecX dst, vecX src, vecS shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (LShiftVS src shift)); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll8S_reg_evex_special(vecX dst, vecX src, vecS shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 8); + match(Set dst (LShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll8S_reg_imm_avx(vecX dst, vecX src, immI8 shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (LShiftVS src shift)); format %{ "vpsllw $dst,$src,$shift\t! left shift packed8S" %} ins_encode %{ @@ -7836,8 +8972,31 @@ instruct vsll8S_reg_imm(vecX dst, vecX src, immI8 shift) %{ ins_pipe( pipe_slow ); %} -instruct vsll16S_reg(vecY dst, vecY src, vecS shift) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 16); +instruct vsll8S_reg_imm_evex(vecX dst, vecX src, immI8 shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (LShiftVS src shift)); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll8S_reg_imm_evex_special(vecX dst, vecX src, immI8 shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 8); + match(Set dst (LShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll16S_reg_avx(vecY dst, vecY src, vecS shift) %{ + predicate(VM_Version::supports_avx256only() && n->as_Vector()->length() == 16); match(Set dst (LShiftVS src shift)); format %{ "vpsllw $dst,$src,$shift\t! left shift packed16S" %} ins_encode %{ @@ -7847,10 +9006,56 @@ instruct vsll16S_reg(vecY dst, vecY src, vecS shift) %{ ins_pipe( pipe_slow ); %} -instruct vsll16S_reg_imm(vecY dst, vecY src, immI8 shift) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 16); +instruct vsll16S_reg_evex(vecY dst, vecY src, vecS shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); match(Set dst (LShiftVS src shift)); format %{ "vpsllw $dst,$src,$shift\t! left shift packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll16S_reg_evex_special(vecY dst, vecY src, vecS shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 16); + match(Set dst (LShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll16S_reg_imm_avx(vecY dst, vecY src, immI8 shift) %{ + predicate(VM_Version::supports_avx256only() && n->as_Vector()->length() == 16); + match(Set dst (LShiftVS src shift)); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll16S_reg_imm_evex(vecY dst, vecY src, immI8 shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); + match(Set dst (LShiftVS src shift)); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsll16S_reg_imm_evex_special(vecY dst, vecY src, immI8 shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 16); + match(Set dst (LShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsllw $dst,$src,$shift\t! left shift packed16S" %} ins_encode %{ int vector_len = 1; __ vpsllw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); @@ -7859,7 +9064,7 @@ instruct vsll16S_reg_imm(vecY dst, vecY src, immI8 shift) %{ %} instruct vsll32S_reg(vecZ dst, vecZ src, vecS shift) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 32); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); match(Set dst (LShiftVS src shift)); format %{ "vpsllw $dst,$src,$shift\t! left shift packed32S" %} ins_encode %{ @@ -7870,7 +9075,7 @@ instruct vsll32S_reg(vecZ dst, vecZ src, vecS shift) %{ %} instruct vsll32S_reg_imm(vecZ dst, vecZ src, immI8 shift) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 32); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); match(Set dst (LShiftVS src shift)); format %{ "vpsllw $dst,$src,$shift\t! left shift packed32S" %} ins_encode %{ @@ -8104,7 +9309,7 @@ instruct vsll8L_reg_imm(vecZ dst, vecZ src, immI8 shift) %{ // unsigned values. instruct vsrl2S(vecS dst, vecS shift) %{ - predicate(n->as_Vector()->length() == 2); + predicate(UseAVX == 0 && n->as_Vector()->length() == 2); match(Set dst (URShiftVS dst shift)); format %{ "psrlw $dst,$shift\t! logical right shift packed2S" %} ins_encode %{ @@ -8114,7 +9319,7 @@ instruct vsrl2S(vecS dst, vecS shift) %{ %} instruct vsrl2S_imm(vecS dst, immI8 shift) %{ - predicate(n->as_Vector()->length() == 2); + predicate(UseAVX == 0 && n->as_Vector()->length() == 2); match(Set dst (URShiftVS dst shift)); format %{ "psrlw $dst,$shift\t! logical right shift packed2S" %} ins_encode %{ @@ -8123,8 +9328,8 @@ instruct vsrl2S_imm(vecS dst, immI8 shift) %{ ins_pipe( pipe_slow ); %} -instruct vsrl2S_reg(vecS dst, vecS src, vecS shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 2); +instruct vsrl2S_reg_avx(vecS dst, vecS src, vecS shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 2); match(Set dst (URShiftVS src shift)); format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed2S" %} ins_encode %{ @@ -8134,10 +9339,56 @@ instruct vsrl2S_reg(vecS dst, vecS src, vecS shift) %{ ins_pipe( pipe_slow ); %} -instruct vsrl2S_reg_imm(vecS dst, vecS src, immI8 shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 2); +instruct vsrl2S_reg_evex(vecS dst, vecS src, vecS shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 2); match(Set dst (URShiftVS src shift)); format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl2S_reg_evex_special(vecS dst, vecS src, vecS shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 2); + match(Set dst (URShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl2S_reg_imm_avx(vecS dst, vecS src, immI8 shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 2); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl2S_reg_imm_evex(vecS dst, vecS src, immI8 shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 2); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl2S_reg_imm_evex_special(vecS dst, vecS src, immI8 shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 2); + match(Set dst (URShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed2S" %} ins_encode %{ int vector_len = 0; __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); @@ -8146,7 +9397,7 @@ instruct vsrl2S_reg_imm(vecS dst, vecS src, immI8 shift) %{ %} instruct vsrl4S(vecD dst, vecS shift) %{ - predicate(n->as_Vector()->length() == 4); + predicate(UseAVX == 0 && n->as_Vector()->length() == 4); match(Set dst (URShiftVS dst shift)); format %{ "psrlw $dst,$shift\t! logical right shift packed4S" %} ins_encode %{ @@ -8156,7 +9407,7 @@ instruct vsrl4S(vecD dst, vecS shift) %{ %} instruct vsrl4S_imm(vecD dst, immI8 shift) %{ - predicate(n->as_Vector()->length() == 4); + predicate(UseAVX == 0 && n->as_Vector()->length() == 4); match(Set dst (URShiftVS dst shift)); format %{ "psrlw $dst,$shift\t! logical right shift packed4S" %} ins_encode %{ @@ -8165,8 +9416,8 @@ instruct vsrl4S_imm(vecD dst, immI8 shift) %{ ins_pipe( pipe_slow ); %} -instruct vsrl4S_reg(vecD dst, vecD src, vecS shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vsrl4S_reg_avx(vecD dst, vecD src, vecS shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); match(Set dst (URShiftVS src shift)); format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed4S" %} ins_encode %{ @@ -8176,10 +9427,56 @@ instruct vsrl4S_reg(vecD dst, vecD src, vecS shift) %{ ins_pipe( pipe_slow ); %} -instruct vsrl4S_reg_imm(vecD dst, vecD src, immI8 shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vsrl4S_reg_evex(vecD dst, vecD src, vecS shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); match(Set dst (URShiftVS src shift)); format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl4S_reg_evex_special(vecD dst, vecD src, vecS shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 4); + match(Set dst (URShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl4S_reg_imm_avx(vecD dst, vecD src, immI8 shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl4S_reg_imm_evex(vecD dst, vecD src, immI8 shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl4S_reg_imm_evex_special(vecD dst, vecD src, immI8 shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 4); + match(Set dst (URShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed4S" %} ins_encode %{ int vector_len = 0; __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); @@ -8188,7 +9485,7 @@ instruct vsrl4S_reg_imm(vecD dst, vecD src, immI8 shift) %{ %} instruct vsrl8S(vecX dst, vecS shift) %{ - predicate(n->as_Vector()->length() == 8); + predicate(UseAVX == 0 && n->as_Vector()->length() == 8); match(Set dst (URShiftVS dst shift)); format %{ "psrlw $dst,$shift\t! logical right shift packed8S" %} ins_encode %{ @@ -8198,7 +9495,7 @@ instruct vsrl8S(vecX dst, vecS shift) %{ %} instruct vsrl8S_imm(vecX dst, immI8 shift) %{ - predicate(n->as_Vector()->length() == 8); + predicate(UseAVX == 0 && n->as_Vector()->length() == 8); match(Set dst (URShiftVS dst shift)); format %{ "psrlw $dst,$shift\t! logical right shift packed8S" %} ins_encode %{ @@ -8207,8 +9504,8 @@ instruct vsrl8S_imm(vecX dst, immI8 shift) %{ ins_pipe( pipe_slow ); %} -instruct vsrl8S_reg(vecX dst, vecX src, vecS shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vsrl8S_reg_avx(vecX dst, vecX src, vecS shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (URShiftVS src shift)); format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed8S" %} ins_encode %{ @@ -8218,8 +9515,31 @@ instruct vsrl8S_reg(vecX dst, vecX src, vecS shift) %{ ins_pipe( pipe_slow ); %} -instruct vsrl8S_reg_imm(vecX dst, vecX src, immI8 shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vsrl8S_reg_evex(vecX dst, vecX src, vecS shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl8S_reg_evex_special(vecX dst, vecX src, vecS shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 8); + match(Set dst (URShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl8S_reg_imm_avx(vecX dst, vecX src, immI8 shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (URShiftVS src shift)); format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed8S" %} ins_encode %{ @@ -8229,8 +9549,31 @@ instruct vsrl8S_reg_imm(vecX dst, vecX src, immI8 shift) %{ ins_pipe( pipe_slow ); %} -instruct vsrl16S_reg(vecY dst, vecY src, vecS shift) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 16); +instruct vsrl8S_reg_imm_evex(vecX dst, vecX src, immI8 shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl8S_reg_imm_evex_special(vecX dst, vecX src, immI8 shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 8); + match(Set dst (URShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl16S_reg_avx(vecY dst, vecY src, vecS shift) %{ + predicate(VM_Version::supports_avx256only() && n->as_Vector()->length() == 16); match(Set dst (URShiftVS src shift)); format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed16S" %} ins_encode %{ @@ -8240,10 +9583,56 @@ instruct vsrl16S_reg(vecY dst, vecY src, vecS shift) %{ ins_pipe( pipe_slow ); %} -instruct vsrl16S_reg_imm(vecY dst, vecY src, immI8 shift) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 16); +instruct vsrl16S_reg_evex(vecY dst, vecY src, vecS shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); match(Set dst (URShiftVS src shift)); format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl16S_reg_evex_special(vecY dst, vecY src, vecS shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 16); + match(Set dst (URShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl16S_reg_imm_avx(vecY dst, vecY src, immI8 shift) %{ + predicate(VM_Version::supports_avx256only() && n->as_Vector()->length() == 16); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl16S_reg_imm_evex(vecY dst, vecY src, immI8 shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl16S_reg_imm_evex_special(vecY dst, vecY src, immI8 shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 16); + match(Set dst (URShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed16S" %} ins_encode %{ int vector_len = 1; __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); @@ -8252,7 +9641,7 @@ instruct vsrl16S_reg_imm(vecY dst, vecY src, immI8 shift) %{ %} instruct vsrl32S_reg(vecZ dst, vecZ src, vecS shift) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 32); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); match(Set dst (URShiftVS src shift)); format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed32S" %} ins_encode %{ @@ -8263,7 +9652,7 @@ instruct vsrl32S_reg(vecZ dst, vecZ src, vecS shift) %{ %} instruct vsrl32S_reg_imm(vecZ dst, vecZ src, immI8 shift) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 32); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); match(Set dst (URShiftVS src shift)); format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed32S" %} ins_encode %{ @@ -8493,7 +9882,7 @@ instruct vsrl8L_reg_imm(vecZ dst, vecZ src, immI8 shift) %{ // Shorts/Chars vector arithmetic right shift instruct vsra2S(vecS dst, vecS shift) %{ - predicate(n->as_Vector()->length() == 2); + predicate(UseAVX == 0 && n->as_Vector()->length() == 2); match(Set dst (RShiftVS dst shift)); format %{ "psraw $dst,$shift\t! arithmetic right shift packed2S" %} ins_encode %{ @@ -8512,8 +9901,8 @@ instruct vsra2S_imm(vecS dst, immI8 shift) %{ ins_pipe( pipe_slow ); %} -instruct vsra2S_reg(vecS dst, vecS src, vecS shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 2); +instruct vsra2S_reg_avx(vecS dst, vecS src, vecS shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 2); match(Set dst (RShiftVS src shift)); format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed2S" %} ins_encode %{ @@ -8523,10 +9912,56 @@ instruct vsra2S_reg(vecS dst, vecS src, vecS shift) %{ ins_pipe( pipe_slow ); %} -instruct vsra2S_reg_imm(vecS dst, vecS src, immI8 shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 2); +instruct vsra2S_reg_evex(vecS dst, vecS src, vecS shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 2); match(Set dst (RShiftVS src shift)); format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra2S_reg_evex_special(vecS dst, vecS src, vecS shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 2); + match(Set dst (RShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra2S_reg_imm_avx(vecS dst, vecS src, immI8 shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 2); + match(Set dst (RShiftVS src shift)); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra2S_reg_imm_evex(vecS dst, vecS src, immI8 shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 2); + match(Set dst (RShiftVS src shift)); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed2S" %} + ins_encode %{ + int vector_len = 0; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra2S_reg_imm_evex_special(vecS dst, vecS src, immI8 shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 2); + match(Set dst (RShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed2S" %} ins_encode %{ int vector_len = 0; __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); @@ -8535,7 +9970,7 @@ instruct vsra2S_reg_imm(vecS dst, vecS src, immI8 shift) %{ %} instruct vsra4S(vecD dst, vecS shift) %{ - predicate(n->as_Vector()->length() == 4); + predicate(UseAVX == 0 && n->as_Vector()->length() == 4); match(Set dst (RShiftVS dst shift)); format %{ "psraw $dst,$shift\t! arithmetic right shift packed4S" %} ins_encode %{ @@ -8545,7 +9980,7 @@ instruct vsra4S(vecD dst, vecS shift) %{ %} instruct vsra4S_imm(vecD dst, immI8 shift) %{ - predicate(n->as_Vector()->length() == 4); + predicate(UseAVX == 0 && n->as_Vector()->length() == 4); match(Set dst (RShiftVS dst shift)); format %{ "psraw $dst,$shift\t! arithmetic right shift packed4S" %} ins_encode %{ @@ -8554,8 +9989,8 @@ instruct vsra4S_imm(vecD dst, immI8 shift) %{ ins_pipe( pipe_slow ); %} -instruct vsra4S_reg(vecD dst, vecD src, vecS shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vsra4S_reg_avx(vecD dst, vecD src, vecS shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); match(Set dst (RShiftVS src shift)); format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed4S" %} ins_encode %{ @@ -8565,10 +10000,56 @@ instruct vsra4S_reg(vecD dst, vecD src, vecS shift) %{ ins_pipe( pipe_slow ); %} -instruct vsra4S_reg_imm(vecD dst, vecD src, immI8 shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 4); +instruct vsra4S_reg_evex(vecD dst, vecD src, vecS shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); match(Set dst (RShiftVS src shift)); format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra4S_reg_evex_special(vecD dst, vecD src, vecS shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 4); + match(Set dst (RShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra4S_reg_imm_avx(vecD dst, vecD src, immI8 shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 4); + match(Set dst (RShiftVS src shift)); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra4S_reg_imm_evex(vecD dst, vecD src, immI8 shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 4); + match(Set dst (RShiftVS src shift)); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed4S" %} + ins_encode %{ + int vector_len = 0; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra4S_reg_imm_evex_special(vecD dst, vecD src, immI8 shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 4); + match(Set dst (RShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed4S" %} ins_encode %{ int vector_len = 0; __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); @@ -8577,7 +10058,7 @@ instruct vsra4S_reg_imm(vecD dst, vecD src, immI8 shift) %{ %} instruct vsra8S(vecX dst, vecS shift) %{ - predicate(n->as_Vector()->length() == 8); + predicate(UseAVX == 0 && n->as_Vector()->length() == 8); match(Set dst (RShiftVS dst shift)); format %{ "psraw $dst,$shift\t! arithmetic right shift packed8S" %} ins_encode %{ @@ -8587,7 +10068,7 @@ instruct vsra8S(vecX dst, vecS shift) %{ %} instruct vsra8S_imm(vecX dst, immI8 shift) %{ - predicate(n->as_Vector()->length() == 8); + predicate(UseAVX == 0 && n->as_Vector()->length() == 8); match(Set dst (RShiftVS dst shift)); format %{ "psraw $dst,$shift\t! arithmetic right shift packed8S" %} ins_encode %{ @@ -8596,8 +10077,8 @@ instruct vsra8S_imm(vecX dst, immI8 shift) %{ ins_pipe( pipe_slow ); %} -instruct vsra8S_reg(vecX dst, vecX src, vecS shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vsra8S_reg_avx(vecX dst, vecX src, vecS shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (RShiftVS src shift)); format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed8S" %} ins_encode %{ @@ -8607,8 +10088,31 @@ instruct vsra8S_reg(vecX dst, vecX src, vecS shift) %{ ins_pipe( pipe_slow ); %} -instruct vsra8S_reg_imm(vecX dst, vecX src, immI8 shift) %{ - predicate(UseAVX > 0 && n->as_Vector()->length() == 8); +instruct vsra8S_reg_evex(vecX dst, vecX src, vecS shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (RShiftVS src shift)); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra8S_reg_evex_special(vecX dst, vecX src, vecS shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 8); + match(Set dst (RShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra8S_reg_imm_avx(vecX dst, vecX src, immI8 shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 8); match(Set dst (RShiftVS src shift)); format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed8S" %} ins_encode %{ @@ -8618,8 +10122,31 @@ instruct vsra8S_reg_imm(vecX dst, vecX src, immI8 shift) %{ ins_pipe( pipe_slow ); %} -instruct vsra16S_reg(vecY dst, vecY src, vecS shift) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 16); +instruct vsra8S_reg_imm_evex(vecX dst, vecX src, immI8 shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 8); + match(Set dst (RShiftVS src shift)); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra8S_reg_imm_evex_special(vecX dst, vecX src, immI8 shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 8); + match(Set dst (RShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed8S" %} + ins_encode %{ + int vector_len = 0; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra16S_reg_avx(vecY dst, vecY src, vecS shift) %{ + predicate(VM_Version::supports_avx256only() && n->as_Vector()->length() == 16); match(Set dst (RShiftVS src shift)); format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed16S" %} ins_encode %{ @@ -8629,10 +10156,56 @@ instruct vsra16S_reg(vecY dst, vecY src, vecS shift) %{ ins_pipe( pipe_slow ); %} -instruct vsra16S_reg_imm(vecY dst, vecY src, immI8 shift) %{ - predicate(UseAVX > 1 && n->as_Vector()->length() == 16); +instruct vsra16S_reg_evex(vecY dst, vecY src, vecS shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); match(Set dst (RShiftVS src shift)); format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra16S_reg_evex_special(vecY dst, vecY src, vecS shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 16); + match(Set dst (RShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra16S_reg_imm_avx(vecY dst, vecY src, immI8 shift) %{ + predicate(VM_Version::supports_avxonly() && n->as_Vector()->length() == 16); + match(Set dst (RShiftVS src shift)); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra16S_reg_imm_evex(vecY dst, vecY src, immI8 shift) %{ + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 16); + match(Set dst (RShiftVS src shift)); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed16S" %} + ins_encode %{ + int vector_len = 1; + __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsra16S_reg_imm_evex_special(vecY dst, vecY src, immI8 shift) %{ + predicate(VM_Version::supports_avx512nobw() && n->as_Vector()->length() == 16); + match(Set dst (RShiftVS dst shift)); + effect(TEMP src); + format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed16S" %} ins_encode %{ int vector_len = 1; __ vpsraw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector_len); @@ -8641,7 +10214,7 @@ instruct vsra16S_reg_imm(vecY dst, vecY src, immI8 shift) %{ %} instruct vsra32S_reg(vecZ dst, vecZ src, vecS shift) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 32); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); match(Set dst (RShiftVS src shift)); format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed32S" %} ins_encode %{ @@ -8652,7 +10225,7 @@ instruct vsra32S_reg(vecZ dst, vecZ src, vecS shift) %{ %} instruct vsra32S_reg_imm(vecZ dst, vecZ src, immI8 shift) %{ - predicate(UseAVX > 2 && n->as_Vector()->length() == 32); + predicate(VM_Version::supports_avx512bw() && n->as_Vector()->length() == 32); match(Set dst (RShiftVS src shift)); format %{ "vpsraw $dst,$src,$shift\t! arithmetic right shift packed32S" %} ins_encode %{ diff --git a/hotspot/src/cpu/x86/vm/x86_32.ad b/hotspot/src/cpu/x86/vm/x86_32.ad index 36c2b6fba90..1f38927626c 100644 --- a/hotspot/src/cpu/x86/vm/x86_32.ad +++ b/hotspot/src/cpu/x86/vm/x86_32.ad @@ -291,9 +291,7 @@ static int pre_call_resets_size() { size += 6; // fldcw } if (C->max_vector_size() > 16) { - if(UseAVX <= 2) { - size += 3; // vzeroupper - } + size += 3; // vzeroupper } return size; } @@ -1915,7 +1913,7 @@ encode %{ if (stub == NULL) { ciEnv::current()->record_failure("CodeCache is full"); return; - } + } } %} diff --git a/hotspot/src/cpu/x86/vm/x86_64.ad b/hotspot/src/cpu/x86/vm/x86_64.ad index d63e5b68b20..9b4f9f9ebce 100644 --- a/hotspot/src/cpu/x86/vm/x86_64.ad +++ b/hotspot/src/cpu/x86/vm/x86_64.ad @@ -536,11 +536,7 @@ source %{ #define __ _masm. static int clear_avx_size() { - if(UseAVX > 2) { - return 0; // vzeroupper is ignored - } else { - return (Compile::current()->max_vector_size() > 16) ? 3 : 0; // vzeroupper - } + return (Compile::current()->max_vector_size() > 16) ? 3 : 0; // vzeroupper } // !!!!! Special hack to get all types of calls to specify the byte offset @@ -871,7 +867,7 @@ void MachPrologNode::format(PhaseRegAlloc* ra_, outputStream* st) const { if (framesize > 0) { st->print("\n\t"); st->print("addq rbp, #%d", framesize); - } + } } } diff --git a/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp b/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp index 78dad699f97..02497845c28 100644 --- a/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp +++ b/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp @@ -497,12 +497,15 @@ int CppInterpreter::accessor_entry(Method* method, intptr_t UNUSED, TRAPS) { // 1: getfield // 2: index // 3: index - // 4: ireturn/areturn + // 4: ireturn/areturn/freturn/lreturn/dreturn // NB this is not raw bytecode: index is in machine order u1 *code = method->code_base(); assert(code[0] == Bytecodes::_aload_0 && code[1] == Bytecodes::_getfield && (code[4] == Bytecodes::_ireturn || + code[4] == Bytecodes::_freturn || + code[4] == Bytecodes::_lreturn || + code[4] == Bytecodes::_dreturn || code[4] == Bytecodes::_areturn), "should do"); u2 index = Bytes::get_native_u2(&code[2]); diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java index 536c93af36e..4f4e7e4e9b9 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java @@ -32,6 +32,7 @@ import java.lang.reflect.Method; import jdk.vm.ci.code.InstalledCode; import jdk.vm.ci.code.InvalidInstalledCodeException; import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.common.JVMCIError; import jdk.vm.ci.hotspotvmconfig.HotSpotVMField; import jdk.vm.ci.inittimer.InitTimer; import jdk.vm.ci.meta.JavaType; @@ -308,6 +309,8 @@ final class CompilerToVM { * {@link HotSpotVMConfig#codeInstallResultCodeTooLarge}, * {@link HotSpotVMConfig#codeInstallResultDependenciesFailed} or * {@link HotSpotVMConfig#codeInstallResultDependenciesInvalid}. + * @throws JVMCIError if there is something wrong with the compiled code or the associated + * metadata. */ native int installCode(TargetDescription target, HotSpotCompiledCode compiledCode, InstalledCode code, HotSpotSpeculationLog speculationLog); diff --git a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java index 7b924e25ed9..2709c0807ac 100644 --- a/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java +++ b/hotspot/src/jdk.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java @@ -1193,9 +1193,12 @@ public class HotSpotVMConfig { @HotSpotVMConstant(name = "frame::interpreter_frame_sender_sp_offset", archs = {"amd64"}) @Stable public int frameInterpreterFrameSenderSpOffset; @HotSpotVMConstant(name = "frame::interpreter_frame_last_sp_offset", archs = {"amd64"}) @Stable public int frameInterpreterFrameLastSpOffset; - @HotSpotVMField(name = "PtrQueue::_active", type = "bool", get = HotSpotVMField.Type.OFFSET) @Stable public int ptrQueueActiveOffset; - @HotSpotVMField(name = "PtrQueue::_buf", type = "void**", get = HotSpotVMField.Type.OFFSET) @Stable public int ptrQueueBufferOffset; - @HotSpotVMField(name = "PtrQueue::_index", type = "size_t", get = HotSpotVMField.Type.OFFSET) @Stable public int ptrQueueIndexOffset; + @HotSpotVMConstant(name = "dirtyCardQueueBufferOffset") @Stable private int dirtyCardQueueBufferOffset; + @HotSpotVMConstant(name = "dirtyCardQueueIndexOffset") @Stable private int dirtyCardQueueIndexOffset; + + @HotSpotVMConstant(name = "satbMarkQueueBufferOffset") @Stable private int satbMarkQueueBufferOffset; + @HotSpotVMConstant(name = "satbMarkQueueIndexOffset") @Stable private int satbMarkQueueIndexOffset; + @HotSpotVMConstant(name = "satbMarkQueueActiveOffset") @Stable private int satbMarkQueueActiveOffset; @HotSpotVMField(name = "OSThread::_interrupted", type = "jint", get = HotSpotVMField.Type.OFFSET) @Stable public int osThreadInterruptedOffset; @@ -1396,23 +1399,23 @@ public class HotSpotVMConfig { // G1 Collector Related Values. public int g1CardQueueIndexOffset() { - return javaThreadDirtyCardQueueOffset + ptrQueueIndexOffset; + return javaThreadDirtyCardQueueOffset + dirtyCardQueueIndexOffset; } public int g1CardQueueBufferOffset() { - return javaThreadDirtyCardQueueOffset + ptrQueueBufferOffset; + return javaThreadDirtyCardQueueOffset + dirtyCardQueueBufferOffset; } public int g1SATBQueueMarkingOffset() { - return javaThreadSatbMarkQueueOffset + ptrQueueActiveOffset; + return javaThreadSatbMarkQueueOffset + satbMarkQueueActiveOffset; } public int g1SATBQueueIndexOffset() { - return javaThreadSatbMarkQueueOffset + ptrQueueIndexOffset; + return javaThreadSatbMarkQueueOffset + satbMarkQueueIndexOffset; } public int g1SATBQueueBufferOffset() { - return javaThreadSatbMarkQueueOffset + ptrQueueBufferOffset; + return javaThreadSatbMarkQueueOffset + satbMarkQueueBufferOffset; } @HotSpotVMField(name = "java_lang_Class::_klass_offset", type = "int", get = HotSpotVMField.Type.VALUE) @Stable public int klassOffset; @@ -1677,6 +1680,7 @@ public class HotSpotVMConfig { @HotSpotVMField(name = "Deoptimization::UnrollBlock::_caller_adjustment", type = "int", get = HotSpotVMField.Type.OFFSET) @Stable public int deoptimizationUnrollBlockCallerAdjustmentOffset; @HotSpotVMField(name = "Deoptimization::UnrollBlock::_number_of_frames", type = "int", get = HotSpotVMField.Type.OFFSET) @Stable public int deoptimizationUnrollBlockNumberOfFramesOffset; @HotSpotVMField(name = "Deoptimization::UnrollBlock::_total_frame_sizes", type = "int", get = HotSpotVMField.Type.OFFSET) @Stable public int deoptimizationUnrollBlockTotalFrameSizesOffset; + @HotSpotVMField(name = "Deoptimization::UnrollBlock::_unpack_kind", type = "int", get = HotSpotVMField.Type.OFFSET) @Stable public int deoptimizationUnrollBlockUnpackKindOffset; @HotSpotVMField(name = "Deoptimization::UnrollBlock::_frame_sizes", type = "intptr_t*", get = HotSpotVMField.Type.OFFSET) @Stable public int deoptimizationUnrollBlockFrameSizesOffset; @HotSpotVMField(name = "Deoptimization::UnrollBlock::_frame_pcs", type = "address*", get = HotSpotVMField.Type.OFFSET) @Stable public int deoptimizationUnrollBlockFramePcsOffset; @HotSpotVMField(name = "Deoptimization::UnrollBlock::_initial_info", type = "intptr_t", get = HotSpotVMField.Type.OFFSET) @Stable public int deoptimizationUnrollBlockInitialInfoOffset; diff --git a/hotspot/src/os/aix/vm/jvm_aix.cpp b/hotspot/src/os/aix/vm/jvm_aix.cpp index 4e95697a241..7a9fb8969b8 100644 --- a/hotspot/src/os/aix/vm/jvm_aix.cpp +++ b/hotspot/src/os/aix/vm/jvm_aix.cpp @@ -109,92 +109,3 @@ JVM_ENTRY_NO_ENV(jboolean, JVM_RaiseSignal(jint sig)) return JNI_TRUE; JVM_END -/* - All the defined signal names for Linux. - - NOTE that not all of these names are accepted by our Java implementation - - Via an existing claim by the VM, sigaction restrictions, or - the "rules of Unix" some of these names will be rejected at runtime. - For example the VM sets up to handle USR1, sigaction returns EINVAL for - STOP, and Linux simply doesn't allow catching of KILL. - - Here are the names currently accepted by a user of sun.misc.Signal with - 1.4.1 (ignoring potential interaction with use of chaining, etc): - - HUP, INT, TRAP, ABRT, IOT, BUS, USR2, PIPE, ALRM, TERM, STKFLT, - CLD, CHLD, CONT, TSTP, TTIN, TTOU, URG, XCPU, XFSZ, VTALRM, PROF, - WINCH, POLL, IO, PWR, SYS - -*/ - -struct siglabel { - const char *name; - int number; -}; - -struct siglabel siglabels[] = { - /* derived from /usr/include/bits/signum.h on RH7.2 */ - "HUP", SIGHUP, /* Hangup (POSIX). */ - "INT", SIGINT, /* Interrupt (ANSI). */ - "QUIT", SIGQUIT, /* Quit (POSIX). */ - "ILL", SIGILL, /* Illegal instruction (ANSI). */ - "TRAP", SIGTRAP, /* Trace trap (POSIX). */ - "ABRT", SIGABRT, /* Abort (ANSI). */ - "IOT", SIGIOT, /* IOT trap (4.2 BSD). */ - "BUS", SIGBUS, /* BUS error (4.2 BSD). */ - "FPE", SIGFPE, /* Floating-point exception (ANSI). */ - "KILL", SIGKILL, /* Kill, unblockable (POSIX). */ - "USR1", SIGUSR1, /* User-defined signal 1 (POSIX). */ - "SEGV", SIGSEGV, /* Segmentation violation (ANSI). */ - "USR2", SIGUSR2, /* User-defined signal 2 (POSIX). */ - "PIPE", SIGPIPE, /* Broken pipe (POSIX). */ - "ALRM", SIGALRM, /* Alarm clock (POSIX). */ - "TERM", SIGTERM, /* Termination (ANSI). */ -#ifdef SIGSTKFLT - "STKFLT", SIGSTKFLT, /* Stack fault. */ -#endif - "CLD", SIGCLD, /* Same as SIGCHLD (System V). */ - "CHLD", SIGCHLD, /* Child status has changed (POSIX). */ - "CONT", SIGCONT, /* Continue (POSIX). */ - "STOP", SIGSTOP, /* Stop, unblockable (POSIX). */ - "TSTP", SIGTSTP, /* Keyboard stop (POSIX). */ - "TTIN", SIGTTIN, /* Background read from tty (POSIX). */ - "TTOU", SIGTTOU, /* Background write to tty (POSIX). */ - "URG", SIGURG, /* Urgent condition on socket (4.2 BSD). */ - "XCPU", SIGXCPU, /* CPU limit exceeded (4.2 BSD). */ - "XFSZ", SIGXFSZ, /* File size limit exceeded (4.2 BSD). */ - "DANGER", SIGDANGER, /* System crash imminent; free up some page space (AIX). */ - "VTALRM", SIGVTALRM, /* Virtual alarm clock (4.2 BSD). */ - "PROF", SIGPROF, /* Profiling alarm clock (4.2 BSD). */ - "WINCH", SIGWINCH, /* Window size change (4.3 BSD, Sun). */ - "POLL", SIGPOLL, /* Pollable event occurred (System V). */ - "IO", SIGIO, /* I/O now possible (4.2 BSD). */ - "PWR", SIGPWR, /* Power failure restart (System V). */ -#ifdef SIGSYS - "SYS", SIGSYS /* Bad system call. Only on some Linuxen! */ -#endif - }; - -JVM_ENTRY_NO_ENV(jint, JVM_FindSignal(const char *name)) - - /* find and return the named signal's number */ - - for(uint i=0; i -#include -#include -#include - -void VMError::show_message_box(char *buf, int buflen) { - bool yes; - do { - error_string(buf, buflen); - int len = (int)strlen(buf); - char *p = &buf[len]; - - jio_snprintf(p, buflen - len, - "\n\n" - "Do you want to debug the problem?\n\n" - "To debug, run 'dbx -a %d'; then switch to thread tid " INTX_FORMAT ", k-tid " INTX_FORMAT "\n" - "Enter 'yes' to launch dbx automatically (PATH must include dbx)\n" - "Otherwise, press RETURN to abort...", - os::current_process_id(), - os::current_thread_id(), thread_self()); - - yes = os::message_box("Unexpected Error", buf); - - if (yes) { - // yes, user asked VM to launch debugger - jio_snprintf(buf, buflen, "dbx -a %d", os::current_process_id()); - - os::fork_and_exec(buf); - yes = false; - } - } while (yes); -} - -// Handle all synchronous signals which may happen during signal handling, -// not just SIGSEGV and SIGBUS. -static const int SIGNALS[] = { SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP }; // add more if needed -static const int NUM_SIGNALS = sizeof(SIGNALS) / sizeof(int); - -// Space for our "saved" signal flags and handlers -static int resettedSigflags[NUM_SIGNALS]; -static address resettedSighandler[NUM_SIGNALS]; - -static void save_signal(int idx, int sig) { - struct sigaction sa; - sigaction(sig, NULL, &sa); - resettedSigflags[idx] = sa.sa_flags; - resettedSighandler[idx] = (sa.sa_flags & SA_SIGINFO) - ? CAST_FROM_FN_PTR(address, sa.sa_sigaction) - : CAST_FROM_FN_PTR(address, sa.sa_handler); -} - -int VMError::get_resetted_sigflags(int sig) { - for (int i = 0; i < NUM_SIGNALS; i++) { - if (SIGNALS[i] == sig) { - return resettedSigflags[i]; - } - } - return -1; -} - -address VMError::get_resetted_sighandler(int sig) { - for (int i = 0; i < NUM_SIGNALS; i++) { - if (SIGNALS[i] == sig) { - return resettedSighandler[i]; - } - } - return NULL; -} - -static void crash_handler(int sig, siginfo_t* info, void* ucVoid) { - - // Unmask current signal. - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - // and all other synchronous signals too. - for (int i = 0; i < NUM_SIGNALS; i++) { - sigaddset(&newset, SIGNALS[i]); - } - sigthreadmask(SIG_UNBLOCK, &newset, NULL); - - // support safefetch faults in error handling - ucontext_t* const uc = (ucontext_t*) ucVoid; - address const pc = uc ? os::Aix::ucontext_get_pc(uc) : NULL; - if (uc && pc && StubRoutines::is_safefetch_fault(pc)) { - os::Aix::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); - return; - } - - VMError::report_and_die(NULL, sig, pc, info, ucVoid); -} - -void VMError::reset_signal_handlers() { - sigset_t newset; - sigemptyset(&newset); - - for (int i = 0; i < NUM_SIGNALS; i++) { - save_signal(i, SIGNALS[i]); - os::signal(SIGNALS[i], CAST_FROM_FN_PTR(void *, crash_handler)); - sigaddset(&newset, SIGNALS[i]); - } - - sigthreadmask(SIG_UNBLOCK, &newset, NULL); -} diff --git a/hotspot/src/os/bsd/vm/jvm_bsd.cpp b/hotspot/src/os/bsd/vm/jvm_bsd.cpp index e2e140e2055..f891f7c1b91 100644 --- a/hotspot/src/os/bsd/vm/jvm_bsd.cpp +++ b/hotspot/src/os/bsd/vm/jvm_bsd.cpp @@ -108,84 +108,3 @@ JVM_ENTRY_NO_ENV(jboolean, JVM_RaiseSignal(jint sig)) return JNI_TRUE; JVM_END -/* - All the defined signal names for Bsd. - - NOTE that not all of these names are accepted by our Java implementation - - Via an existing claim by the VM, sigaction restrictions, or - the "rules of Unix" some of these names will be rejected at runtime. - For example the VM sets up to handle USR1, sigaction returns EINVAL for - STOP, and Bsd simply doesn't allow catching of KILL. - - Here are the names currently accepted by a user of sun.misc.Signal with - 1.4.1 (ignoring potential interaction with use of chaining, etc): - - HUP, INT, TRAP, ABRT, IOT, BUS, USR2, PIPE, ALRM, TERM, STKFLT, - CLD, CHLD, CONT, TSTP, TTIN, TTOU, URG, XCPU, XFSZ, VTALRM, PROF, - WINCH, POLL, IO, PWR, SYS - -*/ - -struct siglabel { - const char *name; - int number; -}; - -struct siglabel siglabels[] = { - /* derived from /usr/include/bits/signum.h on RH7.2 */ - "HUP", SIGHUP, /* Hangup (POSIX). */ - "INT", SIGINT, /* Interrupt (ANSI). */ - "QUIT", SIGQUIT, /* Quit (POSIX). */ - "ILL", SIGILL, /* Illegal instruction (ANSI). */ - "TRAP", SIGTRAP, /* Trace trap (POSIX). */ - "ABRT", SIGABRT, /* Abort (ANSI). */ - "EMT", SIGEMT, /* EMT trap */ - "FPE", SIGFPE, /* Floating-point exception (ANSI). */ - "KILL", SIGKILL, /* Kill, unblockable (POSIX). */ - "BUS", SIGBUS, /* BUS error (4.2 BSD). */ - "SEGV", SIGSEGV, /* Segmentation violation (ANSI). */ - "SYS", SIGSYS, /* Bad system call. Only on some Bsden! */ - "PIPE", SIGPIPE, /* Broken pipe (POSIX). */ - "ALRM", SIGALRM, /* Alarm clock (POSIX). */ - "TERM", SIGTERM, /* Termination (ANSI). */ - "URG", SIGURG, /* Urgent condition on socket (4.2 BSD). */ - "STOP", SIGSTOP, /* Stop, unblockable (POSIX). */ - "TSTP", SIGTSTP, /* Keyboard stop (POSIX). */ - "CONT", SIGCONT, /* Continue (POSIX). */ - "CHLD", SIGCHLD, /* Child status has changed (POSIX). */ - "TTIN", SIGTTIN, /* Background read from tty (POSIX). */ - "TTOU", SIGTTOU, /* Background write to tty (POSIX). */ - "IO", SIGIO, /* I/O now possible (4.2 BSD). */ - "XCPU", SIGXCPU, /* CPU limit exceeded (4.2 BSD). */ - "XFSZ", SIGXFSZ, /* File size limit exceeded (4.2 BSD). */ - "VTALRM", SIGVTALRM, /* Virtual alarm clock (4.2 BSD). */ - "PROF", SIGPROF, /* Profiling alarm clock (4.2 BSD). */ - "WINCH", SIGWINCH, /* Window size change (4.3 BSD, Sun). */ - "INFO", SIGINFO, /* Information request. */ - "USR1", SIGUSR1, /* User-defined signal 1 (POSIX). */ - "USR2", SIGUSR2 /* User-defined signal 2 (POSIX). */ - }; - -JVM_ENTRY_NO_ENV(jint, JVM_FindSignal(const char *name)) - - /* find and return the named signal's number */ - - for(uint i=0; i -#include -#include -#include -#include - -void VMError::show_message_box(char *buf, int buflen) { - bool yes; - do { - error_string(buf, buflen); - int len = (int)strlen(buf); - char *p = &buf[len]; - - jio_snprintf(p, buflen - len, - "\n\n" - "Do you want to debug the problem?\n\n" - "To debug, run 'gdb /proc/%d/exe %d'; then switch to thread " INTX_FORMAT " (" INTPTR_FORMAT ")\n" - "Enter 'yes' to launch gdb automatically (PATH must include gdb)\n" - "Otherwise, press RETURN to abort...", - os::current_process_id(), os::current_process_id(), - os::current_thread_id(), os::current_thread_id()); - - yes = os::message_box("Unexpected Error", buf); - - if (yes) { - // yes, user asked VM to launch debugger - jio_snprintf(buf, buflen, "gdb /proc/%d/exe %d", - os::current_process_id(), os::current_process_id()); - - os::fork_and_exec(buf); - yes = false; - } - } while (yes); -} - -// handle all synchronous program error signals which may happen during error -// reporting. They must be unblocked, caught, handled. - -static const int SIGNALS[] = { SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP }; // add more if needed -static const int NUM_SIGNALS = sizeof(SIGNALS) / sizeof(int); - -// Space for our "saved" signal flags and handlers -static int resettedSigflags[NUM_SIGNALS]; -static address resettedSighandler[NUM_SIGNALS]; - -static void save_signal(int idx, int sig) -{ - struct sigaction sa; - sigaction(sig, NULL, &sa); - resettedSigflags[idx] = sa.sa_flags; - resettedSighandler[idx] = (sa.sa_flags & SA_SIGINFO) - ? CAST_FROM_FN_PTR(address, sa.sa_sigaction) - : CAST_FROM_FN_PTR(address, sa.sa_handler); -} - -int VMError::get_resetted_sigflags(int sig) { - for (int i = 0; i < NUM_SIGNALS; i++) { - if (SIGNALS[i] == sig) { - return resettedSigflags[i]; - } - } - return -1; -} - -address VMError::get_resetted_sighandler(int sig) { - for (int i = 0; i < NUM_SIGNALS; i++) { - if (SIGNALS[i] == sig) { - return resettedSighandler[i]; - } - } - return NULL; -} - -static void crash_handler(int sig, siginfo_t* info, void* ucVoid) { - // unmask current signal - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - // also unmask other synchronous signals - for (int i = 0; i < NUM_SIGNALS; i++) { - sigaddset(&newset, SIGNALS[i]); - } - pthread_sigmask(SIG_UNBLOCK, &newset, NULL); - - // support safefetch faults in error handling - ucontext_t* const uc = (ucontext_t*) ucVoid; - address const pc = uc ? os::Bsd::ucontext_get_pc(uc) : NULL; - - if (uc && pc && StubRoutines::is_safefetch_fault(pc)) { - os::Bsd::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); - return; - } - - VMError::report_and_die(NULL, sig, pc, info, ucVoid); -} - -void VMError::reset_signal_handlers() { - // install signal handlers for all synchronous program error signals - sigset_t newset; - sigemptyset(&newset); - - for (int i = 0; i < NUM_SIGNALS; i++) { - save_signal(i, SIGNALS[i]); - os::signal(SIGNALS[i], CAST_FROM_FN_PTR(void *, crash_handler)); - sigaddset(&newset, SIGNALS[i]); - } - pthread_sigmask(SIG_UNBLOCK, &newset, NULL); -} diff --git a/hotspot/src/os/linux/vm/jvm_linux.cpp b/hotspot/src/os/linux/vm/jvm_linux.cpp index 35404b8217a..25e58c01ef4 100644 --- a/hotspot/src/os/linux/vm/jvm_linux.cpp +++ b/hotspot/src/os/linux/vm/jvm_linux.cpp @@ -108,91 +108,3 @@ JVM_ENTRY_NO_ENV(jboolean, JVM_RaiseSignal(jint sig)) return JNI_TRUE; JVM_END -/* - All the defined signal names for Linux. - - NOTE that not all of these names are accepted by our Java implementation - - Via an existing claim by the VM, sigaction restrictions, or - the "rules of Unix" some of these names will be rejected at runtime. - For example the VM sets up to handle USR1, sigaction returns EINVAL for - STOP, and Linux simply doesn't allow catching of KILL. - - Here are the names currently accepted by a user of sun.misc.Signal with - 1.4.1 (ignoring potential interaction with use of chaining, etc): - - HUP, INT, TRAP, ABRT, IOT, BUS, USR2, PIPE, ALRM, TERM, STKFLT, - CLD, CHLD, CONT, TSTP, TTIN, TTOU, URG, XCPU, XFSZ, VTALRM, PROF, - WINCH, POLL, IO, PWR, SYS - -*/ - -struct siglabel { - const char *name; - int number; -}; - -struct siglabel siglabels[] = { - /* derived from /usr/include/bits/signum.h on RH7.2 */ - "HUP", SIGHUP, /* Hangup (POSIX). */ - "INT", SIGINT, /* Interrupt (ANSI). */ - "QUIT", SIGQUIT, /* Quit (POSIX). */ - "ILL", SIGILL, /* Illegal instruction (ANSI). */ - "TRAP", SIGTRAP, /* Trace trap (POSIX). */ - "ABRT", SIGABRT, /* Abort (ANSI). */ - "IOT", SIGIOT, /* IOT trap (4.2 BSD). */ - "BUS", SIGBUS, /* BUS error (4.2 BSD). */ - "FPE", SIGFPE, /* Floating-point exception (ANSI). */ - "KILL", SIGKILL, /* Kill, unblockable (POSIX). */ - "USR1", SIGUSR1, /* User-defined signal 1 (POSIX). */ - "SEGV", SIGSEGV, /* Segmentation violation (ANSI). */ - "USR2", SIGUSR2, /* User-defined signal 2 (POSIX). */ - "PIPE", SIGPIPE, /* Broken pipe (POSIX). */ - "ALRM", SIGALRM, /* Alarm clock (POSIX). */ - "TERM", SIGTERM, /* Termination (ANSI). */ -#ifdef SIGSTKFLT - "STKFLT", SIGSTKFLT, /* Stack fault. */ -#endif - "CLD", SIGCLD, /* Same as SIGCHLD (System V). */ - "CHLD", SIGCHLD, /* Child status has changed (POSIX). */ - "CONT", SIGCONT, /* Continue (POSIX). */ - "STOP", SIGSTOP, /* Stop, unblockable (POSIX). */ - "TSTP", SIGTSTP, /* Keyboard stop (POSIX). */ - "TTIN", SIGTTIN, /* Background read from tty (POSIX). */ - "TTOU", SIGTTOU, /* Background write to tty (POSIX). */ - "URG", SIGURG, /* Urgent condition on socket (4.2 BSD). */ - "XCPU", SIGXCPU, /* CPU limit exceeded (4.2 BSD). */ - "XFSZ", SIGXFSZ, /* File size limit exceeded (4.2 BSD). */ - "VTALRM", SIGVTALRM, /* Virtual alarm clock (4.2 BSD). */ - "PROF", SIGPROF, /* Profiling alarm clock (4.2 BSD). */ - "WINCH", SIGWINCH, /* Window size change (4.3 BSD, Sun). */ - "POLL", SIGPOLL, /* Pollable event occurred (System V). */ - "IO", SIGIO, /* I/O now possible (4.2 BSD). */ - "PWR", SIGPWR, /* Power failure restart (System V). */ -#ifdef SIGSYS - "SYS", SIGSYS /* Bad system call. Only on some Linuxen! */ -#endif - }; - -JVM_ENTRY_NO_ENV(jint, JVM_FindSignal(const char *name)) - - /* find and return the named signal's number */ - - for(uint i=0; i -#include -#include -#include -#include - -void VMError::show_message_box(char *buf, int buflen) { - bool yes; - do { - error_string(buf, buflen); - int len = (int)strlen(buf); - char *p = &buf[len]; - - jio_snprintf(p, buflen - len, - "\n\n" - "Do you want to debug the problem?\n\n" - "To debug, run 'gdb /proc/%d/exe %d'; then switch to thread " UINTX_FORMAT " (" INTPTR_FORMAT ")\n" - "Enter 'yes' to launch gdb automatically (PATH must include gdb)\n" - "Otherwise, press RETURN to abort...", - os::current_process_id(), os::current_process_id(), - os::current_thread_id(), os::current_thread_id()); - - yes = os::message_box("Unexpected Error", buf); - - if (yes) { - // yes, user asked VM to launch debugger - jio_snprintf(buf, buflen, "gdb /proc/%d/exe %d", - os::current_process_id(), os::current_process_id()); - - os::fork_and_exec(buf); - yes = false; - } - } while (yes); -} - -// handle all synchronous program error signals which may happen during error -// reporting. They must be unblocked, caught, handled. - -static const int SIGNALS[] = { SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP }; // add more if needed -static const int NUM_SIGNALS = sizeof(SIGNALS) / sizeof(int); - -// Space for our "saved" signal flags and handlers -static int resettedSigflags[NUM_SIGNALS]; -static address resettedSighandler[NUM_SIGNALS]; - -static void save_signal(int idx, int sig) -{ - struct sigaction sa; - sigaction(sig, NULL, &sa); - resettedSigflags[idx] = sa.sa_flags; - resettedSighandler[idx] = (sa.sa_flags & SA_SIGINFO) - ? CAST_FROM_FN_PTR(address, sa.sa_sigaction) - : CAST_FROM_FN_PTR(address, sa.sa_handler); -} - -int VMError::get_resetted_sigflags(int sig) { - for (int i = 0; i < NUM_SIGNALS; i++) { - if (SIGNALS[i] == sig) { - return resettedSigflags[i]; - } - } - return -1; -} - -address VMError::get_resetted_sighandler(int sig) { - for (int i = 0; i < NUM_SIGNALS; i++) { - if (SIGNALS[i] == sig) { - return resettedSighandler[i]; - } - } - return NULL; -} - -static void crash_handler(int sig, siginfo_t* info, void* ucVoid) { - // unmask current signal - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - // also unmask other synchronous signals - for (int i = 0; i < NUM_SIGNALS; i++) { - sigaddset(&newset, SIGNALS[i]); - } - pthread_sigmask(SIG_UNBLOCK, &newset, NULL); - - // support safefetch faults in error handling - ucontext_t* const uc = (ucontext_t*) ucVoid; - address const pc = uc ? os::Linux::ucontext_get_pc(uc) : NULL; - - if (uc && pc && StubRoutines::is_safefetch_fault(pc)) { - os::Linux::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); - return; - } - - VMError::report_and_die(NULL, sig, pc, info, ucVoid); -} - -void VMError::reset_signal_handlers() { - // install signal handlers for all synchronous program error signals - sigset_t newset; - sigemptyset(&newset); - - for (int i = 0; i < NUM_SIGNALS; i++) { - save_signal(i, SIGNALS[i]); - os::signal(SIGNALS[i], CAST_FROM_FN_PTR(void *, crash_handler)); - sigaddset(&newset, SIGNALS[i]); - } - pthread_sigmask(SIG_UNBLOCK, &newset, NULL); - -} diff --git a/hotspot/src/os/posix/vm/os_posix.cpp b/hotspot/src/os/posix/vm/os_posix.cpp index 47ad1217d1e..55de3ebc259 100644 --- a/hotspot/src/os/posix/vm/os_posix.cpp +++ b/hotspot/src/os/posix/vm/os_posix.cpp @@ -493,166 +493,171 @@ bool os::is_interrupted(Thread* thread, bool clear_interrupted) { return interrupted; } -// Returned string is a constant. For unknown signals "UNKNOWN" is returned. -const char* os::Posix::get_signal_name(int sig, char* out, size_t outlen) { - static const struct { - int sig; const char* name; - } - info[] = + +static const struct { + int sig; const char* name; +} + g_signal_info[] = { - { SIGABRT, "SIGABRT" }, + { SIGABRT, "SIGABRT" }, #ifdef SIGAIO - { SIGAIO, "SIGAIO" }, + { SIGAIO, "SIGAIO" }, #endif - { SIGALRM, "SIGALRM" }, + { SIGALRM, "SIGALRM" }, #ifdef SIGALRM1 - { SIGALRM1, "SIGALRM1" }, + { SIGALRM1, "SIGALRM1" }, #endif - { SIGBUS, "SIGBUS" }, + { SIGBUS, "SIGBUS" }, #ifdef SIGCANCEL - { SIGCANCEL, "SIGCANCEL" }, + { SIGCANCEL, "SIGCANCEL" }, #endif - { SIGCHLD, "SIGCHLD" }, + { SIGCHLD, "SIGCHLD" }, #ifdef SIGCLD - { SIGCLD, "SIGCLD" }, + { SIGCLD, "SIGCLD" }, #endif - { SIGCONT, "SIGCONT" }, + { SIGCONT, "SIGCONT" }, #ifdef SIGCPUFAIL - { SIGCPUFAIL, "SIGCPUFAIL" }, + { SIGCPUFAIL, "SIGCPUFAIL" }, #endif #ifdef SIGDANGER - { SIGDANGER, "SIGDANGER" }, + { SIGDANGER, "SIGDANGER" }, #endif #ifdef SIGDIL - { SIGDIL, "SIGDIL" }, + { SIGDIL, "SIGDIL" }, #endif #ifdef SIGEMT - { SIGEMT, "SIGEMT" }, + { SIGEMT, "SIGEMT" }, #endif - { SIGFPE, "SIGFPE" }, + { SIGFPE, "SIGFPE" }, #ifdef SIGFREEZE - { SIGFREEZE, "SIGFREEZE" }, + { SIGFREEZE, "SIGFREEZE" }, #endif #ifdef SIGGFAULT - { SIGGFAULT, "SIGGFAULT" }, + { SIGGFAULT, "SIGGFAULT" }, #endif #ifdef SIGGRANT - { SIGGRANT, "SIGGRANT" }, + { SIGGRANT, "SIGGRANT" }, #endif - { SIGHUP, "SIGHUP" }, - { SIGILL, "SIGILL" }, - { SIGINT, "SIGINT" }, + { SIGHUP, "SIGHUP" }, + { SIGILL, "SIGILL" }, + { SIGINT, "SIGINT" }, #ifdef SIGIO - { SIGIO, "SIGIO" }, + { SIGIO, "SIGIO" }, #endif #ifdef SIGIOINT - { SIGIOINT, "SIGIOINT" }, + { SIGIOINT, "SIGIOINT" }, #endif #ifdef SIGIOT - // SIGIOT is there for BSD compatibility, but on most Unices just a - // synonym for SIGABRT. The result should be "SIGABRT", not - // "SIGIOT". - #if (SIGIOT != SIGABRT ) - { SIGIOT, "SIGIOT" }, - #endif +// SIGIOT is there for BSD compatibility, but on most Unices just a +// synonym for SIGABRT. The result should be "SIGABRT", not +// "SIGIOT". +#if (SIGIOT != SIGABRT ) + { SIGIOT, "SIGIOT" }, +#endif #endif #ifdef SIGKAP - { SIGKAP, "SIGKAP" }, + { SIGKAP, "SIGKAP" }, #endif - { SIGKILL, "SIGKILL" }, + { SIGKILL, "SIGKILL" }, #ifdef SIGLOST - { SIGLOST, "SIGLOST" }, + { SIGLOST, "SIGLOST" }, #endif #ifdef SIGLWP - { SIGLWP, "SIGLWP" }, + { SIGLWP, "SIGLWP" }, #endif #ifdef SIGLWPTIMER - { SIGLWPTIMER, "SIGLWPTIMER" }, + { SIGLWPTIMER, "SIGLWPTIMER" }, #endif #ifdef SIGMIGRATE - { SIGMIGRATE, "SIGMIGRATE" }, + { SIGMIGRATE, "SIGMIGRATE" }, #endif #ifdef SIGMSG - { SIGMSG, "SIGMSG" }, + { SIGMSG, "SIGMSG" }, #endif - { SIGPIPE, "SIGPIPE" }, + { SIGPIPE, "SIGPIPE" }, #ifdef SIGPOLL - { SIGPOLL, "SIGPOLL" }, + { SIGPOLL, "SIGPOLL" }, #endif #ifdef SIGPRE - { SIGPRE, "SIGPRE" }, + { SIGPRE, "SIGPRE" }, #endif - { SIGPROF, "SIGPROF" }, + { SIGPROF, "SIGPROF" }, #ifdef SIGPTY - { SIGPTY, "SIGPTY" }, + { SIGPTY, "SIGPTY" }, #endif #ifdef SIGPWR - { SIGPWR, "SIGPWR" }, + { SIGPWR, "SIGPWR" }, #endif - { SIGQUIT, "SIGQUIT" }, + { SIGQUIT, "SIGQUIT" }, #ifdef SIGRECONFIG - { SIGRECONFIG, "SIGRECONFIG" }, + { SIGRECONFIG, "SIGRECONFIG" }, #endif #ifdef SIGRECOVERY - { SIGRECOVERY, "SIGRECOVERY" }, + { SIGRECOVERY, "SIGRECOVERY" }, #endif #ifdef SIGRESERVE - { SIGRESERVE, "SIGRESERVE" }, + { SIGRESERVE, "SIGRESERVE" }, #endif #ifdef SIGRETRACT - { SIGRETRACT, "SIGRETRACT" }, + { SIGRETRACT, "SIGRETRACT" }, #endif #ifdef SIGSAK - { SIGSAK, "SIGSAK" }, + { SIGSAK, "SIGSAK" }, #endif - { SIGSEGV, "SIGSEGV" }, + { SIGSEGV, "SIGSEGV" }, #ifdef SIGSOUND - { SIGSOUND, "SIGSOUND" }, + { SIGSOUND, "SIGSOUND" }, #endif - { SIGSTOP, "SIGSTOP" }, - { SIGSYS, "SIGSYS" }, +#ifdef SIGSTKFLT + { SIGSTKFLT, "SIGSTKFLT" }, +#endif + { SIGSTOP, "SIGSTOP" }, + { SIGSYS, "SIGSYS" }, #ifdef SIGSYSERROR - { SIGSYSERROR, "SIGSYSERROR" }, + { SIGSYSERROR, "SIGSYSERROR" }, #endif #ifdef SIGTALRM - { SIGTALRM, "SIGTALRM" }, + { SIGTALRM, "SIGTALRM" }, #endif - { SIGTERM, "SIGTERM" }, + { SIGTERM, "SIGTERM" }, #ifdef SIGTHAW - { SIGTHAW, "SIGTHAW" }, + { SIGTHAW, "SIGTHAW" }, #endif - { SIGTRAP, "SIGTRAP" }, + { SIGTRAP, "SIGTRAP" }, #ifdef SIGTSTP - { SIGTSTP, "SIGTSTP" }, + { SIGTSTP, "SIGTSTP" }, #endif - { SIGTTIN, "SIGTTIN" }, - { SIGTTOU, "SIGTTOU" }, + { SIGTTIN, "SIGTTIN" }, + { SIGTTOU, "SIGTTOU" }, #ifdef SIGURG - { SIGURG, "SIGURG" }, + { SIGURG, "SIGURG" }, #endif - { SIGUSR1, "SIGUSR1" }, - { SIGUSR2, "SIGUSR2" }, + { SIGUSR1, "SIGUSR1" }, + { SIGUSR2, "SIGUSR2" }, #ifdef SIGVIRT - { SIGVIRT, "SIGVIRT" }, + { SIGVIRT, "SIGVIRT" }, #endif - { SIGVTALRM, "SIGVTALRM" }, + { SIGVTALRM, "SIGVTALRM" }, #ifdef SIGWAITING - { SIGWAITING, "SIGWAITING" }, + { SIGWAITING, "SIGWAITING" }, #endif #ifdef SIGWINCH - { SIGWINCH, "SIGWINCH" }, + { SIGWINCH, "SIGWINCH" }, #endif #ifdef SIGWINDOW - { SIGWINDOW, "SIGWINDOW" }, + { SIGWINDOW, "SIGWINDOW" }, #endif - { SIGXCPU, "SIGXCPU" }, - { SIGXFSZ, "SIGXFSZ" }, + { SIGXCPU, "SIGXCPU" }, + { SIGXFSZ, "SIGXFSZ" }, #ifdef SIGXRES - { SIGXRES, "SIGXRES" }, + { SIGXRES, "SIGXRES" }, #endif - { -1, NULL } - }; + { -1, NULL } +}; + +// Returned string is a constant. For unknown signals "UNKNOWN" is returned. +const char* os::Posix::get_signal_name(int sig, char* out, size_t outlen) { const char* ret = NULL; @@ -670,9 +675,9 @@ const char* os::Posix::get_signal_name(int sig, char* out, size_t outlen) { #endif if (sig > 0) { - for (int idx = 0; info[idx].sig != -1; idx ++) { - if (info[idx].sig == sig) { - ret = info[idx].name; + for (int idx = 0; g_signal_info[idx].sig != -1; idx ++) { + if (g_signal_info[idx].sig == sig) { + ret = g_signal_info[idx].name; break; } } @@ -693,6 +698,25 @@ const char* os::Posix::get_signal_name(int sig, char* out, size_t outlen) { return out; } +int os::Posix::get_signal_number(const char* signal_name) { + char tmp[30]; + const char* s = signal_name; + if (s[0] != 'S' || s[1] != 'I' || s[2] != 'G') { + jio_snprintf(tmp, sizeof(tmp), "SIG%s", signal_name); + s = tmp; + } + for (int idx = 0; g_signal_info[idx].sig != -1; idx ++) { + if (strcmp(g_signal_info[idx].name, s) == 0) { + return g_signal_info[idx].sig; + } + } + return -1; +} + +int os::get_signal_number(const char* signal_name) { + return os::Posix::get_signal_number(signal_name); +} + // Returns true if signal number is valid. bool os::Posix::is_valid_signal(int sig) { // MacOS not really POSIX compliant: sigaddset does not return @@ -711,6 +735,21 @@ bool os::Posix::is_valid_signal(int sig) { #endif } +// Returns: +// "invalid ()" for an invalid signal number +// "SIG" for a valid but unknown signal number +// signal name otherwise. +const char* os::exception_name(int sig, char* buf, size_t size) { + if (!os::Posix::is_valid_signal(sig)) { + jio_snprintf(buf, size, "invalid (%d)", sig); + } + const char* const name = os::Posix::get_signal_name(sig, buf, size); + if (strcmp(name, "UNKNOWN") == 0) { + jio_snprintf(buf, size, "SIG%d", sig); + } + return buf; +} + #define NUM_IMPORTANT_SIGS 32 // Returns one-line short description of a signal set in a user provided buffer. const char* os::Posix::describe_signal_set_short(const sigset_t* set, char* buffer, size_t buf_size) { @@ -837,6 +876,21 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t #if defined(IA64) && !defined(AIX) { SIGSEGV, SEGV_PSTKOVF, "SEGV_PSTKOVF", "Paragraph stack overflow" }, #endif +#if defined(__sparc) && defined(SOLARIS) +// define Solaris Sparc M7 ADI SEGV signals +#if !defined(SEGV_ACCADI) +#define SEGV_ACCADI 3 +#endif + { SIGSEGV, SEGV_ACCADI, "SEGV_ACCADI", "ADI not enabled for mapped object." }, +#if !defined(SEGV_ACCDERR) +#define SEGV_ACCDERR 4 +#endif + { SIGSEGV, SEGV_ACCDERR, "SEGV_ACCDERR", "ADI disrupting exception." }, +#if !defined(SEGV_ACCPERR) +#define SEGV_ACCPERR 5 +#endif + { SIGSEGV, SEGV_ACCPERR, "SEGV_ACCPERR", "ADI precise exception." }, +#endif // defined(__sparc) && defined(SOLARIS) { SIGBUS, BUS_ADRALN, "BUS_ADRALN", "Invalid address alignment." }, { SIGBUS, BUS_ADRERR, "BUS_ADRERR", "Nonexistent physical address." }, { SIGBUS, BUS_OBJERR, "BUS_OBJERR", "Object-specific hardware error." }, @@ -973,6 +1027,39 @@ void os::Posix::print_siginfo_brief(outputStream* os, const siginfo_t* si) { } } +int os::Posix::unblock_thread_signal_mask(const sigset_t *set) { + return pthread_sigmask(SIG_UNBLOCK, set, NULL); +} + +address os::Posix::ucontext_get_pc(ucontext_t* ctx) { +#ifdef TARGET_OS_FAMILY_linux + return Linux::ucontext_get_pc(ctx); +#elif defined(TARGET_OS_FAMILY_solaris) + return Solaris::ucontext_get_pc(ctx); +#elif defined(TARGET_OS_FAMILY_aix) + return Aix::ucontext_get_pc(ctx); +#elif defined(TARGET_OS_FAMILY_bsd) + return Bsd::ucontext_get_pc(ctx); +#else + VMError::report_and_die("unimplemented ucontext_get_pc"); +#endif +} + +void os::Posix::ucontext_set_pc(ucontext_t* ctx, address pc) { +#ifdef TARGET_OS_FAMILY_linux + Linux::ucontext_set_pc(ctx, pc); +#elif defined(TARGET_OS_FAMILY_solaris) + Solaris::ucontext_set_pc(ctx, pc); +#elif defined(TARGET_OS_FAMILY_aix) + Aix::ucontext_set_pc(ctx, pc); +#elif defined(TARGET_OS_FAMILY_bsd) + Bsd::ucontext_set_pc(ctx, pc); +#else + VMError::report_and_die("unimplemented ucontext_get_pc"); +#endif +} + + os::WatcherThreadCrashProtection::WatcherThreadCrashProtection() { assert(Thread::current()->is_Watcher_thread(), "Must be WatcherThread"); } diff --git a/hotspot/src/os/posix/vm/os_posix.hpp b/hotspot/src/os/posix/vm/os_posix.hpp index ed08ad74abc..27b0554881e 100644 --- a/hotspot/src/os/posix/vm/os_posix.hpp +++ b/hotspot/src/os/posix/vm/os_posix.hpp @@ -51,12 +51,21 @@ public: // Returned string is a constant. For unknown signals "UNKNOWN" is returned. static const char* get_signal_name(int sig, char* out, size_t outlen); + // Helper function, returns a signal number for a given signal name, e.g. 11 + // for "SIGSEGV". Name can be given with or without "SIG" prefix, so both + // "SEGV" or "SIGSEGV" work. Name must be uppercase. + // Returns -1 for an unknown signal name. + static int get_signal_number(const char* signal_name); + // Returns one-line short description of a signal set in a user provided buffer. static const char* describe_signal_set_short(const sigset_t* set, char* buffer, size_t size); // Prints a short one-line description of a signal set. static void print_signal_set_short(outputStream* st, const sigset_t* set); + // unblocks the signal masks for current thread + static int unblock_thread_signal_mask(const sigset_t *set); + // Writes a one-line description of a combination of sigaction.sa_flags // into a user provided buffer. Returns that buffer. static const char* describe_sa_flags(int flags, char* buffer, size_t size); @@ -67,6 +76,9 @@ public: // A POSIX conform, platform-independend siginfo print routine. static void print_siginfo_brief(outputStream* os, const siginfo_t* si); + static address ucontext_get_pc(ucontext_t* ctx); + // Set PC into context. Needed for continuation after signal. + static void ucontext_set_pc(ucontext_t* ctx, address pc); }; /* diff --git a/hotspot/src/os/solaris/vm/vmError_solaris.cpp b/hotspot/src/os/posix/vm/vmError_posix.cpp similarity index 74% rename from hotspot/src/os/solaris/vm/vmError_solaris.cpp rename to hotspot/src/os/posix/vm/vmError_posix.cpp index 8358801854e..6fb04af4dd3 100644 --- a/hotspot/src/os/solaris/vm/vmError_solaris.cpp +++ b/hotspot/src/os/posix/vm/vmError_posix.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2015, 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 @@ -30,35 +30,23 @@ #include #include -#include #include -void VMError::show_message_box(char *buf, int buflen) { - bool yes; - do { - error_string(buf, buflen); - int len = (int)strlen(buf); - char *p = &buf[len]; +#ifdef TARGET_OS_FAMILY_linux +#include +#include +#endif +#ifdef TARGET_OS_FAMILY_solaris +#include +#endif +#ifdef TARGET_OS_FAMILY_aix +#include +#endif +#ifdef TARGET_OS_FAMILY_bsd +#include +#include +#endif - jio_snprintf(p, buflen - len, - "\n\n" - "Do you want to debug the problem?\n\n" - "To debug, run 'dbx - %d'; then switch to thread " INTX_FORMAT "\n" - "Enter 'yes' to launch dbx automatically (PATH must include dbx)\n" - "Otherwise, press RETURN to abort...", - os::current_process_id(), os::current_thread_id()); - - yes = os::message_box("Unexpected Error", buf); - - if (yes) { - // yes, user asked VM to launch debugger - jio_snprintf(buf, buflen, "dbx - %d", os::current_process_id()); - - os::fork_and_exec(buf); - yes = false; - } - } while (yes); -} // handle all synchronous program error signals which may happen during error // reporting. They must be unblocked, caught, handled. @@ -107,13 +95,14 @@ static void crash_handler(int sig, siginfo_t* info, void* ucVoid) { for (int i = 0; i < NUM_SIGNALS; i++) { sigaddset(&newset, SIGNALS[i]); } - thr_sigsetmask(SIG_UNBLOCK, &newset, NULL); + os::Posix::unblock_thread_signal_mask(&newset); // support safefetch faults in error handling ucontext_t* const uc = (ucontext_t*) ucVoid; - address const pc = uc ? os::Solaris::ucontext_get_pc(uc) : NULL; + address const pc = uc ? os::Posix::ucontext_get_pc(uc) : NULL; + if (uc && pc && StubRoutines::is_safefetch_fault(pc)) { - os::Solaris::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); + os::Posix::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); return; } @@ -130,5 +119,6 @@ void VMError::reset_signal_handlers() { os::signal(SIGNALS[i], CAST_FROM_FN_PTR(void *, crash_handler)); sigaddset(&newset, SIGNALS[i]); } - thr_sigsetmask(SIG_UNBLOCK, &newset, NULL); + os::Posix::unblock_thread_signal_mask(&newset); + } diff --git a/hotspot/src/os/solaris/vm/jvm_solaris.cpp b/hotspot/src/os/solaris/vm/jvm_solaris.cpp index ae2eb037dc8..3dfa84e0f9e 100644 --- a/hotspot/src/os/solaris/vm/jvm_solaris.cpp +++ b/hotspot/src/os/solaris/vm/jvm_solaris.cpp @@ -106,40 +106,3 @@ JVM_ENTRY_NO_ENV(jboolean, JVM_RaiseSignal(jint sig)) return JNI_TRUE; JVM_END - -/* - All the defined signal names for Solaris are defined by str2sig(). - - NOTE that not all of these names are accepted by our Java implementation - - Via an existing claim by the VM, sigaction restrictions, or - the "rules of Unix" some of these names will be rejected at runtime. - For example the VM sets up to handle USR1, sigaction returns EINVAL for - CANCEL, and Solaris simply doesn't allow catching of KILL. - - Here are the names currently accepted by a user of sun.misc.Signal with - 1.4.1 (ignoring potential interaction with use of chaining, etc): - - HUP, INT, TRAP, IOT, ABRT, EMT, BUS, SYS, PIPE, ALRM, TERM, USR2, - CLD, CHLD, PWR, WINCH, URG, POLL, IO, TSTP, CONT, TTIN, TTOU, VTALRM, - PROF, XCPU, XFSZ, FREEZE, THAW, LOST -*/ - -JVM_ENTRY_NO_ENV(jint, JVM_FindSignal(const char *name)) - - int sig; - - /* return the named signal's number */ - - if(str2sig(name, &sig)) - return -1; - else - return sig; - -JVM_END - - -//Reconciliation History -// 1.4 98/10/07 13:39:41 jvm_win32.cpp -// 1.6 99/06/22 16:39:00 jvm_win32.cpp -//End diff --git a/hotspot/src/os/solaris/vm/os_solaris.cpp b/hotspot/src/os/solaris/vm/os_solaris.cpp index de90c5ca4f8..96a0831949b 100644 --- a/hotspot/src/os/solaris/vm/os_solaris.cpp +++ b/hotspot/src/os/solaris/vm/os_solaris.cpp @@ -3611,7 +3611,7 @@ void os::Solaris::SR_handler(Thread* thread, ucontext_t* uc) { void os::print_statistics() { } -int os::message_box(const char* title, const char* message) { +bool os::message_box(const char* title, const char* message) { int i; fdStream err(defaultStream::error_fd()); for (i = 0; i < 78; i++) err.print_raw("="); @@ -4144,32 +4144,6 @@ void os::Solaris::install_signal_handlers() { void report_error(const char* file_name, int line_no, const char* title, const char* format, ...); -const char * signames[] = { - "SIG0", - "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", - "SIGABRT", "SIGEMT", "SIGFPE", "SIGKILL", "SIGBUS", - "SIGSEGV", "SIGSYS", "SIGPIPE", "SIGALRM", "SIGTERM", - "SIGUSR1", "SIGUSR2", "SIGCLD", "SIGPWR", "SIGWINCH", - "SIGURG", "SIGPOLL", "SIGSTOP", "SIGTSTP", "SIGCONT", - "SIGTTIN", "SIGTTOU", "SIGVTALRM", "SIGPROF", "SIGXCPU", - "SIGXFSZ", "SIGWAITING", "SIGLWP", "SIGFREEZE", "SIGTHAW", - "SIGCANCEL", "SIGLOST" -}; - -const char* os::exception_name(int exception_code, char* buf, size_t size) { - if (0 < exception_code && exception_code <= SIGRTMAX) { - // signal - if (exception_code < sizeof(signames)/sizeof(const char*)) { - jio_snprintf(buf, size, "%s", signames[exception_code]); - } else { - jio_snprintf(buf, size, "SIG%d", exception_code); - } - return buf; - } else { - return NULL; - } -} - // (Static) wrapper for getisax(2) call. os::Solaris::getisax_func_t os::Solaris::_getisax = 0; @@ -5804,3 +5778,27 @@ void TestReserveMemorySpecial_test() { // No tests available for this platform } #endif + +bool os::start_debugging(char *buf, int buflen) { + int len = (int)strlen(buf); + char *p = &buf[len]; + + jio_snprintf(p, buflen-len, + "\n\n" + "Do you want to debug the problem?\n\n" + "To debug, run 'dbx - %d'; then switch to thread " INTX_FORMAT "\n" + "Enter 'yes' to launch dbx automatically (PATH must include dbx)\n" + "Otherwise, press RETURN to abort...", + os::current_process_id(), os::current_thread_id()); + + bool yes = os::message_box("Unexpected Error", buf); + + if (yes) { + // yes, user asked VM to launch debugger + jio_snprintf(buf, sizeof(buf), "dbx - %d", os::current_process_id()); + + os::fork_and_exec(buf); + yes = false; + } + return yes; +} diff --git a/hotspot/src/os/windows/vm/jvm_windows.cpp b/hotspot/src/os/windows/vm/jvm_windows.cpp index d6a299a239c..1e9a3fe5c49 100644 --- a/hotspot/src/os/windows/vm/jvm_windows.cpp +++ b/hotspot/src/os/windows/vm/jvm_windows.cpp @@ -89,39 +89,3 @@ JVM_ENTRY_NO_ENV(jboolean, JVM_RaiseSignal(jint sig)) JVM_END -/* - All the defined signal names for Windows. - - NOTE that not all of these names are accepted by FindSignal! - - For various reasons some of these may be rejected at runtime. - - Here are the names currently accepted by a user of sun.misc.Signal with - 1.4.1 (ignoring potential interaction with use of chaining, etc): - - (LIST TBD) - -*/ -struct siglabel { - char *name; - int number; -}; - -struct siglabel siglabels[] = - /* derived from version 6.0 VC98/include/signal.h */ - {"ABRT", SIGABRT, /* abnormal termination triggered by abort cl */ - "FPE", SIGFPE, /* floating point exception */ - "SEGV", SIGSEGV, /* segment violation */ - "INT", SIGINT, /* interrupt */ - "TERM", SIGTERM, /* software term signal from kill */ - "BREAK", SIGBREAK, /* Ctrl-Break sequence */ - "ILL", SIGILL}; /* illegal instruction */ - -JVM_ENTRY_NO_ENV(jint, JVM_FindSignal(const char *name)) - /* find and return the named signal's number */ - - for(int i=0;iprint_cr("C heap has been corrupted (time: %d allocations)", mallocDebugCounter); tty->print_cr("corrupted block near address %#x, length %d", phe.lpData, phe.cbData); + HeapUnlock(heap); fatal("corrupted C heap"); } } DWORD err = GetLastError(); if (err != ERROR_NO_MORE_ITEMS && err != ERROR_CALL_NOT_IMPLEMENTED) { + HeapUnlock(heap); fatal("heap walk aborted with error %d", err); } HeapUnlock(heap); @@ -5505,7 +5513,31 @@ void os::Kernel32Dll::initializeCommon() { } } +bool os::start_debugging(char *buf, int buflen) { + int len = (int)strlen(buf); + char *p = &buf[len]; + jio_snprintf(p, buflen-len, + "\n\n" + "Do you want to debug the problem?\n\n" + "To debug, attach Visual Studio to process %d; then switch to thread 0x%x\n" + "Select 'Yes' to launch Visual Studio automatically (PATH must include msdev)\n" + "Otherwise, select 'No' to abort...", + os::current_process_id(), os::current_thread_id()); + + bool yes = os::message_box("Unexpected Error", buf); + + if (yes) { + // os::breakpoint() calls DebugBreak(), which causes a breakpoint + // exception. If VM is running inside a debugger, the debugger will + // catch the exception. Otherwise, the breakpoint exception will reach + // the default windows exception handler, which can spawn a debugger and + // automatically attach to the dying VM. + os::breakpoint(); + yes = false; + } + return yes; +} #ifndef JDK6_OR_EARLIER @@ -6001,3 +6033,36 @@ void TestReserveMemorySpecial_test() { UseNUMAInterleaving = old_use_numa_interleaving; } #endif // PRODUCT + +/* + All the defined signal names for Windows. + + NOTE that not all of these names are accepted by FindSignal! + + For various reasons some of these may be rejected at runtime. + + Here are the names currently accepted by a user of sun.misc.Signal with + 1.4.1 (ignoring potential interaction with use of chaining, etc): + + (LIST TBD) + +*/ +int os::get_signal_number(const char* name) { + static const struct { + char* name; + int number; + } siglabels [] = + // derived from version 6.0 VC98/include/signal.h + {"ABRT", SIGABRT, // abnormal termination triggered by abort cl + "FPE", SIGFPE, // floating point exception + "SEGV", SIGSEGV, // segment violation + "INT", SIGINT, // interrupt + "TERM", SIGTERM, // software term signal from kill + "BREAK", SIGBREAK, // Ctrl-Break sequence + "ILL", SIGILL}; // illegal instruction + for(int i=0;iprint_cr("Detected Linux on Niagara");) + if (PrintMiscellaneous && Verbose) { tty->print_cr("Detected Linux on Niagara"); } features = niagara1_m | T_family_m; } if (detect_M_family()) { - NOT_PRODUCT(if (PrintMiscellaneous && Verbose) tty->print_cr("Detected Linux on M family");) + if (PrintMiscellaneous && Verbose) { tty->print_cr("Detected Linux on M family"); } features = sun4v_m | generic_v9_m | M_family_m | T_family_m; } diff --git a/hotspot/src/share/vm/c1/c1_Compilation.hpp b/hotspot/src/share/vm/c1/c1_Compilation.hpp index a20ea9f8def..66e277d263e 100644 --- a/hotspot/src/share/vm/c1/c1_Compilation.hpp +++ b/hotspot/src/share/vm/c1/c1_Compilation.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, 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 @@ -192,7 +192,7 @@ class Compilation: public StackObj { const char* bailout_msg() const { return _bailout_msg; } static int desired_max_code_buffer_size() { -#ifndef PPC +#ifndef PPC32 return (int) NMethodSizeLimit; // default 256K or 512K #else // conditional branches on PPC are restricted to 16 bit signed diff --git a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp index 6513fe1ab33..6fac3e7b56d 100644 --- a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp +++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp @@ -707,12 +707,10 @@ BlockBegin* GraphBuilder::ScopeData::block_at(int bci) { BlockBegin* block = bci2block()->at(bci); if (block != NULL && block == parent()->bci2block()->at(bci)) { BlockBegin* new_block = new BlockBegin(block->bci()); -#ifndef PRODUCT if (PrintInitialBlockList) { tty->print_cr("CFG: cloned block %d (bci %d) as block %d for jsr", block->block_id(), block->bci(), new_block->block_id()); } -#endif // copy data from cloned blocked new_block->set_depth_first_number(block->depth_first_number()); if (block->is_set(BlockBegin::parser_loop_header_flag)) new_block->set(BlockBegin::parser_loop_header_flag); @@ -1438,7 +1436,9 @@ void GraphBuilder::method_return(Value x) { bool need_mem_bar = false; if (method()->name() == ciSymbol::object_initializer_name() && - (scope()->wrote_final() || (AlwaysSafeConstructors && scope()->wrote_fields()))) { + (scope()->wrote_final() || (AlwaysSafeConstructors && scope()->wrote_fields()) + || (support_IRIW_for_not_multiple_copy_atomic_cpu && scope()->wrote_volatile()) + )){ need_mem_bar = true; } @@ -1554,6 +1554,9 @@ void GraphBuilder::access_field(Bytecodes::Code code) { if (code == Bytecodes::_putfield) { scope()->set_wrote_fields(); + if (field->is_volatile()) { + scope()->set_wrote_volatile(); + } } const int offset = !needs_patching ? field->offset() : -1; @@ -3785,12 +3788,10 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, Bytecode cont = new BlockBegin(next_bci()); // low number so that continuation gets parsed as early as possible cont->set_depth_first_number(0); -#ifndef PRODUCT if (PrintInitialBlockList) { tty->print_cr("CFG: created block %d (bci %d) as continuation for inline at bci %d", cont->block_id(), cont->bci(), bci()); } -#endif continuation_existed = false; } // Record number of predecessors of continuation block before diff --git a/hotspot/src/share/vm/c1/c1_IR.cpp b/hotspot/src/share/vm/c1/c1_IR.cpp index e2f33a21170..6b015b44d85 100644 --- a/hotspot/src/share/vm/c1/c1_IR.cpp +++ b/hotspot/src/share/vm/c1/c1_IR.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, 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 @@ -143,6 +143,7 @@ IRScope::IRScope(Compilation* compilation, IRScope* caller, int caller_bci, ciMe _monitor_pairing_ok = method->has_balanced_monitors(); _wrote_final = false; _wrote_fields = false; + _wrote_volatile = false; _start = NULL; if (osr_bci == -1) { diff --git a/hotspot/src/share/vm/c1/c1_IR.hpp b/hotspot/src/share/vm/c1/c1_IR.hpp index e3dc4ba1950..31d3b01aff2 100644 --- a/hotspot/src/share/vm/c1/c1_IR.hpp +++ b/hotspot/src/share/vm/c1/c1_IR.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, 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 @@ -151,6 +151,7 @@ class IRScope: public CompilationResourceObj { bool _monitor_pairing_ok; // the monitor pairing info bool _wrote_final; // has written final field bool _wrote_fields; // has written fields + bool _wrote_volatile; // has written volatile field BlockBegin* _start; // the start block, successsors are method entries BitMap _requires_phi_function; // bit is set if phi functions at loop headers are necessary for a local variable @@ -187,7 +188,8 @@ class IRScope: public CompilationResourceObj { bool wrote_final () const { return _wrote_final; } void set_wrote_fields() { _wrote_fields = true; } bool wrote_fields () const { return _wrote_fields; } - + void set_wrote_volatile() { _wrote_volatile = true; } + bool wrote_volatile () const { return _wrote_volatile; } }; diff --git a/hotspot/src/share/vm/c1/c1_LIR.cpp b/hotspot/src/share/vm/c1/c1_LIR.cpp index 9247a1c6eb5..44f7364ec5f 100644 --- a/hotspot/src/share/vm/c1/c1_LIR.cpp +++ b/hotspot/src/share/vm/c1/c1_LIR.cpp @@ -55,7 +55,7 @@ XMMRegister LIR_OprDesc::as_xmm_double_reg() const { #endif // X86 -#if defined(SPARC) || defined(PPC) +#if defined(SPARC) || defined(PPC32) FloatRegister LIR_OprDesc::as_float_reg() const { return FrameMap::nr2floatreg(fpu_regnr()); @@ -67,7 +67,7 @@ FloatRegister LIR_OprDesc::as_double_reg() const { #endif -#if defined(ARM) || defined (AARCH64) +#if defined(ARM) || defined(AARCH64) || defined(PPC64) FloatRegister LIR_OprDesc::as_float_reg() const { return as_FloatRegister(fpu_regnr()); @@ -207,17 +207,17 @@ void LIR_OprDesc::validate_type() const { size_field() == double_size, "must match"); break; case T_FLOAT: - // FP return values can be also in CPU registers on ARM and PPC (softfp ABI) + // FP return values can be also in CPU registers on ARM and PPC32 (softfp ABI) assert((kindfield == fpu_register || kindfield == stack_value ARM_ONLY(|| kindfield == cpu_register) - PPC_ONLY(|| kindfield == cpu_register) ) && + PPC32_ONLY(|| kindfield == cpu_register) ) && size_field() == single_size, "must match"); break; case T_DOUBLE: - // FP return values can be also in CPU registers on ARM and PPC (softfp ABI) + // FP return values can be also in CPU registers on ARM and PPC32 (softfp ABI) assert((kindfield == fpu_register || kindfield == stack_value ARM_ONLY(|| kindfield == cpu_register) - PPC_ONLY(|| kindfield == cpu_register) ) && + PPC32_ONLY(|| kindfield == cpu_register) ) && size_field() == double_size, "must match"); break; case T_BOOLEAN: @@ -558,7 +558,7 @@ void LIR_OpVisitState::visit(LIR_Op* op) { assert(opConvert->_info == NULL, "must be"); if (opConvert->_opr->is_valid()) do_input(opConvert->_opr); if (opConvert->_result->is_valid()) do_output(opConvert->_result); -#ifdef PPC +#ifdef PPC32 if (opConvert->_tmp1->is_valid()) do_temp(opConvert->_tmp1); if (opConvert->_tmp2->is_valid()) do_temp(opConvert->_tmp2); #endif @@ -1953,7 +1953,7 @@ void LIR_OpConvert::print_instr(outputStream* out) const { print_bytecode(out, bytecode()); in_opr()->print(out); out->print(" "); result_opr()->print(out); out->print(" "); -#ifdef PPC +#ifdef PPC32 if(tmp1()->is_valid()) { tmp1()->print(out); out->print(" "); tmp2()->print(out); out->print(" "); @@ -2004,7 +2004,7 @@ void LIR_OpRoundFP::print_instr(outputStream* out) const { // LIR_Op2 void LIR_Op2::print_instr(outputStream* out) const { - if (code() == lir_cmove) { + if (code() == lir_cmove || code() == lir_cmp) { print_condition(out, condition()); out->print(" "); } in_opr1()->print(out); out->print(" "); diff --git a/hotspot/src/share/vm/c1/c1_LIR.hpp b/hotspot/src/share/vm/c1/c1_LIR.hpp index 7b203182885..14651e14078 100644 --- a/hotspot/src/share/vm/c1/c1_LIR.hpp +++ b/hotspot/src/share/vm/c1/c1_LIR.hpp @@ -648,12 +648,14 @@ class LIR_OprFact: public AllStatic { LIR_OprDesc::double_size | LIR_OprDesc::is_xmm_mask); } #endif // X86 -#ifdef PPC +#if defined(PPC) static LIR_Opr double_fpu(int reg) { return (LIR_Opr)(intptr_t)((reg << LIR_OprDesc::reg1_shift) | (reg << LIR_OprDesc::reg2_shift) | LIR_OprDesc::double_type | LIR_OprDesc::fpu_register | LIR_OprDesc::double_size); } +#endif +#ifdef PPC32 static LIR_Opr single_softfp(int reg) { return (LIR_Opr)((reg << LIR_OprDesc::reg1_shift) | LIR_OprDesc::float_type | LIR_OprDesc::cpu_register | @@ -663,7 +665,7 @@ class LIR_OprFact: public AllStatic { LIR_OprDesc::double_type | LIR_OprDesc::cpu_register | LIR_OprDesc::double_size); } -#endif // PPC +#endif // PPC32 static LIR_Opr virtual_register(int index, BasicType type) { LIR_Opr res; @@ -1475,7 +1477,7 @@ class LIR_OpConvert: public LIR_Op1 { private: Bytecodes::Code _bytecode; ConversionStub* _stub; -#ifdef PPC +#ifdef PPC32 LIR_Opr _tmp1; LIR_Opr _tmp2; #endif @@ -1484,13 +1486,13 @@ class LIR_OpConvert: public LIR_Op1 { LIR_OpConvert(Bytecodes::Code code, LIR_Opr opr, LIR_Opr result, ConversionStub* stub) : LIR_Op1(lir_convert, opr, result) , _stub(stub) -#ifdef PPC +#ifdef PPC32 , _tmp1(LIR_OprDesc::illegalOpr()) , _tmp2(LIR_OprDesc::illegalOpr()) #endif , _bytecode(code) {} -#ifdef PPC +#ifdef PPC32 LIR_OpConvert(Bytecodes::Code code, LIR_Opr opr, LIR_Opr result, ConversionStub* stub ,LIR_Opr tmp1, LIR_Opr tmp2) : LIR_Op1(lir_convert, opr, result) @@ -1502,7 +1504,7 @@ class LIR_OpConvert: public LIR_Op1 { Bytecodes::Code bytecode() const { return _bytecode; } ConversionStub* stub() const { return _stub; } -#ifdef PPC +#ifdef PPC32 LIR_Opr tmp1() const { return _tmp1; } LIR_Opr tmp2() const { return _tmp2; } #endif @@ -2142,7 +2144,7 @@ class LIR_List: public CompilationResourceObj { void safepoint(LIR_Opr tmp, CodeEmitInfo* info) { append(new LIR_Op1(lir_safepoint, tmp, info)); } -#ifdef PPC +#ifdef PPC32 void convert(Bytecodes::Code code, LIR_Opr left, LIR_Opr dst, LIR_Opr tmp1, LIR_Opr tmp2) { append(new LIR_OpConvert(code, left, dst, NULL, tmp1, tmp2)); } #endif void convert(Bytecodes::Code code, LIR_Opr left, LIR_Opr dst, ConversionStub* stub = NULL/*, bool is_32bit = false*/) { append(new LIR_OpConvert(code, left, dst, stub)); } diff --git a/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp b/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp index ae9c1b85c8e..c3638147e13 100644 --- a/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp +++ b/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2015, 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 @@ -413,13 +413,14 @@ void LIR_Assembler::record_non_safepoint_debug_info() { } -void LIR_Assembler::add_debug_info_for_null_check_here(CodeEmitInfo* cinfo) { - add_debug_info_for_null_check(code_offset(), cinfo); +ImplicitNullCheckStub* LIR_Assembler::add_debug_info_for_null_check_here(CodeEmitInfo* cinfo) { + return add_debug_info_for_null_check(code_offset(), cinfo); } -void LIR_Assembler::add_debug_info_for_null_check(int pc_offset, CodeEmitInfo* cinfo) { +ImplicitNullCheckStub* LIR_Assembler::add_debug_info_for_null_check(int pc_offset, CodeEmitInfo* cinfo) { ImplicitNullCheckStub* stub = new ImplicitNullCheckStub(pc_offset, cinfo); append_code_stub(stub); + return stub; } void LIR_Assembler::add_debug_info_for_div0_here(CodeEmitInfo* info) { @@ -557,10 +558,10 @@ void LIR_Assembler::emit_op1(LIR_Op1* op) { case lir_null_check: if (GenerateCompilerNullChecks) { - add_debug_info_for_null_check_here(op->info()); + ImplicitNullCheckStub* stub = add_debug_info_for_null_check_here(op->info()); if (op->in_opr()->is_single_cpu()) { - _masm->null_check(op->in_opr()->as_register()); + _masm->null_check(op->in_opr()->as_register(), stub->entry()); } else { Unimplemented(); } diff --git a/hotspot/src/share/vm/c1/c1_LIRAssembler.hpp b/hotspot/src/share/vm/c1/c1_LIRAssembler.hpp index e8bc40eba93..8c2c97ece41 100644 --- a/hotspot/src/share/vm/c1/c1_LIRAssembler.hpp +++ b/hotspot/src/share/vm/c1/c1_LIRAssembler.hpp @@ -99,8 +99,8 @@ class LIR_Assembler: public CompilationResourceObj { void add_debug_info_for_branch(CodeEmitInfo* info); void add_debug_info_for_div0(int pc_offset, CodeEmitInfo* cinfo); void add_debug_info_for_div0_here(CodeEmitInfo* info); - void add_debug_info_for_null_check(int pc_offset, CodeEmitInfo* cinfo); - void add_debug_info_for_null_check_here(CodeEmitInfo* info); + ImplicitNullCheckStub* add_debug_info_for_null_check(int pc_offset, CodeEmitInfo* cinfo); + ImplicitNullCheckStub* add_debug_info_for_null_check_here(CodeEmitInfo* info); void set_24bit_FPU(); void reset_FPU(); diff --git a/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp b/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp index a45d221f740..98b519b58eb 100644 --- a/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp +++ b/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp @@ -1464,10 +1464,10 @@ void LIRGenerator::G1SATBCardTableModRef_pre_barrier(LIR_Opr addr_opr, LIR_Opr p bool do_load, bool patch, CodeEmitInfo* info) { // First we test whether marking is in progress. BasicType flag_type; - if (in_bytes(PtrQueue::byte_width_of_active()) == 4) { + if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) { flag_type = T_INT; } else { - guarantee(in_bytes(PtrQueue::byte_width_of_active()) == 1, + guarantee(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption"); // Use unsigned type T_BOOLEAN here rather than signed T_BYTE since some platforms, eg. ARM, // need to use unsigned instructions to use the large offset to load the satb_mark_queue. @@ -1477,7 +1477,7 @@ void LIRGenerator::G1SATBCardTableModRef_pre_barrier(LIR_Opr addr_opr, LIR_Opr p LIR_Address* mark_active_flag_addr = new LIR_Address(thrd, in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_active()), + SATBMarkQueue::byte_offset_of_active()), flag_type); // Read the marking-in-progress flag. LIR_Opr flag_val = new_register(T_INT); @@ -1761,7 +1761,7 @@ void LIRGenerator::do_StoreField(StoreField* x) { post_barrier(object.result(), value.result()); } - if (is_volatile && os::is_MP()) { + if (!support_IRIW_for_not_multiple_copy_atomic_cpu && is_volatile && os::is_MP()) { __ membar(); } } @@ -1822,6 +1822,10 @@ void LIRGenerator::do_LoadField(LoadField* x) { address = generate_address(object.result(), x->offset(), field_type); } + if (support_IRIW_for_not_multiple_copy_atomic_cpu && is_volatile && os::is_MP()) { + __ membar(); + } + bool needs_atomic_access = is_volatile || AlwaysAtomicAccesses; if (needs_atomic_access && !needs_patching) { volatile_field_load(address, reg, info); @@ -2238,6 +2242,10 @@ void LIRGenerator::do_UnsafeGetObject(UnsafeGetObject* x) { LIR_Opr value = rlock_result(x, x->basic_type()); + if (support_IRIW_for_not_multiple_copy_atomic_cpu && x->is_volatile() && os::is_MP()) { + __ membar(); + } + get_Object_unsafe(value, src.result(), off.result(), type, x->is_volatile()); #if INCLUDE_ALL_GCS @@ -2395,7 +2403,7 @@ void LIRGenerator::do_UnsafePutObject(UnsafePutObject* x) { if (x->is_volatile() && os::is_MP()) __ membar_release(); put_Object_unsafe(src.result(), off.result(), data.result(), type, x->is_volatile()); - if (x->is_volatile() && os::is_MP()) __ membar(); + if (!support_IRIW_for_not_multiple_copy_atomic_cpu && x->is_volatile() && os::is_MP()) __ membar(); } @@ -2794,7 +2802,7 @@ void LIRGenerator::do_Base(Base* x) { assert(obj->is_valid(), "must be valid"); if (method()->is_synchronized() && GenerateSynchronizationCode) { - LIR_Opr lock = new_register(T_INT); + LIR_Opr lock = syncLockOpr(); __ load_stack_address_monitor(0, lock); CodeEmitInfo* info = new CodeEmitInfo(scope()->start()->state()->copy(ValueStack::StateBefore, SynchronizationEntryBCI), NULL, x->check_flag(Instruction::DeoptimizeOnException)); @@ -3421,14 +3429,18 @@ void LIRGenerator::increment_event_counter_impl(CodeEmitInfo* info, __ add(result, LIR_OprFact::intConst(InvocationCounter::count_increment), result); __ store(result, counter); if (notify) { - LIR_Opr mask = load_immediate(frequency << InvocationCounter::count_shift, T_INT); - LIR_Opr meth = new_register(T_METADATA); - __ metadata2reg(method->constant_encoding(), meth); - __ logical_and(result, mask, result); - __ cmp(lir_cond_equal, result, LIR_OprFact::intConst(0)); + LIR_Opr meth = LIR_OprFact::metadataConst(method->constant_encoding()); // The bci for info can point to cmp for if's we want the if bci CodeStub* overflow = new CounterOverflowStub(info, bci, meth); - __ branch(lir_cond_equal, T_INT, overflow); + int freq = frequency << InvocationCounter::count_shift; + if (freq == 0) { + __ branch(lir_cond_always, T_ILLEGAL, overflow); + } else { + LIR_Opr mask = load_immediate(freq, T_INT); + __ logical_and(result, mask, result); + __ cmp(lir_cond_equal, result, LIR_OprFact::intConst(0)); + __ branch(lir_cond_equal, T_INT, overflow); + } __ branch_destination(overflow->continuation()); } } diff --git a/hotspot/src/share/vm/c1/c1_LIRGenerator.hpp b/hotspot/src/share/vm/c1/c1_LIRGenerator.hpp index 69956e7d9e0..89f5960182a 100644 --- a/hotspot/src/share/vm/c1/c1_LIRGenerator.hpp +++ b/hotspot/src/share/vm/c1/c1_LIRGenerator.hpp @@ -495,6 +495,7 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure { static LIR_Opr divOutOpr(); static LIR_Opr remOutOpr(); static LIR_Opr shiftCountOpr(); + LIR_Opr syncLockOpr(); LIR_Opr syncTempOpr(); LIR_Opr atomicLockOpr(); diff --git a/hotspot/src/share/vm/c1/c1_LinearScan.cpp b/hotspot/src/share/vm/c1/c1_LinearScan.cpp index c5e4dc44012..d87862707cb 100644 --- a/hotspot/src/share/vm/c1/c1_LinearScan.cpp +++ b/hotspot/src/share/vm/c1/c1_LinearScan.cpp @@ -2087,7 +2087,7 @@ LIR_Opr LinearScan::calc_operand_for_interval(const Interval* interval) { #ifdef _LP64 return LIR_OprFact::double_cpu(assigned_reg, assigned_reg); #else -#if defined(SPARC) || defined(PPC) +#if defined(SPARC) || defined(PPC32) return LIR_OprFact::double_cpu(assigned_regHi, assigned_reg); #else return LIR_OprFact::double_cpu(assigned_reg, assigned_regHi); @@ -2728,7 +2728,7 @@ int LinearScan::append_scope_value_for_operand(LIR_Opr opr, GrowableArrayfpu_regnrHi() == opr->fpu_regnrLo() + 1, "assumed in calculation (only fpu_regnrLo is used)"); #endif -#ifdef PPC +#ifdef PPC32 assert(opr->fpu_regnrLo() == opr->fpu_regnrHi(), "assumed in calculation (only fpu_regnrHi is used)"); #endif @@ -6233,9 +6233,19 @@ void ControlFlowOptimizer::delete_unnecessary_jumps(BlockList* code) { if (prev_branch->stub() == NULL) { LIR_Op2* prev_cmp = NULL; + // There might be a cmove inserted for profiling which depends on the same + // compare. If we change the condition of the respective compare, we have + // to take care of this cmove as well. + LIR_Op2* prev_cmove = NULL; for(int j = instructions->length() - 3; j >= 0 && prev_cmp == NULL; j--) { prev_op = instructions->at(j); + // check for the cmove + if (prev_op->code() == lir_cmove) { + assert(prev_op->as_Op2() != NULL, "cmove must be of type LIR_Op2"); + prev_cmove = (LIR_Op2*)prev_op; + assert(prev_branch->cond() == prev_cmove->condition(), "should be the same"); + } if (prev_op->code() == lir_cmp) { assert(prev_op->as_Op2() != NULL, "branch must be of type LIR_Op2"); prev_cmp = (LIR_Op2*)prev_op; @@ -6252,6 +6262,13 @@ void ControlFlowOptimizer::delete_unnecessary_jumps(BlockList* code) { prev_branch->negate_cond(); prev_cmp->set_condition(prev_branch->cond()); instructions->truncate(instructions->length() - 1); + // if we do change the condition, we have to change the cmove as well + if (prev_cmove != NULL) { + prev_cmove->set_condition(prev_branch->cond()); + LIR_Opr t = prev_cmove->in_opr1(); + prev_cmove->set_in_opr1(prev_cmove->in_opr2()); + prev_cmove->set_in_opr2(t); + } } } } diff --git a/hotspot/src/share/vm/c1/c1_Runtime1.cpp b/hotspot/src/share/vm/c1/c1_Runtime1.cpp index 70f7d5f9041..cf6099d0d65 100644 --- a/hotspot/src/share/vm/c1/c1_Runtime1.cpp +++ b/hotspot/src/share/vm/c1/c1_Runtime1.cpp @@ -210,7 +210,7 @@ void Runtime1::generate_blob_for(BufferBlob* buffer_blob, StubID id) { case fpu2long_stub_id: case unwind_exception_id: case counter_overflow_id: -#if defined(SPARC) || defined(PPC) +#if defined(SPARC) || defined(PPC32) case handle_exception_nofpu_id: // Unused on sparc #endif break; @@ -1097,7 +1097,7 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* thread, Runtime1::StubID stub_i ShouldNotReachHere(); } -#if defined(SPARC) || defined(PPC) +#if defined(SPARC) || defined(PPC32) if (load_klass_or_mirror_patch_id || stub_id == Runtime1::load_appendix_patching_id) { // Update the location in the nmethod with the proper @@ -1195,7 +1195,7 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* thread, Runtime1::StubID stub_i relocInfo::change_reloc_info_for_address(&iter2, (address) instr_pc2, relocInfo::none, rtype); #endif -#ifdef PPC +#ifdef PPC32 { address instr_pc2 = instr_pc + NativeMovConstReg::lo_offset; RelocIterator iter2(nm, instr_pc2, instr_pc2 + 1); relocInfo::change_reloc_info_for_address(&iter2, (address) instr_pc2, diff --git a/hotspot/src/share/vm/ci/ciMethod.cpp b/hotspot/src/share/vm/ci/ciMethod.cpp index 0acf602a4cc..e8d69c1ae3b 100644 --- a/hotspot/src/share/vm/ci/ciMethod.cpp +++ b/hotspot/src/share/vm/ci/ciMethod.cpp @@ -1262,6 +1262,8 @@ bool ciMethod::is_empty_method() const { FETCH_FLAG_FROM_VM(is_empty_met bool ciMethod::is_vanilla_constructor() const { FETCH_FLAG_FROM_VM(is_vanilla_constructor); } bool ciMethod::has_loops () const { FETCH_FLAG_FROM_VM(has_loops); } bool ciMethod::has_jsrs () const { FETCH_FLAG_FROM_VM(has_jsrs); } +bool ciMethod::is_getter () const { FETCH_FLAG_FROM_VM(is_getter); } +bool ciMethod::is_setter () const { FETCH_FLAG_FROM_VM(is_setter); } bool ciMethod::is_accessor () const { FETCH_FLAG_FROM_VM(is_accessor); } bool ciMethod::is_initializer () const { FETCH_FLAG_FROM_VM(is_initializer); } diff --git a/hotspot/src/share/vm/ci/ciMethod.hpp b/hotspot/src/share/vm/ci/ciMethod.hpp index 743a16463ee..5b19e95d39f 100644 --- a/hotspot/src/share/vm/ci/ciMethod.hpp +++ b/hotspot/src/share/vm/ci/ciMethod.hpp @@ -311,6 +311,8 @@ class ciMethod : public ciMetadata { bool is_final_method() const { return is_final() || holder()->is_final(); } bool has_loops () const; bool has_jsrs () const; + bool is_getter () const; + bool is_setter () const; bool is_accessor () const; bool is_initializer () const; bool can_be_statically_bound() const { return _can_be_statically_bound; } diff --git a/hotspot/src/share/vm/ci/ciTypeFlow.cpp b/hotspot/src/share/vm/ci/ciTypeFlow.cpp index 8a5ad392bed..a1675d17a14 100644 --- a/hotspot/src/share/vm/ci/ciTypeFlow.cpp +++ b/hotspot/src/share/vm/ci/ciTypeFlow.cpp @@ -1588,6 +1588,7 @@ ciTypeFlow::Block::Block(ciTypeFlow* outer, _exceptions = NULL; _exc_klasses = NULL; _successors = NULL; + _predecessors = new (outer->arena()) GrowableArray(outer->arena(), 1, 0, NULL); _state = new (outer->arena()) StateVector(outer); JsrSet* new_jsrs = new (outer->arena()) JsrSet(outer->arena(), jsrs->size()); @@ -1771,6 +1772,12 @@ ciTypeFlow::Block::successors(ciBytecodeStream* str, break; } } + + // Set predecessor information + for (int i = 0; i < _successors->length(); i++) { + Block* block = _successors->at(i); + block->predecessors()->append(this); + } } return _successors; } @@ -1813,7 +1820,9 @@ void ciTypeFlow::Block::compute_exceptions() { } else { klass = handler->catch_klass(); } - _exceptions->append(analyzer->block_at(bci, _jsrs)); + Block* block = analyzer->block_at(bci, _jsrs); + _exceptions->append(block); + block->predecessors()->append(this); _exc_klasses->append(klass); } } @@ -1909,6 +1918,18 @@ void ciTypeFlow::Block::print_on(outputStream* st) const { st->cr(); } } + if (_predecessors == NULL) { + st->print_cr(" No predecessor information"); + } else { + int num_predecessors = _predecessors->length(); + st->print_cr(" Predecessors : %d", num_predecessors); + for (int i = 0; i < num_predecessors; i++) { + Block* predecessor = _predecessors->at(i); + st->print(" "); + predecessor->print_value_on(st); + st->cr(); + } + } if (_exceptions == NULL) { st->print_cr(" No exception information"); } else { @@ -2270,6 +2291,9 @@ ciTypeFlow::Block* ciTypeFlow::clone_loop_head(Loop* lp, StateVector* temp_vecto for (SuccIter iter(tail); !iter.done(); iter.next()) { if (iter.succ() == head) { iter.set_succ(clone); + // Update predecessor information + head->predecessors()->remove(tail); + clone->predecessors()->append(tail); } } flow_block(tail, temp_vector, temp_set); @@ -2279,6 +2303,9 @@ ciTypeFlow::Block* ciTypeFlow::clone_loop_head(Loop* lp, StateVector* temp_vecto for (SuccIter iter(clone); !iter.done(); iter.next()) { if (iter.succ() == head) { iter.set_succ(clone); + // Update predecessor information + head->predecessors()->remove(clone); + clone->predecessors()->append(clone); break; } } @@ -2883,6 +2910,69 @@ void ciTypeFlow::do_flow() { } } +// ------------------------------------------------------------------ +// ciTypeFlow::is_dominated_by +// +// Determine if the instruction at bci is dominated by the instruction at dom_bci. +bool ciTypeFlow::is_dominated_by(int bci, int dom_bci) { + assert(!method()->has_jsrs(), "jsrs are not supported"); + + ResourceMark rm; + JsrSet* jsrs = new ciTypeFlow::JsrSet(NULL); + int index = _methodBlocks->block_containing(bci)->index(); + int dom_index = _methodBlocks->block_containing(dom_bci)->index(); + Block* block = get_block_for(index, jsrs, ciTypeFlow::no_create); + Block* dom_block = get_block_for(dom_index, jsrs, ciTypeFlow::no_create); + + // Start block dominates all other blocks + if (start_block()->rpo() == dom_block->rpo()) { + return true; + } + + // Dominated[i] is true if block i is dominated by dom_block + int num_blocks = _methodBlocks->num_blocks(); + bool* dominated = NEW_RESOURCE_ARRAY(bool, num_blocks); + for (int i = 0; i < num_blocks; ++i) { + dominated[i] = true; + } + dominated[start_block()->rpo()] = false; + + // Iterative dominator algorithm + bool changed = true; + while (changed) { + changed = false; + // Use reverse postorder iteration + for (Block* blk = _rpo_list; blk != NULL; blk = blk->rpo_next()) { + if (blk->is_start()) { + // Ignore start block + continue; + } + // The block is dominated if it is the dominating block + // itself or if all predecessors are dominated. + int index = blk->rpo(); + bool dom = (index == dom_block->rpo()); + if (!dom) { + // Check if all predecessors are dominated + dom = true; + for (int i = 0; i < blk->predecessors()->length(); ++i) { + Block* pred = blk->predecessors()->at(i); + if (!dominated[pred->rpo()]) { + dom = false; + break; + } + } + } + // Update dominator information + if (dominated[index] != dom) { + changed = true; + dominated[index] = dom; + } + } + } + // block dominated by dom_block? + return dominated[block->rpo()]; +} + // ------------------------------------------------------------------ // ciTypeFlow::record_failure() // The ciTypeFlow object keeps track of failure reasons separately from the ciEnv. diff --git a/hotspot/src/share/vm/ci/ciTypeFlow.hpp b/hotspot/src/share/vm/ci/ciTypeFlow.hpp index 012ffb05ed0..381fff86a14 100644 --- a/hotspot/src/share/vm/ci/ciTypeFlow.hpp +++ b/hotspot/src/share/vm/ci/ciTypeFlow.hpp @@ -529,6 +529,7 @@ public: GrowableArray* _exceptions; GrowableArray* _exc_klasses; GrowableArray* _successors; + GrowableArray* _predecessors; StateVector* _state; JsrSet* _jsrs; @@ -617,6 +618,12 @@ public: return _successors; } + // Predecessors of this block (including exception edges) + GrowableArray* predecessors() { + assert(_predecessors != NULL, "must be filled in"); + return _predecessors; + } + // Get the exceptional successors for this Block. GrowableArray* exceptions() { if (_exceptions == NULL) { @@ -941,6 +948,9 @@ public: // Perform type inference flow analysis. void do_flow(); + // Determine if bci is dominated by dom_bci + bool is_dominated_by(int bci, int dom_bci); + void print_on(outputStream* st) const PRODUCT_RETURN; void rpo_print_on(outputStream* st) const PRODUCT_RETURN; diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp index c9296533878..aa788362bc7 100644 --- a/hotspot/src/share/vm/classfile/classFileParser.cpp +++ b/hotspot/src/share/vm/classfile/classFileParser.cpp @@ -1693,7 +1693,7 @@ void ClassFileParser::parse_annotations(u1* buffer, int limit, if (id == AnnotationCollector::_unknown) continue; coll->set_annotation(id); - if (id == AnnotationCollector::_sun_misc_Contended) { + if (id == AnnotationCollector::_jdk_internal_vm_annotation_Contended) { // @Contended can optionally specify the contention group. // // Contended group defines the equivalence class over the fields: @@ -1767,10 +1767,10 @@ ClassFileParser::AnnotationCollector::annotation_index(ClassLoaderData* loader_d if (_location != _in_field) break; // only allow for fields if (!privileged) break; // only allow in privileged code return _field_Stable; - case vmSymbols::VM_SYMBOL_ENUM_NAME(sun_misc_Contended_signature): + case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_Contended_signature): if (_location != _in_field && _location != _in_class) break; // only allow for fields and classes if (!EnableContended || (RestrictContended && !privileged)) break; // honor privileges - return _sun_misc_Contended; + return _jdk_internal_vm_annotation_Contended; default: break; } return AnnotationCollector::_unknown; diff --git a/hotspot/src/share/vm/classfile/classFileParser.hpp b/hotspot/src/share/vm/classfile/classFileParser.hpp index fc9b7a0c179..ec7ea19cba2 100644 --- a/hotspot/src/share/vm/classfile/classFileParser.hpp +++ b/hotspot/src/share/vm/classfile/classFileParser.hpp @@ -131,7 +131,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { _method_LambdaForm_Compiled, _method_LambdaForm_Hidden, _method_HotSpotIntrinsicCandidate, - _sun_misc_Contended, + _jdk_internal_vm_annotation_Contended, _field_Stable, _annotation_LIMIT }; @@ -164,7 +164,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { void set_contended_group(u2 group) { _contended_group = group; } u2 contended_group() const { return _contended_group; } - bool is_contended() const { return has_annotation(_sun_misc_Contended); } + bool is_contended() const { return has_annotation(_jdk_internal_vm_annotation_Contended); } void set_stable(bool stable) { set_annotation(_field_Stable); } bool is_stable() const { return has_annotation(_field_Stable); } diff --git a/hotspot/src/share/vm/classfile/classListParser.cpp b/hotspot/src/share/vm/classfile/classListParser.cpp new file mode 100644 index 00000000000..8f7be316b50 --- /dev/null +++ b/hotspot/src/share/vm/classfile/classListParser.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +#include "precompiled.hpp" +#include "classfile/classListParser.hpp" +#include "runtime/os.hpp" +#include "runtime/java.hpp" + +ClassListParser::ClassListParser(const char* file) { + _classlist_file = file; + _file = fopen(file, "r"); + if (_file == NULL) { + char errmsg[JVM_MAXPATHLEN]; + os::lasterror(errmsg, JVM_MAXPATHLEN); + vm_exit_during_initialization("Loading classlist failed", errmsg); + } +} + +ClassListParser::~ClassListParser() { + if (_file) { + fclose(_file); + } +} + +bool ClassListParser::parse_one_line() { + for (;;) { + if (fgets(_line, sizeof(_line), _file) == NULL) { + return false; + } + int line_len = (int)strlen(_line); + if (line_len > _max_allowed_line_len) { + tty->print_cr("input line too long (must be no longer than %d chars)", _max_allowed_line_len); + vm_exit_during_initialization("Loading classlist failed"); + } + if (*_line == '#') { // comment + continue; + } + break; + } + + // Remove trailing \r\n + _line[strcspn(_line, "\r\n")] = 0; + return true; +} + diff --git a/hotspot/src/share/vm/classfile/classListParser.hpp b/hotspot/src/share/vm/classfile/classListParser.hpp new file mode 100644 index 00000000000..912ae3175a3 --- /dev/null +++ b/hotspot/src/share/vm/classfile/classListParser.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +#ifndef SHARE_VM_MEMORY_CLASSLISTPARSER_HPP +#define SHARE_VM_MEMORY_CLASSLISTPARSER_HPP + +#include "utilities/exceptions.hpp" +#include "utilities/globalDefinitions.hpp" + +class ClassListParser : public StackObj { + enum { + // Max number of bytes allowed per line in the classlist. + // Theoretically Java class names could be 65535 bytes in length. In reality, + // 4K bytes is more than enough. + _max_allowed_line_len = 4096, + _line_buf_extra = 10, // for detecting input too long + _line_buf_size = _max_allowed_line_len + _line_buf_extra + }; + + const char* _classlist_file; + FILE* _file; + char _line[_line_buf_size]; // The buffer that holds the current line. + +public: + ClassListParser(const char* file); + ~ClassListParser(); + bool parse_one_line(); + + const char* current_class_name() { + return _line; + } +}; + + +#endif // SHARE_VM_MEMORY_CLASSLISTPARSER_HPP diff --git a/hotspot/src/share/vm/classfile/classLoader.cpp b/hotspot/src/share/vm/classfile/classLoader.cpp index 21140f33c6e..a89d50db966 100644 --- a/hotspot/src/share/vm/classfile/classLoader.cpp +++ b/hotspot/src/share/vm/classfile/classLoader.cpp @@ -414,30 +414,30 @@ void ClassLoader::exit_with_path_failure(const char* error, const char* message) } #endif -void ClassLoader::trace_class_path(const char* msg, const char* name) { +void ClassLoader::trace_class_path(outputStream* out, const char* msg, const char* name) { if (!TraceClassPaths) { return; } if (msg) { - tty->print("%s", msg); + out->print("%s", msg); } if (name) { if (strlen(name) < 256) { - tty->print("%s", name); + out->print("%s", name); } else { // For very long paths, we need to print each character separately, // as print_cr() has a length limit while (name[0] != '\0') { - tty->print("%c", name[0]); + out->print("%c", name[0]); name++; } } } if (msg && msg[0] == '[') { - tty->print_cr("]"); + out->print_cr("]"); } else { - tty->cr(); + out->cr(); } } @@ -466,7 +466,7 @@ void ClassLoader::setup_bootstrap_search_path() { // Don't print sys_class_path - this is the bootcp of this current VM process, not necessarily // the same as the bootcp of the shared archive. } else { - trace_class_path("[Bootstrap loader class path=", sys_class_path); + trace_class_path(tty, "[Bootstrap loader class path=", sys_class_path); } #if INCLUDE_CDS if (DumpSharedSpaces) { diff --git a/hotspot/src/share/vm/classfile/classLoader.hpp b/hotspot/src/share/vm/classfile/classLoader.hpp index 4971c7c3b9a..63034372251 100644 --- a/hotspot/src/share/vm/classfile/classLoader.hpp +++ b/hotspot/src/share/vm/classfile/classLoader.hpp @@ -328,7 +328,7 @@ class ClassLoader: AllStatic { static void exit_with_path_failure(const char* error, const char* message); #endif - static void trace_class_path(const char* msg, const char* name = NULL); + static void trace_class_path(outputStream* out, const char* msg, const char* name = NULL); // VM monitoring and management support static jlong classloader_time_ms(); diff --git a/hotspot/src/share/vm/classfile/classLoaderData.cpp b/hotspot/src/share/vm/classfile/classLoaderData.cpp index 3d0727ed67e..734b4acb225 100644 --- a/hotspot/src/share/vm/classfile/classLoaderData.cpp +++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp @@ -82,7 +82,7 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool is_anonymous, Depen _keep_alive(is_anonymous || h_class_loader.is_null()), _metaspace(NULL), _unloading(false), _klasses(NULL), _claimed(0), _jmethod_ids(NULL), _handles(NULL), _deallocate_list(NULL), - _next(NULL), _dependencies(dependencies), + _next(NULL), _dependencies(dependencies), _shared_class_loader_id(-1), _metaspace_lock(new Mutex(Monitor::leaf+1, "Metaspace allocation lock", true, Monitor::_safepoint_check_never)) { // empty diff --git a/hotspot/src/share/vm/classfile/classLoaderData.hpp b/hotspot/src/share/vm/classfile/classLoaderData.hpp index fce18be32f0..ce323c799f8 100644 --- a/hotspot/src/share/vm/classfile/classLoaderData.hpp +++ b/hotspot/src/share/vm/classfile/classLoaderData.hpp @@ -187,6 +187,9 @@ class ClassLoaderData : public CHeapObj { // Support for walking class loader data objects ClassLoaderData* _next; /// Next loader_datas created + // CDS + int _shared_class_loader_id; + // ReadOnly and ReadWrite metaspaces (static because only on the null // class loader for now). static Metaspace* _ro_metaspace; @@ -308,6 +311,15 @@ class ClassLoaderData : public CHeapObj { Metaspace* ro_metaspace(); Metaspace* rw_metaspace(); void initialize_shared_metaspaces(); + + int shared_class_loader_id() { + return _shared_class_loader_id; + } + void set_shared_class_loader_id(int id) { + assert(id >= 0, "sanity"); + assert(_shared_class_loader_id <0, "cannot be assigned more than once"); + _shared_class_loader_id = id; + } }; // An iterator that distributes Klasses to parallel worker threads. diff --git a/hotspot/src/share/vm/classfile/classLoaderExt.cpp b/hotspot/src/share/vm/classfile/classLoaderExt.cpp new file mode 100644 index 00000000000..44efabec083 --- /dev/null +++ b/hotspot/src/share/vm/classfile/classLoaderExt.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +#include "precompiled.hpp" +#include "classfile/classListParser.hpp" +#include "classfile/classLoaderExt.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" + + +Klass* ClassLoaderExt::load_one_class(ClassListParser* parser, TRAPS) { + TempNewSymbol class_name_symbol = SymbolTable::new_symbol(parser->current_class_name(), THREAD); + guarantee(!HAS_PENDING_EXCEPTION, "Exception creating a symbol."); + return SystemDictionary::resolve_or_null(class_name_symbol, THREAD); +} diff --git a/hotspot/src/share/vm/classfile/classLoaderExt.hpp b/hotspot/src/share/vm/classfile/classLoaderExt.hpp index 52d9c07d32c..c455a25bd5c 100644 --- a/hotspot/src/share/vm/classfile/classLoaderExt.hpp +++ b/hotspot/src/share/vm/classfile/classLoaderExt.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2015, 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 @@ -29,6 +29,8 @@ #include "oops/instanceKlass.hpp" #include "runtime/handles.hpp" +class ClassListParser; + class ClassLoaderExt: public ClassLoader { // AllStatic public: @@ -69,6 +71,7 @@ public: ClassLoader::add_to_list(new_entry); } static void setup_search_paths() {} + static Klass* load_one_class(ClassListParser* parser, TRAPS); }; #endif // SHARE_VM_CLASSFILE_CLASSLOADEREXT_HPP diff --git a/hotspot/src/share/vm/classfile/compactHashtable.cpp b/hotspot/src/share/vm/classfile/compactHashtable.cpp index 39dd55f9a17..c951c512558 100644 --- a/hotspot/src/share/vm/classfile/compactHashtable.cpp +++ b/hotspot/src/share/vm/classfile/compactHashtable.cpp @@ -137,7 +137,7 @@ juint* CompactHashtableWriter::dump_buckets(juint* compact_table, juint* p, if (_type == CompactHashtable::_symbol_table) { base_address = uintx(MetaspaceShared::shared_rs()->base()); max_delta = uintx(MetaspaceShared::shared_rs()->size()); - assert(max_delta <= 0x7fffffff, "range check"); + assert(max_delta <= MAX_SHARED_DELTA, "range check"); } else { assert((_type == CompactHashtable::_string_table), "unknown table"); assert(UseCompressedOops, "UseCompressedOops is required"); diff --git a/hotspot/src/share/vm/classfile/dictionary.cpp b/hotspot/src/share/vm/classfile/dictionary.cpp index 82e4e71cf81..fde1bb4c6c7 100644 --- a/hotspot/src/share/vm/classfile/dictionary.cpp +++ b/hotspot/src/share/vm/classfile/dictionary.cpp @@ -23,8 +23,10 @@ */ #include "precompiled.hpp" +#include "classfile/sharedClassUtil.hpp" #include "classfile/dictionary.hpp" #include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "memory/iterator.hpp" #include "oops/oop.inline.hpp" #include "prims/jvmtiRedefineClassesTrace.hpp" @@ -34,9 +36,16 @@ DictionaryEntry* Dictionary::_current_class_entry = NULL; int Dictionary::_current_class_index = 0; +size_t Dictionary::entry_size() { + if (DumpSharedSpaces) { + return SystemDictionaryShared::dictionary_entry_size(); + } else { + return sizeof(DictionaryEntry); + } +} Dictionary::Dictionary(int table_size) - : TwoOopHashtable(table_size, sizeof(DictionaryEntry)) { + : TwoOopHashtable(table_size, (int)entry_size()) { _current_class_index = 0; _current_class_entry = NULL; _pd_cache_table = new ProtectionDomainCacheTable(defaultProtectionDomainCacheSize); @@ -45,7 +54,7 @@ Dictionary::Dictionary(int table_size) Dictionary::Dictionary(int table_size, HashtableBucket* t, int number_of_entries) - : TwoOopHashtable(table_size, sizeof(DictionaryEntry), t, number_of_entries) { + : TwoOopHashtable(table_size, (int)entry_size(), t, number_of_entries) { _current_class_index = 0; _current_class_entry = NULL; _pd_cache_table = new ProtectionDomainCacheTable(defaultProtectionDomainCacheSize); @@ -61,6 +70,9 @@ DictionaryEntry* Dictionary::new_entry(unsigned int hash, Klass* klass, entry->set_loader_data(loader_data); entry->set_pd_set(NULL); assert(klass->is_instance_klass(), "Must be"); + if (DumpSharedSpaces) { + SystemDictionaryShared::init_shared_dictionary_entry(klass, entry); + } return entry; } diff --git a/hotspot/src/share/vm/classfile/dictionary.hpp b/hotspot/src/share/vm/classfile/dictionary.hpp index 1f6b568670e..19bd96e1380 100644 --- a/hotspot/src/share/vm/classfile/dictionary.hpp +++ b/hotspot/src/share/vm/classfile/dictionary.hpp @@ -53,6 +53,7 @@ private: DictionaryEntry* get_entry(int index, unsigned int hash, Symbol* name, ClassLoaderData* loader_data); +protected: DictionaryEntry* bucket(int i) { return (DictionaryEntry*)Hashtable::bucket(i); } @@ -66,6 +67,7 @@ private: Hashtable::add_entry(index, (HashtableEntry*)new_entry); } + static size_t entry_size(); public: Dictionary(int table_size); Dictionary(int table_size, HashtableBucket* t, int number_of_entries); diff --git a/hotspot/src/share/vm/classfile/javaClasses.cpp b/hotspot/src/share/vm/classfile/javaClasses.cpp index a9f7c4616c6..1d9179aa9ef 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.cpp +++ b/hotspot/src/share/vm/classfile/javaClasses.cpp @@ -28,6 +28,7 @@ #include "classfile/stringTable.hpp" #include "classfile/vmSymbols.hpp" #include "code/debugInfo.hpp" +#include "code/dependencyContext.hpp" #include "code/pcDesc.hpp" #include "interpreter/interpreter.hpp" #include "memory/oopFactory.hpp" @@ -1518,43 +1519,11 @@ void java_lang_Throwable::print(Handle throwable, outputStream* st) { // After this many redefines, the stack trace is unreliable. const int MAX_VERSION = USHRT_MAX; -// Helper backtrace functions to store bci|version together. -static inline int merge_bci_and_version(int bci, int version) { - // only store u2 for version, checking for overflow. - if (version > USHRT_MAX || version < 0) version = MAX_VERSION; - assert((jushort)bci == bci, "bci should be short"); - return build_int_from_shorts(version, bci); -} - -static inline int bci_at(unsigned int merged) { - return extract_high_short_from_int(merged); -} -static inline int version_at(unsigned int merged) { - return extract_low_short_from_int(merged); -} - static inline bool version_matches(Method* method, int version) { assert(version < MAX_VERSION, "version is too big"); return method != NULL && (method->constants()->version() == version); } -static inline int get_line_number(Method* method, int bci) { - int line_number = 0; - if (method->is_native()) { - // Negative value different from -1 below, enabling Java code in - // class java.lang.StackTraceElement to distinguish "native" from - // "no LineNumberTable". JDK tests for -2. - line_number = -2; - } else { - // Returns -1 if no LineNumberTable, and otherwise actual line number - line_number = method->line_number_from_bci(bci); - if (line_number == -1 && ShowHiddenFrames) { - line_number = bci + 1000000; - } - } - return line_number; -} - // This class provides a simple wrapper over the internal structure of // exception backtrace to insulate users of the backtrace from needing // to know what it looks like. @@ -1676,7 +1645,7 @@ class BacktraceBuilder: public StackObj { } _methods->short_at_put(_index, method->orig_method_idnum()); - _bcis->int_at_put(_index, merge_bci_and_version(bci, method->constants()->version())); + _bcis->int_at_put(_index, Backtrace::merge_bci_and_version(bci, method->constants()->version())); _cprefs->short_at_put(_index, method->name_index()); // We need to save the mirrors in the backtrace to keep the class @@ -1688,19 +1657,6 @@ class BacktraceBuilder: public StackObj { }; -Symbol* get_source_file_name(InstanceKlass* holder, int version) { - // Find the specific ik version that contains this source_file_name_index - // via the previous versions list, but use the current version's - // constant pool to look it up. The previous version's index has been - // merged for the current constant pool. - InstanceKlass* ik = holder->get_klass_version(version); - // This version has been cleaned up. - if (ik == NULL) return NULL; - int source_file_name_index = ik->source_file_name_index(); - return (source_file_name_index == 0) ? - (Symbol*)NULL : holder->constants()->symbol_at(source_file_name_index); -} - // Print stack trace element to resource allocated buffer char* java_lang_Throwable::print_stack_element_to_buffer(Handle mirror, int method_id, int version, int bci, int cpref) { @@ -1718,7 +1674,7 @@ char* java_lang_Throwable::print_stack_element_to_buffer(Handle mirror, buf_len += (int)strlen(method_name); char* source_file_name = NULL; - Symbol* source = get_source_file_name(holder, version); + Symbol* source = Backtrace::get_source_file_name(holder, version); if (source != NULL) { source_file_name = source->as_C_string(); buf_len += (int)strlen(source_file_name); @@ -1733,7 +1689,7 @@ char* java_lang_Throwable::print_stack_element_to_buffer(Handle mirror, if (!version_matches(method, version)) { strcat(buf, "(Redefined)"); } else { - int line_number = get_line_number(method, bci); + int line_number = Backtrace::get_line_number(method, bci); if (line_number == -2) { strcat(buf, "(Native Method)"); } else { @@ -1802,8 +1758,8 @@ void java_lang_Throwable::print_stack_trace(oop throwable, outputStream* st) { // NULL mirror means end of stack trace if (mirror.is_null()) goto handle_cause; int method = methods->short_at(index); - int version = version_at(bcis->int_at(index)); - int bci = bci_at(bcis->int_at(index)); + int version = Backtrace::version_at(bcis->int_at(index)); + int bci = Backtrace::bci_at(bcis->int_at(index)); int cpref = cprefs->short_at(index); print_stack_element(st, mirror, method, version, bci, cpref); } @@ -2090,8 +2046,8 @@ oop java_lang_Throwable::get_stack_trace_element(oop throwable, int index, TRAPS assert(methods != NULL && bcis != NULL && mirrors != NULL, "sanity check"); int method = methods->short_at(chunk_index); - int version = version_at(bcis->int_at(chunk_index)); - int bci = bci_at(bcis->int_at(chunk_index)); + int version = Backtrace::version_at(bcis->int_at(chunk_index)); + int bci = Backtrace::bci_at(bcis->int_at(chunk_index)); int cpref = cprefs->short_at(chunk_index); Handle mirror(THREAD, mirrors->obj_at(chunk_index)); @@ -2114,6 +2070,7 @@ oop java_lang_StackTraceElement::create(Handle mirror, int method_id, } Handle element = ik->allocate_instance_handle(CHECK_0); + // Fill in class name ResourceMark rm(THREAD); InstanceKlass* holder = InstanceKlass::cast(java_lang_Class::as_Klass(mirror())); @@ -2136,13 +2093,13 @@ oop java_lang_StackTraceElement::create(Handle mirror, int method_id, java_lang_StackTraceElement::set_lineNumber(element(), -1); } else { // Fill in source file name and line number. - Symbol* source = get_source_file_name(holder, version); + Symbol* source = Backtrace::get_source_file_name(holder, version); if (ShowHiddenFrames && source == NULL) source = vmSymbols::unknown_class_name(); oop filename = StringTable::intern(source, CHECK_0); java_lang_StackTraceElement::set_fileName(element(), filename); - int line_number = get_line_number(method, bci); + int line_number = Backtrace::get_line_number(method, bci); java_lang_StackTraceElement::set_lineNumber(element(), line_number); } return element(); @@ -2155,6 +2112,108 @@ oop java_lang_StackTraceElement::create(const methodHandle& method, int bci, TRA return create(mirror, method_id, method->constants()->version(), bci, cpref, THREAD); } +Method* java_lang_StackFrameInfo::get_method(Handle stackFrame, InstanceKlass* holder, TRAPS) { + if (MemberNameInStackFrame) { + Handle mname(THREAD, stackFrame->obj_field(_memberName_offset)); + Method* method = (Method*)java_lang_invoke_MemberName::vmtarget(mname()); + // we should expand MemberName::name when Throwable uses StackTrace + // MethodHandles::expand_MemberName(mname, MethodHandles::_suppress_defc|MethodHandles::_suppress_type, CHECK_NULL); + return method; + } else { + short mid = stackFrame->short_field(_mid_offset); + short version = stackFrame->short_field(_version_offset); + return holder->method_with_orig_idnum(mid, version); + } +} + +Symbol* java_lang_StackFrameInfo::get_file_name(Handle stackFrame, InstanceKlass* holder) { + if (MemberNameInStackFrame) { + return holder->source_file_name(); + } else { + short version = stackFrame->short_field(_version_offset); + return Backtrace::get_source_file_name(holder, version); + } +} + +void java_lang_StackFrameInfo::set_method_and_bci(Handle stackFrame, const methodHandle& method, int bci) { + // set Method* or mid/cpref + if (MemberNameInStackFrame) { + oop mname = stackFrame->obj_field(_memberName_offset); + InstanceKlass* ik = method->method_holder(); + CallInfo info(method(), ik); + MethodHandles::init_method_MemberName(mname, info); + } else { + int mid = method->orig_method_idnum(); + int cpref = method->name_index(); + assert((jushort)mid == mid, "mid should be short"); + assert((jushort)cpref == cpref, "cpref should be short"); + java_lang_StackFrameInfo::set_mid(stackFrame(), (short)mid); + java_lang_StackFrameInfo::set_cpref(stackFrame(), (short)cpref); + } + // set bci + java_lang_StackFrameInfo::set_bci(stackFrame(), bci); + // method may be redefined; store the version + int version = method->constants()->version(); + assert((jushort)version == version, "version should be short"); + java_lang_StackFrameInfo::set_version(stackFrame(), (short)version); +} + +void java_lang_StackFrameInfo::fill_methodInfo(Handle stackFrame, TRAPS) { + ResourceMark rm(THREAD); + oop k = stackFrame->obj_field(_declaringClass_offset); + InstanceKlass* holder = InstanceKlass::cast(java_lang_Class::as_Klass(k)); + Method* method = java_lang_StackFrameInfo::get_method(stackFrame, holder, CHECK); + int bci = stackFrame->int_field(_bci_offset); + + // The method can be NULL if the requested class version is gone + Symbol* sym = (method != NULL) ? method->name() : NULL; + if (MemberNameInStackFrame) { + assert(sym != NULL, "MemberName must have method name"); + } else { + // The method can be NULL if the requested class version is gone + if (sym == NULL) { + short cpref = stackFrame->short_field(_cpref_offset); + sym = holder->constants()->symbol_at(cpref); + } + } + + // set method name + oop methodname = StringTable::intern(sym, CHECK); + java_lang_StackFrameInfo::set_methodName(stackFrame(), methodname); + + // set file name and line number + Symbol* source = get_file_name(stackFrame, holder); + if (source != NULL) { + oop filename = StringTable::intern(source, CHECK); + java_lang_StackFrameInfo::set_fileName(stackFrame(), filename); + } + + // if the method has been redefined, the bci is no longer applicable + short version = stackFrame->short_field(_version_offset); + if (version_matches(method, version)) { + int line_number = Backtrace::get_line_number(method, bci); + java_lang_StackFrameInfo::set_lineNumber(stackFrame(), line_number); + } +} + +void java_lang_StackFrameInfo::compute_offsets() { + Klass* k = SystemDictionary::StackFrameInfo_klass(); + compute_offset(_declaringClass_offset, k, vmSymbols::declaringClass_name(), vmSymbols::class_signature()); + compute_offset(_memberName_offset, k, vmSymbols::memberName_name(), vmSymbols::object_signature()); + compute_offset(_bci_offset, k, vmSymbols::bci_name(), vmSymbols::int_signature()); + compute_offset(_methodName_offset, k, vmSymbols::methodName_name(), vmSymbols::string_signature()); + compute_offset(_fileName_offset, k, vmSymbols::fileName_name(), vmSymbols::string_signature()); + compute_offset(_lineNumber_offset, k, vmSymbols::lineNumber_name(), vmSymbols::int_signature()); + STACKFRAMEINFO_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET); +} + +void java_lang_LiveStackFrameInfo::compute_offsets() { + Klass* k = SystemDictionary::LiveStackFrameInfo_klass(); + compute_offset(_monitors_offset, k, vmSymbols::monitors_name(), vmSymbols::object_array_signature()); + compute_offset(_locals_offset, k, vmSymbols::locals_name(), vmSymbols::object_array_signature()); + compute_offset(_operands_offset, k, vmSymbols::operands_name(), vmSymbols::object_array_signature()); +} + void java_lang_reflect_AccessibleObject::compute_offsets() { Klass* k = SystemDictionary::reflect_AccessibleObject_klass(); compute_offset(override_offset, k, vmSymbols::override_name(), vmSymbols::bool_signature()); @@ -3216,14 +3275,11 @@ void java_lang_invoke_MethodHandleNatives_CallSiteContext::compute_offsets() { } } -nmethodBucket* java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(oop call_site) { +DependencyContext java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(oop call_site) { assert(java_lang_invoke_MethodHandleNatives_CallSiteContext::is_instance(call_site), ""); - return (nmethodBucket*) (address) call_site->long_field(_vmdependencies_offset); -} - -void java_lang_invoke_MethodHandleNatives_CallSiteContext::set_vmdependencies(oop call_site, nmethodBucket* context) { - assert(java_lang_invoke_MethodHandleNatives_CallSiteContext::is_instance(call_site), ""); - call_site->long_field_put(_vmdependencies_offset, (jlong) (address) context); + intptr_t* vmdeps_addr = (intptr_t*)call_site->address_field_addr(_vmdependencies_offset); + DependencyContext dep_ctx(vmdeps_addr); + return dep_ctx; } // Support for java_security_AccessControlContext @@ -3471,6 +3527,18 @@ int java_lang_StackTraceElement::declaringClass_offset; int java_lang_StackTraceElement::methodName_offset; int java_lang_StackTraceElement::fileName_offset; int java_lang_StackTraceElement::lineNumber_offset; +int java_lang_StackFrameInfo::_declaringClass_offset; +int java_lang_StackFrameInfo::_memberName_offset; +int java_lang_StackFrameInfo::_bci_offset; +int java_lang_StackFrameInfo::_methodName_offset; +int java_lang_StackFrameInfo::_fileName_offset; +int java_lang_StackFrameInfo::_lineNumber_offset; +int java_lang_StackFrameInfo::_mid_offset; +int java_lang_StackFrameInfo::_version_offset; +int java_lang_StackFrameInfo::_cpref_offset; +int java_lang_LiveStackFrameInfo::_monitors_offset; +int java_lang_LiveStackFrameInfo::_locals_offset; +int java_lang_LiveStackFrameInfo::_operands_offset; int java_lang_AssertionStatusDirectives::classes_offset; int java_lang_AssertionStatusDirectives::classEnabled_offset; int java_lang_AssertionStatusDirectives::packages_offset; @@ -3500,6 +3568,50 @@ void java_lang_StackTraceElement::set_lineNumber(oop element, int value) { element->int_field_put(lineNumber_offset, value); } +// Support for java_lang_StackFrameInfo +void java_lang_StackFrameInfo::set_declaringClass(oop element, oop value) { + element->obj_field_put(_declaringClass_offset, value); +} + +void java_lang_StackFrameInfo::set_mid(oop element, short value) { + element->short_field_put(_mid_offset, value); +} + +void java_lang_StackFrameInfo::set_version(oop element, short value) { + element->short_field_put(_version_offset, value); +} + +void java_lang_StackFrameInfo::set_cpref(oop element, short value) { + element->short_field_put(_cpref_offset, value); +} + +void java_lang_StackFrameInfo::set_bci(oop element, int value) { + element->int_field_put(_bci_offset, value); +} + +void java_lang_StackFrameInfo::set_fileName(oop element, oop value) { + element->obj_field_put(_fileName_offset, value); +} + +void java_lang_StackFrameInfo::set_methodName(oop element, oop value) { + element->obj_field_put(_methodName_offset, value); +} + +void java_lang_StackFrameInfo::set_lineNumber(oop element, int value) { + element->int_field_put(_lineNumber_offset, value); +} + +void java_lang_LiveStackFrameInfo::set_monitors(oop element, oop value) { + element->obj_field_put(_monitors_offset, value); +} + +void java_lang_LiveStackFrameInfo::set_locals(oop element, oop value) { + element->obj_field_put(_locals_offset, value); +} + +void java_lang_LiveStackFrameInfo::set_operands(oop element, oop value) { + element->obj_field_put(_operands_offset, value); +} // Support for java Assertions - java_lang_AssertionStatusDirectives. @@ -3633,6 +3745,8 @@ void JavaClasses::compute_offsets() { sun_reflect_ConstantPool::compute_offsets(); sun_reflect_UnsafeStaticFieldAccessorImpl::compute_offsets(); java_lang_reflect_Parameter::compute_offsets(); + java_lang_StackFrameInfo::compute_offsets(); + java_lang_LiveStackFrameInfo::compute_offsets(); // generated interpreter code wants to know about the offsets we just computed: AbstractAssembler::update_delayed_values(); diff --git a/hotspot/src/share/vm/classfile/javaClasses.hpp b/hotspot/src/share/vm/classfile/javaClasses.hpp index 403e33e9ee3..192d11ccede 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.hpp +++ b/hotspot/src/share/vm/classfile/javaClasses.hpp @@ -1212,6 +1212,8 @@ public: #define CALLSITECONTEXT_INJECTED_FIELDS(macro) \ macro(java_lang_invoke_MethodHandleNatives_CallSiteContext, vmdependencies, intptr_signature, false) +class DependencyContext; + class java_lang_invoke_MethodHandleNatives_CallSiteContext : AllStatic { friend class JavaClasses; @@ -1222,8 +1224,7 @@ private: public: // Accessors - static nmethodBucket* vmdependencies(oop context); - static void set_vmdependencies(oop context, nmethodBucket* bucket); + static DependencyContext vmdependencies(oop context); // Testers static bool is_subclass(Klass* klass) { @@ -1359,6 +1360,85 @@ class java_lang_StackTraceElement: AllStatic { }; +class Backtrace: AllStatic { + public: + // Helper backtrace functions to store bci|version together. + static int merge_bci_and_version(int bci, int version); + static int merge_mid_and_cpref(int mid, int cpref); + static int bci_at(unsigned int merged); + static int version_at(unsigned int merged); + static int mid_at(unsigned int merged); + static int cpref_at(unsigned int merged); + static int get_line_number(const methodHandle& method, int bci); + static Symbol* get_source_file_name(InstanceKlass* holder, int version); + + // Debugging + friend class JavaClasses; +}; + +// Interface to java.lang.StackFrameInfo objects + +#define STACKFRAMEINFO_INJECTED_FIELDS(macro) \ + macro(java_lang_StackFrameInfo, mid, short_signature, false) \ + macro(java_lang_StackFrameInfo, version, short_signature, false) \ + macro(java_lang_StackFrameInfo, cpref, short_signature, false) + +class java_lang_StackFrameInfo: AllStatic { +private: + static int _declaringClass_offset; + static int _memberName_offset; + static int _bci_offset; + static int _methodName_offset; + static int _fileName_offset; + static int _lineNumber_offset; + + static int _mid_offset; + static int _version_offset; + static int _cpref_offset; + + static Method* get_method(Handle stackFrame, InstanceKlass* holder, TRAPS); + static Symbol* get_file_name(Handle stackFrame, InstanceKlass* holder); + +public: + // Setters + static void set_declaringClass(oop info, oop value); + static void set_method_and_bci(Handle stackFrame, const methodHandle& method, int bci); + static void set_bci(oop info, int value); + + // set method info in an instance of StackFrameInfo + static void fill_methodInfo(Handle info, TRAPS); + static void set_methodName(oop info, oop value); + static void set_fileName(oop info, oop value); + static void set_lineNumber(oop info, int value); + + // these injected fields are only used if -XX:-MemberNameInStackFrame set + static void set_mid(oop info, short value); + static void set_version(oop info, short value); + static void set_cpref(oop info, short value); + + static void compute_offsets(); + + // Debugging + friend class JavaClasses; +}; + +class java_lang_LiveStackFrameInfo: AllStatic { + private: + static int _monitors_offset; + static int _locals_offset; + static int _operands_offset; + + public: + static void set_monitors(oop info, oop value); + static void set_locals(oop info, oop value); + static void set_operands(oop info, oop value); + + static void compute_offsets(); + + // Debugging + friend class JavaClasses; +}; + // Interface to java.lang.AssertionStatusDirectives objects class java_lang_AssertionStatusDirectives: AllStatic { @@ -1442,7 +1522,9 @@ class InjectedField { CLASS_INJECTED_FIELDS(macro) \ CLASSLOADER_INJECTED_FIELDS(macro) \ MEMBERNAME_INJECTED_FIELDS(macro) \ - CALLSITECONTEXT_INJECTED_FIELDS(macro) + CALLSITECONTEXT_INJECTED_FIELDS(macro) \ + STACKFRAMEINFO_INJECTED_FIELDS(macro) + // Interface to hard-coded offset checking diff --git a/hotspot/src/share/vm/classfile/javaClasses.inline.hpp b/hotspot/src/share/vm/classfile/javaClasses.inline.hpp index 723c2e86757..ac35ccb8439 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.inline.hpp +++ b/hotspot/src/share/vm/classfile/javaClasses.inline.hpp @@ -73,4 +73,67 @@ inline bool java_lang_invoke_DirectMethodHandle::is_instance(oop obj) { return obj != NULL && is_subclass(obj->klass()); } +inline int Backtrace::merge_bci_and_version(int bci, int version) { + // only store u2 for version, checking for overflow. + if (version > USHRT_MAX || version < 0) version = USHRT_MAX; + assert((jushort)bci == bci, "bci should be short"); + return build_int_from_shorts(version, bci); +} + +inline int Backtrace::merge_mid_and_cpref(int mid, int cpref) { + // only store u2 for mid and cpref, checking for overflow. + assert((jushort)mid == mid, "mid should be short"); + assert((jushort)cpref == cpref, "cpref should be short"); + return build_int_from_shorts(cpref, mid); +} + +inline int Backtrace::bci_at(unsigned int merged) { + return extract_high_short_from_int(merged); +} + +inline int Backtrace::version_at(unsigned int merged) { + return extract_low_short_from_int(merged); +} + +inline int Backtrace::mid_at(unsigned int merged) { + return extract_high_short_from_int(merged); +} + +inline int Backtrace::cpref_at(unsigned int merged) { + return extract_low_short_from_int(merged); +} + +inline int Backtrace::get_line_number(const methodHandle& method, int bci) { + int line_number = 0; + if (method->is_native()) { + // Negative value different from -1 below, enabling Java code in + // class java.lang.StackTraceElement to distinguish "native" from + // "no LineNumberTable". JDK tests for -2. + line_number = -2; + } else { + // Returns -1 if no LineNumberTable, and otherwise actual line number + line_number = method->line_number_from_bci(bci); + if (line_number == -1 && ShowHiddenFrames) { + line_number = bci + 1000000; + } + } + return line_number; +} + +/* + * Returns the source file name of a given InstanceKlass and version + */ +inline Symbol* Backtrace::get_source_file_name(InstanceKlass* holder, int version) { + // Find the specific ik version that contains this source_file_name_index + // via the previous versions list, but use the current version's + // constant pool to look it up. The previous version's index has been + // merged for the current constant pool. + InstanceKlass* ik = holder->get_klass_version(version); + // This version has been cleaned up. + if (ik == NULL) return NULL; + int source_file_name_index = ik->source_file_name_index(); + return (source_file_name_index == 0) ? + (Symbol*)NULL : holder->constants()->symbol_at(source_file_name_index); +} + #endif // SHARE_VM_CLASSFILE_JAVACLASSES_INLINE_HPP diff --git a/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.cpp b/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.cpp index 9bb82f7fd19..9233e3243ad 100644 --- a/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.cpp +++ b/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2015, 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 @@ -67,7 +67,7 @@ bool SharedPathsMiscInfo::read(void* ptr, size_t size) { } bool SharedPathsMiscInfo::fail(const char* msg, const char* name) { - ClassLoader::trace_class_path(msg, name); + ClassLoader::trace_class_path(tty, msg, name); MetaspaceShared::set_archive_loading_failed(); return false; } diff --git a/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp b/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp index d1992e4bd4a..652d20c883a 100644 --- a/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp +++ b/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2015, 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 @@ -25,6 +25,7 @@ #ifndef SHARE_VM_CLASSFILE_SHAREDPATHSMISCINFO_HPP #define SHARE_VM_CLASSFILE_SHAREDPATHSMISCINFO_HPP +#include "classfile/classLoader.hpp" #include "runtime/os.hpp" // During dumping time, when processing class paths, we build up the dump-time @@ -64,7 +65,7 @@ protected: bool read(void* ptr, size_t size); static void trace_class_path(const char* msg, const char* name = NULL) { - ClassLoader::trace_class_path(msg, name); + ClassLoader::trace_class_path(tty, msg, name); } protected: static bool fail(const char* msg, const char* name = NULL); @@ -106,19 +107,6 @@ public: add_path(path, NON_EXIST); } - // The path must exist and have required size and modification time - void add_required_file(const char* path) { - add_path(path, REQUIRED); - - struct stat st; - if (os::stat(path, &st) != 0) { - assert(0, "sanity"); - ClassLoader::exit_with_path_failure("failed to os::stat(%s)", path); // should not happen - } - write_time(st.st_mtime); - write_long(st.st_size); - } - // The path must exist, and must contain exactly files/dirs void add_boot_classpath(const char* path) { add_path(path, BOOT); diff --git a/hotspot/src/share/vm/classfile/symbolTable.cpp b/hotspot/src/share/vm/classfile/symbolTable.cpp index eeb23890265..b268e08bdba 100644 --- a/hotspot/src/share/vm/classfile/symbolTable.cpp +++ b/hotspot/src/share/vm/classfile/symbolTable.cpp @@ -97,7 +97,7 @@ int SymbolTable::_symbols_removed = 0; int SymbolTable::_symbols_counted = 0; volatile int SymbolTable::_parallel_claimed_idx = 0; -void SymbolTable::buckets_unlink(int start_idx, int end_idx, int* processed, int* removed, size_t* memory_total) { +void SymbolTable::buckets_unlink(int start_idx, int end_idx, int* processed, int* removed) { for (int i = start_idx; i < end_idx; ++i) { HashtableEntry** p = the_table()->bucket_addr(i); HashtableEntry* entry = the_table()->bucket(i); @@ -110,7 +110,6 @@ void SymbolTable::buckets_unlink(int start_idx, int end_idx, int* processed, int break; } Symbol* s = entry->literal(); - (*memory_total) += s->size(); (*processed)++; assert(s != NULL, "just checking"); // If reference count is zero, remove. @@ -133,15 +132,9 @@ void SymbolTable::buckets_unlink(int start_idx, int end_idx, int* processed, int // This is done late during GC. void SymbolTable::unlink(int* processed, int* removed) { size_t memory_total = 0; - buckets_unlink(0, the_table()->table_size(), processed, removed, &memory_total); + buckets_unlink(0, the_table()->table_size(), processed, removed); _symbols_removed += *removed; _symbols_counted += *processed; - // Exclude printing for normal PrintGCDetails because people parse - // this output. - if (PrintGCDetails && Verbose && WizardMode) { - gclog_or_tty->print(" [Symbols=%d size=" SIZE_FORMAT "K] ", *processed, - (memory_total*HeapWordSize)/1024); - } } void SymbolTable::possibly_parallel_unlink(int* processed, int* removed) { @@ -158,16 +151,10 @@ void SymbolTable::possibly_parallel_unlink(int* processed, int* removed) { } int end_idx = MIN2(limit, start_idx + ClaimChunkSize); - buckets_unlink(start_idx, end_idx, processed, removed, &memory_total); + buckets_unlink(start_idx, end_idx, processed, removed); } Atomic::add(*processed, &_symbols_counted); Atomic::add(*removed, &_symbols_removed); - // Exclude printing for normal PrintGCDetails because people parse - // this output. - if (PrintGCDetails && Verbose && WizardMode) { - gclog_or_tty->print(" [Symbols: scanned=%d removed=%d size=" SIZE_FORMAT "K] ", *processed, *removed, - (memory_total*HeapWordSize)/1024); - } } // Create a new table and using alternate hash code, populate the new table diff --git a/hotspot/src/share/vm/classfile/symbolTable.hpp b/hotspot/src/share/vm/classfile/symbolTable.hpp index 29ee8d2b779..8f310e70e1b 100644 --- a/hotspot/src/share/vm/classfile/symbolTable.hpp +++ b/hotspot/src/share/vm/classfile/symbolTable.hpp @@ -132,7 +132,7 @@ private: static volatile int _parallel_claimed_idx; // Release any dead symbols - static void buckets_unlink(int start_idx, int end_idx, int* processed, int* removed, size_t* memory_total); + static void buckets_unlink(int start_idx, int end_idx, int* processed, int* removed); public: enum { symbol_alloc_batch_size = 8, diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp index ec634070767..2d519c629ef 100644 --- a/hotspot/src/share/vm/classfile/systemDictionary.cpp +++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp @@ -322,6 +322,17 @@ Klass* SystemDictionary::resolve_super_or_fail(Symbol* child_name, Handle protection_domain, bool is_superclass, TRAPS) { +#if INCLUDE_CDS + if (DumpSharedSpaces) { + // Special processing for CDS dump time. + Klass* k = SystemDictionaryShared::dump_time_resolve_super_or_fail(child_name, + class_name, class_loader, protection_domain, is_superclass, CHECK_NULL); + if (k) { + return k; + } + } +#endif // INCLUDE_CDS + // Double-check, if child class is already loaded, just return super-class,interface // Don't add a placedholder if already loaded, i.e. already in system dictionary // Make sure there's a placeholder for the *child* before resolving. @@ -1079,12 +1090,30 @@ Klass* SystemDictionary::resolve_from_stream(Symbol* class_name, // // Note: "name" is updated. - instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name, - loader_data, - protection_domain, - parsed_name, - verify, - THREAD); + instanceKlassHandle k; + +#if INCLUDE_CDS + k = SystemDictionaryShared::lookup_from_stream(class_name, + class_loader, + protection_domain, + st, + verify, + CHECK_NULL); +#endif + + if (k.not_null()) { + parsed_name = k->name(); + } else { + if (st->buffer() == NULL) { + return NULL; + } + k = ClassFileParser(st).parseClassFile(class_name, + loader_data, + protection_domain, + parsed_name, + verify, + THREAD); + } const char* pkg = "java/"; if (!HAS_PENDING_EXCEPTION && @@ -1201,8 +1230,13 @@ instanceKlassHandle SystemDictionary::load_shared_class(instanceKlassHandle ik, if (ik->super() != NULL) { Symbol* cn = ik->super()->name(); - resolve_super_or_fail(class_name, cn, - class_loader, protection_domain, true, CHECK_(nh)); + Klass *s = resolve_super_or_fail(class_name, cn, + class_loader, protection_domain, true, CHECK_(nh)); + if (s != ik->super()) { + // The dynamically resolved super class is not the same as the one we used during dump time, + // so we cannot use ik. + return nh; + } } Array* interfaces = ik->local_interfaces(); @@ -1215,7 +1249,12 @@ instanceKlassHandle SystemDictionary::load_shared_class(instanceKlassHandle ik, // reinitialized yet (they will be once the interface classes // are loaded) Symbol* name = k->name(); - resolve_super_or_fail(class_name, name, class_loader, protection_domain, false, CHECK_(nh)); + Klass* i = resolve_super_or_fail(class_name, name, class_loader, protection_domain, false, CHECK_(nh)); + if (k != i) { + // The dynamically resolved interface class is not the same as the one we used during dump time, + // so we cannot use ik. + return nh; + } } // Adjust methods to recover missing data. They need addresses for @@ -2285,7 +2324,7 @@ methodHandle SystemDictionary::find_method_handle_intrinsic(vmIntrinsics::ID iid // Check if have the compiled code. if (!m->has_compiled_code()) { THROW_MSG_(vmSymbols::java_lang_VirtualMachineError(), - "out of space in CodeCache for method handle intrinsic", empty); + "Out of space in CodeCache for method handle intrinsic", empty); } } // Now grab the lock. We might have to throw away the new method, diff --git a/hotspot/src/share/vm/classfile/systemDictionary.hpp b/hotspot/src/share/vm/classfile/systemDictionary.hpp index b9491903cfd..41811e343cd 100644 --- a/hotspot/src/share/vm/classfile/systemDictionary.hpp +++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp @@ -27,6 +27,7 @@ #include "classfile/classFileStream.hpp" #include "classfile/classLoader.hpp" +#include "classfile/systemDictionary_ext.hpp" #include "oops/objArrayOop.hpp" #include "oops/symbol.hpp" #include "runtime/java.hpp" @@ -179,11 +180,17 @@ class Ticks; do_klass(sun_misc_Launcher_klass, sun_misc_Launcher, Pre ) \ do_klass(CodeSource_klass, java_security_CodeSource, Pre ) \ \ - /* It's NULL in non-1.4 JDKs. */ \ do_klass(StackTraceElement_klass, java_lang_StackTraceElement, Opt ) \ + \ /* It's okay if this turns out to be NULL in non-1.4 JDKs. */ \ do_klass(nio_Buffer_klass, java_nio_Buffer, Opt ) \ \ + /* Stack Walking */ \ + do_klass(StackWalker_klass, java_lang_StackWalker, Opt ) \ + do_klass(AbstractStackWalker_klass, java_lang_StackStreamFactory_AbstractStackWalker, Opt ) \ + do_klass(StackFrameInfo_klass, java_lang_StackFrameInfo, Opt ) \ + do_klass(LiveStackFrameInfo_klass, java_lang_LiveStackFrameInfo, Opt ) \ + \ /* Preload boxing klasses */ \ do_klass(Boolean_klass, java_lang_Boolean, Pre ) \ do_klass(Character_klass, java_lang_Character, Pre ) \ @@ -194,15 +201,18 @@ class Ticks; do_klass(Integer_klass, java_lang_Integer, Pre ) \ do_klass(Long_klass, java_lang_Long, Pre ) \ \ + /* Extensions */ \ + WK_KLASSES_DO_EXT(do_klass) \ /* JVMCI classes. These are loaded on-demand. */ \ - JVMCI_WK_KLASSES_DO(do_klass) \ - + JVMCI_WK_KLASSES_DO(do_klass) \ + \ /*end*/ class SystemDictionary : AllStatic { friend class VMStructs; friend class SystemDictionaryHandles; + friend class SharedClassUtil; public: enum WKID { @@ -667,11 +677,6 @@ protected: // Basic find on classes in the midst of being loaded static Symbol* find_placeholder(Symbol* name, ClassLoaderData* loader_data); - // Updating entry in dictionary - // Add a completely loaded class - static void add_klass(int index, Symbol* class_name, - ClassLoaderData* loader_data, KlassHandle obj); - // Add a placeholder for a class being loaded static void add_placeholder(int index, Symbol* class_name, diff --git a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp index bc688798ce8..246ed1dfea6 100644 --- a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp +++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2015, 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 @@ -22,11 +22,13 @@ * */ - #ifndef SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP #define SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP #include "classfile/systemDictionary.hpp" +#include "classfile/dictionary.hpp" + +class ClassFileStream; class SystemDictionaryShared: public SystemDictionary { public: @@ -42,6 +44,30 @@ public: oop class_loader = loader_data->class_loader(); return (class_loader == NULL); } + + static Klass* dump_time_resolve_super_or_fail(Symbol* child_name, + Symbol* class_name, + Handle class_loader, + Handle protection_domain, + bool is_superclass, + TRAPS) { + return NULL; + } + + static size_t dictionary_entry_size() { + return sizeof(DictionaryEntry); + } + + static void init_shared_dictionary_entry(Klass* k, DictionaryEntry* entry) {} + + static InstanceKlass* lookup_from_stream(Symbol* class_name, + Handle class_loader, + Handle protection_domain, + ClassFileStream* st, + bool verify, + TRAPS) { + return NULL; + } }; #endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP diff --git a/hotspot/src/share/vm/classfile/systemDictionary_ext.hpp b/hotspot/src/share/vm/classfile/systemDictionary_ext.hpp new file mode 100644 index 00000000000..698805b657d --- /dev/null +++ b/hotspot/src/share/vm/classfile/systemDictionary_ext.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +#ifndef SHARE_VM_CLASSFILE_SYSTEMDICTIONARY_EXT_HPP +#define SHARE_VM_CLASSFILE_SYSTEMDICTIONARY_EXT_HPP + +#define WK_KLASSES_DO_EXT(do_klass) + +#endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARY_EXT_HPP diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp index f98f400064d..df477a8462f 100644 --- a/hotspot/src/share/vm/classfile/vmSymbols.hpp +++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_VM_CLASSFILE_VMSYMBOLS_HPP #define SHARE_VM_CLASSFILE_VMSYMBOLS_HPP +#include "classfile/vmSymbols_ext.hpp" #include "oops/symbol.hpp" #include "memory/iterator.hpp" #include "trace/traceMacros.hpp" @@ -109,6 +110,7 @@ template(java_io_ByteArrayInputStream, "java/io/ByteArrayInputStream") \ template(java_io_Serializable, "java/io/Serializable") \ template(java_util_Arrays, "java/util/Arrays") \ + template(java_util_Objects, "java/util/Objects") \ template(java_util_Properties, "java/util/Properties") \ template(java_util_Vector, "java/util/Vector") \ template(java_util_AbstractList, "java/util/AbstractList") \ @@ -209,7 +211,7 @@ template(java_util_concurrent_atomic_AtomicLongFieldUpdater_CASUpdater, "java/util/concurrent/atomic/AtomicLongFieldUpdater$CASUpdater") \ template(java_util_concurrent_atomic_AtomicLongFieldUpdater_LockedUpdater, "java/util/concurrent/atomic/AtomicLongFieldUpdater$LockedUpdater") \ template(java_util_concurrent_atomic_AtomicReferenceFieldUpdater_Impl, "java/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl") \ - template(sun_misc_Contended_signature, "Lsun/misc/Contended;") \ + template(jdk_internal_vm_annotation_Contended_signature, "Ljdk/internal/vm/annotation/Contended;") \ \ /* class symbols needed by intrinsics */ \ VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, template, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE) \ @@ -310,6 +312,22 @@ /* Support for JVMCI */ \ JVMCI_VM_SYMBOLS_DO(template, do_alias) \ \ + template(java_lang_StackWalker, "java/lang/StackWalker") \ + template(java_lang_StackFrameInfo, "java/lang/StackFrameInfo") \ + template(java_lang_LiveStackFrameInfo, "java/lang/LiveStackFrameInfo") \ + template(java_lang_StackStreamFactory_AbstractStackWalker, "java/lang/StackStreamFactory$AbstractStackWalker") \ + template(doStackWalk_name, "doStackWalk") \ + template(doStackWalk_signature, "(JIIII)Ljava/lang/Object;") \ + template(asPrimitive_name, "asPrimitive") \ + template(asPrimitive_int_signature, "(I)Ljava/lang/LiveStackFrame$PrimitiveValue;") \ + template(asPrimitive_long_signature, "(J)Ljava/lang/LiveStackFrame$PrimitiveValue;") \ + template(asPrimitive_short_signature, "(S)Ljava/lang/LiveStackFrame$PrimitiveValue;") \ + template(asPrimitive_byte_signature, "(B)Ljava/lang/LiveStackFrame$PrimitiveValue;") \ + template(asPrimitive_char_signature, "(C)Ljava/lang/LiveStackFrame$PrimitiveValue;") \ + template(asPrimitive_float_signature, "(F)Ljava/lang/LiveStackFrame$PrimitiveValue;") \ + template(asPrimitive_double_signature, "(D)Ljava/lang/LiveStackFrame$PrimitiveValue;") \ + template(asPrimitive_boolean_signature, "(Z)Ljava/lang/LiveStackFrame$PrimitiveValue;") \ + \ /* common method and field names */ \ template(object_initializer_name, "") \ template(class_initializer_name, "") \ @@ -410,6 +428,18 @@ template(append_name, "append") \ template(klass_name, "klass") \ template(array_klass_name, "array_klass") \ + template(declaringClass_name, "declaringClass") \ + template(memberName_name, "memberName") \ + template(mid_name, "mid") \ + template(cpref_name, "cpref") \ + template(version_name, "version") \ + template(bci_name, "bci") \ + template(methodName_name, "methodName") \ + template(fileName_name, "fileName") \ + template(lineNumber_name, "lineNumber") \ + template(monitors_name, "monitors") \ + template(locals_name, "locals") \ + template(operands_name, "operands") \ template(oop_size_name, "oop_size") \ template(static_oop_field_count_name, "static_oop_field_count") \ template(protection_domain_name, "protection_domain") \ @@ -514,6 +544,7 @@ template(class_array_signature, "[Ljava/lang/Class;") \ template(classloader_signature, "Ljava/lang/ClassLoader;") \ template(object_signature, "Ljava/lang/Object;") \ + template(object_array_signature, "[Ljava/lang/Object;") \ template(class_signature, "Ljava/lang/Class;") \ template(string_signature, "Ljava/lang/String;") \ template(reference_signature, "Ljava/lang/ref/Reference;") \ @@ -617,6 +648,9 @@ /* trace signatures */ \ TRACE_TEMPLATES(template) \ \ + /* extensions */ \ + VM_SYMBOLS_DO_EXT(template, do_alias) \ + \ /*end*/ // Here are all the intrinsics known to the runtime and the CI. @@ -883,6 +917,9 @@ do_intrinsic(_equalsL, java_lang_StringLatin1,equals_name, equalsB_signature, F_S) \ do_intrinsic(_equalsU, java_lang_StringUTF16, equals_name, equalsB_signature, F_S) \ \ + do_intrinsic(_Objects_checkIndex, java_util_Objects, checkIndex_name, Objects_checkIndex_signature, F_S) \ + do_signature(Objects_checkIndex_signature, "(IILjava/util/function/BiFunction;)I") \ + \ do_class(java_nio_Buffer, "java/nio/Buffer") \ do_intrinsic(_checkIndex, java_nio_Buffer, checkIndex_name, int_int_signature, F_R) \ do_name( checkIndex_name, "checkIndex") \ diff --git a/hotspot/src/share/vm/classfile/vmSymbols_ext.hpp b/hotspot/src/share/vm/classfile/vmSymbols_ext.hpp new file mode 100644 index 00000000000..4b68d8f234b --- /dev/null +++ b/hotspot/src/share/vm/classfile/vmSymbols_ext.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +#ifndef SHARE_VM_CLASSFILE_VMSYMBOLS_EXT_HPP +#define SHARE_VM_CLASSFILE_VMSYMBOLS_EXT_HPP + +#define VM_SYMBOLS_DO_EXT(template, do_alias) + +#endif // SHARE_VM_CLASSFILE_VMSYMBOLS_EXT_HPP + diff --git a/hotspot/src/share/vm/code/codeCache.cpp b/hotspot/src/share/vm/code/codeCache.cpp index 94552fa7872..bd1957eff2c 100644 --- a/hotspot/src/share/vm/code/codeCache.cpp +++ b/hotspot/src/share/vm/code/codeCache.cpp @@ -133,18 +133,47 @@ class CodeBlob_sizes { address CodeCache::_low_bound = 0; address CodeCache::_high_bound = 0; -int CodeCache::_number_of_blobs = 0; -int CodeCache::_number_of_adapters = 0; -int CodeCache::_number_of_nmethods = 0; int CodeCache::_number_of_nmethods_with_dependencies = 0; bool CodeCache::_needs_cache_clean = false; nmethod* CodeCache::_scavenge_root_nmethods = NULL; -int CodeCache::_codemem_full_count = 0; // Initialize array of CodeHeaps GrowableArray* CodeCache::_heaps = new(ResourceObj::C_HEAP, mtCode) GrowableArray (CodeBlobType::All, true); +void CodeCache::check_heap_sizes(size_t non_nmethod_size, size_t profiled_size, size_t non_profiled_size, size_t cache_size, bool all_set) { + size_t total_size = non_nmethod_size + profiled_size + non_profiled_size; + // Prepare error message + const char* error = "Invalid code heap sizes"; + err_msg message("NonNMethodCodeHeapSize (%zuK) + ProfiledCodeHeapSize (%zuK) + NonProfiledCodeHeapSize (%zuK) = %zuK", + non_nmethod_size/K, profiled_size/K, non_profiled_size/K, total_size/K); + + if (total_size > cache_size) { + // Some code heap sizes were explicitly set: total_size must be <= cache_size + message.append(" is greater than ReservedCodeCacheSize (%zuK).", cache_size/K); + vm_exit_during_initialization(error, message); + } else if (all_set && total_size != cache_size) { + // All code heap sizes were explicitly set: total_size must equal cache_size + message.append(" is not equal to ReservedCodeCacheSize (%zuK).", cache_size/K); + vm_exit_during_initialization(error, message); + } +} + void CodeCache::initialize_heaps() { + bool non_nmethod_set = FLAG_IS_CMDLINE(NonNMethodCodeHeapSize); + bool profiled_set = FLAG_IS_CMDLINE(ProfiledCodeHeapSize); + bool non_profiled_set = FLAG_IS_CMDLINE(NonProfiledCodeHeapSize); + size_t min_size = os::vm_page_size(); + size_t cache_size = ReservedCodeCacheSize; + size_t non_nmethod_size = NonNMethodCodeHeapSize; + size_t profiled_size = ProfiledCodeHeapSize; + size_t non_profiled_size = NonProfiledCodeHeapSize; + // Check if total size set via command line flags exceeds the reserved size + check_heap_sizes((non_nmethod_set ? non_nmethod_size : min_size), + (profiled_set ? profiled_size : min_size), + (non_profiled_set ? non_profiled_size : min_size), + cache_size, + non_nmethod_set && profiled_set && non_profiled_set); + // Determine size of compiler buffers size_t code_buffers_size = 0; #ifdef COMPILER1 @@ -159,51 +188,94 @@ void CodeCache::initialize_heaps() { code_buffers_size += c2_count * C2Compiler::initial_code_buffer_size(); #endif + // Increase default non_nmethod_size to account for compiler buffers + if (!non_nmethod_set) { + non_nmethod_size += code_buffers_size; + } // Calculate default CodeHeap sizes if not set by user - if (!FLAG_IS_CMDLINE(NonNMethodCodeHeapSize) && !FLAG_IS_CMDLINE(ProfiledCodeHeapSize) - && !FLAG_IS_CMDLINE(NonProfiledCodeHeapSize)) { - // Increase default NonNMethodCodeHeapSize to account for compiler buffers - FLAG_SET_ERGO(uintx, NonNMethodCodeHeapSize, NonNMethodCodeHeapSize + code_buffers_size); - + if (!non_nmethod_set && !profiled_set && !non_profiled_set) { // Check if we have enough space for the non-nmethod code heap - if (ReservedCodeCacheSize > NonNMethodCodeHeapSize) { - // Use the default value for NonNMethodCodeHeapSize and one half of the - // remaining size for non-profiled methods and one half for profiled methods - size_t remaining_size = ReservedCodeCacheSize - NonNMethodCodeHeapSize; - size_t profiled_size = remaining_size / 2; - size_t non_profiled_size = remaining_size - profiled_size; - FLAG_SET_ERGO(uintx, ProfiledCodeHeapSize, profiled_size); - FLAG_SET_ERGO(uintx, NonProfiledCodeHeapSize, non_profiled_size); + if (cache_size > non_nmethod_size) { + // Use the default value for non_nmethod_size and one half of the + // remaining size for non-profiled and one half for profiled methods + size_t remaining_size = cache_size - non_nmethod_size; + profiled_size = remaining_size / 2; + non_profiled_size = remaining_size - profiled_size; } else { // Use all space for the non-nmethod heap and set other heaps to minimal size - FLAG_SET_ERGO(uintx, NonNMethodCodeHeapSize, ReservedCodeCacheSize - os::vm_page_size() * 2); - FLAG_SET_ERGO(uintx, ProfiledCodeHeapSize, os::vm_page_size()); - FLAG_SET_ERGO(uintx, NonProfiledCodeHeapSize, os::vm_page_size()); + non_nmethod_size = cache_size - 2 * min_size; + profiled_size = min_size; + non_profiled_size = min_size; + } + } else if (!non_nmethod_set || !profiled_set || !non_profiled_set) { + // The user explicitly set some code heap sizes. Increase or decrease the (default) + // sizes of the other code heaps accordingly. First adapt non-profiled and profiled + // code heap sizes and then only change non-nmethod code heap size if still necessary. + intx diff_size = cache_size - (non_nmethod_size + profiled_size + non_profiled_size); + if (non_profiled_set) { + if (!profiled_set) { + // Adapt size of profiled code heap + if (diff_size < 0 && ((intx)profiled_size + diff_size) <= 0) { + // Not enough space available, set to minimum size + diff_size += profiled_size - min_size; + profiled_size = min_size; + } else { + profiled_size += diff_size; + diff_size = 0; + } + } + } else if (profiled_set) { + // Adapt size of non-profiled code heap + if (diff_size < 0 && ((intx)non_profiled_size + diff_size) <= 0) { + // Not enough space available, set to minimum size + diff_size += non_profiled_size - min_size; + non_profiled_size = min_size; + } else { + non_profiled_size += diff_size; + diff_size = 0; + } + } else if (non_nmethod_set) { + // Distribute remaining size between profiled and non-profiled code heaps + diff_size = cache_size - non_nmethod_size; + profiled_size = diff_size / 2; + non_profiled_size = diff_size - profiled_size; + diff_size = 0; + } + if (diff_size != 0) { + // Use non-nmethod code heap for remaining space requirements + assert(!non_nmethod_set && ((intx)non_nmethod_size + diff_size) > 0, "sanity"); + non_nmethod_size += diff_size; } } // We do not need the profiled CodeHeap, use all space for the non-profiled CodeHeap if(!heap_available(CodeBlobType::MethodProfiled)) { - FLAG_SET_ERGO(uintx, NonProfiledCodeHeapSize, NonProfiledCodeHeapSize + ProfiledCodeHeapSize); - FLAG_SET_ERGO(uintx, ProfiledCodeHeapSize, 0); + non_profiled_size += profiled_size; + profiled_size = 0; } // We do not need the non-profiled CodeHeap, use all space for the non-nmethod CodeHeap if(!heap_available(CodeBlobType::MethodNonProfiled)) { - FLAG_SET_ERGO(uintx, NonNMethodCodeHeapSize, NonNMethodCodeHeapSize + NonProfiledCodeHeapSize); - FLAG_SET_ERGO(uintx, NonProfiledCodeHeapSize, 0); + non_nmethod_size += non_profiled_size; + non_profiled_size = 0; } - // Make sure we have enough space for VM internal code uint min_code_cache_size = CodeCacheMinimumUseSpace DEBUG_ONLY(* 3); - if (NonNMethodCodeHeapSize < (min_code_cache_size + code_buffers_size)) { - vm_exit_during_initialization("Not enough space in non-nmethod code heap to run VM."); + if (non_nmethod_size < (min_code_cache_size + code_buffers_size)) { + vm_exit_during_initialization(err_msg( + "Not enough space in non-nmethod code heap to run VM: %zuK < %zuK", + non_nmethod_size/K, (min_code_cache_size + code_buffers_size)/K)); } - guarantee(NonProfiledCodeHeapSize + ProfiledCodeHeapSize + NonNMethodCodeHeapSize <= ReservedCodeCacheSize, "Size check"); + + // Verify sizes and update flag values + assert(non_profiled_size + profiled_size + non_nmethod_size == cache_size, "Invalid code heap sizes"); + FLAG_SET_ERGO(uintx, NonNMethodCodeHeapSize, non_nmethod_size); + FLAG_SET_ERGO(uintx, ProfiledCodeHeapSize, profiled_size); + FLAG_SET_ERGO(uintx, NonProfiledCodeHeapSize, non_profiled_size); // Align CodeHeaps size_t alignment = heap_alignment(); - size_t non_method_size = align_size_up(NonNMethodCodeHeapSize, alignment); - size_t profiled_size = align_size_down(ProfiledCodeHeapSize, alignment); + non_nmethod_size = align_size_up(non_nmethod_size, alignment); + profiled_size = align_size_down(profiled_size, alignment); // Reserve one continuous chunk of memory for CodeHeaps and split it into // parts for the individual heaps. The memory layout looks like this: @@ -212,9 +284,9 @@ void CodeCache::initialize_heaps() { // Profiled nmethods // Non-nmethods // ---------- low ------------ - ReservedCodeSpace rs = reserve_heap_memory(ReservedCodeCacheSize); - ReservedSpace non_method_space = rs.first_part(non_method_size); - ReservedSpace rest = rs.last_part(non_method_size); + ReservedCodeSpace rs = reserve_heap_memory(cache_size); + ReservedSpace non_method_space = rs.first_part(non_nmethod_size); + ReservedSpace rest = rs.last_part(non_nmethod_size); ReservedSpace profiled_space = rest.first_part(profiled_size); ReservedSpace non_profiled_space = rest.last_part(profiled_size); @@ -420,42 +492,41 @@ CodeBlob* CodeCache::allocate(int size, int code_blob_type, bool strict) { } } print_trace("allocation", cb, size); - _number_of_blobs++; return cb; } void CodeCache::free(CodeBlob* cb) { assert_locked_or_safepoint(CodeCache_lock); - + CodeHeap* heap = get_code_heap(cb); print_trace("free", cb); if (cb->is_nmethod()) { - _number_of_nmethods--; + heap->set_nmethod_count(heap->nmethod_count() - 1); if (((nmethod *)cb)->has_dependencies()) { _number_of_nmethods_with_dependencies--; } } if (cb->is_adapter_blob()) { - _number_of_adapters--; + heap->set_adapter_count(heap->adapter_count() - 1); } - _number_of_blobs--; // Get heap for given CodeBlob and deallocate get_code_heap(cb)->deallocate(cb); - assert(_number_of_blobs >= 0, "sanity check"); + assert(heap->blob_count() >= 0, "sanity check"); } void CodeCache::commit(CodeBlob* cb) { // this is called by nmethod::nmethod, which must already own CodeCache_lock assert_locked_or_safepoint(CodeCache_lock); + CodeHeap* heap = get_code_heap(cb); if (cb->is_nmethod()) { - _number_of_nmethods++; + heap->set_nmethod_count(heap->nmethod_count() + 1); if (((nmethod *)cb)->has_dependencies()) { _number_of_nmethods_with_dependencies++; } } if (cb->is_adapter_blob()) { - _number_of_adapters++; + heap->set_adapter_count(heap->adapter_count() + 1); } // flush the hardware I-cache @@ -577,11 +648,9 @@ void CodeCache::scavenge_root_nmethods_do(CodeBlobClosure* f) { assert(cur->on_scavenge_root_list(), "else shouldn't be on this list"); bool is_live = (!cur->is_zombie() && !cur->is_unloaded()); -#ifndef PRODUCT if (TraceScavenge) { cur->print_on(tty, is_live ? "scavenge root" : "dead scavenge root"); tty->cr(); } -#endif //PRODUCT if (is_live) { // Perform cur->oops_do(f), maybe just once per nmethod. f->do_code_blob(cur); @@ -774,6 +843,55 @@ void CodeCache::verify_oops() { } } +int CodeCache::blob_count(int code_blob_type) { + CodeHeap* heap = get_code_heap(code_blob_type); + return (heap != NULL) ? heap->blob_count() : 0; +} + +int CodeCache::blob_count() { + int count = 0; + FOR_ALL_HEAPS(heap) { + count += (*heap)->blob_count(); + } + return count; +} + +int CodeCache::nmethod_count(int code_blob_type) { + CodeHeap* heap = get_code_heap(code_blob_type); + return (heap != NULL) ? heap->nmethod_count() : 0; +} + +int CodeCache::nmethod_count() { + int count = 0; + FOR_ALL_HEAPS(heap) { + count += (*heap)->nmethod_count(); + } + return count; +} + +int CodeCache::adapter_count(int code_blob_type) { + CodeHeap* heap = get_code_heap(code_blob_type); + return (heap != NULL) ? heap->adapter_count() : 0; +} + +int CodeCache::adapter_count() { + int count = 0; + FOR_ALL_HEAPS(heap) { + count += (*heap)->adapter_count(); + } + return count; +} + +address CodeCache::low_bound(int code_blob_type) { + CodeHeap* heap = get_code_heap(code_blob_type); + return (heap != NULL) ? (address)heap->low_boundary() : NULL; +} + +address CodeCache::high_bound(int code_blob_type) { + CodeHeap* heap = get_code_heap(code_blob_type); + return (heap != NULL) ? (address)heap->high_boundary() : NULL; +} + size_t CodeCache::capacity() { size_t cap = 0; FOR_ALL_HEAPS(heap) { @@ -863,6 +981,9 @@ void CodeCache::initialize() { initialize_heaps(); } else { // Use a single code heap + FLAG_SET_ERGO(uintx, NonNMethodCodeHeapSize, 0); + FLAG_SET_ERGO(uintx, ProfiledCodeHeapSize, 0); + FLAG_SET_ERGO(uintx, NonProfiledCodeHeapSize, 0); ReservedCodeSpace rs = reserve_heap_memory(ReservedCodeCacheSize); add_heap(rs, "CodeCache", CodeBlobType::All); } @@ -1104,9 +1225,8 @@ void CodeCache::report_codemem_full(int code_blob_type, bool print) { CodeHeap* heap = get_code_heap(code_blob_type); assert(heap != NULL, "heap is null"); - if (!heap->was_full() || print) { + if ((heap->full_count() == 0) || print) { // Not yet reported for this heap, report - heap->report_full(); if (SegmentedCodeCache) { warning("%s is full. Compiler has been disabled.", get_code_heap_name(code_blob_type)); warning("Try increasing the code heap size using -XX:%s=", get_code_heap_flag_name(code_blob_type)); @@ -1125,18 +1245,19 @@ void CodeCache::report_codemem_full(int code_blob_type, bool print) { tty->print("%s", s.as_string()); } - _codemem_full_count++; + heap->report_full(); + EventCodeCacheFull event; if (event.should_commit()) { event.set_codeBlobType((u1)code_blob_type); event.set_startAddress((u8)heap->low_boundary()); event.set_commitedTopAddress((u8)heap->high()); event.set_reservedTopAddress((u8)heap->high_boundary()); - event.set_entryCount(nof_blobs()); - event.set_methodCount(nof_nmethods()); - event.set_adaptorCount(nof_adapters()); + event.set_entryCount(heap->blob_count()); + event.set_methodCount(heap->nmethod_count()); + event.set_adaptorCount(heap->adapter_count()); event.set_unallocatedCapacity(heap->unallocated_capacity()/K); - event.set_fullCount(_codemem_full_count); + event.set_fullCount(heap->full_count()); event.commit(); } } @@ -1360,7 +1481,7 @@ void CodeCache::print_summary(outputStream* st, bool detailed) { if (detailed) { st->print_cr(" total_blobs=" UINT32_FORMAT " nmethods=" UINT32_FORMAT " adapters=" UINT32_FORMAT, - nof_blobs(), nof_nmethods(), nof_adapters()); + blob_count(), nmethod_count(), adapter_count()); st->print_cr(" compilation: %s", CompileBroker::should_compile_new_jobs() ? "enabled" : Arguments::mode() == Arguments::_int ? "disabled (interpreter mode)" : @@ -1392,6 +1513,6 @@ void CodeCache::print_layout(outputStream* st) { void CodeCache::log_state(outputStream* st) { st->print(" total_blobs='" UINT32_FORMAT "' nmethods='" UINT32_FORMAT "'" " adapters='" UINT32_FORMAT "' free_code_cache='" SIZE_FORMAT "'", - nof_blobs(), nof_nmethods(), nof_adapters(), + blob_count(), nmethod_count(), adapter_count(), unallocated_capacity()); } diff --git a/hotspot/src/share/vm/code/codeCache.hpp b/hotspot/src/share/vm/code/codeCache.hpp index 05a5ab12605..88594bd80a7 100644 --- a/hotspot/src/share/vm/code/codeCache.hpp +++ b/hotspot/src/share/vm/code/codeCache.hpp @@ -85,26 +85,23 @@ class CodeCache : AllStatic { static address _low_bound; // Lower bound of CodeHeap addresses static address _high_bound; // Upper bound of CodeHeap addresses - static int _number_of_blobs; // Total number of CodeBlobs in the cache - static int _number_of_adapters; // Total number of Adapters in the cache - static int _number_of_nmethods; // Total number of nmethods in the cache static int _number_of_nmethods_with_dependencies; // Total number of nmethods with dependencies static bool _needs_cache_clean; // True if inline caches of the nmethods needs to be flushed static nmethod* _scavenge_root_nmethods; // linked via nm->scavenge_root_link() - static int _codemem_full_count; // Number of times a CodeHeap in the cache was full static void mark_scavenge_root_nmethods() PRODUCT_RETURN; static void verify_perm_nmethods(CodeBlobClosure* f_or_null) PRODUCT_RETURN; // CodeHeap management static void initialize_heaps(); // Initializes the CodeHeaps + // Check the code heap sizes set by the user via command line + static void check_heap_sizes(size_t non_nmethod_size, size_t profiled_size, size_t non_profiled_size, size_t cache_size, bool all_set); // Creates a new heap with the given name and size, containing CodeBlobs of the given type static void add_heap(ReservedSpace rs, const char* name, int code_blob_type); static CodeHeap* get_code_heap(const CodeBlob* cb); // Returns the CodeHeap for the given CodeBlob static CodeHeap* get_code_heap(int code_blob_type); // Returns the CodeHeap for the given CodeBlobType // Returns the name of the VM option to set the size of the corresponding CodeHeap static const char* get_code_heap_flag_name(int code_blob_type); - static bool heap_available(int code_blob_type); // Returns true if an own CodeHeap for the given CodeBlobType is available static size_t heap_alignment(); // Returns the alignment of the CodeHeaps in bytes static ReservedCodeSpace reserve_heap_memory(size_t size); // Reserves one continuous chunk of memory for the CodeHeaps @@ -139,9 +136,12 @@ class CodeCache : AllStatic { static CodeBlob* find_blob_unsafe(void* start); // Same as find_blob but does not fail if looking up a zombie method static nmethod* find_nmethod(void* start); // Returns the nmethod containing the given address - static int nof_blobs() { return _number_of_blobs; } // Returns the total number of CodeBlobs in the cache - static int nof_adapters() { return _number_of_adapters; } // Returns the total number of Adapters in the cache - static int nof_nmethods() { return _number_of_nmethods; } // Returns the total number of nmethods in the cache + static int blob_count(); // Returns the total number of CodeBlobs in the cache + static int blob_count(int code_blob_type); + static int adapter_count(); // Returns the total number of Adapters in the cache + static int adapter_count(int code_blob_type); + static int nmethod_count(); // Returns the total number of nmethods in the cache + static int nmethod_count(int code_blob_type); // GC support static void gc_epilogue(); @@ -177,7 +177,9 @@ class CodeCache : AllStatic { // The full limits of the codeCache static address low_bound() { return _low_bound; } + static address low_bound(int code_blob_type); static address high_bound() { return _high_bound; } + static address high_bound(int code_blob_type); // Profiling static size_t capacity(); @@ -191,6 +193,9 @@ class CodeCache : AllStatic { static void set_needs_cache_clean(bool v) { _needs_cache_clean = v; } static void clear_inline_caches(); // clear all inline caches + // Returns true if an own CodeHeap for the given CodeBlobType is available + static bool heap_available(int code_blob_type); + // Returns the CodeBlobType for the given nmethod static int get_code_blob_type(nmethod* nm) { return get_code_heap(nm)->code_blob_type(); @@ -239,7 +244,10 @@ class CodeCache : AllStatic { // tells how many nmethods have dependencies static int number_of_nmethods_with_dependencies(); - static int get_codemem_full_count() { return _codemem_full_count; } + static int get_codemem_full_count(int code_blob_type) { + CodeHeap* heap = get_code_heap(code_blob_type); + return (heap != NULL) ? heap->full_count() : 0; + } }; diff --git a/hotspot/src/share/vm/code/dependencyContext.cpp b/hotspot/src/share/vm/code/dependencyContext.cpp new file mode 100644 index 00000000000..dc19c4a0886 --- /dev/null +++ b/hotspot/src/share/vm/code/dependencyContext.cpp @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +#include "precompiled.hpp" +#include "code/nmethod.hpp" +#include "code/dependencies.hpp" +#include "code/dependencyContext.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/atomic.hpp" +#include "runtime/perfData.hpp" +#include "utilities/exceptions.hpp" + +PerfCounter* DependencyContext::_perf_total_buckets_allocated_count = NULL; +PerfCounter* DependencyContext::_perf_total_buckets_deallocated_count = NULL; +PerfCounter* DependencyContext::_perf_total_buckets_stale_count = NULL; +PerfCounter* DependencyContext::_perf_total_buckets_stale_acc_count = NULL; + +void dependencyContext_init() { + DependencyContext::init(); +} + +void DependencyContext::init() { + if (UsePerfData) { + EXCEPTION_MARK; + _perf_total_buckets_allocated_count = + PerfDataManager::create_counter(SUN_CI, "nmethodBucketsAllocated", PerfData::U_Events, CHECK); + _perf_total_buckets_deallocated_count = + PerfDataManager::create_counter(SUN_CI, "nmethodBucketsDeallocated", PerfData::U_Events, CHECK); + _perf_total_buckets_stale_count = + PerfDataManager::create_counter(SUN_CI, "nmethodBucketsStale", PerfData::U_Events, CHECK); + _perf_total_buckets_stale_acc_count = + PerfDataManager::create_counter(SUN_CI, "nmethodBucketsStaleAccumulated", PerfData::U_Events, CHECK); + } +} + +// +// Walk the list of dependent nmethods searching for nmethods which +// are dependent on the changes that were passed in and mark them for +// deoptimization. Returns the number of nmethods found. +// +int DependencyContext::mark_dependent_nmethods(DepChange& changes) { + int found = 0; + for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { + nmethod* nm = b->get_nmethod(); + // since dependencies aren't removed until an nmethod becomes a zombie, + // the dependency list may contain nmethods which aren't alive. + if (b->count() > 0 && nm->is_alive() && !nm->is_marked_for_deoptimization() && nm->check_dependency_on(changes)) { + if (TraceDependencies) { + ResourceMark rm; + tty->print_cr("Marked for deoptimization"); + changes.print(); + nm->print(); + nm->print_dependencies(); + } + nm->mark_for_deoptimization(); + found++; + } + } + return found; +} + +// +// Add an nmethod to the dependency context. +// It's possible that an nmethod has multiple dependencies on a klass +// so a count is kept for each bucket to guarantee that creation and +// deletion of dependencies is consistent. +// +void DependencyContext::add_dependent_nmethod(nmethod* nm, bool expunge) { + assert_lock_strong(CodeCache_lock); + for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { + if (nm == b->get_nmethod()) { + b->increment(); + return; + } + } + set_dependencies(new nmethodBucket(nm, dependencies())); + if (UsePerfData) { + _perf_total_buckets_allocated_count->inc(); + } + if (expunge) { + // Remove stale entries from the list. + expunge_stale_entries(); + } +} + +// +// Remove an nmethod dependency from the context. +// Decrement count of the nmethod in the dependency list and, optionally, remove +// the bucket completely when the count goes to 0. This method must find +// a corresponding bucket otherwise there's a bug in the recording of dependencies. +// Can be called concurrently by parallel GC threads. +// +void DependencyContext::remove_dependent_nmethod(nmethod* nm, bool expunge) { + assert_locked_or_safepoint(CodeCache_lock); + nmethodBucket* first = dependencies(); + nmethodBucket* last = NULL; + for (nmethodBucket* b = first; b != NULL; b = b->next()) { + if (nm == b->get_nmethod()) { + int val = b->decrement(); + guarantee(val >= 0, "Underflow: %d", val); + if (val == 0) { + if (expunge) { + if (last == NULL) { + set_dependencies(b->next()); + } else { + last->set_next(b->next()); + } + delete b; + if (UsePerfData) { + _perf_total_buckets_deallocated_count->inc(); + } + } else { + // Mark the context as having stale entries, since it is not safe to + // expunge the list right now. + set_has_stale_entries(true); + if (UsePerfData) { + _perf_total_buckets_stale_count->inc(); + _perf_total_buckets_stale_acc_count->inc(); + } + } + } + if (expunge) { + // Remove stale entries from the list. + expunge_stale_entries(); + } + return; + } + last = b; + } +#ifdef ASSERT + tty->print_raw_cr("### can't find dependent nmethod"); + nm->print(); +#endif // ASSERT + ShouldNotReachHere(); +} + +// +// Reclaim all unused buckets. +// +void DependencyContext::expunge_stale_entries() { + assert_locked_or_safepoint(CodeCache_lock); + if (!has_stale_entries()) { + assert(!find_stale_entries(), "inconsistent info"); + return; + } + nmethodBucket* first = dependencies(); + nmethodBucket* last = NULL; + int removed = 0; + for (nmethodBucket* b = first; b != NULL;) { + assert(b->count() >= 0, "bucket count: %d", b->count()); + nmethodBucket* next = b->next(); + if (b->count() == 0) { + if (last == NULL) { + first = next; + } else { + last->set_next(next); + } + removed++; + delete b; + // last stays the same. + } else { + last = b; + } + b = next; + } + set_dependencies(first); + set_has_stale_entries(false); + if (UsePerfData && removed > 0) { + _perf_total_buckets_deallocated_count->inc(removed); + _perf_total_buckets_stale_count->dec(removed); + } +} + +// +// Invalidate all dependencies in the context +int DependencyContext::remove_all_dependents() { + assert_locked_or_safepoint(CodeCache_lock); + nmethodBucket* b = dependencies(); + set_dependencies(NULL); + int marked = 0; + int removed = 0; + while (b != NULL) { + nmethod* nm = b->get_nmethod(); + if (b->count() > 0 && nm->is_alive() && !nm->is_marked_for_deoptimization()) { + nm->mark_for_deoptimization(); + marked++; + } + nmethodBucket* next = b->next(); + removed++; + delete b; + b = next; + } + set_has_stale_entries(false); + if (UsePerfData && removed > 0) { + _perf_total_buckets_deallocated_count->inc(removed); + } + return marked; +} + +void DependencyContext::wipe() { + assert_locked_or_safepoint(CodeCache_lock); + nmethodBucket* b = dependencies(); + set_dependencies(NULL); + set_has_stale_entries(false); + while (b != NULL) { + nmethodBucket* next = b->next(); + delete b; + b = next; + } +} + +#ifndef PRODUCT +void DependencyContext::print_dependent_nmethods(bool verbose) { + int idx = 0; + for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { + nmethod* nm = b->get_nmethod(); + tty->print("[%d] count=%d { ", idx++, b->count()); + if (!verbose) { + nm->print_on(tty, "nmethod"); + tty->print_cr(" } "); + } else { + nm->print(); + nm->print_dependencies(); + tty->print_cr("--- } "); + } + } +} + +bool DependencyContext::is_dependent_nmethod(nmethod* nm) { + for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { + if (nm == b->get_nmethod()) { +#ifdef ASSERT + int count = b->count(); + assert(count >= 0, "count shouldn't be negative: %d", count); +#endif + return true; + } + } + return false; +} + +bool DependencyContext::find_stale_entries() { + for (nmethodBucket* b = dependencies(); b != NULL; b = b->next()) { + if (b->count() == 0) return true; + } + return false; +} + +#endif //PRODUCT + +int nmethodBucket::decrement() { + return Atomic::add(-1, (volatile int *)&_count); +} + +/////////////// Unit tests /////////////// + +#ifndef PRODUCT + +class TestDependencyContext { + public: + nmethod* _nmethods[3]; + + intptr_t _dependency_context; + + DependencyContext dependencies() { + DependencyContext depContext(&_dependency_context); + return depContext; + } + + TestDependencyContext() : _dependency_context(DependencyContext::EMPTY) { + CodeCache_lock->lock_without_safepoint_check(); + + _nmethods[0] = reinterpret_cast(0x8 * 0); + _nmethods[1] = reinterpret_cast(0x8 * 1); + _nmethods[2] = reinterpret_cast(0x8 * 2); + + dependencies().add_dependent_nmethod(_nmethods[2]); + dependencies().add_dependent_nmethod(_nmethods[1]); + dependencies().add_dependent_nmethod(_nmethods[0]); + } + + ~TestDependencyContext() { + dependencies().wipe(); + CodeCache_lock->unlock(); + } + + static void testRemoveDependentNmethod(int id, bool delete_immediately) { + TestDependencyContext c; + DependencyContext depContext = c.dependencies(); + assert(!has_stale_entries(depContext), "check"); + + nmethod* nm = c._nmethods[id]; + depContext.remove_dependent_nmethod(nm, delete_immediately); + + if (!delete_immediately) { + assert(has_stale_entries(depContext), "check"); + assert(depContext.is_dependent_nmethod(nm), "check"); + depContext.expunge_stale_entries(); + } + + assert(!has_stale_entries(depContext), "check"); + assert(!depContext.is_dependent_nmethod(nm), "check"); + } + + static void testRemoveDependentNmethod() { + testRemoveDependentNmethod(0, false); + testRemoveDependentNmethod(1, false); + testRemoveDependentNmethod(2, false); + + testRemoveDependentNmethod(0, true); + testRemoveDependentNmethod(1, true); + testRemoveDependentNmethod(2, true); + } + + static void test() { + testRemoveDependentNmethod(); + } + + static bool has_stale_entries(DependencyContext ctx) { + assert(ctx.has_stale_entries() == ctx.find_stale_entries(), "check"); + return ctx.has_stale_entries(); + } +}; + +void TestDependencyContext_test() { + TestDependencyContext::test(); +} + +#endif // PRODUCT diff --git a/hotspot/src/share/vm/code/dependencyContext.hpp b/hotspot/src/share/vm/code/dependencyContext.hpp new file mode 100644 index 00000000000..014de0e237a --- /dev/null +++ b/hotspot/src/share/vm/code/dependencyContext.hpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +#ifndef SHARE_VM_CODE_DEPENDENCYCONTEXT_HPP +#define SHARE_VM_CODE_DEPENDENCYCONTEXT_HPP + +#include "memory/allocation.hpp" +#include "oops/oop.hpp" +#include "runtime/handles.hpp" +#include "runtime/perfData.hpp" + +class nmethod; +class DepChange; + +// +// nmethodBucket is used to record dependent nmethods for +// deoptimization. nmethod dependencies are actually +// pairs but we really only care about the klass part for purposes of +// finding nmethods which might need to be deoptimized. Instead of +// recording the method, a count of how many times a particular nmethod +// was recorded is kept. This ensures that any recording errors are +// noticed since an nmethod should be removed as many times are it's +// added. +// +class nmethodBucket: public CHeapObj { + friend class VMStructs; + private: + nmethod* _nmethod; + int _count; + nmethodBucket* _next; + + public: + nmethodBucket(nmethod* nmethod, nmethodBucket* next) : + _nmethod(nmethod), _next(next), _count(1) {} + + int count() { return _count; } + int increment() { _count += 1; return _count; } + int decrement(); + nmethodBucket* next() { return _next; } + void set_next(nmethodBucket* b) { _next = b; } + nmethod* get_nmethod() { return _nmethod; } +}; + +// +// Utility class to manipulate nmethod dependency context. +// The context consists of nmethodBucket* (a head of a linked list) +// and a boolean flag (does the list contains stale entries). The structure is +// encoded as an intptr_t: lower bit is used for the flag. It is possible since +// nmethodBucket* is aligned - the structure is malloc'ed in C heap. +// Dependency context can be attached either to an InstanceKlass (_dep_context field) +// or CallSiteContext oop for call_site_target dependencies (see javaClasses.hpp). +// DependencyContext class operates on some location which holds a intptr_t value. +// +class DependencyContext : public StackObj { + friend class VMStructs; + friend class TestDependencyContext; + private: + enum TagBits { _has_stale_entries_bit = 1, _has_stale_entries_mask = 1 }; + + intptr_t* _dependency_context_addr; + + void set_dependencies(nmethodBucket* b) { + assert((intptr_t(b) & _has_stale_entries_mask) == 0, "should be aligned"); + if (has_stale_entries()) { + *_dependency_context_addr = intptr_t(b) | _has_stale_entries_mask; + } else { + *_dependency_context_addr = intptr_t(b); + } + } + + void set_has_stale_entries(bool x) { + if (x) { + *_dependency_context_addr |= _has_stale_entries_mask; + } else { + *_dependency_context_addr &= ~_has_stale_entries_mask; + } + } + + nmethodBucket* dependencies() { + intptr_t value = *_dependency_context_addr; + return (nmethodBucket*) (value & ~_has_stale_entries_mask); + } + + bool has_stale_entries() const { + intptr_t value = *_dependency_context_addr; + return (value & _has_stale_entries_mask) != 0; + } + + static PerfCounter* _perf_total_buckets_allocated_count; + static PerfCounter* _perf_total_buckets_deallocated_count; + static PerfCounter* _perf_total_buckets_stale_count; + static PerfCounter* _perf_total_buckets_stale_acc_count; + + public: +#ifdef ASSERT + // Safepoints are forbidden during DC lifetime. GC can invalidate + // _dependency_context_addr if it relocates the holder + // (e.g. CallSiteContext Java object). + int _safepoint_counter; + + DependencyContext(intptr_t* addr) : _dependency_context_addr(addr), + _safepoint_counter(SafepointSynchronize::_safepoint_counter) {} + + ~DependencyContext() { + assert(_safepoint_counter == SafepointSynchronize::_safepoint_counter, "safepoint happened"); + } +#else + DependencyContext(intptr_t* addr) : _dependency_context_addr(addr) {} +#endif // ASSERT + + static const intptr_t EMPTY = 0; // dependencies = NULL, has_stale_entries = false + + static void init(); + + int mark_dependent_nmethods(DepChange& changes); + void add_dependent_nmethod(nmethod* nm, bool expunge_stale_entries = false); + void remove_dependent_nmethod(nmethod* nm, bool expunge_stale_entries = false); + int remove_all_dependents(); + + void expunge_stale_entries(); + + // Unsafe deallocation of nmethodBuckets. Used in IK::release_C_heap_structures + // to clean up the context possibly containing live entries pointing to unloaded nmethods. + void wipe(); + +#ifndef PRODUCT + void print_dependent_nmethods(bool verbose); + bool is_dependent_nmethod(nmethod* nm); + bool find_stale_entries(); +#endif //PRODUCT +}; +#endif // SHARE_VM_CODE_DEPENDENCYCONTEXT_HPP diff --git a/hotspot/src/share/vm/code/nmethod.cpp b/hotspot/src/share/vm/code/nmethod.cpp index 50e2b4c6930..307f0f71b98 100644 --- a/hotspot/src/share/vm/code/nmethod.cpp +++ b/hotspot/src/share/vm/code/nmethod.cpp @@ -1539,7 +1539,7 @@ void nmethod::flush() { if (PrintMethodFlushing) { tty->print_cr("*flushing nmethod %3d/" INTPTR_FORMAT ". Live blobs:" UINT32_FORMAT "/Free CodeCache:" SIZE_FORMAT "Kb", - _compile_id, p2i(this), CodeCache::nof_blobs(), + _compile_id, p2i(this), CodeCache::blob_count(), CodeCache::unallocated_capacity(CodeCache::get_code_blob_type(this))/1024); } @@ -1819,9 +1819,7 @@ void nmethod::do_unloading(BoolObjectClosure* is_alive, bool unloading_occurred) if (_jvmci_installed_code != NULL) { if (_jvmci_installed_code->is_a(HotSpotNmethod::klass()) && HotSpotNmethod::isDefault(_jvmci_installed_code)) { if (!is_alive->do_object_b(_jvmci_installed_code)) { - bs->write_ref_nmethod_pre(&_jvmci_installed_code, this); - _jvmci_installed_code = NULL; - bs->write_ref_nmethod_post(&_jvmci_installed_code, this); + clear_jvmci_installed_code(); } } else { if (can_unload(is_alive, (oop*)&_jvmci_installed_code, unloading_occurred)) { @@ -1922,27 +1920,6 @@ bool nmethod::do_unloading_parallel(BoolObjectClosure* is_alive, bool unloading_ unloading_occurred = true; } -#if INCLUDE_JVMCI - // Follow JVMCI method - if (_jvmci_installed_code != NULL) { - if (_jvmci_installed_code->is_a(HotSpotNmethod::klass()) && HotSpotNmethod::isDefault(_jvmci_installed_code)) { - if (!is_alive->do_object_b(_jvmci_installed_code)) { - _jvmci_installed_code = NULL; - } - } else { - if (can_unload(is_alive, (oop*)&_jvmci_installed_code, unloading_occurred)) { - return false; - } - } - } - - if (_speculation_log != NULL) { - if (!is_alive->do_object_b(_speculation_log)) { - _speculation_log = NULL; - } - } -#endif - // Exception cache clean_exception_cache(is_alive); @@ -2006,9 +1983,7 @@ bool nmethod::do_unloading_parallel(BoolObjectClosure* is_alive, bool unloading_ if (_jvmci_installed_code != NULL) { if (_jvmci_installed_code->is_a(HotSpotNmethod::klass()) && HotSpotNmethod::isDefault(_jvmci_installed_code)) { if (!is_alive->do_object_b(_jvmci_installed_code)) { - bs->write_ref_nmethod_pre(&_jvmci_installed_code, this); - _jvmci_installed_code = NULL; - bs->write_ref_nmethod_post(&_jvmci_installed_code, this); + clear_jvmci_installed_code(); } } else { if (can_unload(is_alive, (oop*)&_jvmci_installed_code, unloading_occurred)) { @@ -2271,7 +2246,7 @@ bool nmethod::test_set_oops_do_mark() { break; } // Mark was clear when we first saw this guy. - NOT_PRODUCT(if (TraceScavenge) print_on(tty, "oops_do, mark")); + if (TraceScavenge) { print_on(tty, "oops_do, mark"); } return false; } } @@ -2280,7 +2255,7 @@ bool nmethod::test_set_oops_do_mark() { } void nmethod::oops_do_marking_prologue() { - NOT_PRODUCT(if (TraceScavenge) tty->print_cr("[oops_do_marking_prologue")); + if (TraceScavenge) { tty->print_cr("[oops_do_marking_prologue"); } assert(_oops_do_mark_nmethods == NULL, "must not call oops_do_marking_prologue twice in a row"); // We use cmpxchg_ptr instead of regular assignment here because the user // may fork a bunch of threads, and we need them all to see the same state. @@ -2302,7 +2277,7 @@ void nmethod::oops_do_marking_epilogue() { void* required = _oops_do_mark_nmethods; void* observed = Atomic::cmpxchg_ptr(NULL, &_oops_do_mark_nmethods, required); guarantee(observed == required, "no races in this sequential code"); - NOT_PRODUCT(if (TraceScavenge) tty->print_cr("oops_do_marking_epilogue]")); + if (TraceScavenge) { tty->print_cr("oops_do_marking_epilogue]"); } } class DetectScavengeRoot: public OopClosure { @@ -3373,6 +3348,14 @@ void nmethod::print_statistics() { #endif // !PRODUCT #if INCLUDE_JVMCI +void nmethod::clear_jvmci_installed_code() { + // This must be done carefully to maintain nmethod remembered sets properly + BarrierSet* bs = Universe::heap()->barrier_set(); + bs->write_ref_nmethod_pre(&_jvmci_installed_code, this); + _jvmci_installed_code = NULL; + bs->write_ref_nmethod_post(&_jvmci_installed_code, this); +} + void nmethod::maybe_invalidate_installed_code() { if (_jvmci_installed_code != NULL) { if (!is_alive()) { @@ -3382,7 +3365,7 @@ void nmethod::maybe_invalidate_installed_code() { // might want to invalidate all existing activations. InstalledCode::set_address(_jvmci_installed_code, 0); InstalledCode::set_entryPoint(_jvmci_installed_code, 0); - _jvmci_installed_code = NULL; + clear_jvmci_installed_code(); } else if (is_not_entrant()) { InstalledCode::set_entryPoint(_jvmci_installed_code, 0); } diff --git a/hotspot/src/share/vm/code/nmethod.hpp b/hotspot/src/share/vm/code/nmethod.hpp index 9510caed36e..6134378aac0 100644 --- a/hotspot/src/share/vm/code/nmethod.hpp +++ b/hotspot/src/share/vm/code/nmethod.hpp @@ -602,7 +602,7 @@ public: #if INCLUDE_JVMCI oop jvmci_installed_code() { return _jvmci_installed_code ; } char* jvmci_installed_code_name(char* buf, size_t buflen); - void set_jvmci_installed_code(oop installed_code) { _jvmci_installed_code = installed_code; } + void clear_jvmci_installed_code(); void maybe_invalidate_installed_code(); oop speculation_log() { return _speculation_log ; } void set_speculation_log(oop speculation_log) { _speculation_log = speculation_log; } diff --git a/hotspot/src/share/vm/compiler/compileBroker.cpp b/hotspot/src/share/vm/compiler/compileBroker.cpp index b508b2e893c..e5eac46910c 100644 --- a/hotspot/src/share/vm/compiler/compileBroker.cpp +++ b/hotspot/src/share/vm/compiler/compileBroker.cpp @@ -26,6 +26,7 @@ #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" #include "code/codeCache.hpp" +#include "code/dependencyContext.hpp" #include "compiler/compileBroker.hpp" #include "compiler/compileLog.hpp" #include "compiler/compilerOracle.hpp" @@ -237,10 +238,27 @@ CompileTaskWrapper::~CompileTaskWrapper() { task->set_code_handle(NULL); thread->set_env(NULL); if (task->is_blocking()) { - MutexLocker notifier(task->lock(), thread); - task->mark_complete(); - // Notify the waiting thread that the compilation has completed. - task->lock()->notify_all(); + bool free_task = false; + { + MutexLocker notifier(task->lock(), thread); + task->mark_complete(); +#if INCLUDE_JVMCI + if (CompileBroker::compiler(task->comp_level())->is_jvmci() && + !task->has_waiter()) { + // The waiting thread timed out and thus did not free the task. + free_task = true; + } +#endif + if (!free_task) { + // Notify the waiting thread that the compilation has completed + // so that it can free the task. + task->lock()->notify_all(); + } + } + if (free_task) { + // The task can only be freed once the task lock is released. + CompileTask::free(task); + } } else { task->mark_complete(); @@ -547,7 +565,6 @@ void CompileBroker::compilation_init() { PerfData::U_Ticks, CHECK); } - if (UsePerfData) { EXCEPTION_MARK; @@ -1302,6 +1319,11 @@ CompileTask* CompileBroker::create_compile_task(CompileQueue* queue, return new_task; } +// 1 second should be long enough to complete most JVMCI compilations +// and not too long to stall a blocking JVMCI compilation that +// is trying to acquire a lock held by the app thread that submitted the +// compilation. +static const long BLOCKING_JVMCI_COMPILATION_TIMEOUT = 1000; /** * Wait for the compilation task to complete. @@ -1318,30 +1340,47 @@ void CompileBroker::wait_for_completion(CompileTask* task) { thread->set_blocked_on_compilation(true); methodHandle method(thread, task->method()); + bool free_task; +#if INCLUDE_JVMCI + if (compiler(task->comp_level())->is_jvmci()) { + MutexLocker waiter(task->lock(), thread); + // No need to check if compilation has completed - just + // rely on the time out. The JVMCI compiler thread will + // recycle the CompileTask. + task->lock()->wait(!Mutex::_no_safepoint_check_flag, BLOCKING_JVMCI_COMPILATION_TIMEOUT); + // If the compilation completes while has_waiter is true then + // this thread is responsible for freeing the task. Otherwise + // the compiler thread will free the task. + task->clear_waiter(); + free_task = task->is_complete(); + } else +#endif { MutexLocker waiter(task->lock(), thread); - + free_task = true; while (!task->is_complete() && !is_compilation_disabled_forever()) { task->lock()->wait(); } } thread->set_blocked_on_compilation(false); - if (is_compilation_disabled_forever()) { + if (free_task) { + if (is_compilation_disabled_forever()) { + CompileTask::free(task); + return; + } + + // It is harmless to check this status without the lock, because + // completion is a stable property (until the task object is recycled). + assert(task->is_complete(), "Compilation should have completed"); + assert(task->code_handle() == NULL, "must be reset"); + + // By convention, the waiter is responsible for recycling a + // blocking CompileTask. Since there is only one waiter ever + // waiting on a CompileTask, we know that no one else will + // be using this CompileTask; we can free it. CompileTask::free(task); - return; } - - // It is harmless to check this status without the lock, because - // completion is a stable property (until the task object is recycled). - assert(task->is_complete(), "Compilation should have completed"); - assert(task->code_handle() == NULL, "must be reset"); - - // By convention, the waiter is responsible for recycling a - // blocking CompileTask. Since there is only one waiter ever - // waiting on a CompileTask, we know that no one else will - // be using this CompileTask; we can free it. - CompileTask::free(task); } /** @@ -1676,13 +1715,7 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { bool should_break = false; int task_level = task->comp_level(); - // Look up matching directives - DirectiveSet* directive = DirectivesStack::getMatchingDirective(task->method(), compiler(task_level)); - - should_break = directive->BreakAtExecuteOption || task->check_break_at_flags(); - if (should_log && !directive->LogOption) { - should_log = false; - } + DirectiveSet* directive; { // create the handle inside it's own block so it can't // accidentally be referenced once the thread transitions to @@ -1691,12 +1724,20 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { methodHandle method(thread, task->method()); assert(!method->is_native(), "no longer compile natives"); + // Look up matching directives + directive = DirectivesStack::getMatchingDirective(method, compiler(task_level)); + // Save information about this method in case of failure. set_last_compile(thread, method, is_osr, task_level); DTRACE_METHOD_COMPILE_BEGIN_PROBE(method, compiler_name(task_level)); } + should_break = directive->BreakAtExecuteOption || task->check_break_at_flags(); + if (should_log && !directive->LogOption) { + should_log = false; + } + // Allocate a new set of JNI handles. push_jni_handle_block(); Method* target_handle = task->method(); @@ -1716,7 +1757,8 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { EventCompilation event; JVMCIEnv env(task, system_dictionary_modification_counter); - jvmci->compile_method(target_handle, osr_bci, &env); + methodHandle method(thread, target_handle); + jvmci->compile_method(method, osr_bci, &env); post_compile(thread, task, event, task->code() != NULL, NULL); } else diff --git a/hotspot/src/share/vm/compiler/compileTask.cpp b/hotspot/src/share/vm/compiler/compileTask.cpp index 0cd15c9592e..a0efba91365 100644 --- a/hotspot/src/share/vm/compiler/compileTask.cpp +++ b/hotspot/src/share/vm/compiler/compileTask.cpp @@ -47,7 +47,7 @@ CompileTask* CompileTask::allocate() { } else { task = new CompileTask(); DEBUG_ONLY(_num_allocated_tasks++;) - assert (WhiteBoxAPI || _num_allocated_tasks < 10000, "Leaking compilation tasks?"); + assert (WhiteBoxAPI || JVMCI_ONLY(UseJVMCICompiler ||) _num_allocated_tasks < 10000, "Leaking compilation tasks?"); task->set_next(NULL); task->set_is_free(true); } @@ -90,6 +90,7 @@ void CompileTask::initialize(int compile_id, _method_holder = JNIHandles::make_global(method->method_holder()->klass_holder()); _osr_bci = osr_bci; _is_blocking = is_blocking; + JVMCI_ONLY(_has_waiter = CompileBroker::compiler(comp_level)->is_jvmci();) _comp_level = comp_level; _num_inlined_bytecodes = 0; diff --git a/hotspot/src/share/vm/compiler/compileTask.hpp b/hotspot/src/share/vm/compiler/compileTask.hpp index b35d1345095..1ec57bd00ab 100644 --- a/hotspot/src/share/vm/compiler/compileTask.hpp +++ b/hotspot/src/share/vm/compiler/compileTask.hpp @@ -53,6 +53,9 @@ class CompileTask : public CHeapObj { bool _is_complete; bool _is_success; bool _is_blocking; +#if INCLUDE_JVMCI + bool _has_waiter; +#endif int _comp_level; int _num_inlined_bytecodes; nmethodLocker* _code_handle; // holder of eventual result @@ -85,6 +88,10 @@ class CompileTask : public CHeapObj { bool is_complete() const { return _is_complete; } bool is_blocking() const { return _is_blocking; } bool is_success() const { return _is_success; } +#if INCLUDE_JVMCI + bool has_waiter() const { return _has_waiter; } + void clear_waiter() { _has_waiter = false; } +#endif nmethodLocker* code_handle() const { return _code_handle; } void set_code_handle(nmethodLocker* l) { _code_handle = l; } diff --git a/hotspot/src/share/vm/compiler/compilerDirectives.cpp b/hotspot/src/share/vm/compiler/compilerDirectives.cpp index 20696ff0aa1..4d10aec5307 100644 --- a/hotspot/src/share/vm/compiler/compilerDirectives.cpp +++ b/hotspot/src/share/vm/compiler/compilerDirectives.cpp @@ -527,12 +527,14 @@ void DirectivesStack::release(CompilerDirectives* dir) { DirectiveSet* DirectivesStack::getMatchingDirective(methodHandle method, AbstractCompiler *comp) { assert(_depth > 0, "Must never be empty"); - CompilerDirectives* dir = _top; - assert(dir != NULL, "Must be initialized"); DirectiveSet* match = NULL; { MutexLockerEx locker(DirectivesStack_lock, Mutex::_no_safepoint_check_flag); + + CompilerDirectives* dir = _top; + assert(dir != NULL, "Must be initialized"); + while (dir != NULL) { if (dir->is_default_directive() || dir->match(method)) { match = dir->get_for(comp); diff --git a/hotspot/src/share/vm/compiler/compilerDirectives.hpp b/hotspot/src/share/vm/compiler/compilerDirectives.hpp index f980f769c7f..421012c687e 100644 --- a/hotspot/src/share/vm/compiler/compilerDirectives.hpp +++ b/hotspot/src/share/vm/compiler/compilerDirectives.hpp @@ -67,7 +67,7 @@ cflags(VectorizeDebug, bool, false, VectorizeDebug) \ cflags(CloneMapDebug, bool, false, CloneMapDebug) \ cflags(DoReserveCopyInSuperWordDebug, bool, false, DoReserveCopyInSuperWordDebug) \ - NOT_PRODUCT( cflags(IGVPrintLevel, intx, PrintIdealGraphLevel, IGVPrintLevel)) \ + cflags(IGVPrintLevel, intx, PrintIdealGraphLevel, IGVPrintLevel) \ cflags(MaxNodeLimit, intx, MaxNodeLimit, MaxNodeLimit) #else #define compilerdirectives_c2_flags(cflags) diff --git a/hotspot/src/share/vm/gc/cms/concurrentMarkSweepGeneration.cpp b/hotspot/src/share/vm/gc/cms/concurrentMarkSweepGeneration.cpp index 8771eec8de9..45439181aa5 100644 --- a/hotspot/src/share/vm/gc/cms/concurrentMarkSweepGeneration.cpp +++ b/hotspot/src/share/vm/gc/cms/concurrentMarkSweepGeneration.cpp @@ -2240,7 +2240,6 @@ bool CMSCollector::have_cms_token() { } return false; } -#endif // Check reachability of the given heap address in CMS generation, // treating all other generations as roots. @@ -2260,21 +2259,21 @@ bool CMSCollector::is_cms_reachable(HeapWord* addr) { // Clear the marking bit map array before starting, but, just // for kicks, first report if the given address is already marked - gclog_or_tty->print_cr("Start: Address " PTR_FORMAT " is%s marked", p2i(addr), + tty->print_cr("Start: Address " PTR_FORMAT " is%s marked", p2i(addr), _markBitMap.isMarked(addr) ? "" : " not"); if (verify_after_remark()) { MutexLockerEx x(verification_mark_bm()->lock(), Mutex::_no_safepoint_check_flag); bool result = verification_mark_bm()->isMarked(addr); - gclog_or_tty->print_cr("TransitiveMark: Address " PTR_FORMAT " %s marked", p2i(addr), - result ? "IS" : "is NOT"); + tty->print_cr("TransitiveMark: Address " PTR_FORMAT " %s marked", p2i(addr), + result ? "IS" : "is NOT"); return result; } else { - gclog_or_tty->print_cr("Could not compute result"); + tty->print_cr("Could not compute result"); return false; } } - +#endif void CMSCollector::print_on_error(outputStream* st) { diff --git a/hotspot/src/share/vm/gc/cms/concurrentMarkSweepGeneration.hpp b/hotspot/src/share/vm/gc/cms/concurrentMarkSweepGeneration.hpp index 1f0be952fc5..10eb76fb35b 100644 --- a/hotspot/src/share/vm/gc/cms/concurrentMarkSweepGeneration.hpp +++ b/hotspot/src/share/vm/gc/cms/concurrentMarkSweepGeneration.hpp @@ -926,7 +926,7 @@ class CMSCollector: public CHeapObj { // one (foreground collector or background collector). static void check_correct_thread_executing() PRODUCT_RETURN; - bool is_cms_reachable(HeapWord* addr); + NOT_PRODUCT(bool is_cms_reachable(HeapWord* addr);) // Performance Counter Support CollectorCounters* counters() { return _gc_counters; } diff --git a/hotspot/src/share/vm/gc/cms/parNewGeneration.cpp b/hotspot/src/share/vm/gc/cms/parNewGeneration.cpp index 4b259afeb1e..890c84bc694 100644 --- a/hotspot/src/share/vm/gc/cms/parNewGeneration.cpp +++ b/hotspot/src/share/vm/gc/cms/parNewGeneration.cpp @@ -1148,7 +1148,6 @@ oop ParNewGeneration::copy_to_survivor_space(ParScanThreadState* par_scan_state, } assert(new_obj != NULL, "just checking"); -#ifndef PRODUCT // This code must come after the CAS test, or it will print incorrect // information. if (TraceScavenge) { @@ -1156,7 +1155,6 @@ oop ParNewGeneration::copy_to_survivor_space(ParScanThreadState* par_scan_state, is_in_reserved(new_obj) ? "copying" : "tenuring", new_obj->klass()->internal_name(), p2i(old), p2i(new_obj), new_obj->size()); } -#endif if (forward_ptr == NULL) { oop obj_to_push = new_obj; diff --git a/hotspot/src/share/vm/gc/cms/parOopClosures.inline.hpp b/hotspot/src/share/vm/gc/cms/parOopClosures.inline.hpp index 171b3581e94..6619cb09225 100644 --- a/hotspot/src/share/vm/gc/cms/parOopClosures.inline.hpp +++ b/hotspot/src/share/vm/gc/cms/parOopClosures.inline.hpp @@ -108,14 +108,11 @@ inline void ParScanClosure::do_oop_work(T* p, if (m->is_marked()) { // Contains forwarding pointer. new_obj = ParNewGeneration::real_forwardee(obj); oopDesc::encode_store_heap_oop_not_null(p, new_obj); -#ifndef PRODUCT if (TraceScavenge) { gclog_or_tty->print_cr("{%s %s ( " PTR_FORMAT " ) " PTR_FORMAT " -> " PTR_FORMAT " (%d)}", "forwarded ", new_obj->klass()->internal_name(), p2i(p), p2i((void *)obj), p2i((void *)new_obj), new_obj->size()); } -#endif - } else { size_t obj_sz = obj->size_given_klass(objK); new_obj = _g->copy_to_survivor_space(_par_scan_state, obj, obj_sz, m); diff --git a/hotspot/src/share/vm/gc/g1/concurrentMark.cpp b/hotspot/src/share/vm/gc/g1/concurrentMark.cpp index 4fed0ca353d..5ad17675ed3 100644 --- a/hotspot/src/share/vm/gc/g1/concurrentMark.cpp +++ b/hotspot/src/share/vm/gc/g1/concurrentMark.cpp @@ -74,9 +74,7 @@ HeapWord* CMBitMapRO::getNextMarkedWordAddress(const HeapWord* addr, addr = (HeapWord*)align_size_up((intptr_t)addr, HeapWordSize << _shifter); size_t addrOffset = heapWordToOffset(addr); - if (limit == NULL) { - limit = _bmStartWord + _bmWordSize; - } + assert(limit != NULL, "limit must not be NULL"); size_t limitOffset = heapWordToOffset(limit); size_t nextOffset = _bm.get_next_one_offset(addrOffset, limitOffset); HeapWord* nextAddr = offsetToHeapWord(nextOffset); @@ -86,26 +84,6 @@ HeapWord* CMBitMapRO::getNextMarkedWordAddress(const HeapWord* addr, return nextAddr; } -HeapWord* CMBitMapRO::getNextUnmarkedWordAddress(const HeapWord* addr, - const HeapWord* limit) const { - size_t addrOffset = heapWordToOffset(addr); - if (limit == NULL) { - limit = _bmStartWord + _bmWordSize; - } - size_t limitOffset = heapWordToOffset(limit); - size_t nextOffset = _bm.get_next_zero_offset(addrOffset, limitOffset); - HeapWord* nextAddr = offsetToHeapWord(nextOffset); - assert(nextAddr >= addr, "get_next_one postcondition"); - assert(nextAddr == limit || !isMarked(nextAddr), - "get_next_one postcondition"); - return nextAddr; -} - -int CMBitMapRO::heapWordDiffToOffsetDiff(size_t diff) const { - assert((diff & ((1 << _shifter) - 1)) == 0, "argument check"); - return (int) (diff >> _shifter); -} - #ifndef PRODUCT bool CMBitMapRO::covers(MemRegion heap_rs) const { // assert(_bm.map() == _virtual_space.low(), "map inconsistency"); @@ -211,17 +189,6 @@ void CMBitMap::clearAll() { return; } -void CMBitMap::markRange(MemRegion mr) { - mr.intersection(MemRegion(_bmStartWord, _bmWordSize)); - assert(!mr.is_empty(), "unexpected empty region"); - assert((offsetToHeapWord(heapWordToOffset(mr.end())) == - ((HeapWord *) mr.end())), - "markRange memory region end is not card aligned"); - // convert address range into offset range - _bm.at_put_range(heapWordToOffset(mr.start()), - heapWordToOffset(mr.end()), true); -} - void CMBitMap::clearRange(MemRegion mr) { mr.intersection(MemRegion(_bmStartWord, _bmWordSize)); assert(!mr.is_empty(), "unexpected empty region"); @@ -230,20 +197,6 @@ void CMBitMap::clearRange(MemRegion mr) { heapWordToOffset(mr.end()), false); } -MemRegion CMBitMap::getAndClearMarkedRegion(HeapWord* addr, - HeapWord* end_addr) { - HeapWord* start = getNextMarkedWordAddress(addr); - start = MIN2(start, end_addr); - HeapWord* end = getNextUnmarkedWordAddress(start); - end = MIN2(end, end_addr); - assert(start <= end, "Consistency check"); - MemRegion mr(start, end); - if (!mr.is_empty()) { - clearRange(mr); - } - return mr; -} - CMMarkStack::CMMarkStack(ConcurrentMark* cm) : _base(NULL), _cm(cm) {} @@ -466,8 +419,6 @@ ConcurrentMark::ConcurrentMark(G1CollectedHeap* g1h, G1RegionToSpaceMapper* prev _max_parallel_marking_threads(0), _sleep_factor(0.0), _marking_task_overhead(1.0), - _cleanup_sleep_factor(0.0), - _cleanup_task_overhead(1.0), _cleanup_list("Cleanup List"), _region_bm((BitMap::idx_t)(g1h->max_regions()), false /* in_resource_area*/), _card_bm((g1h->reserved_region().byte_size() + CardTableModRefBS::card_size - 1) >> @@ -568,22 +519,6 @@ ConcurrentMark::ConcurrentMark(G1CollectedHeap* g1h, G1RegionToSpaceMapper* prev _parallel_marking_threads = ConcGCThreads; _max_parallel_marking_threads = _parallel_marking_threads; - if (parallel_marking_threads() > 1) { - _cleanup_task_overhead = 1.0; - } else { - _cleanup_task_overhead = marking_task_overhead(); - } - _cleanup_sleep_factor = - (1.0 - cleanup_task_overhead()) / cleanup_task_overhead(); - -#if 0 - gclog_or_tty->print_cr("Marking Threads %d", parallel_marking_threads()); - gclog_or_tty->print_cr("CM Marking Task Overhead %1.4lf", marking_task_overhead()); - gclog_or_tty->print_cr("CM Sleep Factor %1.4lf", sleep_factor()); - gclog_or_tty->print_cr("CL Marking Task Overhead %1.4lf", cleanup_task_overhead()); - gclog_or_tty->print_cr("CL Sleep Factor %1.4lf", cleanup_sleep_factor()); -#endif - _parallel_workers = new WorkGang("G1 Marker", _max_parallel_marking_threads, false, true); if (_parallel_workers == NULL) { @@ -840,14 +775,6 @@ void ConcurrentMark::checkpointRootsInitialPre() { void ConcurrentMark::checkpointRootsInitialPost() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); - // If we force an overflow during remark, the remark operation will - // actually abort and we'll restart concurrent marking. If we always - // force an overflow during remark we'll never actually complete the - // marking phase. So, we initialize this here, at the start of the - // cycle, so that at the remaining overflow number will decrease at - // every remark and we'll eventually not need to cause one. - force_overflow_stw()->init(); - // Start Concurrent Marking weak-reference discovery. ReferenceProcessor* rp = g1h->ref_processor_cm(); // enable ("weak") refs discovery @@ -920,7 +847,6 @@ void ConcurrentMark::enter_first_sync_barrier(uint worker_id) { // we exit this method to abort the pause and restart concurrent // marking. reset_marking_state(true /* clear_overflow */); - force_overflow()->update(); if (G1Log::fine()) { gclog_or_tty->gclog_stamp(); @@ -940,32 +866,6 @@ void ConcurrentMark::enter_second_sync_barrier(uint worker_id) { // at this point everything should be re-initialized and ready to go } -#ifndef PRODUCT -void ForceOverflowSettings::init() { - _num_remaining = G1ConcMarkForceOverflow; - _force = false; - update(); -} - -void ForceOverflowSettings::update() { - if (_num_remaining > 0) { - _num_remaining -= 1; - _force = true; - } else { - _force = false; - } -} - -bool ForceOverflowSettings::should_force() { - if (_force) { - _force = false; - return true; - } else { - return false; - } -} -#endif // !PRODUCT - class CMConcurrentMarkingTask: public AbstractGangTask { private: ConcurrentMark* _cm; @@ -1131,7 +1031,6 @@ void ConcurrentMark::markFromRoots() { // stop-the-world GC happens even as we mark in this generation. _restart_for_overflow = false; - force_overflow_conc()->init(); // _g1h has _n_par_threads _parallel_marking_threads = calc_parallel_marking_threads(); @@ -2440,10 +2339,6 @@ void ConcurrentMark::clearRangePrevBitmap(MemRegion mr) { ((CMBitMap*)_prevMarkBitMap)->clearRange(mr); } -void ConcurrentMark::clearRangeNextBitmap(MemRegion mr) { - _nextMarkBitMap->clearRange(mr); -} - HeapRegion* ConcurrentMark::claim_region(uint worker_id) { // "checkpoint" the finger @@ -3535,15 +3430,6 @@ void CMTask::do_marking_step(double time_target_ms, } } - // If we are about to wrap up and go into termination, check if we - // should raise the overflow flag. - if (do_termination && !has_aborted()) { - if (_cm->force_overflow()->should_force()) { - _cm->set_has_overflown(); - regular_clock_call(); - } - } - // We still haven't aborted. Now, let's try to get into the // termination protocol. if (do_termination && !has_aborted()) { diff --git a/hotspot/src/share/vm/gc/g1/concurrentMark.hpp b/hotspot/src/share/vm/gc/g1/concurrentMark.hpp index d5f4fd71fe4..6a4260ed323 100644 --- a/hotspot/src/share/vm/gc/g1/concurrentMark.hpp +++ b/hotspot/src/share/vm/gc/g1/concurrentMark.hpp @@ -65,11 +65,8 @@ class CMBitMapRO VALUE_OBJ_CLASS_SPEC { // constructor CMBitMapRO(int shifter); - enum { do_yield = true }; - // inquiries HeapWord* startWord() const { return _bmStartWord; } - size_t sizeInWords() const { return _bmWordSize; } // the following is one past the last word in space HeapWord* endWord() const { return _bmStartWord + _bmWordSize; } @@ -83,18 +80,12 @@ class CMBitMapRO VALUE_OBJ_CLASS_SPEC { // iteration inline bool iterate(BitMapClosure* cl, MemRegion mr); - inline bool iterate(BitMapClosure* cl); // Return the address corresponding to the next marked bit at or after // "addr", and before "limit", if "limit" is non-NULL. If there is no // such bit, returns "limit" if that is non-NULL, or else "endWord()". HeapWord* getNextMarkedWordAddress(const HeapWord* addr, const HeapWord* limit = NULL) const; - // Return the address corresponding to the next unmarked bit at or after - // "addr", and before "limit", if "limit" is non-NULL. If there is no - // such bit, returns "limit" if that is non-NULL, or else "endWord()". - HeapWord* getNextUnmarkedWordAddress(const HeapWord* addr, - const HeapWord* limit = NULL) const; // conversion utilities HeapWord* offsetToHeapWord(size_t offset) const { @@ -103,7 +94,6 @@ class CMBitMapRO VALUE_OBJ_CLASS_SPEC { size_t heapWordToOffset(const HeapWord* addr) const { return pointer_delta(addr, _bmStartWord) >> _shifter; } - int heapWordDiffToOffsetDiff(size_t diff) const; // The argument addr should be the start address of a valid object HeapWord* nextObject(HeapWord* addr) { @@ -153,20 +143,9 @@ class CMBitMap : public CMBitMapRO { inline void mark(HeapWord* addr); inline void clear(HeapWord* addr); inline bool parMark(HeapWord* addr); - inline bool parClear(HeapWord* addr); - void markRange(MemRegion mr); void clearRange(MemRegion mr); - // Starting at the bit corresponding to "addr" (inclusive), find the next - // "1" bit, if any. This bit starts some run of consecutive "1"'s; find - // the end of this run (stopping at "end_addr"). Return the MemRegion - // covering from the start of the region corresponding to the first bit - // of the run to the end of the region corresponding to the last bit of - // the run. If there is no "1" bit at or after "addr", return an empty - // MemRegion. - MemRegion getAndClearMarkedRegion(HeapWord* addr, HeapWord* end_addr); - // Clear the whole mark bitmap. void clearAll(); }; @@ -231,19 +210,6 @@ class CMMarkStack VALUE_OBJ_CLASS_SPEC { template void iterate(Fn fn); }; -class ForceOverflowSettings VALUE_OBJ_CLASS_SPEC { -private: -#ifndef PRODUCT - uintx _num_remaining; - bool _force; -#endif // !defined(PRODUCT) - -public: - void init() PRODUCT_RETURN; - void update() PRODUCT_RETURN; - bool should_force() PRODUCT_RETURN_( return false; ); -}; - class YoungList; // Root Regions are regions that are not empty at the beginning of a @@ -326,10 +292,6 @@ protected: double _marking_task_overhead; // Marking target overhead for // a single task - // Same as the two above, but for the cleanup task - double _cleanup_sleep_factor; - double _cleanup_task_overhead; - FreeRegionList _cleanup_list; // Concurrent marking support structures @@ -404,9 +366,6 @@ protected: WorkGang* _parallel_workers; - ForceOverflowSettings _force_overflow_conc; - ForceOverflowSettings _force_overflow_stw; - void weakRefsWorkParallelPart(BoolObjectClosure* is_alive, bool purged_classes); void weakRefsWork(bool clear_all_soft_refs); @@ -443,8 +402,6 @@ protected: uint max_parallel_marking_threads() const { return _max_parallel_marking_threads;} double sleep_factor() { return _sleep_factor; } double marking_task_overhead() { return _marking_task_overhead;} - double cleanup_sleep_factor() { return _cleanup_sleep_factor; } - double cleanup_task_overhead() { return _cleanup_task_overhead;} HeapWord* finger() { return _finger; } bool concurrent() { return _concurrent; } @@ -502,22 +459,6 @@ protected: void enter_first_sync_barrier(uint worker_id); void enter_second_sync_barrier(uint worker_id); - ForceOverflowSettings* force_overflow_conc() { - return &_force_overflow_conc; - } - - ForceOverflowSettings* force_overflow_stw() { - return &_force_overflow_stw; - } - - ForceOverflowSettings* force_overflow() { - if (concurrent()) { - return force_overflow_conc(); - } else { - return force_overflow_stw(); - } - } - // Live Data Counting data structures... // These data structures are initialized at the start of // marking. They are written to while marking is active. @@ -625,28 +566,6 @@ public: uint worker_id, HeapRegion* hr = NULL); - // It iterates over the heap and for each object it comes across it - // will dump the contents of its reference fields, as well as - // liveness information for the object and its referents. The dump - // will be written to a file with the following name: - // G1PrintReachableBaseFile + "." + str. - // vo decides whether the prev (vo == UsePrevMarking), the next - // (vo == UseNextMarking) marking information, or the mark word - // (vo == UseMarkWord) will be used to determine the liveness of - // each object / referent. - // If all is true, all objects in the heap will be dumped, otherwise - // only the live ones. In the dump the following symbols / breviations - // are used: - // M : an explicitly live object (its bitmap bit is set) - // > : an implicitly live object (over tams) - // O : an object outside the G1 heap (typically: in the perm gen) - // NOT : a reference field whose referent is not live - // AND MARKED : indicates that an object is both explicitly and - // implicitly live (it should be one or the other, not both) - void print_reachable(const char* str, - VerifyOption vo, - bool all) PRODUCT_RETURN; - // Clear the next marking bitmap (will be called concurrently). void clearNextBitmap(); @@ -686,7 +605,6 @@ public: // next bitmaps. NB: the previous bitmap is usually // read-only, so use this carefully! void clearRangePrevBitmap(MemRegion mr); - void clearRangeNextBitmap(MemRegion mr); // Notify data structures that a GC has started. void note_start_of_gc() { diff --git a/hotspot/src/share/vm/gc/g1/concurrentMark.inline.hpp b/hotspot/src/share/vm/gc/g1/concurrentMark.inline.hpp index c75f6898713..fe26975cc20 100644 --- a/hotspot/src/share/vm/gc/g1/concurrentMark.inline.hpp +++ b/hotspot/src/share/vm/gc/g1/concurrentMark.inline.hpp @@ -185,11 +185,6 @@ inline bool CMBitMapRO::iterate(BitMapClosure* cl, MemRegion mr) { return true; } -inline bool CMBitMapRO::iterate(BitMapClosure* cl) { - MemRegion mr(startWord(), sizeInWords()); - return iterate(cl, mr); -} - #define check_mark(addr) \ assert(_bmStartWord <= (addr) && (addr) < (_bmStartWord + _bmWordSize), \ "outside underlying space?"); \ @@ -213,11 +208,6 @@ inline bool CMBitMap::parMark(HeapWord* addr) { return _bm.par_set_bit(heapWordToOffset(addr)); } -inline bool CMBitMap::parClear(HeapWord* addr) { - check_mark(addr); - return _bm.par_clear_bit(heapWordToOffset(addr)); -} - #undef check_mark template diff --git a/hotspot/src/share/vm/gc/g1/dirtyCardQueue.hpp b/hotspot/src/share/vm/gc/g1/dirtyCardQueue.hpp index 965c83b1d67..f3f9bb18a07 100644 --- a/hotspot/src/share/vm/gc/g1/dirtyCardQueue.hpp +++ b/hotspot/src/share/vm/gc/g1/dirtyCardQueue.hpp @@ -72,6 +72,18 @@ public: void **get_buf() { return _buf;} size_t get_index() { return _index;} void reinitialize() { _buf = 0; _sz = 0; _index = 0;} + + // Compiler support. + static ByteSize byte_offset_of_index() { + return PtrQueue::byte_offset_of_index(); + } + using PtrQueue::byte_width_of_index; + + static ByteSize byte_offset_of_buf() { + return PtrQueue::byte_offset_of_buf(); + } + using PtrQueue::byte_width_of_buf; + }; diff --git a/hotspot/src/share/vm/gc/g1/g1AllocRegion.cpp b/hotspot/src/share/vm/gc/g1/g1AllocRegion.cpp index 9a61b0da769..f7a9c8e8414 100644 --- a/hotspot/src/share/vm/gc/g1/g1AllocRegion.cpp +++ b/hotspot/src/share/vm/gc/g1/g1AllocRegion.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "gc/g1/g1AllocRegion.inline.hpp" +#include "gc/g1/g1EvacStats.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "runtime/orderAccess.inline.hpp" diff --git a/hotspot/src/share/vm/gc/g1/g1Allocator.cpp b/hotspot/src/share/vm/gc/g1/g1Allocator.cpp index 8bd8b376c72..665a7b5ed61 100644 --- a/hotspot/src/share/vm/gc/g1/g1Allocator.cpp +++ b/hotspot/src/share/vm/gc/g1/g1Allocator.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "gc/g1/g1Allocator.inline.hpp" #include "gc/g1/g1AllocRegion.inline.hpp" +#include "gc/g1/g1EvacStats.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectorPolicy.hpp" #include "gc/g1/g1MarkSweep.hpp" diff --git a/hotspot/src/share/vm/gc/g1/g1BlockOffsetTable.cpp b/hotspot/src/share/vm/gc/g1/g1BlockOffsetTable.cpp index 801e4c9f5cf..a1542adf8e5 100644 --- a/hotspot/src/share/vm/gc/g1/g1BlockOffsetTable.cpp +++ b/hotspot/src/share/vm/gc/g1/g1BlockOffsetTable.cpp @@ -499,11 +499,14 @@ HeapWord* G1BlockOffsetArrayContigSpace::initialize_threshold() { return _next_offset_threshold; } -void G1BlockOffsetArrayContigSpace::set_for_starts_humongous(HeapWord* obj_top) { +void G1BlockOffsetArrayContigSpace::set_for_starts_humongous(HeapWord* obj_top, size_t fill_size) { // The first BOT entry should have offset 0. reset_bot(); alloc_block(_bottom, obj_top); - } + if (fill_size > 0) { + alloc_block(obj_top, fill_size); + } +} #ifndef PRODUCT void G1BlockOffsetArrayContigSpace::print_on(outputStream* out) { diff --git a/hotspot/src/share/vm/gc/g1/g1BlockOffsetTable.hpp b/hotspot/src/share/vm/gc/g1/g1BlockOffsetTable.hpp index 7592dec864d..7c900fb960a 100644 --- a/hotspot/src/share/vm/gc/g1/g1BlockOffsetTable.hpp +++ b/hotspot/src/share/vm/gc/g1/g1BlockOffsetTable.hpp @@ -372,7 +372,7 @@ class G1BlockOffsetArrayContigSpace: public G1BlockOffsetArray { HeapWord* block_start_unsafe(const void* addr); HeapWord* block_start_unsafe_const(const void* addr) const; - void set_for_starts_humongous(HeapWord* obj_top); + void set_for_starts_humongous(HeapWord* obj_top, size_t fill_size); virtual void print_on(outputStream* out) PRODUCT_RETURN; }; diff --git a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp index cba9a3eaf36..abb38f00ecd 100644 --- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp +++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp @@ -36,7 +36,7 @@ #include "gc/g1/g1CollectorPolicy.hpp" #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1ErgoVerbose.hpp" -#include "gc/g1/g1EvacFailure.hpp" +#include "gc/g1/g1EvacStats.inline.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1Log.hpp" #include "gc/g1/g1MarkSweep.hpp" @@ -112,18 +112,37 @@ public: class RedirtyLoggedCardTableEntryClosure : public CardTableEntryClosure { private: - size_t _num_processed; + size_t _num_dirtied; + G1CollectedHeap* _g1h; + G1SATBCardTableLoggingModRefBS* _g1_bs; + + HeapRegion* region_for_card(jbyte* card_ptr) const { + return _g1h->heap_region_containing(_g1_bs->addr_for(card_ptr)); + } + + bool will_become_free(HeapRegion* hr) const { + // A region will be freed by free_collection_set if the region is in the + // collection set and has not had an evacuation failure. + return _g1h->is_in_cset(hr) && !hr->evacuation_failed(); + } public: - RedirtyLoggedCardTableEntryClosure() : CardTableEntryClosure(), _num_processed(0) { } + RedirtyLoggedCardTableEntryClosure(G1CollectedHeap* g1h) : CardTableEntryClosure(), + _num_dirtied(0), _g1h(g1h), _g1_bs(g1h->g1_barrier_set()) { } bool do_card_ptr(jbyte* card_ptr, uint worker_i) { - *card_ptr = CardTableModRefBS::dirty_card_val(); - _num_processed++; + HeapRegion* hr = region_for_card(card_ptr); + + // Should only dirty cards in regions that won't be freed. + if (!will_become_free(hr)) { + *card_ptr = CardTableModRefBS::dirty_card_val(); + _num_dirtied++; + } + return true; } - size_t num_processed() const { return _num_processed; } + size_t num_dirtied() const { return _num_dirtied; } }; @@ -300,8 +319,8 @@ G1CollectedHeap::humongous_obj_allocate_initialize_regions(uint first, assert(is_humongous(word_size), "word_size should be humongous"); assert(num_regions * HeapRegion::GrainWords >= word_size, "pre-condition"); - // Index of last region in the series + 1. - uint last = first + num_regions; + // Index of last region in the series. + uint last = first + num_regions - 1; // We need to initialize the region(s) we just discovered. This is // a bit tricky given that it can happen concurrently with @@ -338,16 +357,30 @@ G1CollectedHeap::humongous_obj_allocate_initialize_regions(uint first, // thread to calculate the object size incorrectly. Copy::fill_to_words(new_obj, oopDesc::header_size(), 0); + // How many words we use for filler objects. + size_t word_fill_size = word_size_sum - word_size; + + // How many words memory we "waste" which cannot hold a filler object. + size_t words_not_fillable = 0; + + if (word_fill_size >= min_fill_size()) { + fill_with_objects(obj_top, word_fill_size); + } else if (word_fill_size > 0) { + // We have space to fill, but we cannot fit an object there. + words_not_fillable = word_fill_size; + word_fill_size = 0; + } + // We will set up the first region as "starts humongous". This // will also update the BOT covering all the regions to reflect // that there is a single object that starts at the bottom of the // first region. - first_hr->set_starts_humongous(obj_top); + first_hr->set_starts_humongous(obj_top, word_fill_size); first_hr->set_allocation_context(context); // Then, if there are any, we will set up the "continues // humongous" regions. HeapRegion* hr = NULL; - for (uint i = first + 1; i < last; ++i) { + for (uint i = first + 1; i <= last; ++i) { hr = region_at(i); hr->set_continues_humongous(first_hr); hr->set_allocation_context(context); @@ -362,45 +395,48 @@ G1CollectedHeap::humongous_obj_allocate_initialize_regions(uint first, // object header and the BOT initialization. OrderAccess::storestore(); - // Now that the BOT and the object header have been initialized, - // we can update top of the "starts humongous" region. - first_hr->set_top(MIN2(first_hr->end(), obj_top)); - if (_hr_printer.is_active()) { - _hr_printer.alloc(G1HRPrinter::StartsHumongous, first_hr, first_hr->top()); + // Now, we will update the top fields of the "continues humongous" + // regions except the last one. + for (uint i = first; i < last; ++i) { + hr = region_at(i); + hr->set_top(hr->end()); } - // Now, we will update the top fields of the "continues humongous" - // regions. - hr = NULL; - for (uint i = first + 1; i < last; ++i) { - hr = region_at(i); - if ((i + 1) == last) { - // last continues humongous region - assert(hr->bottom() < obj_top && obj_top <= hr->end(), - "new_top should fall on this region"); - hr->set_top(obj_top); - _hr_printer.alloc(G1HRPrinter::ContinuesHumongous, hr, obj_top); - } else { - // not last one - assert(obj_top > hr->end(), "obj_top should be above this region"); - hr->set_top(hr->end()); - _hr_printer.alloc(G1HRPrinter::ContinuesHumongous, hr, hr->end()); - } - } - // If we have continues humongous regions (hr != NULL), its top should - // match obj_top. - assert(hr == NULL || (hr->top() == obj_top), "sanity"); + hr = region_at(last); + // If we cannot fit a filler object, we must set top to the end + // of the humongous object, otherwise we cannot iterate the heap + // and the BOT will not be complete. + hr->set_top(hr->end() - words_not_fillable); + + assert(hr->bottom() < obj_top && obj_top <= hr->end(), + "obj_top should be in last region"); + check_bitmaps("Humongous Region Allocation", first_hr); - increase_used(word_size * HeapWordSize); + assert(words_not_fillable == 0 || + first_hr->bottom() + word_size_sum - words_not_fillable == hr->top(), + "Miscalculation in humongous allocation"); - for (uint i = first; i < last; ++i) { - _humongous_set.add(region_at(i)); + increase_used((word_size_sum - words_not_fillable) * HeapWordSize); + + for (uint i = first; i <= last; ++i) { + hr = region_at(i); + _humongous_set.add(hr); + if (i == first) { + _hr_printer.alloc(G1HRPrinter::StartsHumongous, hr, hr->top()); + } else { + _hr_printer.alloc(G1HRPrinter::ContinuesHumongous, hr, hr->top()); + } } return new_obj; } +size_t G1CollectedHeap::humongous_obj_size_in_regions(size_t word_size) { + assert(is_humongous(word_size), "Object of size " SIZE_FORMAT " must be humongous here", word_size); + return align_size_up_(word_size, HeapRegion::GrainWords) / HeapRegion::GrainWords; +} + // If could fit into free regions w/o expansion, try. // Otherwise, if can expand, do so. // Otherwise, if using ex regions might help, try with ex given back. @@ -410,7 +446,7 @@ HeapWord* G1CollectedHeap::humongous_obj_allocate(size_t word_size, AllocationCo verify_region_sets_optional(); uint first = G1_NO_HRM_INDEX; - uint obj_regions = (uint)(align_size_up_(word_size, HeapRegion::GrainWords) / HeapRegion::GrainWords); + uint obj_regions = (uint) humongous_obj_size_in_regions(word_size); if (obj_regions == 1) { // Only one region to allocate, try to use a fast path by directly allocating @@ -1010,6 +1046,8 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size, // collection hoping that there's enough space in the heap. result = humongous_obj_allocate(word_size, AllocationContext::current()); if (result != NULL) { + size_t size_in_regions = humongous_obj_size_in_regions(word_size); + g1_policy()->add_bytes_allocated_in_old_since_last_gc(size_in_regions * HeapRegion::GrainBytes); return result; } @@ -1201,9 +1239,8 @@ void G1CollectedHeap::print_hrm_post_compaction() { heap_region_iterate(&cl); } -bool G1CollectedHeap::do_collection(bool explicit_gc, - bool clear_all_soft_refs, - size_t word_size) { +bool G1CollectedHeap::do_full_collection(bool explicit_gc, + bool clear_all_soft_refs) { assert_at_safepoint(true /* should_be_vm_thread */); if (GC_locker::check_active_before_gc()) { @@ -1361,8 +1398,7 @@ bool G1CollectedHeap::do_collection(bool explicit_gc, clear_rsets_post_compaction(); check_gc_time_stamps(); - // Resize the heap if necessary. - resize_if_necessary_after_full_collection(explicit_gc ? 0 : word_size); + resize_if_necessary_after_full_collection(); if (_hr_printer.is_active()) { // We should do this after we potentially resize the heap so @@ -1470,22 +1506,15 @@ bool G1CollectedHeap::do_collection(bool explicit_gc, } void G1CollectedHeap::do_full_collection(bool clear_all_soft_refs) { - // do_collection() will return whether it succeeded in performing - // the GC. Currently, there is no facility on the - // do_full_collection() API to notify the caller than the collection - // did not succeed (e.g., because it was locked out by the GC - // locker). So, right now, we'll ignore the return value. - bool dummy = do_collection(true, /* explicit_gc */ - clear_all_soft_refs, - 0 /* word_size */); + // Currently, there is no facility in the do_full_collection(bool) API to notify + // the caller that the collection did not succeed (e.g., because it was locked + // out by the GC locker). So, right now, we'll ignore the return value. + bool dummy = do_full_collection(true, /* explicit_gc */ + clear_all_soft_refs); } -// This code is mostly copied from TenuredGeneration. -void -G1CollectedHeap:: -resize_if_necessary_after_full_collection(size_t word_size) { - // Include the current allocation, if any, and bytes that will be - // pre-allocated to support collections, as "used". +void G1CollectedHeap::resize_if_necessary_after_full_collection() { + // Include bytes that will be pre-allocated to support collections, as "used". const size_t used_after_gc = used(); const size_t capacity_after_gc = capacity(); const size_t free_after_gc = capacity_after_gc - used_after_gc; @@ -1597,9 +1626,8 @@ HeapWord* G1CollectedHeap::satisfy_failed_allocation_helper(size_t word_size, if (do_gc) { // Expansion didn't work, we'll try to do a Full GC. - *gc_succeeded = do_collection(false, /* explicit_gc */ - clear_all_soft_refs, - word_size); + *gc_succeeded = do_full_collection(false, /* explicit_gc */ + clear_all_soft_refs); } return NULL; @@ -2259,15 +2287,21 @@ size_t G1CollectedHeap::recalculate_used() const { return blk.result(); } +bool G1CollectedHeap::is_user_requested_concurrent_full_gc(GCCause::Cause cause) { + switch (cause) { + case GCCause::_java_lang_system_gc: return ExplicitGCInvokesConcurrent; + case GCCause::_dcmd_gc_run: return ExplicitGCInvokesConcurrent; + case GCCause::_update_allocation_context_stats_inc: return true; + case GCCause::_wb_conc_mark: return true; + default : return false; + } +} + bool G1CollectedHeap::should_do_concurrent_full_gc(GCCause::Cause cause) { switch (cause) { case GCCause::_gc_locker: return GCLockerInvokesConcurrent; - case GCCause::_java_lang_system_gc: return ExplicitGCInvokesConcurrent; - case GCCause::_dcmd_gc_run: return ExplicitGCInvokesConcurrent; case GCCause::_g1_humongous_allocation: return true; - case GCCause::_update_allocation_context_stats_inc: return true; - case GCCause::_wb_conc_mark: return true; - default: return false; + default: return is_user_requested_concurrent_full_gc(cause); } } @@ -3816,7 +3850,11 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { // reference processing currently works in G1. // Enable discovery in the STW reference processor - ref_processor_stw()->enable_discovery(); + if (g1_policy()->should_process_references()) { + ref_processor_stw()->enable_discovery(); + } else { + ref_processor_stw()->disable_discovery(); + } { // We want to temporarily turn off discovery by the @@ -4076,21 +4114,21 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { return true; } -void G1CollectedHeap::remove_self_forwarding_pointers() { - double remove_self_forwards_start = os::elapsedTime(); +void G1CollectedHeap::restore_preserved_marks() { + G1RestorePreservedMarksTask rpm_task(_preserved_objs); + workers()->run_task(&rpm_task); +} +void G1CollectedHeap::remove_self_forwarding_pointers() { G1ParRemoveSelfForwardPtrsTask rsfp_task; workers()->run_task(&rsfp_task); +} - // Now restore saved marks, if any. - for (uint i = 0; i < ParallelGCThreads; i++) { - OopAndMarkOopStack& cur = _preserved_objs[i]; - while (!cur.is_empty()) { - OopAndMarkOop elem = cur.pop(); - elem.set_mark(); - } - cur.clear(true); - } +void G1CollectedHeap::restore_after_evac_failure() { + double remove_self_forwards_start = os::elapsedTime(); + + remove_self_forwarding_pointers(); + restore_preserved_marks(); g1_policy()->phase_times()->record_evac_fail_remove_self_forwards((os::elapsedTime() - remove_self_forwards_start) * 1000.0); } @@ -4110,42 +4148,6 @@ void G1CollectedHeap::preserve_mark_during_evac_failure(uint worker_id, oop obj, } } -class G1ParEvacuateFollowersClosure : public VoidClosure { -private: - double _start_term; - double _term_time; - size_t _term_attempts; - - void start_term_time() { _term_attempts++; _start_term = os::elapsedTime(); } - void end_term_time() { _term_time += os::elapsedTime() - _start_term; } -protected: - G1CollectedHeap* _g1h; - G1ParScanThreadState* _par_scan_state; - RefToScanQueueSet* _queues; - ParallelTaskTerminator* _terminator; - - G1ParScanThreadState* par_scan_state() { return _par_scan_state; } - RefToScanQueueSet* queues() { return _queues; } - ParallelTaskTerminator* terminator() { return _terminator; } - -public: - G1ParEvacuateFollowersClosure(G1CollectedHeap* g1h, - G1ParScanThreadState* par_scan_state, - RefToScanQueueSet* queues, - ParallelTaskTerminator* terminator) - : _g1h(g1h), _par_scan_state(par_scan_state), - _queues(queues), _terminator(terminator), - _start_term(0.0), _term_time(0.0), _term_attempts(0) {} - - void do_void(); - - double term_time() const { return _term_time; } - size_t term_attempts() const { return _term_attempts; } - -private: - inline bool offer_termination(); -}; - bool G1ParEvacuateFollowersClosure::offer_termination() { G1ParScanThreadState* const pss = par_scan_state(); start_term_time(); @@ -4642,24 +4644,26 @@ void G1CollectedHeap::unlink_string_and_symbol_table(BoolObjectClosure* is_alive class G1RedirtyLoggedCardsTask : public AbstractGangTask { private: DirtyCardQueueSet* _queue; + G1CollectedHeap* _g1h; public: - G1RedirtyLoggedCardsTask(DirtyCardQueueSet* queue) : AbstractGangTask("Redirty Cards"), _queue(queue) { } + G1RedirtyLoggedCardsTask(DirtyCardQueueSet* queue, G1CollectedHeap* g1h) : AbstractGangTask("Redirty Cards"), + _queue(queue), _g1h(g1h) { } virtual void work(uint worker_id) { - G1GCPhaseTimes* phase_times = G1CollectedHeap::heap()->g1_policy()->phase_times(); + G1GCPhaseTimes* phase_times = _g1h->g1_policy()->phase_times(); G1GCParPhaseTimesTracker x(phase_times, G1GCPhaseTimes::RedirtyCards, worker_id); - RedirtyLoggedCardTableEntryClosure cl; + RedirtyLoggedCardTableEntryClosure cl(_g1h); _queue->par_apply_closure_to_all_completed_buffers(&cl); - phase_times->record_thread_work_item(G1GCPhaseTimes::RedirtyCards, worker_id, cl.num_processed()); + phase_times->record_thread_work_item(G1GCPhaseTimes::RedirtyCards, worker_id, cl.num_dirtied()); } }; void G1CollectedHeap::redirty_logged_cards() { double redirty_logged_cards_start = os::elapsedTime(); - G1RedirtyLoggedCardsTask redirty_task(&dirty_card_queue_set()); + G1RedirtyLoggedCardsTask redirty_task(&dirty_card_queue_set(), this); dirty_card_queue_set().reset_for_par_iteration(); workers()->run_task(&redirty_task); @@ -4999,6 +5003,17 @@ public: } }; +void G1CollectedHeap::process_weak_jni_handles() { + double ref_proc_start = os::elapsedTime(); + + G1STWIsAliveClosure is_alive(this); + G1KeepAliveClosure keep_alive(this); + JNIHandles::weak_oops_do(&is_alive, &keep_alive); + + double ref_proc_time = os::elapsedTime() - ref_proc_start; + g1_policy()->phase_times()->record_ref_proc_time(ref_proc_time * 1000.0); +} + // Weak Reference processing during an evacuation pause (part 1). void G1CollectedHeap::process_discovered_references(G1ParScanThreadStateSet* per_thread_states) { double ref_proc_start = os::elapsedTime(); @@ -5133,11 +5148,10 @@ void G1CollectedHeap::pre_evacuate_collection_set() { hot_card_cache->reset_hot_cache_claimed_index(); hot_card_cache->set_use_cache(false); + g1_rem_set()->prepare_for_oops_into_collection_set_do(); } void G1CollectedHeap::evacuate_collection_set(EvacuationInfo& evacuation_info, G1ParScanThreadStateSet* per_thread_states) { - g1_rem_set()->prepare_for_oops_into_collection_set_do(); - // Should G1EvacuationFailureALot be in effect for this GC? NOT_PRODUCT(set_evacuation_failure_alot_for_current_gc();) @@ -5177,29 +5191,36 @@ void G1CollectedHeap::evacuate_collection_set(EvacuationInfo& evacuation_info, G double code_root_fixup_time_ms = (os::elapsedTime() - end_par_time_sec) * 1000.0; phase_times->record_code_root_fixup_time(code_root_fixup_time_ms); +} +void G1CollectedHeap::post_evacuate_collection_set(EvacuationInfo& evacuation_info, G1ParScanThreadStateSet* per_thread_states) { // Process any discovered reference objects - we have // to do this _before_ we retire the GC alloc regions // as we may have to copy some 'reachable' referent // objects (and their reachable sub-graphs) that were // not copied during the pause. - process_discovered_references(per_thread_states); + if (g1_policy()->should_process_references()) { + process_discovered_references(per_thread_states); + } else { + ref_processor_stw()->verify_no_references_recorded(); + process_weak_jni_handles(); + } if (G1StringDedup::is_enabled()) { double fixup_start = os::elapsedTime(); G1STWIsAliveClosure is_alive(this); G1KeepAliveClosure keep_alive(this); - G1StringDedup::unlink_or_oops_do(&is_alive, &keep_alive, true, phase_times); + G1StringDedup::unlink_or_oops_do(&is_alive, &keep_alive, true, g1_policy()->phase_times()); double fixup_time_ms = (os::elapsedTime() - fixup_start) * 1000.0; - phase_times->record_string_dedup_fixup_time(fixup_time_ms); + g1_policy()->phase_times()->record_string_dedup_fixup_time(fixup_time_ms); } g1_rem_set()->cleanup_after_oops_into_collection_set_do(); if (evacuation_failed()) { - remove_self_forwarding_pointers(); + restore_after_evac_failure(); // Reset the G1EvacuationFailureALot counters and flags // Note: the values are reset only when an actual @@ -5214,10 +5235,12 @@ void G1CollectedHeap::evacuate_collection_set(EvacuationInfo& evacuation_info, G // will log these updates (and dirty their associated // cards). We need these updates logged to update any // RSets. - enqueue_discovered_references(per_thread_states); -} + if (g1_policy()->should_process_references()) { + enqueue_discovered_references(per_thread_states); + } else { + g1_policy()->phase_times()->record_ref_enq_time(0); + } -void G1CollectedHeap::post_evacuate_collection_set(EvacuationInfo& evacuation_info, G1ParScanThreadStateSet* per_thread_states) { _allocator->release_gc_alloc_regions(evacuation_info); per_thread_states->flush(); @@ -5243,6 +5266,8 @@ void G1CollectedHeap::post_evacuate_collection_set(EvacuationInfo& evacuation_in } void G1CollectedHeap::record_obj_copy_mem_stats() { + g1_policy()->add_bytes_allocated_in_old_since_last_gc(_old_evac_stats.allocated() * HeapWordSize); + _gc_tracer_stw->report_evacuation_statistics(create_g1_evac_summary(&_survivor_evac_stats), create_g1_evac_summary(&_old_evac_stats)); } @@ -5627,6 +5652,14 @@ void G1CollectedHeap::free_collection_set(HeapRegion* cs_head, EvacuationInfo& e cur->set_young_index_in_cset(-1); } cur->set_evacuation_failed(false); + // When moving a young gen region to old gen, we "allocate" that whole region + // there. This is in addition to any already evacuated objects. Notify the + // policy about that. + // Old gen regions do not cause an additional allocation: both the objects + // still in the region and the ones already moved are accounted for elsewhere. + if (cur->is_young()) { + policy->add_bytes_allocated_in_old_since_last_gc(HeapRegion::GrainBytes); + } // The region is now considered to be old. cur->set_old(); // Do some allocation statistics accounting. Regions that failed evacuation diff --git a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp index e34e1684493..cfedf798e2d 100644 --- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp +++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp @@ -33,6 +33,7 @@ #include "gc/g1/g1HRPrinter.hpp" #include "gc/g1/g1InCSetState.hpp" #include "gc/g1/g1MonitoringSupport.hpp" +#include "gc/g1/g1EvacFailure.hpp" #include "gc/g1/g1EvacStats.hpp" #include "gc/g1/g1SATBCardTableModRefBS.hpp" #include "gc/g1/g1YCTypes.hpp" @@ -244,9 +245,11 @@ private: // instead of doing a STW GC. Currently, a concurrent cycle is // explicitly started if: // (a) cause == _gc_locker and +GCLockerInvokesConcurrent, or - // (b) cause == _java_lang_system_gc and +ExplicitGCInvokesConcurrent. - // (c) cause == _dcmd_gc_run and +ExplicitGCInvokesConcurrent. - // (d) cause == _g1_humongous_allocation + // (b) cause == _g1_humongous_allocation + // (c) cause == _java_lang_system_gc and +ExplicitGCInvokesConcurrent. + // (d) cause == _dcmd_gc_run and +ExplicitGCInvokesConcurrent. + // (e) cause == _update_allocation_context_stats_inc + // (f) cause == _wb_conc_mark bool should_do_concurrent_full_gc(GCCause::Cause cause); // indicates whether we are in young or mixed GC mode @@ -292,6 +295,8 @@ private: void trace_heap(GCWhen::Type when, const GCTracer* tracer); + void process_weak_jni_handles(); + // These are macros so that, if the assert fires, we get the correct // line number, file, etc. @@ -471,26 +476,20 @@ protected: void retire_gc_alloc_region(HeapRegion* alloc_region, size_t allocated_bytes, InCSetState dest); - // - if explicit_gc is true, the GC is for a System.gc() or a heap - // inspection request and should collect the entire heap + // - if explicit_gc is true, the GC is for a System.gc() etc, + // otherwise it's for a failed allocation. // - if clear_all_soft_refs is true, all soft references should be - // cleared during the GC - // - if explicit_gc is false, word_size describes the allocation that - // the GC should attempt (at least) to satisfy + // cleared during the GC. // - it returns false if it is unable to do the collection due to the - // GC locker being active, true otherwise - bool do_collection(bool explicit_gc, - bool clear_all_soft_refs, - size_t word_size); + // GC locker being active, true otherwise. + bool do_full_collection(bool explicit_gc, + bool clear_all_soft_refs); - // Callback from VM_G1CollectFull operation. - // Perform a full collection. + // Callback from VM_G1CollectFull operation, or collect_as_vm_thread. virtual void do_full_collection(bool clear_all_soft_refs); - // Resize the heap if necessary after a full collection. If this is - // after a collect-for allocation, "word_size" is the allocation size, - // and will be considered part of the used portion of the heap. - void resize_if_necessary_after_full_collection(size_t word_size); + // Resize the heap if necessary after a full collection. + void resize_if_necessary_after_full_collection(); // Callback from VM_G1CollectForAllocation operation. // This function does everything necessary/possible to satisfy a @@ -582,6 +581,8 @@ public: _in_cset_fast_test.clear(); } + bool is_user_requested_concurrent_full_gc(GCCause::Cause cause); + // This is called at the start of either a concurrent cycle or a Full // GC to update the number of old marking cycles started. void increment_old_marking_cycles_started(); @@ -784,20 +785,13 @@ protected: // forwarding pointers to themselves. Reset them. void remove_self_forwarding_pointers(); - struct OopAndMarkOop { - private: - oop _o; - markOop _m; - public: - OopAndMarkOop(oop obj, markOop m) : _o(obj), _m(m) { - } + // Restore the preserved mark words for objects with self-forwarding pointers. + void restore_preserved_marks(); - void set_mark() { - _o->set_mark(_m); - } - }; + // Restore the objects in the regions in the collection set after an + // evacuation failure. + void restore_after_evac_failure(); - typedef Stack OopAndMarkOopStack; // Stores marks with the corresponding oop that we need to preserve during evacuation // failure. OopAndMarkOopStack* _preserved_objs; @@ -1150,9 +1144,6 @@ public: // "CollectedHeap" supports. virtual void collect(GCCause::Cause cause); - // The same as above but assume that the caller holds the Heap_lock. - void collect_locked(GCCause::Cause cause); - virtual bool copy_allocation_context_stats(const jint* contexts, jlong* totals, jbyte* accuracy, @@ -1352,13 +1343,9 @@ public: return (region_size / 2); } - // Update mod union table with the set of dirty cards. - void updateModUnion(); - - // Set the mod union bits corresponding to the given memRegion. Note - // that this is always a safe operation, since it doesn't clear any - // bits. - void markModUnionRange(MemRegion mr); + // Returns the number of regions the humongous object of the given word size + // requires. + static size_t humongous_obj_size_in_regions(size_t word_size); // Print the maximum heap capacity. virtual size_t max_capacity() const; @@ -1535,4 +1522,40 @@ protected: size_t _max_heap_capacity; }; +class G1ParEvacuateFollowersClosure : public VoidClosure { +private: + double _start_term; + double _term_time; + size_t _term_attempts; + + void start_term_time() { _term_attempts++; _start_term = os::elapsedTime(); } + void end_term_time() { _term_time += os::elapsedTime() - _start_term; } +protected: + G1CollectedHeap* _g1h; + G1ParScanThreadState* _par_scan_state; + RefToScanQueueSet* _queues; + ParallelTaskTerminator* _terminator; + + G1ParScanThreadState* par_scan_state() { return _par_scan_state; } + RefToScanQueueSet* queues() { return _queues; } + ParallelTaskTerminator* terminator() { return _terminator; } + +public: + G1ParEvacuateFollowersClosure(G1CollectedHeap* g1h, + G1ParScanThreadState* par_scan_state, + RefToScanQueueSet* queues, + ParallelTaskTerminator* terminator) + : _g1h(g1h), _par_scan_state(par_scan_state), + _queues(queues), _terminator(terminator), + _start_term(0.0), _term_time(0.0), _term_attempts(0) {} + + void do_void(); + + double term_time() const { return _term_time; } + size_t term_attempts() const { return _term_attempts; } + +private: + inline bool offer_termination(); +}; + #endif // SHARE_VM_GC_G1_G1COLLECTEDHEAP_HPP diff --git a/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.cpp b/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.cpp index 2a5ec5e9366..e3c39c71a6e 100644 --- a/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.cpp +++ b/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.cpp @@ -28,6 +28,7 @@ #include "gc/g1/concurrentMarkThread.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectorPolicy.hpp" +#include "gc/g1/g1IHOPControl.hpp" #include "gc/g1/g1ErgoVerbose.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1Log.hpp" @@ -38,6 +39,7 @@ #include "runtime/java.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/debug.hpp" +#include "utilities/pair.hpp" // Different defaults for different number of GC threads // They were chosen by running GCOld and SPECjbb on debris with different @@ -148,7 +150,11 @@ G1CollectorPolicy::G1CollectorPolicy() : _recorded_survivor_tail(NULL), _survivors_age_table(true), - _gc_overhead_perc(0.0) { + _gc_overhead_perc(0.0), + + _bytes_allocated_in_old_since_last_gc(0), + _ihop_control(NULL), + _initial_mark_to_mixed() { // SurvRateGroups below must be initialized after the predictor because they // indirectly use it through this object passed to their constructor. @@ -285,7 +291,11 @@ G1CollectorPolicy::G1CollectorPolicy() : // for the first time during initialization. _reserve_regions = 0; - _collectionSetChooser = new CollectionSetChooser(); + _cset_chooser = new CollectionSetChooser(); +} + +G1CollectorPolicy::~G1CollectorPolicy() { + delete _ihop_control; } double G1CollectorPolicy::get_new_prediction(TruncatedSeq const* seq) const { @@ -317,6 +327,8 @@ void G1CollectorPolicy::post_heap_initialize() { if (max_young_size != MaxNewSize) { FLAG_SET_ERGO(size_t, MaxNewSize, max_young_size); } + + _ihop_control = create_ihop_control(); } G1CollectorState* G1CollectorPolicy::collector_state() const { return _g1->collector_state(); } @@ -522,25 +534,26 @@ uint G1CollectorPolicy::calculate_young_list_desired_max_length() const { return _young_gen_sizer->max_desired_young_length(); } -void G1CollectorPolicy::update_young_list_max_and_target_length() { - update_young_list_max_and_target_length(get_new_prediction(_rs_lengths_seq)); +uint G1CollectorPolicy::update_young_list_max_and_target_length() { + return update_young_list_max_and_target_length(get_new_prediction(_rs_lengths_seq)); } -void G1CollectorPolicy::update_young_list_max_and_target_length(size_t rs_lengths) { - update_young_list_target_length(rs_lengths); +uint G1CollectorPolicy::update_young_list_max_and_target_length(size_t rs_lengths) { + uint unbounded_target_length = update_young_list_target_length(rs_lengths); update_max_gc_locker_expansion(); + return unbounded_target_length; } -void G1CollectorPolicy::update_young_list_target_length(size_t rs_lengths) { - _young_list_target_length = bounded_young_list_target_length(rs_lengths); +uint G1CollectorPolicy::update_young_list_target_length(size_t rs_lengths) { + YoungTargetLengths young_lengths = young_list_target_lengths(rs_lengths); + _young_list_target_length = young_lengths.first; + return young_lengths.second; } -void G1CollectorPolicy::update_young_list_target_length() { - update_young_list_target_length(get_new_prediction(_rs_lengths_seq)); -} +G1CollectorPolicy::YoungTargetLengths G1CollectorPolicy::young_list_target_lengths(size_t rs_lengths) const { + YoungTargetLengths result; -uint G1CollectorPolicy::bounded_young_list_target_length(size_t rs_lengths) const { - // Calculate the absolute and desired min bounds. + // Calculate the absolute and desired min bounds first. // This is how many young regions we already have (currently: the survivors). uint base_min_length = recorded_survivor_regions(); @@ -552,15 +565,7 @@ uint G1CollectorPolicy::bounded_young_list_target_length(size_t rs_lengths) cons desired_min_length = MAX2(desired_min_length, absolute_min_length); // Calculate the absolute and desired max bounds. - // We will try our best not to "eat" into the reserve. - uint absolute_max_length = 0; - if (_free_regions_at_end_of_collection > _reserve_regions) { - absolute_max_length = _free_regions_at_end_of_collection - _reserve_regions; - } uint desired_max_length = calculate_young_list_desired_max_length(); - if (desired_max_length > absolute_max_length) { - desired_max_length = absolute_max_length; - } uint young_list_target_length = 0; if (adaptive_young_list_length()) { @@ -581,6 +586,17 @@ uint G1CollectorPolicy::bounded_young_list_target_length(size_t rs_lengths) cons young_list_target_length = _young_list_fixed_length; } + result.second = young_list_target_length; + + // We will try our best not to "eat" into the reserve. + uint absolute_max_length = 0; + if (_free_regions_at_end_of_collection > _reserve_regions) { + absolute_max_length = _free_regions_at_end_of_collection - _reserve_regions; + } + if (desired_max_length > absolute_max_length) { + desired_max_length = absolute_max_length; + } + // Make sure we don't go over the desired max length, nor under the // desired min length. In case they clash, desired_min_length wins // which is why that test is second. @@ -595,7 +611,8 @@ uint G1CollectorPolicy::bounded_young_list_target_length(size_t rs_lengths) cons "we should be able to allocate at least one eden region"); assert(young_list_target_length >= absolute_min_length, "post-condition"); - return young_list_target_length; + result.first = young_list_target_length; + return result; } uint @@ -837,7 +854,11 @@ void G1CollectorPolicy::record_full_collection_end() { _survivor_surv_rate_group->reset(); update_young_list_max_and_target_length(); update_rs_lengths_prediction(); - _collectionSetChooser->clear(); + cset_chooser()->clear(); + + _bytes_allocated_in_old_since_last_gc = 0; + + record_pause(FullGC, _full_collection_start_sec, end_sec); } void G1CollectorPolicy::record_stop_world_start() { @@ -895,7 +916,7 @@ void G1CollectorPolicy::record_concurrent_mark_remark_end() { _cur_mark_stop_world_time_ms += elapsed_time_ms; _prev_collection_pause_end_ms += elapsed_time_ms; - _mmu_tracker->add_pause(_mark_remark_start_sec, end_time_sec); + record_pause(Remark, _mark_remark_start_sec, end_time_sec); } void G1CollectorPolicy::record_concurrent_mark_cleanup_start() { @@ -906,6 +927,10 @@ void G1CollectorPolicy::record_concurrent_mark_cleanup_completed() { bool should_continue_with_reclaim = next_gc_should_be_mixed("request last young-only gc", "skip last young-only gc"); collector_state()->set_last_young_gc(should_continue_with_reclaim); + // We skip the marking phase. + if (!should_continue_with_reclaim) { + abort_time_to_mixed_tracking(); + } collector_state()->set_in_marking_window(false); } @@ -952,12 +977,13 @@ bool G1CollectorPolicy::need_to_start_conc_mark(const char* source, size_t alloc return false; } - size_t marking_initiating_used_threshold = - (_g1->capacity() / 100) * InitiatingHeapOccupancyPercent; + size_t marking_initiating_used_threshold = _ihop_control->get_conc_mark_start_threshold(); + size_t cur_used_bytes = _g1->non_young_capacity_bytes(); size_t alloc_byte_size = alloc_word_size * HeapWordSize; + size_t marking_request_bytes = cur_used_bytes + alloc_byte_size; - if ((cur_used_bytes + alloc_byte_size) > marking_initiating_used_threshold) { + if (marking_request_bytes > marking_initiating_used_threshold) { if (collector_state()->gcs_are_young() && !collector_state()->last_young_gc()) { ergo_verbose5(ErgoConcCycles, "request concurrent cycle initiation", @@ -969,7 +995,7 @@ bool G1CollectorPolicy::need_to_start_conc_mark(const char* source, size_t alloc cur_used_bytes, alloc_byte_size, marking_initiating_used_threshold, - (double) InitiatingHeapOccupancyPercent, + (double) marking_initiating_used_threshold / _g1->capacity() * 100, source); return true; } else { @@ -996,10 +1022,7 @@ bool G1CollectorPolicy::need_to_start_conc_mark(const char* source, size_t alloc void G1CollectorPolicy::record_collection_pause_end(double pause_time_ms, size_t cards_scanned) { double end_time_sec = os::elapsedTime(); - assert(_cur_collection_pause_used_regions_at_start >= cset_region_length(), - "otherwise, the subtraction below does not make sense"); - size_t rs_size = - _cur_collection_pause_used_regions_at_start - cset_region_length(); + size_t cur_used_bytes = _g1->used(); assert(cur_used_bytes == _g1->recalculate_used(), "It should!"); bool last_pause_included_initial_mark = false; @@ -1013,6 +1036,8 @@ void G1CollectorPolicy::record_collection_pause_end(double pause_time_ms, size_t } #endif // PRODUCT + record_pause(young_gc_pause_kind(), end_time_sec - pause_time_ms / 1000.0, end_time_sec); + last_pause_included_initial_mark = collector_state()->during_initial_mark_pause(); if (last_pause_included_initial_mark) { record_concurrent_mark_init_end(0.0); @@ -1020,19 +1045,16 @@ void G1CollectorPolicy::record_collection_pause_end(double pause_time_ms, size_t maybe_start_marking(); } - _mmu_tracker->add_pause(end_time_sec - pause_time_ms/1000.0, end_time_sec); + double app_time_ms = (phase_times()->cur_collection_start_sec() * 1000.0 - _prev_collection_pause_end_ms); + if (app_time_ms < MIN_TIMER_GRANULARITY) { + // This usually happens due to the timer not having the required + // granularity. Some Linuxes are the usual culprits. + // We'll just set it to something (arbitrarily) small. + app_time_ms = 1.0; + } if (update_stats) { _trace_young_gen_time_data.record_end_collection(pause_time_ms, phase_times()); - // this is where we update the allocation rate of the application - double app_time_ms = - (phase_times()->cur_collection_start_sec() * 1000.0 - _prev_collection_pause_end_ms); - if (app_time_ms < MIN_TIMER_GRANULARITY) { - // This usually happens due to the timer not having the required - // granularity. Some Linuxes are the usual culprits. - // We'll just set it to something (arbitrarily) small. - app_time_ms = 1.0; - } // We maintain the invariant that all objects allocated by mutator // threads will be allocated out of eden regions. So, we can use // the eden region number allocated since the previous GC to @@ -1077,6 +1099,9 @@ void G1CollectorPolicy::record_collection_pause_end(double pause_time_ms, size_t if (next_gc_should_be_mixed("start mixed GCs", "do not start mixed GCs")) { collector_state()->set_gcs_are_young(false); + } else { + // We aborted the mixed GC phase early. + abort_time_to_mixed_tracking(); } collector_state()->set_last_young_gc(false); @@ -1085,7 +1110,6 @@ void G1CollectorPolicy::record_collection_pause_end(double pause_time_ms, size_t if (!collector_state()->last_gc_was_young()) { // This is a mixed GC. Here we decide whether to continue doing // mixed GCs or not. - if (!next_gc_should_be_mixed("continue mixed GCs", "do not continue mixed GCs")) { collector_state()->set_gcs_are_young(true); @@ -1177,9 +1201,20 @@ void G1CollectorPolicy::record_collection_pause_end(double pause_time_ms, size_t collector_state()->set_in_marking_window(new_in_marking_window); collector_state()->set_in_marking_window_im(new_in_marking_window_im); _free_regions_at_end_of_collection = _g1->num_free_regions(); - update_young_list_max_and_target_length(); + // IHOP control wants to know the expected young gen length if it were not + // restrained by the heap reserve. Using the actual length would make the + // prediction too small and the limit the young gen every time we get to the + // predicted target occupancy. + size_t last_unrestrained_young_length = update_young_list_max_and_target_length(); update_rs_lengths_prediction(); + update_ihop_prediction(app_time_ms / 1000.0, + _bytes_allocated_in_old_since_last_gc, + last_unrestrained_young_length * HeapRegion::GrainBytes); + _bytes_allocated_in_old_since_last_gc = 0; + + _ihop_control->send_trace_event(_g1->gc_tracer_stw()); + // Note that _mmu_tracker->max_gc_time() returns the time in seconds. double update_rs_time_goal_ms = _mmu_tracker->max_gc_time() * MILLIUNITS * G1RSetUpdatingPauseTimePercent / 100.0; @@ -1202,7 +1237,62 @@ void G1CollectorPolicy::record_collection_pause_end(double pause_time_ms, size_t phase_times()->sum_thread_work_items(G1GCPhaseTimes::UpdateRS), update_rs_time_goal_ms); - _collectionSetChooser->verify(); + cset_chooser()->verify(); +} + +G1IHOPControl* G1CollectorPolicy::create_ihop_control() const { + if (G1UseAdaptiveIHOP) { + return new G1AdaptiveIHOPControl(InitiatingHeapOccupancyPercent, + G1CollectedHeap::heap()->max_capacity(), + &_predictor, + G1ReservePercent, + G1HeapWastePercent); + } else { + return new G1StaticIHOPControl(InitiatingHeapOccupancyPercent, + G1CollectedHeap::heap()->max_capacity()); + } +} + +void G1CollectorPolicy::update_ihop_prediction(double mutator_time_s, + size_t mutator_alloc_bytes, + size_t young_gen_size) { + // Always try to update IHOP prediction. Even evacuation failures give information + // about e.g. whether to start IHOP earlier next time. + + // Avoid using really small application times that might create samples with + // very high or very low values. They may be caused by e.g. back-to-back gcs. + double const min_valid_time = 1e-6; + + bool report = false; + + double marking_to_mixed_time = -1.0; + if (!collector_state()->last_gc_was_young() && _initial_mark_to_mixed.has_result()) { + marking_to_mixed_time = _initial_mark_to_mixed.last_marking_time(); + assert(marking_to_mixed_time > 0.0, + "Initial mark to mixed time must be larger than zero but is %.3f", + marking_to_mixed_time); + if (marking_to_mixed_time > min_valid_time) { + _ihop_control->update_marking_length(marking_to_mixed_time); + report = true; + } + } + + // As an approximation for the young gc promotion rates during marking we use + // all of them. In many applications there are only a few if any young gcs during + // marking, which makes any prediction useless. This increases the accuracy of the + // prediction. + if (collector_state()->last_gc_was_young() && mutator_time_s > min_valid_time) { + _ihop_control->update_allocation_info(mutator_time_s, mutator_alloc_bytes, young_gen_size); + report = true; + } + + if (report) { + report_ihop_statistics(); + } +} + +void G1CollectorPolicy::report_ihop_statistics() { + _ihop_control->print(); } #define EXT_SIZE_FORMAT "%.1f%s" @@ -1216,7 +1306,6 @@ void G1CollectorPolicy::record_heap_size_info_at_start(bool full) { _survivor_used_bytes_before_gc = young_list->survivor_used_bytes(); _heap_capacity_bytes_before_gc = _g1->capacity(); _heap_used_bytes_before_gc = _g1->used(); - _cur_collection_pause_used_regions_at_start = _g1->num_used_regions(); _eden_capacity_bytes_before_gc = (_young_list_target_length * HeapRegion::GrainBytes) - _survivor_used_bytes_before_gc; @@ -1621,6 +1710,11 @@ bool G1CollectorPolicy::force_initial_mark_if_outside_cycle(GCCause::Cause gc_ca } } +void G1CollectorPolicy::initiate_conc_mark() { + collector_state()->set_during_initial_mark_pause(true); + collector_state()->set_initiate_conc_mark_if_possible(false); +} + void G1CollectorPolicy::decide_on_conc_mark_initiation() { // We are about to decide on whether this pause will be an // initial-mark pause. @@ -1637,17 +1731,22 @@ void G1CollectorPolicy::decide_on_conc_mark_initiation() { // concurrent marking cycle. So we might initiate one. if (!about_to_start_mixed_phase() && collector_state()->gcs_are_young()) { - // Initiate a new initial mark only if there is no marking or reclamation going - // on. - - collector_state()->set_during_initial_mark_pause(true); - // And we can now clear initiate_conc_mark_if_possible() as - // we've already acted on it. - collector_state()->set_initiate_conc_mark_if_possible(false); - + // Initiate a new initial mark if there is no marking or reclamation going on. + initiate_conc_mark(); ergo_verbose0(ErgoConcCycles, - "initiate concurrent cycle", - ergo_format_reason("concurrent cycle initiation requested")); + "initiate concurrent cycle", + ergo_format_reason("concurrent cycle initiation requested")); + } else if (_g1->is_user_requested_concurrent_full_gc(_g1->gc_cause())) { + // Initiate a user requested initial mark. An initial mark must be young only + // GC, so the collector state must be updated to reflect this. + collector_state()->set_gcs_are_young(true); + collector_state()->set_last_young_gc(false); + + abort_time_to_mixed_tracking(); + initiate_conc_mark(); + ergo_verbose0(ErgoConcCycles, + "initiate concurrent cycle", + ergo_format_reason("user requested concurrent cycle")); } else { // The concurrent marking thread is still finishing up the // previous cycle. If we start one right now the two cycles @@ -1717,27 +1816,27 @@ uint G1CollectorPolicy::calculate_parallel_work_chunk_size(uint n_workers, uint return MAX2(n_regions / (n_workers * overpartition_factor), min_chunk_size); } -void -G1CollectorPolicy::record_concurrent_mark_cleanup_end() { - _collectionSetChooser->clear(); +void G1CollectorPolicy::record_concurrent_mark_cleanup_end() { + cset_chooser()->clear(); WorkGang* workers = _g1->workers(); uint n_workers = workers->active_workers(); uint n_regions = _g1->num_regions(); uint chunk_size = calculate_parallel_work_chunk_size(n_workers, n_regions); - _collectionSetChooser->prepare_for_par_region_addition(n_workers, n_regions, chunk_size); - ParKnownGarbageTask par_known_garbage_task(_collectionSetChooser, chunk_size, n_workers); + cset_chooser()->prepare_for_par_region_addition(n_workers, n_regions, chunk_size); + ParKnownGarbageTask par_known_garbage_task(cset_chooser(), chunk_size, n_workers); workers->run_task(&par_known_garbage_task); - _collectionSetChooser->sort_regions(); + cset_chooser()->sort_regions(); double end_sec = os::elapsedTime(); double elapsed_time_ms = (end_sec - _mark_cleanup_start_sec) * 1000.0; _concurrent_mark_cleanup_times_ms->add(elapsed_time_ms); _cur_mark_stop_world_time_ms += elapsed_time_ms; _prev_collection_pause_end_ms += elapsed_time_ms; - _mmu_tracker->add_pause(_mark_cleanup_start_sec, end_sec); + + record_pause(Cleanup, _mark_cleanup_start_sec, end_sec); } // Add the heap region at the head of the non-incremental collection set @@ -1953,10 +2052,62 @@ void G1CollectorPolicy::maybe_start_marking() { } } +G1CollectorPolicy::PauseKind G1CollectorPolicy::young_gc_pause_kind() const { + assert(!collector_state()->full_collection(), "must be"); + if (collector_state()->during_initial_mark_pause()) { + assert(collector_state()->last_gc_was_young(), "must be"); + assert(!collector_state()->last_young_gc(), "must be"); + return InitialMarkGC; + } else if (collector_state()->last_young_gc()) { + assert(!collector_state()->during_initial_mark_pause(), "must be"); + assert(collector_state()->last_gc_was_young(), "must be"); + return LastYoungGC; + } else if (!collector_state()->last_gc_was_young()) { + assert(!collector_state()->during_initial_mark_pause(), "must be"); + assert(!collector_state()->last_young_gc(), "must be"); + return MixedGC; + } else { + assert(collector_state()->last_gc_was_young(), "must be"); + assert(!collector_state()->during_initial_mark_pause(), "must be"); + assert(!collector_state()->last_young_gc(), "must be"); + return YoungOnlyGC; + } +} + +void G1CollectorPolicy::record_pause(PauseKind kind, double start, double end) { + // Manage the MMU tracker. For some reason it ignores Full GCs. + if (kind != FullGC) { + _mmu_tracker->add_pause(start, end); + } + // Manage the mutator time tracking from initial mark to first mixed gc. + switch (kind) { + case FullGC: + abort_time_to_mixed_tracking(); + break; + case Cleanup: + case Remark: + case YoungOnlyGC: + case LastYoungGC: + _initial_mark_to_mixed.add_pause(end - start); + break; + case InitialMarkGC: + _initial_mark_to_mixed.record_initial_mark_end(end); + break; + case MixedGC: + _initial_mark_to_mixed.record_mixed_gc_start(start); + break; + default: + ShouldNotReachHere(); + } +} + +void G1CollectorPolicy::abort_time_to_mixed_tracking() { + _initial_mark_to_mixed.reset(); +} + bool G1CollectorPolicy::next_gc_should_be_mixed(const char* true_action_str, const char* false_action_str) const { - CollectionSetChooser* cset_chooser = _collectionSetChooser; - if (cset_chooser->is_empty()) { + if (cset_chooser()->is_empty()) { ergo_verbose0(ErgoMixedGCs, false_action_str, ergo_format_reason("candidate old regions not available")); @@ -1964,7 +2115,7 @@ bool G1CollectorPolicy::next_gc_should_be_mixed(const char* true_action_str, } // Is the amount of uncollected reclaimable space above G1HeapWastePercent? - size_t reclaimable_bytes = cset_chooser->remaining_reclaimable_bytes(); + size_t reclaimable_bytes = cset_chooser()->remaining_reclaimable_bytes(); double reclaimable_perc = reclaimable_bytes_perc(reclaimable_bytes); double threshold = (double) G1HeapWastePercent; if (reclaimable_perc <= threshold) { @@ -1974,7 +2125,7 @@ bool G1CollectorPolicy::next_gc_should_be_mixed(const char* true_action_str, ergo_format_region("candidate old regions") ergo_format_byte_perc("reclaimable") ergo_format_perc("threshold"), - cset_chooser->remaining_regions(), + cset_chooser()->remaining_regions(), reclaimable_bytes, reclaimable_perc, threshold); return false; @@ -1986,7 +2137,7 @@ bool G1CollectorPolicy::next_gc_should_be_mixed(const char* true_action_str, ergo_format_region("candidate old regions") ergo_format_byte_perc("reclaimable") ergo_format_perc("threshold"), - cset_chooser->remaining_regions(), + cset_chooser()->remaining_regions(), reclaimable_bytes, reclaimable_perc, threshold); return true; @@ -2003,7 +2154,7 @@ uint G1CollectorPolicy::calc_min_old_cset_length() const { // to the CSet chooser in the first place, not how many remain, so // that the result is the same during all mixed GCs that follow a cycle. - const size_t region_num = (size_t) _collectionSetChooser->length(); + const size_t region_num = (size_t) cset_chooser()->length(); const size_t gc_num = (size_t) MAX2(G1MixedGCCountTarget, (uintx) 1); size_t result = region_num / gc_num; // emulate ceiling @@ -2112,15 +2263,14 @@ void G1CollectorPolicy::finalize_old_cset_part(double time_remaining_ms) { if (!collector_state()->gcs_are_young()) { - CollectionSetChooser* cset_chooser = _collectionSetChooser; - cset_chooser->verify(); + cset_chooser()->verify(); const uint min_old_cset_length = calc_min_old_cset_length(); const uint max_old_cset_length = calc_max_old_cset_length(); uint expensive_region_num = 0; bool check_time_remaining = adaptive_young_list_length(); - HeapRegion* hr = cset_chooser->peek(); + HeapRegion* hr = cset_chooser()->peek(); while (hr != NULL) { if (old_cset_region_length() >= max_old_cset_length) { // Added maximum number of old regions to the CSet. @@ -2136,7 +2286,7 @@ void G1CollectorPolicy::finalize_old_cset_part(double time_remaining_ms) { // Stop adding regions if the remaining reclaimable space is // not above G1HeapWastePercent. - size_t reclaimable_bytes = cset_chooser->remaining_reclaimable_bytes(); + size_t reclaimable_bytes = cset_chooser()->remaining_reclaimable_bytes(); double reclaimable_perc = reclaimable_bytes_perc(reclaimable_bytes); double threshold = (double) G1HeapWastePercent; if (reclaimable_perc <= threshold) { @@ -2198,11 +2348,11 @@ void G1CollectorPolicy::finalize_old_cset_part(double time_remaining_ms) { // We will add this region to the CSet. time_remaining_ms = MAX2(time_remaining_ms - predicted_time_ms, 0.0); predicted_old_time_ms += predicted_time_ms; - cset_chooser->pop(); // already have region via peek() + cset_chooser()->pop(); // already have region via peek() _g1->old_set_remove(hr); add_old_region_to_cset(hr); - hr = cset_chooser->peek(); + hr = cset_chooser()->peek(); } if (hr == NULL) { ergo_verbose0(ErgoCSetConstruction, @@ -2227,7 +2377,7 @@ void G1CollectorPolicy::finalize_old_cset_part(double time_remaining_ms) { time_remaining_ms); } - cset_chooser->verify(); + cset_chooser()->verify(); } stop_incremental_cset_building(); diff --git a/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.hpp b/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.hpp index 416d695f30d..232f14488d5 100644 --- a/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.hpp +++ b/hotspot/src/share/vm/gc/g1/g1CollectorPolicy.hpp @@ -29,9 +29,11 @@ #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1InCSetState.hpp" +#include "gc/g1/g1InitialMarkToMixedTimeTracker.hpp" #include "gc/g1/g1MMUTracker.hpp" #include "gc/g1/g1Predictions.hpp" #include "gc/shared/collectorPolicy.hpp" +#include "utilities/pair.hpp" // A G1CollectorPolicy makes policy decisions that determine the // characteristics of the collector. Examples include: @@ -40,6 +42,7 @@ class HeapRegion; class CollectionSetChooser; +class G1IHOPControl; // TraceYoungGenTime collects data on _both_ young and mixed evacuation pauses // (the latter may contain non-young regions - i.e. regions that are @@ -163,6 +166,15 @@ public: class G1CollectorPolicy: public CollectorPolicy { private: + G1IHOPControl* _ihop_control; + + G1IHOPControl* create_ihop_control() const; + // Update the IHOP control with necessary statistics. + void update_ihop_prediction(double mutator_time_s, + size_t mutator_alloc_bytes, + size_t young_gen_size); + void report_ihop_statistics(); + G1Predictions _predictor; double get_new_prediction(TruncatedSeq const* seq) const; @@ -179,10 +191,9 @@ class G1CollectorPolicy: public CollectorPolicy { void initialize_alignments(); void initialize_flags(); - CollectionSetChooser* _collectionSetChooser; + CollectionSetChooser* _cset_chooser; double _full_collection_start_sec; - uint _cur_collection_pause_used_regions_at_start; // These exclude marking times. TruncatedSeq* _recent_gc_times_ms; @@ -271,9 +282,17 @@ class G1CollectorPolicy: public CollectorPolicy { size_t _pending_cards; + // The amount of allocated bytes in old gen during the last mutator and the following + // young GC phase. + size_t _bytes_allocated_in_old_since_last_gc; + + G1InitialMarkToMixedTimeTracker _initial_mark_to_mixed; public: const G1Predictions& predictor() const { return _predictor; } + // Add the given number of bytes to the total number of allocated bytes in the old gen. + void add_bytes_allocated_in_old_since_last_gc(size_t bytes) { _bytes_allocated_in_old_since_last_gc += bytes; } + // Accessors void set_region_eden(HeapRegion* hr, int young_index_in_cset) { @@ -386,6 +405,10 @@ protected: double non_young_other_time_ms() const; double constant_other_time_ms(double pause_time_ms) const; + CollectionSetChooser* cset_chooser() const { + return _cset_chooser; + } + private: // Statistics kept per GC stoppage, pause or full. TruncatedSeq* _recent_prev_end_times_for_all_gcs_sec; @@ -473,16 +496,18 @@ private: double _mark_remark_start_sec; double _mark_cleanup_start_sec; - void update_young_list_max_and_target_length(); - void update_young_list_max_and_target_length(size_t rs_lengths); + // Updates the internal young list maximum and target lengths. Returns the + // unbounded young list target length. + uint update_young_list_max_and_target_length(); + uint update_young_list_max_and_target_length(size_t rs_lengths); // Update the young list target length either by setting it to the // desired fixed value or by calculating it using G1's pause // prediction model. If no rs_lengths parameter is passed, predict // the RS lengths using the prediction model, otherwise use the // given rs_lengths as the prediction. - void update_young_list_target_length(); - void update_young_list_target_length(size_t rs_lengths); + // Returns the unbounded young list target length. + uint update_young_list_target_length(size_t rs_lengths); // Calculate and return the minimum desired young list target // length. This is the minimum desired young list length according @@ -505,7 +530,10 @@ private: uint desired_min_length, uint desired_max_length) const; - uint bounded_young_list_target_length(size_t rs_lengths) const; + // Result of the bounded_young_list_target_length() method, containing both the + // bounded as well as the unbounded young list target lengths in this order. + typedef Pair YoungTargetLengths; + YoungTargetLengths young_list_target_lengths(size_t rs_lengths) const; void update_rs_lengths_prediction(); void update_rs_lengths_prediction(size_t prediction); @@ -536,10 +564,30 @@ private: // Sets up marking if proper conditions are met. void maybe_start_marking(); + + // The kind of STW pause. + enum PauseKind { + FullGC, + YoungOnlyGC, + MixedGC, + LastYoungGC, + InitialMarkGC, + Cleanup, + Remark + }; + + // Calculate PauseKind from internal state. + PauseKind young_gc_pause_kind() const; + // Record the given STW pause with the given start and end times (in s). + void record_pause(PauseKind kind, double start, double end); + // Indicate that we aborted marking before doing any mixed GCs. + void abort_time_to_mixed_tracking(); public: G1CollectorPolicy(); + virtual ~G1CollectorPolicy(); + virtual G1CollectorPolicy* as_g1_policy() { return this; } G1CollectorState* collector_state() const; @@ -681,6 +729,11 @@ private: // (should not be called directly). void add_region_to_incremental_cset_common(HeapRegion* hr); + // Set the state to start a concurrent marking cycle and clear + // _initiate_conc_mark_if_possible because it has now been + // acted on. + void initiate_conc_mark(); + public: // Add hr to the LHS of the incremental collection set. void add_region_to_incremental_cset_lhs(HeapRegion* hr); @@ -739,6 +792,10 @@ public: return _young_gen_sizer->adaptive_young_list_length(); } + virtual bool should_process_references() const { + return true; + } + private: // // Survivor regions policy. diff --git a/hotspot/src/share/vm/gc/g1/g1ErgoVerbose.cpp b/hotspot/src/share/vm/gc/g1/g1ErgoVerbose.cpp index f984848b0cb..72a2217b0d5 100644 --- a/hotspot/src/share/vm/gc/g1/g1ErgoVerbose.cpp +++ b/hotspot/src/share/vm/gc/g1/g1ErgoVerbose.cpp @@ -57,6 +57,7 @@ const char* G1ErgoVerbose::to_string(int tag) { case ErgoConcCycles: return "Concurrent Cycles"; case ErgoMixedGCs: return "Mixed GCs"; case ErgoTiming: return "Timing"; + case ErgoIHOP: return "IHOP"; default: ShouldNotReachHere(); // Keep the Windows compiler happy diff --git a/hotspot/src/share/vm/gc/g1/g1ErgoVerbose.hpp b/hotspot/src/share/vm/gc/g1/g1ErgoVerbose.hpp index c36fe98521d..8021f46beaf 100644 --- a/hotspot/src/share/vm/gc/g1/g1ErgoVerbose.hpp +++ b/hotspot/src/share/vm/gc/g1/g1ErgoVerbose.hpp @@ -71,6 +71,7 @@ typedef enum { ErgoConcCycles, ErgoMixedGCs, ErgoTiming, + ErgoIHOP, ErgoHeuristicNum } ErgoHeuristic; diff --git a/hotspot/src/share/vm/gc/g1/g1EvacFailure.cpp b/hotspot/src/share/vm/gc/g1/g1EvacFailure.cpp index 1126e40e7c0..d71a1b941c7 100644 --- a/hotspot/src/share/vm/gc/g1/g1EvacFailure.cpp +++ b/hotspot/src/share/vm/gc/g1/g1EvacFailure.cpp @@ -47,8 +47,9 @@ public: virtual void do_oop( oop* p) { do_oop_work(p); } template void do_oop_work(T* p) { assert(_from->is_in_reserved(p), "paranoia"); - if (!_from->is_in_reserved(oopDesc::load_decode_heap_oop(p)) && - !_from->is_survivor()) { + assert(!_from->is_survivor(), "Unexpected evac failure in survivor region"); + + if (!_from->is_in_reserved(oopDesc::load_decode_heap_oop(p))) { size_t card_index = _ct_bs->index_for(p); if (_ct_bs->mark_card_deferred(card_index)) { _dcq->enqueue((jbyte*)_ct_bs->byte_for_index(card_index)); @@ -259,3 +260,16 @@ void G1ParRemoveSelfForwardPtrsTask::work(uint worker_id) { HeapRegion* hr = _g1h->start_cset_region_for_worker(worker_id); _g1h->collection_set_iterate_from(hr, &rsfp_cl); } + +G1RestorePreservedMarksTask::G1RestorePreservedMarksTask(OopAndMarkOopStack* preserved_objs) : + AbstractGangTask("G1 Restore Preserved Marks"), + _preserved_objs(preserved_objs) {} + +void G1RestorePreservedMarksTask::work(uint worker_id) { + OopAndMarkOopStack& cur = _preserved_objs[worker_id]; + while (!cur.is_empty()) { + OopAndMarkOop elem = cur.pop(); + elem.set_mark(); + } + cur.clear(true); +} diff --git a/hotspot/src/share/vm/gc/g1/g1EvacFailure.hpp b/hotspot/src/share/vm/gc/g1/g1EvacFailure.hpp index 09135595643..1db22eb46a9 100644 --- a/hotspot/src/share/vm/gc/g1/g1EvacFailure.hpp +++ b/hotspot/src/share/vm/gc/g1/g1EvacFailure.hpp @@ -32,6 +32,20 @@ class G1CollectedHeap; +class OopAndMarkOop { + oop _o; + markOop _m; + public: + OopAndMarkOop(oop obj, markOop m) : _o(obj), _m(m) { + } + + void set_mark() { + _o->set_mark(_m); + } +}; + +typedef Stack OopAndMarkOopStack; + // Task to fixup self-forwarding pointers // installed as a result of an evacuation failure. class G1ParRemoveSelfForwardPtrsTask: public AbstractGangTask { @@ -45,4 +59,12 @@ public: void work(uint worker_id); }; +class G1RestorePreservedMarksTask : public AbstractGangTask { + OopAndMarkOopStack* _preserved_objs; + public: + G1RestorePreservedMarksTask(OopAndMarkOopStack* preserved_objs); + + void work(uint worker_id); +}; + #endif // SHARE_VM_GC_G1_G1EVACFAILURE_HPP diff --git a/hotspot/src/share/vm/gc/g1/g1EvacStats.cpp b/hotspot/src/share/vm/gc/g1/g1EvacStats.cpp index de0e5eda296..8a2317c59bd 100644 --- a/hotspot/src/share/vm/gc/g1/g1EvacStats.cpp +++ b/hotspot/src/share/vm/gc/g1/g1EvacStats.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "memory/allocation.inline.hpp" #include "gc/g1/g1EvacStats.hpp" #include "gc/shared/gcId.hpp" #include "trace/tracing.hpp" @@ -114,3 +115,4 @@ void G1EvacStats::adjust_desired_plab_sz() { reset(); } +G1EvacStats::~G1EvacStats() { } diff --git a/hotspot/src/share/vm/gc/g1/g1EvacStats.hpp b/hotspot/src/share/vm/gc/g1/g1EvacStats.hpp index ffc548d16a3..1d0a53f5453 100644 --- a/hotspot/src/share/vm/gc/g1/g1EvacStats.hpp +++ b/hotspot/src/share/vm/gc/g1/g1EvacStats.hpp @@ -22,11 +22,10 @@ * */ -#ifndef SHARE_VM_gc_G1_G1EVACSTATS_HPP -#define SHARE_VM_gc_G1_G1EVACSTATS_HPP +#ifndef SHARE_VM_GC_G1_G1EVACSTATS_HPP +#define SHARE_VM_GC_G1_G1EVACSTATS_HPP #include "gc/shared/plab.hpp" -#include "runtime/atomic.hpp" // Records various memory allocation statistics gathered during evacuation. class G1EvacStats : public PLABStats { @@ -75,19 +74,11 @@ class G1EvacStats : public PLABStats { // Amount of space in heapwords wasted (unused) in the failing regions when an evacuation failure happens. size_t failure_waste() const { return _failure_waste; } - void add_direct_allocated(size_t value) { - Atomic::add_ptr(value, &_direct_allocated); - } + inline void add_direct_allocated(size_t value); + inline void add_region_end_waste(size_t value); + inline void add_failure_used_and_waste(size_t used, size_t waste); - void add_region_end_waste(size_t value) { - Atomic::add_ptr(value, &_region_end_waste); - Atomic::add_ptr(1, &_regions_filled); - } - - void add_failure_used_and_waste(size_t used, size_t waste) { - Atomic::add_ptr(used, &_failure_used); - Atomic::add_ptr(waste, &_failure_waste); - } + ~G1EvacStats(); }; -#endif // SHARE_VM_gc_G1_G1EVACSTATS_HPP +#endif // SHARE_VM_GC_G1_G1EVACSTATS_HPP diff --git a/hotspot/src/share/vm/gc/g1/g1EvacStats.inline.hpp b/hotspot/src/share/vm/gc/g1/g1EvacStats.inline.hpp new file mode 100644 index 00000000000..337d4625707 --- /dev/null +++ b/hotspot/src/share/vm/gc/g1/g1EvacStats.inline.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +#ifndef SHARE_VM_GC_G1_G1EVACSTATS_INLINE_HPP +#define SHARE_VM_GC_G1_G1EVACSTATS_INLINE_HPP + +#include "gc/g1/g1EvacStats.hpp" +#include "runtime/atomic.inline.hpp" + +inline void G1EvacStats::add_direct_allocated(size_t value) { + Atomic::add_ptr(value, &_direct_allocated); +} + +inline void G1EvacStats::add_region_end_waste(size_t value) { + Atomic::add_ptr(value, &_region_end_waste); + Atomic::add_ptr(1, &_regions_filled); +} + +inline void G1EvacStats::add_failure_used_and_waste(size_t used, size_t waste) { + Atomic::add_ptr(used, &_failure_used); + Atomic::add_ptr(waste, &_failure_waste); +} + +#endif // SHARE_VM_GC_G1_G1EVACSTATS_INLINE_HPP diff --git a/hotspot/src/share/vm/gc/g1/g1GCPhaseTimes.cpp b/hotspot/src/share/vm/gc/g1/g1GCPhaseTimes.cpp index 0e928b2089d..836ca8f09b8 100644 --- a/hotspot/src/share/vm/gc/g1/g1GCPhaseTimes.cpp +++ b/hotspot/src/share/vm/gc/g1/g1GCPhaseTimes.cpp @@ -141,6 +141,7 @@ void G1GCPhaseTimes::note_gc_start(uint active_gc_threads) { assert(active_gc_threads <= _max_gc_threads, "The number of active threads must be <= the max number of threads"); _active_gc_threads = active_gc_threads; _cur_expand_heap_time_ms = 0.0; + _external_accounted_time_ms = 0.0; for (int i = 0; i < GCParPhasesSentinel; i++) { _gc_par_phases[i]->reset(); @@ -185,9 +186,12 @@ void G1GCPhaseTimes::print_stats(int level, const char* str, double value, uint } double G1GCPhaseTimes::accounted_time_ms() { + // First subtract any externally accounted time + double misc_time_ms = _external_accounted_time_ms; + // Subtract the root region scanning wait time. It's initialized to // zero at the start of the pause. - double misc_time_ms = _root_region_scan_wait_time_ms; + misc_time_ms += _root_region_scan_wait_time_ms; misc_time_ms += _cur_collection_par_time_ms; diff --git a/hotspot/src/share/vm/gc/g1/g1GCPhaseTimes.hpp b/hotspot/src/share/vm/gc/g1/g1GCPhaseTimes.hpp index f8685548912..3c84ed89ac8 100644 --- a/hotspot/src/share/vm/gc/g1/g1GCPhaseTimes.hpp +++ b/hotspot/src/share/vm/gc/g1/g1GCPhaseTimes.hpp @@ -99,6 +99,8 @@ class G1GCPhaseTimes : public CHeapObj { double _cur_collection_start_sec; double _root_region_scan_wait_time_ms; + double _external_accounted_time_ms; + double _recorded_young_cset_choice_time_ms; double _recorded_non_young_cset_choice_time_ms; @@ -244,6 +246,10 @@ class G1GCPhaseTimes : public CHeapObj { _cur_verify_after_time_ms = time_ms; } + void inc_external_accounted_time_ms(double time_ms) { + _external_accounted_time_ms += time_ms; + } + double accounted_time_ms(); double cur_collection_start_sec() { diff --git a/hotspot/src/share/vm/gc/g1/g1IHOPControl.cpp b/hotspot/src/share/vm/gc/g1/g1IHOPControl.cpp new file mode 100644 index 00000000000..077f7b7ecf5 --- /dev/null +++ b/hotspot/src/share/vm/gc/g1/g1IHOPControl.cpp @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +#include "precompiled.hpp" +#include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1ErgoVerbose.hpp" +#include "gc/g1/g1IHOPControl.hpp" +#include "gc/g1/g1Predictions.hpp" +#include "gc/shared/gcTrace.hpp" + +G1IHOPControl::G1IHOPControl(double initial_ihop_percent, size_t target_occupancy) : + _initial_ihop_percent(initial_ihop_percent), + _target_occupancy(target_occupancy), + _last_allocated_bytes(0), + _last_allocation_time_s(0.0) +{ + assert(_initial_ihop_percent >= 0.0 && _initial_ihop_percent <= 100.0, "Initial IHOP value must be between 0 and 100 but is %.3f", initial_ihop_percent); +} + +void G1IHOPControl::update_allocation_info(double allocation_time_s, size_t allocated_bytes, size_t additional_buffer_size) { + assert(allocation_time_s >= 0.0, "Allocation time must be positive but is %.3f", allocation_time_s); + + _last_allocation_time_s = allocation_time_s; + _last_allocated_bytes = allocated_bytes; +} + +void G1IHOPControl::print() { + size_t cur_conc_mark_start_threshold = get_conc_mark_start_threshold(); + ergo_verbose6(ErgoIHOP, + "basic information", + ergo_format_reason("value update") + ergo_format_byte_perc("threshold") + ergo_format_byte("target occupancy") + ergo_format_byte("current occupancy") + ergo_format_double("recent old gen allocation rate") + ergo_format_double("recent marking phase length"), + cur_conc_mark_start_threshold, + cur_conc_mark_start_threshold * 100.0 / _target_occupancy, + _target_occupancy, + G1CollectedHeap::heap()->used(), + _last_allocation_time_s > 0.0 ? _last_allocated_bytes / _last_allocation_time_s : 0.0, + last_marking_length_s()); +} + +void G1IHOPControl::send_trace_event(G1NewTracer* tracer) { + tracer->report_basic_ihop_statistics(get_conc_mark_start_threshold(), + _target_occupancy, + G1CollectedHeap::heap()->used(), + _last_allocated_bytes, + _last_allocation_time_s, + last_marking_length_s()); +} + +G1StaticIHOPControl::G1StaticIHOPControl(double ihop_percent, size_t target_occupancy) : + G1IHOPControl(ihop_percent, target_occupancy), + _last_marking_length_s(0.0) { + assert(_target_occupancy > 0, "Target occupancy must be larger than zero."); +} + +#ifndef PRODUCT +static void test_update(G1IHOPControl* ctrl, double alloc_time, size_t alloc_amount, size_t young_size, double mark_time) { + for (int i = 0; i < 100; i++) { + ctrl->update_allocation_info(alloc_time, alloc_amount, young_size); + ctrl->update_marking_length(mark_time); + } +} + +void G1StaticIHOPControl::test() { + size_t const initial_ihop = 45; + + G1StaticIHOPControl ctrl(initial_ihop, 100); + + size_t threshold = ctrl.get_conc_mark_start_threshold(); + assert(threshold == initial_ihop, + "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); + + ctrl.update_allocation_info(100.0, 100, 100); + threshold = ctrl.get_conc_mark_start_threshold(); + assert(threshold == initial_ihop, + "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); + + ctrl.update_marking_length(1000.0); + threshold = ctrl.get_conc_mark_start_threshold(); + assert(threshold == initial_ihop, + "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); + + // Whatever we pass, the IHOP value must stay the same. + test_update(&ctrl, 2, 10, 10, 3); + threshold = ctrl.get_conc_mark_start_threshold(); + assert(threshold == initial_ihop, + "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); + + test_update(&ctrl, 12, 10, 10, 3); + threshold = ctrl.get_conc_mark_start_threshold(); + assert(threshold == initial_ihop, + "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); +} +#endif + +G1AdaptiveIHOPControl::G1AdaptiveIHOPControl(double ihop_percent, + size_t initial_target_occupancy, + G1Predictions const* predictor, + size_t heap_reserve_percent, + size_t heap_waste_percent) : + G1IHOPControl(ihop_percent, initial_target_occupancy), + _predictor(predictor), + _marking_times_s(10, 0.95), + _allocation_rate_s(10, 0.95), + _last_unrestrained_young_size(0), + _heap_reserve_percent(heap_reserve_percent), + _heap_waste_percent(heap_waste_percent) +{ +} + +size_t G1AdaptiveIHOPControl::actual_target_threshold() const { + // The actual target threshold takes the heap reserve and the expected waste in + // free space into account. + // _heap_reserve is that part of the total heap capacity that is reserved for + // eventual promotion failure. + // _heap_waste is the amount of space will never be reclaimed in any + // heap, so can not be used for allocation during marking and must always be + // considered. + + double safe_total_heap_percentage = MIN2((double)(_heap_reserve_percent + _heap_waste_percent), 100.0); + + return MIN2( + G1CollectedHeap::heap()->max_capacity() * (100.0 - safe_total_heap_percentage) / 100.0, + _target_occupancy * (100.0 - _heap_waste_percent) / 100.0 + ); +} + +bool G1AdaptiveIHOPControl::have_enough_data_for_prediction() const { + return ((size_t)_marking_times_s.num() >= G1AdaptiveIHOPNumInitialSamples) && + ((size_t)_allocation_rate_s.num() >= G1AdaptiveIHOPNumInitialSamples); +} + +size_t G1AdaptiveIHOPControl::get_conc_mark_start_threshold() { + if (have_enough_data_for_prediction()) { + double pred_marking_time = _predictor->get_new_prediction(&_marking_times_s); + double pred_promotion_rate = _predictor->get_new_prediction(&_allocation_rate_s); + + size_t predicted_needed_bytes_during_marking = + (pred_marking_time * pred_promotion_rate + + _last_unrestrained_young_size); // In reality we would need the maximum size of the young gen during marking. This is a conservative estimate. + + size_t internal_threshold = actual_target_threshold(); + size_t predicted_initiating_threshold = predicted_needed_bytes_during_marking < internal_threshold ? + internal_threshold - predicted_needed_bytes_during_marking : + 0; + return predicted_initiating_threshold; + } else { + // Use the initial value. + return _initial_ihop_percent * _target_occupancy / 100.0; + } +} + +void G1AdaptiveIHOPControl::update_allocation_info(double allocation_time_s, size_t allocated_bytes, size_t additional_buffer_size) { + G1IHOPControl::update_allocation_info(allocation_time_s, allocated_bytes, additional_buffer_size); + + double allocation_rate = (double) allocated_bytes / allocation_time_s; + _allocation_rate_s.add(allocation_rate); + + _last_unrestrained_young_size = additional_buffer_size; +} + +void G1AdaptiveIHOPControl::update_marking_length(double marking_length_s) { + assert(marking_length_s >= 0.0, "Marking length must be larger than zero but is %.3f", marking_length_s); + _marking_times_s.add(marking_length_s); +} + +void G1AdaptiveIHOPControl::print() { + G1IHOPControl::print(); + size_t actual_target = actual_target_threshold(); + ergo_verbose6(ErgoIHOP, + "adaptive IHOP information", + ergo_format_reason("value update") + ergo_format_byte_perc("threshold") + ergo_format_byte("internal target occupancy") + ergo_format_double("predicted old gen allocation rate") + ergo_format_double("predicted marking phase length") + ergo_format_str("prediction active"), + get_conc_mark_start_threshold(), + percent_of(get_conc_mark_start_threshold(), actual_target), + actual_target, + _predictor->get_new_prediction(&_allocation_rate_s), + _predictor->get_new_prediction(&_marking_times_s), + have_enough_data_for_prediction() ? "true" : "false" + ); +} + +void G1AdaptiveIHOPControl::send_trace_event(G1NewTracer* tracer) { + G1IHOPControl::send_trace_event(tracer); + tracer->report_adaptive_ihop_statistics(get_conc_mark_start_threshold(), + actual_target_threshold(), + G1CollectedHeap::heap()->used(), + _last_unrestrained_young_size, + _predictor->get_new_prediction(&_allocation_rate_s), + _predictor->get_new_prediction(&_marking_times_s), + have_enough_data_for_prediction()); +} + +#ifndef PRODUCT +void G1AdaptiveIHOPControl::test() { + size_t const initial_threshold = 45; + size_t const young_size = 10; + size_t const target_size = 100; + + // The final IHOP value is always + // target_size - (young_size + alloc_amount/alloc_time * marking_time) + + G1Predictions pred(0.95); + G1AdaptiveIHOPControl ctrl(initial_threshold, target_size, &pred, 0, 0); + + // First "load". + size_t const alloc_time1 = 2; + size_t const alloc_amount1 = 10; + size_t const marking_time1 = 2; + size_t const settled_ihop1 = target_size - (young_size + alloc_amount1/alloc_time1 * marking_time1); + + size_t threshold; + threshold = ctrl.get_conc_mark_start_threshold(); + assert(threshold == initial_threshold, + "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_threshold, threshold); + for (size_t i = 0; i < G1AdaptiveIHOPNumInitialSamples - 1; i++) { + ctrl.update_allocation_info(alloc_time1, alloc_amount1, young_size); + ctrl.update_marking_length(marking_time1); + // Not enough data yet. + threshold = ctrl.get_conc_mark_start_threshold(); + assert(threshold == initial_threshold, + "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_threshold, threshold); + } + + test_update(&ctrl, alloc_time1, alloc_amount1, young_size, marking_time1); + + threshold = ctrl.get_conc_mark_start_threshold(); + assert(threshold == settled_ihop1, + "Expected IHOP threshold to settle at " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop1, threshold); + + // Second "load". A bit higher allocation rate. + size_t const alloc_time2 = 2; + size_t const alloc_amount2 = 30; + size_t const marking_time2 = 2; + size_t const settled_ihop2 = target_size - (young_size + alloc_amount2/alloc_time2 * marking_time2); + + test_update(&ctrl, alloc_time2, alloc_amount2, young_size, marking_time2); + + threshold = ctrl.get_conc_mark_start_threshold(); + assert(threshold < settled_ihop1, + "Expected IHOP threshold to settle at a value lower than " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop1, threshold); + + // Third "load". Very high (impossible) allocation rate. + size_t const alloc_time3 = 1; + size_t const alloc_amount3 = 50; + size_t const marking_time3 = 2; + size_t const settled_ihop3 = 0; + + test_update(&ctrl, alloc_time3, alloc_amount3, young_size, marking_time3); + threshold = ctrl.get_conc_mark_start_threshold(); + + assert(threshold == settled_ihop3, + "Expected IHOP threshold to settle at " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop3, threshold); + + // And back to some arbitrary value. + test_update(&ctrl, alloc_time2, alloc_amount2, young_size, marking_time2); + + threshold = ctrl.get_conc_mark_start_threshold(); + assert(threshold > settled_ihop3, + "Expected IHOP threshold to settle at value larger than " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop3, threshold); +} + +void IHOP_test() { + G1StaticIHOPControl::test(); +} +#endif diff --git a/hotspot/src/share/vm/gc/g1/g1IHOPControl.hpp b/hotspot/src/share/vm/gc/g1/g1IHOPControl.hpp new file mode 100644 index 00000000000..085b8798a0c --- /dev/null +++ b/hotspot/src/share/vm/gc/g1/g1IHOPControl.hpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +#ifndef SHARE_VM_GC_G1_G1IHOPCONTROL_HPP +#define SHARE_VM_GC_G1_G1IHOPCONTROL_HPP + +#include "memory/allocation.hpp" +#include "utilities/numberSeq.hpp" + +class G1Predictions; +class G1NewTracer; + +// Base class for algorithms that calculate the heap occupancy at which +// concurrent marking should start. This heap usage threshold should be relative +// to old gen size. +class G1IHOPControl : public CHeapObj { + protected: + // The initial IHOP value relative to the target occupancy. + double _initial_ihop_percent; + // The target maximum occupancy of the heap. + size_t _target_occupancy; + + // Most recent complete mutator allocation period in seconds. + double _last_allocation_time_s; + // Amount of bytes allocated during _last_allocation_time_s. + size_t _last_allocated_bytes; + + // Initialize an instance with the initial IHOP value in percent and the target + // occupancy. The target occupancy is the number of bytes when marking should + // be finished and reclaim started. + G1IHOPControl(double initial_ihop_percent, size_t target_occupancy); + + // Most recent time from the end of the initial mark to the start of the first + // mixed gc. + virtual double last_marking_length_s() const = 0; + public: + virtual ~G1IHOPControl() { } + + // Get the current non-young occupancy at which concurrent marking should start. + virtual size_t get_conc_mark_start_threshold() = 0; + + // Update information about time during which allocations in the Java heap occurred, + // how large these allocations were in bytes, and an additional buffer. + // The allocations should contain any amount of space made unusable for further + // allocation, e.g. any waste caused by TLAB allocation, space at the end of + // humongous objects that can not be used for allocation, etc. + // Together with the target occupancy, this additional buffer should contain the + // difference between old gen size and total heap size at the start of reclamation, + // and space required for that reclamation. + virtual void update_allocation_info(double allocation_time_s, size_t allocated_bytes, size_t additional_buffer_size); + // Update the time spent in the mutator beginning from the end of initial mark to + // the first mixed gc. + virtual void update_marking_length(double marking_length_s) = 0; + + virtual void print(); + virtual void send_trace_event(G1NewTracer* tracer); +}; + +// The returned concurrent mark starting occupancy threshold is a fixed value +// relative to the maximum heap size. +class G1StaticIHOPControl : public G1IHOPControl { + // Most recent mutator time between the end of initial mark to the start of the + // first mixed gc. + double _last_marking_length_s; + protected: + double last_marking_length_s() const { return _last_marking_length_s; } + public: + G1StaticIHOPControl(double ihop_percent, size_t target_occupancy); + + size_t get_conc_mark_start_threshold() { return (size_t) (_initial_ihop_percent * _target_occupancy / 100.0); } + + virtual void update_marking_length(double marking_length_s) { + assert(marking_length_s > 0.0, "Marking length must be larger than zero but is %.3f", marking_length_s); + _last_marking_length_s = marking_length_s; + } + +#ifndef PRODUCT + static void test(); +#endif +}; + +// This algorithm tries to return a concurrent mark starting occupancy value that +// makes sure that during marking the given target occupancy is never exceeded, +// based on predictions of current allocation rate and time periods between +// initial mark and the first mixed gc. +class G1AdaptiveIHOPControl : public G1IHOPControl { + size_t _heap_reserve_percent; // Percentage of maximum heap capacity we should avoid to touch + size_t _heap_waste_percent; // Percentage of free heap that should be considered as waste. + + const G1Predictions * _predictor; + + TruncatedSeq _marking_times_s; + TruncatedSeq _allocation_rate_s; + + // The most recent unrestrained size of the young gen. This is used as an additional + // factor in the calculation of the threshold, as the threshold is based on + // non-young gen occupancy at the end of GC. For the IHOP threshold, we need to + // consider the young gen size during that time too. + // Since we cannot know what young gen sizes are used in the future, we will just + // use the current one. We expect that this one will be one with a fairly large size, + // as there is no marking or mixed gc that could impact its size too much. + size_t _last_unrestrained_young_size; + + bool have_enough_data_for_prediction() const; + + // The "actual" target threshold the algorithm wants to keep during and at the + // end of marking. This is typically lower than the requested threshold, as the + // algorithm needs to consider restrictions by the environment. + size_t actual_target_threshold() const; + protected: + virtual double last_marking_length_s() const { return _marking_times_s.last(); } + public: + G1AdaptiveIHOPControl(double ihop_percent, + size_t initial_target_occupancy, + G1Predictions const* predictor, + size_t heap_reserve_percent, // The percentage of total heap capacity that should not be tapped into. + size_t heap_waste_percent); // The percentage of the free space in the heap that we think is not usable for allocation. + + virtual size_t get_conc_mark_start_threshold(); + + virtual void update_allocation_info(double allocation_time_s, size_t allocated_bytes, size_t additional_buffer_size); + virtual void update_marking_length(double marking_length_s); + + virtual void print(); + virtual void send_trace_event(G1NewTracer* tracer); +#ifndef PRODUCT + static void test(); +#endif +}; + +#endif // SHARE_VM_GC_G1_G1IHOPCONTROL_HPP diff --git a/hotspot/src/share/vm/gc/g1/g1InitialMarkToMixedTimeTracker.hpp b/hotspot/src/share/vm/gc/g1/g1InitialMarkToMixedTimeTracker.hpp new file mode 100644 index 00000000000..625e07311d4 --- /dev/null +++ b/hotspot/src/share/vm/gc/g1/g1InitialMarkToMixedTimeTracker.hpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2001, 2015, 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. + * + */ + +#ifndef SHARE_VM_GC_G1_G1INITIALMARKTOMIXEDTIMETRACKER_HPP +#define SHARE_VM_GC_G1_G1INITIALMARKTOMIXEDTIMETRACKER_HPP + +#include "utilities/globalDefinitions.hpp" +#include "utilities/debug.hpp" + +// Used to track time from the end of initial mark to the first mixed GC. +// After calling the initial mark/mixed gc notifications, the result can be +// obtained in last_marking_time() once, after which the tracking resets. +// Any pauses recorded by add_pause() will be subtracted from that results. +class G1InitialMarkToMixedTimeTracker VALUE_OBJ_CLASS_SPEC { +private: + bool _active; + double _initial_mark_end_time; + double _mixed_start_time; + double _total_pause_time; + + double wall_time() const { + return _mixed_start_time - _initial_mark_end_time; + } +public: + G1InitialMarkToMixedTimeTracker() { reset(); } + + // Record initial mark pause end, starting the time tracking. + void record_initial_mark_end(double end_time) { + assert(!_active, "Initial mark out of order."); + _initial_mark_end_time = end_time; + _active = true; + } + + // Record the first mixed gc pause start, ending the time tracking. + void record_mixed_gc_start(double start_time) { + if (_active) { + _mixed_start_time = start_time; + _active = false; + } + } + + double last_marking_time() { + assert(has_result(), "Do not have all measurements yet."); + double result = (_mixed_start_time - _initial_mark_end_time) - _total_pause_time; + reset(); + return result; + } + + void reset() { + _active = false; + _total_pause_time = 0.0; + _initial_mark_end_time = -1.0; + _mixed_start_time = -1.0; + } + + void add_pause(double time) { + if (_active) { + _total_pause_time += time; + } + } + + // Returns whether we have a result that can be retrieved. + bool has_result() const { return _mixed_start_time > 0.0 && _initial_mark_end_time > 0.0; } +}; + +#endif // SHARE_VM_GC_G1_G1INITIALMARKTOMIXEDTIMETRACKER_HPP diff --git a/hotspot/src/share/vm/gc/g1/g1OopClosures.hpp b/hotspot/src/share/vm/gc/g1/g1OopClosures.hpp index 4ea0c653c44..cc5dbd9cf7f 100644 --- a/hotspot/src/share/vm/gc/g1/g1OopClosures.hpp +++ b/hotspot/src/share/vm/gc/g1/g1OopClosures.hpp @@ -123,16 +123,13 @@ enum G1Mark { template class G1ParCopyClosure : public G1ParCopyHelper { -private: - template void do_oop_work(T* p); - public: G1ParCopyClosure(G1CollectedHeap* g1, G1ParScanThreadState* par_scan_state) : G1ParCopyHelper(g1, par_scan_state) { assert(ref_processor() == NULL, "sanity"); } - template void do_oop_nv(T* p) { do_oop_work(p); } + template void do_oop_nv(T* p); virtual void do_oop(oop* p) { do_oop_nv(p); } virtual void do_oop(narrowOop* p) { do_oop_nv(p); } }; diff --git a/hotspot/src/share/vm/gc/g1/g1OopClosures.inline.hpp b/hotspot/src/share/vm/gc/g1/g1OopClosures.inline.hpp index 176191f7445..82a997eca79 100644 --- a/hotspot/src/share/vm/gc/g1/g1OopClosures.inline.hpp +++ b/hotspot/src/share/vm/gc/g1/g1OopClosures.inline.hpp @@ -91,7 +91,7 @@ inline void G1ParScanClosure::do_oop_nv(T* p) { if (state.is_humongous()) { _g1->set_humongous_is_live(obj); } - _par_scan_state->update_rs(_from, p); + _par_scan_state->update_rs(_from, p, obj); } } } @@ -251,7 +251,7 @@ void G1ParCopyHelper::mark_forwarded_object(oop from_obj, oop to_obj) { template template -void G1ParCopyClosure::do_oop_work(T* p) { +void G1ParCopyClosure::do_oop_nv(T* p) { T heap_oop = oopDesc::load_heap_oop(p); if (oopDesc::is_null(heap_oop)) { diff --git a/hotspot/src/share/vm/gc/g1/g1ParScanThreadState.hpp b/hotspot/src/share/vm/gc/g1/g1ParScanThreadState.hpp index e143ac87474..0b86e4c9c71 100644 --- a/hotspot/src/share/vm/gc/g1/g1ParScanThreadState.hpp +++ b/hotspot/src/share/vm/gc/g1/g1ParScanThreadState.hpp @@ -98,10 +98,10 @@ class G1ParScanThreadState : public CHeapObj { template void push_on_queue(T* ref); - template void update_rs(HeapRegion* from, T* p) { + template void update_rs(HeapRegion* from, T* p, oop o) { // If the new value of the field points to the same region or // is the to-space, we don't need to include it in the Rset updates. - if (!from->is_in_reserved(oopDesc::load_decode_heap_oop(p)) && !from->is_survivor()) { + if (!HeapRegion::is_in_same_region(p, o) && !from->is_young()) { size_t card_index = ctbs()->index_for(p); // If the card hasn't been added to the buffer, do it. if (ctbs()->mark_card_deferred(card_index)) { diff --git a/hotspot/src/share/vm/gc/g1/g1ParScanThreadState.inline.hpp b/hotspot/src/share/vm/gc/g1/g1ParScanThreadState.inline.hpp index ae4b08a72da..86774c4723e 100644 --- a/hotspot/src/share/vm/gc/g1/g1ParScanThreadState.inline.hpp +++ b/hotspot/src/share/vm/gc/g1/g1ParScanThreadState.inline.hpp @@ -40,14 +40,13 @@ template void G1ParScanThreadState::do_oop_evac(T* p, HeapRegion* from // processed multiple times. So redo this check. const InCSetState in_cset_state = _g1h->in_cset_state(obj); if (in_cset_state.is_in_cset()) { - oop forwardee; markOop m = obj->mark(); if (m->is_marked()) { - forwardee = (oop) m->decode_pointer(); + obj = (oop) m->decode_pointer(); } else { - forwardee = copy_to_survivor_space(in_cset_state, obj, m); + obj = copy_to_survivor_space(in_cset_state, obj, m); } - oopDesc::encode_store_heap_oop(p, forwardee); + oopDesc::encode_store_heap_oop(p, obj); } else if (in_cset_state.is_humongous()) { _g1h->set_humongous_is_live(obj); } else { @@ -56,7 +55,7 @@ template void G1ParScanThreadState::do_oop_evac(T* p, HeapRegion* from } assert(obj != NULL, "Must be"); - update_rs(from, p); + update_rs(from, p, obj); } template inline void G1ParScanThreadState::push_on_queue(T* ref) { diff --git a/hotspot/src/share/vm/gc/g1/g1RemSet.cpp b/hotspot/src/share/vm/gc/g1/g1RemSet.cpp index 8ea400204c4..ea395791423 100644 --- a/hotspot/src/share/vm/gc/g1/g1RemSet.cpp +++ b/hotspot/src/share/vm/gc/g1/g1RemSet.cpp @@ -73,138 +73,111 @@ G1RemSet::~G1RemSet() { FREE_C_HEAP_ARRAY(G1ParPushHeapRSClosure*, _cset_rs_update_cl); } -class ScanRSClosure : public HeapRegionClosure { - size_t _cards_done, _cards; - G1CollectedHeap* _g1h; - - G1ParPushHeapRSClosure* _oc; - CodeBlobClosure* _code_root_cl; - - G1BlockOffsetSharedArray* _bot_shared; - G1SATBCardTableModRefBS *_ct_bs; - - double _strong_code_root_scan_time_sec; - uint _worker_i; - size_t _block_size; - bool _try_claimed; - -public: - ScanRSClosure(G1ParPushHeapRSClosure* oc, - CodeBlobClosure* code_root_cl, - uint worker_i) : +ScanRSClosure::ScanRSClosure(G1ParPushHeapRSClosure* oc, + CodeBlobClosure* code_root_cl, + uint worker_i) : _oc(oc), _code_root_cl(code_root_cl), _strong_code_root_scan_time_sec(0.0), _cards(0), _cards_done(0), _worker_i(worker_i), - _try_claimed(false) - { - _g1h = G1CollectedHeap::heap(); - _bot_shared = _g1h->bot_shared(); - _ct_bs = _g1h->g1_barrier_set(); - _block_size = MAX2(G1RSetScanBlockSize, 1); + _try_claimed(false) { + _g1h = G1CollectedHeap::heap(); + _bot_shared = _g1h->bot_shared(); + _ct_bs = _g1h->g1_barrier_set(); + _block_size = MAX2(G1RSetScanBlockSize, 1); +} + +void ScanRSClosure::scanCard(size_t index, HeapRegion *r) { + // Stack allocate the DirtyCardToOopClosure instance + HeapRegionDCTOC cl(_g1h, r, _oc, + CardTableModRefBS::Precise); + + // Set the "from" region in the closure. + _oc->set_region(r); + MemRegion card_region(_bot_shared->address_for_index(index), G1BlockOffsetSharedArray::N_words); + MemRegion pre_gc_allocated(r->bottom(), r->scan_top()); + MemRegion mr = pre_gc_allocated.intersection(card_region); + if (!mr.is_empty() && !_ct_bs->is_card_claimed(index)) { + // We make the card as "claimed" lazily (so races are possible + // but they're benign), which reduces the number of duplicate + // scans (the rsets of the regions in the cset can intersect). + _ct_bs->set_card_claimed(index); + _cards_done++; + cl.do_MemRegion(mr); } +} - void set_try_claimed() { _try_claimed = true; } +void ScanRSClosure::printCard(HeapRegion* card_region, size_t card_index, + HeapWord* card_start) { + gclog_or_tty->print_cr("T %u Region [" PTR_FORMAT ", " PTR_FORMAT ") " + "RS names card " SIZE_FORMAT_HEX ": " + "[" PTR_FORMAT ", " PTR_FORMAT ")", + _worker_i, + p2i(card_region->bottom()), p2i(card_region->end()), + card_index, + p2i(card_start), p2i(card_start + G1BlockOffsetSharedArray::N_words)); +} - void scanCard(size_t index, HeapRegion *r) { - // Stack allocate the DirtyCardToOopClosure instance - HeapRegionDCTOC cl(_g1h, r, _oc, - CardTableModRefBS::Precise); +void ScanRSClosure::scan_strong_code_roots(HeapRegion* r) { + double scan_start = os::elapsedTime(); + r->strong_code_roots_do(_code_root_cl); + _strong_code_root_scan_time_sec += (os::elapsedTime() - scan_start); +} - // Set the "from" region in the closure. - _oc->set_region(r); - MemRegion card_region(_bot_shared->address_for_index(index), G1BlockOffsetSharedArray::N_words); - MemRegion pre_gc_allocated(r->bottom(), r->scan_top()); - MemRegion mr = pre_gc_allocated.intersection(card_region); - if (!mr.is_empty() && !_ct_bs->is_card_claimed(index)) { - // We make the card as "claimed" lazily (so races are possible - // but they're benign), which reduces the number of duplicate - // scans (the rsets of the regions in the cset can intersect). - _ct_bs->set_card_claimed(index); - _cards_done++; - cl.do_MemRegion(mr); +bool ScanRSClosure::doHeapRegion(HeapRegion* r) { + assert(r->in_collection_set(), "should only be called on elements of CS."); + HeapRegionRemSet* hrrs = r->rem_set(); + if (hrrs->iter_is_complete()) return false; // All done. + if (!_try_claimed && !hrrs->claim_iter()) return false; + // If we ever free the collection set concurrently, we should also + // clear the card table concurrently therefore we won't need to + // add regions of the collection set to the dirty cards region. + _g1h->push_dirty_cards_region(r); + // If we didn't return above, then + // _try_claimed || r->claim_iter() + // is true: either we're supposed to work on claimed-but-not-complete + // regions, or we successfully claimed the region. + + HeapRegionRemSetIterator iter(hrrs); + size_t card_index; + + // We claim cards in block so as to reduce the contention. The block size is determined by + // the G1RSetScanBlockSize parameter. + size_t jump_to_card = hrrs->iter_claimed_next(_block_size); + for (size_t current_card = 0; iter.has_next(card_index); current_card++) { + if (current_card >= jump_to_card + _block_size) { + jump_to_card = hrrs->iter_claimed_next(_block_size); } - } - - void printCard(HeapRegion* card_region, size_t card_index, - HeapWord* card_start) { - gclog_or_tty->print_cr("T %u Region [" PTR_FORMAT ", " PTR_FORMAT ") " - "RS names card " SIZE_FORMAT_HEX ": " - "[" PTR_FORMAT ", " PTR_FORMAT ")", - _worker_i, - p2i(card_region->bottom()), p2i(card_region->end()), - card_index, - p2i(card_start), p2i(card_start + G1BlockOffsetSharedArray::N_words)); - } - - void scan_strong_code_roots(HeapRegion* r) { - double scan_start = os::elapsedTime(); - r->strong_code_roots_do(_code_root_cl); - _strong_code_root_scan_time_sec += (os::elapsedTime() - scan_start); - } - - bool doHeapRegion(HeapRegion* r) { - assert(r->in_collection_set(), "should only be called on elements of CS."); - HeapRegionRemSet* hrrs = r->rem_set(); - if (hrrs->iter_is_complete()) return false; // All done. - if (!_try_claimed && !hrrs->claim_iter()) return false; - // If we ever free the collection set concurrently, we should also - // clear the card table concurrently therefore we won't need to - // add regions of the collection set to the dirty cards region. - _g1h->push_dirty_cards_region(r); - // If we didn't return above, then - // _try_claimed || r->claim_iter() - // is true: either we're supposed to work on claimed-but-not-complete - // regions, or we successfully claimed the region. - - HeapRegionRemSetIterator iter(hrrs); - size_t card_index; - - // We claim cards in block so as to reduce the contention. The block size is determined by - // the G1RSetScanBlockSize parameter. - size_t jump_to_card = hrrs->iter_claimed_next(_block_size); - for (size_t current_card = 0; iter.has_next(card_index); current_card++) { - if (current_card >= jump_to_card + _block_size) { - jump_to_card = hrrs->iter_claimed_next(_block_size); - } - if (current_card < jump_to_card) continue; - HeapWord* card_start = _g1h->bot_shared()->address_for_index(card_index); + if (current_card < jump_to_card) continue; + HeapWord* card_start = _g1h->bot_shared()->address_for_index(card_index); #if 0 - gclog_or_tty->print("Rem set iteration yielded card [" PTR_FORMAT ", " PTR_FORMAT ").\n", - card_start, card_start + CardTableModRefBS::card_size_in_words); + gclog_or_tty->print("Rem set iteration yielded card [" PTR_FORMAT ", " PTR_FORMAT ").\n", + card_start, card_start + CardTableModRefBS::card_size_in_words); #endif - HeapRegion* card_region = _g1h->heap_region_containing(card_start); - _cards++; + HeapRegion* card_region = _g1h->heap_region_containing(card_start); + _cards++; - if (!card_region->is_on_dirty_cards_region_list()) { - _g1h->push_dirty_cards_region(card_region); - } - - // If the card is dirty, then we will scan it during updateRS. - if (!card_region->in_collection_set() && - !_ct_bs->is_card_dirty(card_index)) { - scanCard(card_index, card_region); - } + if (!card_region->is_on_dirty_cards_region_list()) { + _g1h->push_dirty_cards_region(card_region); } - if (!_try_claimed) { - // Scan the strong code root list attached to the current region - scan_strong_code_roots(r); - hrrs->set_iter_complete(); + // If the card is dirty, then we will scan it during updateRS. + if (!card_region->in_collection_set() && + !_ct_bs->is_card_dirty(card_index)) { + scanCard(card_index, card_region); } - return false; } + if (!_try_claimed) { + // Scan the strong code root list attached to the current region + scan_strong_code_roots(r); - double strong_code_root_scan_time_sec() { - return _strong_code_root_scan_time_sec; + hrrs->set_iter_complete(); } - - size_t cards_done() { return _cards_done;} - size_t cards_looked_up() { return _cards;} -}; + return false; +} size_t G1RemSet::scanRS(G1ParPushHeapRSClosure* oc, CodeBlobClosure* heap_region_codeblobs, diff --git a/hotspot/src/share/vm/gc/g1/g1RemSet.hpp b/hotspot/src/share/vm/gc/g1/g1RemSet.hpp index 1ecb25c27dc..72a259ef417 100644 --- a/hotspot/src/share/vm/gc/g1/g1RemSet.hpp +++ b/hotspot/src/share/vm/gc/g1/g1RemSet.hpp @@ -54,16 +54,6 @@ protected: uint n_workers(); protected: - enum SomePrivateConstants { - UpdateRStoMergeSync = 0, - MergeRStoDoDirtySync = 1, - DoDirtySync = 2, - LastSync = 3, - - SeqTask = 0, - NumSeqTasks = 1 - }; - CardTableModRefBS* _ct_bs; G1CollectorPolicy* _g1p; @@ -123,7 +113,6 @@ public: // Record, if necessary, the fact that *p (where "p" is in region "from", // which is required to be non-NULL) has changed to a new non-NULL value. - template void write_ref(HeapRegion* from, T* p); template void par_write_ref(HeapRegion* from, T* p, uint tid); // Requires "region_bm" and "card_bm" to be bitmaps with 1 bit per region @@ -156,6 +145,41 @@ public: } }; +class ScanRSClosure : public HeapRegionClosure { + size_t _cards_done, _cards; + G1CollectedHeap* _g1h; + + G1ParPushHeapRSClosure* _oc; + CodeBlobClosure* _code_root_cl; + + G1BlockOffsetSharedArray* _bot_shared; + G1SATBCardTableModRefBS *_ct_bs; + + double _strong_code_root_scan_time_sec; + uint _worker_i; + size_t _block_size; + bool _try_claimed; + +public: + ScanRSClosure(G1ParPushHeapRSClosure* oc, + CodeBlobClosure* code_root_cl, + uint worker_i); + + bool doHeapRegion(HeapRegion* r); + + double strong_code_root_scan_time_sec() { + return _strong_code_root_scan_time_sec; + } + size_t cards_done() { return _cards_done;} + size_t cards_looked_up() { return _cards;} + void set_try_claimed() { _try_claimed = true; } +private: + void scanCard(size_t index, HeapRegion *r); + void printCard(HeapRegion* card_region, size_t card_index, + HeapWord* card_start); + void scan_strong_code_roots(HeapRegion* r); +}; + class UpdateRSOopClosure: public ExtendedOopClosure { HeapRegion* _from; G1RemSet* _rs; diff --git a/hotspot/src/share/vm/gc/g1/g1RemSet.inline.hpp b/hotspot/src/share/vm/gc/g1/g1RemSet.inline.hpp index af8f2e96f16..f90c966e051 100644 --- a/hotspot/src/share/vm/gc/g1/g1RemSet.inline.hpp +++ b/hotspot/src/share/vm/gc/g1/g1RemSet.inline.hpp @@ -34,11 +34,6 @@ inline uint G1RemSet::n_workers() { return _g1->workers()->total_workers(); } -template -inline void G1RemSet::write_ref(HeapRegion* from, T* p) { - par_write_ref(from, p, 0); -} - template inline void G1RemSet::par_write_ref(HeapRegion* from, T* p, uint tid) { oop obj = oopDesc::load_decode_heap_oop(p); diff --git a/hotspot/src/share/vm/gc/g1/g1RemSetSummary.cpp b/hotspot/src/share/vm/gc/g1/g1RemSetSummary.cpp index 474ce953482..2a9c9332770 100644 --- a/hotspot/src/share/vm/gc/g1/g1RemSetSummary.cpp +++ b/hotspot/src/share/vm/gc/g1/g1RemSetSummary.cpp @@ -125,14 +125,6 @@ void G1RemSetSummary::subtract_from(G1RemSetSummary* other) { _sampling_thread_vtime = other->sampling_thread_vtime() - _sampling_thread_vtime; } -static double percent_of(size_t numerator, size_t denominator) { - if (denominator != 0) { - return (double)numerator / denominator * 100.0f; - } else { - return 0.0f; - } -} - static size_t round_to_K(size_t value) { return value / K; } diff --git a/hotspot/src/share/vm/gc/g1/g1RootClosures.cpp b/hotspot/src/share/vm/gc/g1/g1RootClosures.cpp index 2f393e425ae..3d93bf9cec9 100644 --- a/hotspot/src/share/vm/gc/g1/g1RootClosures.cpp +++ b/hotspot/src/share/vm/gc/g1/g1RootClosures.cpp @@ -23,62 +23,7 @@ */ #include "precompiled.hpp" - -#include "gc/g1/bufferingOopClosure.hpp" -#include "gc/g1/g1CodeBlobClosure.hpp" -#include "gc/g1/g1CollectedHeap.hpp" -#include "gc/g1/g1OopClosures.inline.hpp" -#include "gc/g1/g1RootClosures.hpp" - -class G1ParScanThreadState; - -// Simple holder object for a complete set of closures used by the G1 evacuation code. -template -class G1SharedClosures VALUE_OBJ_CLASS_SPEC { -public: - G1ParCopyClosure _oops; - G1ParCopyClosure _oop_in_klass; - G1KlassScanClosure _klass_in_cld_closure; - CLDToKlassAndOopClosure _clds; - G1CodeBlobClosure _codeblobs; - BufferingOopClosure _buffered_oops; - - G1SharedClosures(G1CollectedHeap* g1h, G1ParScanThreadState* pss, bool process_only_dirty_klasses, bool must_claim_cld) : - _oops(g1h, pss), - _oop_in_klass(g1h, pss), - _klass_in_cld_closure(&_oop_in_klass, process_only_dirty_klasses), - _clds(&_klass_in_cld_closure, &_oops, must_claim_cld), - _codeblobs(&_oops), - _buffered_oops(&_oops) {} -}; - -class G1EvacuationClosures : public G1EvacuationRootClosures { - G1SharedClosures _closures; - -public: - G1EvacuationClosures(G1CollectedHeap* g1h, - G1ParScanThreadState* pss, - bool gcs_are_young) : - _closures(g1h, pss, gcs_are_young, /* must_claim_cld */ false) {} - - OopClosure* weak_oops() { return &_closures._buffered_oops; } - OopClosure* strong_oops() { return &_closures._buffered_oops; } - - CLDClosure* weak_clds() { return &_closures._clds; } - CLDClosure* strong_clds() { return &_closures._clds; } - CLDClosure* thread_root_clds() { return NULL; } - CLDClosure* second_pass_weak_clds() { return NULL; } - - CodeBlobClosure* strong_codeblobs() { return &_closures._codeblobs; } - CodeBlobClosure* weak_codeblobs() { return &_closures._codeblobs; } - - void flush() { _closures._buffered_oops.done(); } - double closure_app_seconds() { return _closures._buffered_oops.closure_app_seconds(); } - - OopClosure* raw_strong_oops() { return &_closures._oops; } - - bool trace_metadata() { return false; } -}; +#include "gc/g1/g1RootClosures.inline.hpp" // Closures used during initial mark. // The treatment of "weak" roots is selectable through the template parameter, @@ -137,13 +82,19 @@ public: }; G1EvacuationRootClosures* G1EvacuationRootClosures::create_root_closures(G1ParScanThreadState* pss, G1CollectedHeap* g1h) { + G1EvacuationRootClosures* res = create_root_closures_ext(pss, g1h); + if (res != NULL) { + return res; + } + if (g1h->collector_state()->during_initial_mark_pause()) { if (ClassUnloadingWithConcurrentMark) { - return new G1InitalMarkClosures(g1h, pss); + res = new G1InitalMarkClosures(g1h, pss); } else { - return new G1InitalMarkClosures(g1h, pss); + res = new G1InitalMarkClosures(g1h, pss); } } else { - return new G1EvacuationClosures(g1h, pss, g1h->collector_state()->gcs_are_young()); + res = new G1EvacuationClosures(g1h, pss, g1h->collector_state()->gcs_are_young()); } + return res; } diff --git a/hotspot/src/share/vm/gc/g1/g1RootClosures.hpp b/hotspot/src/share/vm/gc/g1/g1RootClosures.hpp index e4a707f74b0..34e58f0f411 100644 --- a/hotspot/src/share/vm/gc/g1/g1RootClosures.hpp +++ b/hotspot/src/share/vm/gc/g1/g1RootClosures.hpp @@ -49,6 +49,7 @@ public: }; class G1EvacuationRootClosures : public G1RootClosures { + static G1EvacuationRootClosures* create_root_closures_ext(G1ParScanThreadState* pss, G1CollectedHeap* g1h); public: // Flush any buffered state and deferred processing virtual void flush() = 0; diff --git a/hotspot/src/share/vm/gc/g1/g1RootClosures.inline.hpp b/hotspot/src/share/vm/gc/g1/g1RootClosures.inline.hpp new file mode 100644 index 00000000000..57cf4c7c2a4 --- /dev/null +++ b/hotspot/src/share/vm/gc/g1/g1RootClosures.inline.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +#include "gc/g1/bufferingOopClosure.hpp" +#include "gc/g1/g1CodeBlobClosure.hpp" +#include "gc/g1/g1CollectedHeap.hpp" +#include "gc/g1/g1OopClosures.inline.hpp" +#include "gc/g1/g1RootClosures.hpp" + +// Simple holder object for a complete set of closures used by the G1 evacuation code. +template +class G1SharedClosures VALUE_OBJ_CLASS_SPEC { +public: + G1ParCopyClosure _oops; + G1ParCopyClosure _oop_in_klass; + G1KlassScanClosure _klass_in_cld_closure; + CLDToKlassAndOopClosure _clds; + G1CodeBlobClosure _codeblobs; + BufferingOopClosure _buffered_oops; + + G1SharedClosures(G1CollectedHeap* g1h, G1ParScanThreadState* pss, bool process_only_dirty_klasses, bool must_claim_cld) : + _oops(g1h, pss), + _oop_in_klass(g1h, pss), + _klass_in_cld_closure(&_oop_in_klass, process_only_dirty_klasses), + _clds(&_klass_in_cld_closure, &_oops, must_claim_cld), + _codeblobs(&_oops), + _buffered_oops(&_oops) {} +}; + +class G1EvacuationClosures : public G1EvacuationRootClosures { + G1SharedClosures _closures; + +public: + G1EvacuationClosures(G1CollectedHeap* g1h, + G1ParScanThreadState* pss, + bool gcs_are_young) : + _closures(g1h, pss, gcs_are_young, /* must_claim_cld */ false) {} + + OopClosure* weak_oops() { return &_closures._buffered_oops; } + OopClosure* strong_oops() { return &_closures._buffered_oops; } + + CLDClosure* weak_clds() { return &_closures._clds; } + CLDClosure* strong_clds() { return &_closures._clds; } + CLDClosure* thread_root_clds() { return NULL; } + CLDClosure* second_pass_weak_clds() { return NULL; } + + CodeBlobClosure* strong_codeblobs() { return &_closures._codeblobs; } + CodeBlobClosure* weak_codeblobs() { return &_closures._codeblobs; } + + void flush() { _closures._buffered_oops.done(); } + double closure_app_seconds() { return _closures._buffered_oops.closure_app_seconds(); } + + OopClosure* raw_strong_oops() { return &_closures._oops; } + + bool trace_metadata() { return false; } +}; diff --git a/hotspot/src/share/vm/gc/g1/g1RootClosures_ext.cpp b/hotspot/src/share/vm/gc/g1/g1RootClosures_ext.cpp new file mode 100644 index 00000000000..a4f81f29d03 --- /dev/null +++ b/hotspot/src/share/vm/gc/g1/g1RootClosures_ext.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +#include "precompiled.hpp" +#include "gc/g1/g1RootClosures.hpp" + +G1EvacuationRootClosures* G1EvacuationRootClosures::create_root_closures_ext(G1ParScanThreadState* pss, G1CollectedHeap* g1h) { + return NULL; +} diff --git a/hotspot/src/share/vm/gc/g1/g1SATBCardTableModRefBS.cpp b/hotspot/src/share/vm/gc/g1/g1SATBCardTableModRefBS.cpp index 77b9957b9bf..1f872134941 100644 --- a/hotspot/src/share/vm/gc/g1/g1SATBCardTableModRefBS.cpp +++ b/hotspot/src/share/vm/gc/g1/g1SATBCardTableModRefBS.cpp @@ -85,11 +85,6 @@ bool G1SATBCardTableModRefBS::mark_card_deferred(size_t card_index) { return false; } - if (val == g1_young_gen) { - // the card is for a young gen region. We don't need to keep track of all pointers into young - return false; - } - // Cached bit can be installed either on a clean card or on a claimed card. jbyte new_val = val; if (val == clean_card_val()) { diff --git a/hotspot/src/share/vm/gc/g1/g1_globals.hpp b/hotspot/src/share/vm/gc/g1/g1_globals.hpp index a23edc0a5da..6f7ac1fe840 100644 --- a/hotspot/src/share/vm/gc/g1/g1_globals.hpp +++ b/hotspot/src/share/vm/gc/g1/g1_globals.hpp @@ -33,6 +33,16 @@ #define G1_FLAGS(develop, develop_pd, product, product_pd, diagnostic, experimental, notproduct, manageable, product_rw, range, constraint) \ \ + product(bool, G1UseAdaptiveIHOP, false, \ + "Adaptively adjust InitiatingHeapOccupancyPercent from the " \ + "initial value.") \ + \ + experimental(size_t, G1AdaptiveIHOPNumInitialSamples, 3, \ + "How many completed time periods from initial mark to first " \ + "mixed gc are required to use the input values for prediction " \ + "of the optimal occupancy to start marking.") \ + range(1, max_intx) \ + \ product(uintx, G1ConfidencePercent, 50, \ "Confidence level for MMU/pause predictions") \ range(0, 100) \ diff --git a/hotspot/src/share/vm/gc/g1/heapRegion.cpp b/hotspot/src/share/vm/gc/g1/heapRegion.cpp index 6d6081f3af0..73fa00be4ee 100644 --- a/hotspot/src/share/vm/gc/g1/heapRegion.cpp +++ b/hotspot/src/share/vm/gc/g1/heapRegion.cpp @@ -211,14 +211,14 @@ void HeapRegion::calc_gc_efficiency() { _gc_efficiency = (double) reclaimable_bytes() / region_elapsed_time_ms; } -void HeapRegion::set_starts_humongous(HeapWord* obj_top) { +void HeapRegion::set_starts_humongous(HeapWord* obj_top, size_t fill_size) { assert(!is_humongous(), "sanity / pre-condition"); assert(top() == bottom(), "should be empty"); _type.set_starts_humongous(); _humongous_start_region = this; - _offsets.set_for_starts_humongous(obj_top); + _offsets.set_for_starts_humongous(obj_top, fill_size); } void HeapRegion::set_continues_humongous(HeapRegion* first_hr) { @@ -756,16 +756,6 @@ void HeapRegion::verify(VerifyOption vo, size_t obj_size = block_size(p); object_num += 1; - if (is_region_humongous != g1->is_humongous(obj_size) && - !g1->is_obj_dead(obj, this)) { // Dead objects may have bigger block_size since they span several objects. - gclog_or_tty->print_cr("obj " PTR_FORMAT " is of %shumongous size (" - SIZE_FORMAT " words) in a %shumongous region", - p2i(p), g1->is_humongous(obj_size) ? "" : "non-", - obj_size, is_region_humongous ? "" : "non-"); - *failures = true; - return; - } - if (!g1->is_obj_dead_cond(obj, this, vo)) { if (obj->is_oop()) { Klass* klass = obj->klass(); @@ -876,14 +866,6 @@ void HeapRegion::verify(VerifyOption vo, } } - if (is_region_humongous && object_num > 1) { - gclog_or_tty->print_cr("region [" PTR_FORMAT "," PTR_FORMAT "] is humongous " - "but has " SIZE_FORMAT ", objects", - p2i(bottom()), p2i(end()), object_num); - *failures = true; - return; - } - verify_strong_code_roots(vo, failures); } diff --git a/hotspot/src/share/vm/gc/g1/heapRegion.hpp b/hotspot/src/share/vm/gc/g1/heapRegion.hpp index eba1934dda0..77fa01ce9d2 100644 --- a/hotspot/src/share/vm/gc/g1/heapRegion.hpp +++ b/hotspot/src/share/vm/gc/g1/heapRegion.hpp @@ -351,6 +351,15 @@ class HeapRegion: public G1OffsetTableContigSpace { ~((1 << (size_t) LogOfHRGrainBytes) - 1); } + + // Returns whether a field is in the same region as the obj it points to. + template + static bool is_in_same_region(T* p, oop obj) { + assert(p != NULL, "p can't be NULL"); + assert(obj != NULL, "obj can't be NULL"); + return (((uintptr_t) p ^ cast_from_oop(obj)) >> LogOfHRGrainBytes) == 0; + } + static size_t max_region_size(); static size_t min_region_size_in_words(); @@ -455,9 +464,9 @@ class HeapRegion: public G1OffsetTableContigSpace { // the first region in a series of one or more contiguous regions // that will contain a single "humongous" object. // - // obj_top : points to the end of the humongous object that's being - // allocated. - void set_starts_humongous(HeapWord* obj_top); + // obj_top : points to the top of the humongous object. + // fill_size : size of the filler object at the end of the region series. + void set_starts_humongous(HeapWord* obj_top, size_t fill_size); // Makes the current region be a "continues humongous' // region. first_hr is the "start humongous" region of the series diff --git a/hotspot/src/share/vm/gc/g1/ptrQueue.hpp b/hotspot/src/share/vm/gc/g1/ptrQueue.hpp index 39f1c931e74..707d591c0c0 100644 --- a/hotspot/src/share/vm/gc/g1/ptrQueue.hpp +++ b/hotspot/src/share/vm/gc/g1/ptrQueue.hpp @@ -140,19 +140,27 @@ public: } // To support compiler. + +protected: + template static ByteSize byte_offset_of_index() { - return byte_offset_of(PtrQueue, _index); + return byte_offset_of(Derived, _index); } + static ByteSize byte_width_of_index() { return in_ByteSize(sizeof(size_t)); } + template static ByteSize byte_offset_of_buf() { - return byte_offset_of(PtrQueue, _buf); + return byte_offset_of(Derived, _buf); } + static ByteSize byte_width_of_buf() { return in_ByteSize(sizeof(void*)); } + template static ByteSize byte_offset_of_active() { - return byte_offset_of(PtrQueue, _active); + return byte_offset_of(Derived, _active); } + static ByteSize byte_width_of_active() { return in_ByteSize(sizeof(bool)); } }; diff --git a/hotspot/src/share/vm/gc/g1/satbMarkQueue.hpp b/hotspot/src/share/vm/gc/g1/satbMarkQueue.hpp index 0ca3b45c2ff..e3591f4f7df 100644 --- a/hotspot/src/share/vm/gc/g1/satbMarkQueue.hpp +++ b/hotspot/src/share/vm/gc/g1/satbMarkQueue.hpp @@ -68,6 +68,23 @@ public: void print(const char* name); static void print(const char* name, void** buf, size_t index, size_t sz); #endif // PRODUCT + + // Compiler support. + static ByteSize byte_offset_of_index() { + return PtrQueue::byte_offset_of_index(); + } + using PtrQueue::byte_width_of_index; + + static ByteSize byte_offset_of_buf() { + return PtrQueue::byte_offset_of_buf(); + } + using PtrQueue::byte_width_of_buf; + + static ByteSize byte_offset_of_active() { + return PtrQueue::byte_offset_of_active(); + } + using PtrQueue::byte_width_of_active; + }; class SATBMarkQueueSet: public PtrQueueSet { diff --git a/hotspot/src/share/vm/gc/g1/vmStructs_g1.hpp b/hotspot/src/share/vm/gc/g1/vmStructs_g1.hpp index 97daf08e66d..1fad2b64637 100644 --- a/hotspot/src/share/vm/gc/g1/vmStructs_g1.hpp +++ b/hotspot/src/share/vm/gc/g1/vmStructs_g1.hpp @@ -28,6 +28,7 @@ #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/heapRegion.hpp" #include "gc/g1/heapRegionManager.hpp" +#include "utilities/macros.hpp" #define VM_STRUCTS_G1(nonstatic_field, static_field) \ \ @@ -62,6 +63,34 @@ \ nonstatic_field(HeapRegionSetCount, _length, uint) \ nonstatic_field(HeapRegionSetCount, _capacity, size_t) \ + \ + nonstatic_field(PtrQueue, _active, bool) \ + nonstatic_field(PtrQueue, _buf, void**) \ + nonstatic_field(PtrQueue, _index, size_t) \ + + +#define VM_INT_CONSTANTS_G1(declare_constant, declare_constant_with_value) \ + \ + JVMCI_ONLY( \ + declare_constant_with_value( \ + "dirtyCardQueueBufferOffset", \ + in_bytes(DirtyCardQueue::byte_offset_of_buf())) \ + declare_constant_with_value( \ + "dirtyCardQueueIndexOffset", \ + in_bytes(DirtyCardQueue::byte_offset_of_index())) \ + ) /* JVMCI_ONLY */ \ + \ + JVMCI_ONLY( \ + declare_constant_with_value( \ + "satbMarkQueueBufferOffset", \ + in_bytes(SATBMarkQueue::byte_offset_of_buf())) \ + declare_constant_with_value( \ + "satbMarkQueueIndexOffset", \ + in_bytes(SATBMarkQueue::byte_offset_of_index())) \ + declare_constant_with_value( \ + "satbMarkQueueActiveOffset", \ + in_bytes(SATBMarkQueue::byte_offset_of_active())) \ + ) /* JVMCI_ONLY */ \ #define VM_TYPES_G1(declare_type, declare_toplevel_type) \ @@ -76,10 +105,10 @@ declare_toplevel_type(HeapRegionSetBase) \ declare_toplevel_type(HeapRegionSetCount) \ declare_toplevel_type(G1MonitoringSupport) \ + declare_toplevel_type(PtrQueue) \ \ declare_toplevel_type(G1CollectedHeap*) \ declare_toplevel_type(HeapRegion*) \ declare_toplevel_type(G1MonitoringSupport*) \ - #endif // SHARE_VM_GC_G1_VMSTRUCTS_G1_HPP diff --git a/hotspot/src/share/vm/gc/parallel/psPromotionManager.cpp b/hotspot/src/share/vm/gc/parallel/psPromotionManager.cpp index 477b2a15893..6e54c638f22 100644 --- a/hotspot/src/share/vm/gc/parallel/psPromotionManager.cpp +++ b/hotspot/src/share/vm/gc/parallel/psPromotionManager.cpp @@ -430,7 +430,6 @@ oop PSPromotionManager::oop_promotion_failed(oop obj, markOop obj_mark) { obj = obj->forwardee(); } -#ifndef PRODUCT if (TraceScavenge) { gclog_or_tty->print_cr("{%s %s " PTR_FORMAT " (%d)}", "promotion-failure", @@ -438,7 +437,6 @@ oop PSPromotionManager::oop_promotion_failed(oop obj, markOop obj_mark) { p2i(obj), obj->size()); } -#endif return obj; } diff --git a/hotspot/src/share/vm/gc/parallel/psPromotionManager.inline.hpp b/hotspot/src/share/vm/gc/parallel/psPromotionManager.inline.hpp index 2e3b4a28b04..ef3aa4732e0 100644 --- a/hotspot/src/share/vm/gc/parallel/psPromotionManager.inline.hpp +++ b/hotspot/src/share/vm/gc/parallel/psPromotionManager.inline.hpp @@ -260,7 +260,6 @@ inline oop PSPromotionManager::copy_to_survivor_space(oop o) { new_obj = o->forwardee(); } -#ifndef PRODUCT // This code must come after the CAS test, or it will print incorrect // information. if (TraceScavenge) { @@ -268,7 +267,6 @@ inline oop PSPromotionManager::copy_to_survivor_space(oop o) { should_scavenge(&new_obj) ? "copying" : "tenuring", new_obj->klass()->internal_name(), p2i((void *)o), p2i((void *)new_obj), new_obj->size()); } -#endif return new_obj; } @@ -285,15 +283,13 @@ inline void PSPromotionManager::copy_and_push_safe_barrier(T* p) { ? o->forwardee() : copy_to_survivor_space(o); -#ifndef PRODUCT // This code must come after the CAS test, or it will print incorrect // information. - if (TraceScavenge && o->is_forwarded()) { + if (TraceScavenge && o->is_forwarded()) { gclog_or_tty->print_cr("{%s %s " PTR_FORMAT " -> " PTR_FORMAT " (%d)}", "forwarding", new_obj->klass()->internal_name(), p2i((void *)o), p2i((void *)new_obj), new_obj->size()); } -#endif oopDesc::encode_store_heap_oop_not_null(p, new_obj); diff --git a/hotspot/src/share/vm/gc/parallel/psScavenge.inline.hpp b/hotspot/src/share/vm/gc/parallel/psScavenge.inline.hpp index 1881e01a342..0a4d2ef94ed 100644 --- a/hotspot/src/share/vm/gc/parallel/psScavenge.inline.hpp +++ b/hotspot/src/share/vm/gc/parallel/psScavenge.inline.hpp @@ -138,7 +138,6 @@ class PSScavengeKlassClosure: public KlassClosure { // If the klass has not been dirtied we know that there's // no references into the young gen and we can skip it. -#ifndef PRODUCT if (TraceScavenge) { ResourceMark rm; gclog_or_tty->print_cr("PSScavengeKlassClosure::do_klass " PTR_FORMAT ", %s, dirty: %s", @@ -146,7 +145,6 @@ class PSScavengeKlassClosure: public KlassClosure { klass->external_name(), klass->has_modified_oops() ? "true" : "false"); } -#endif if (klass->has_modified_oops()) { // Clean the klass since we're going to scavenge all the metadata. diff --git a/hotspot/src/share/vm/gc/serial/defNewGeneration.cpp b/hotspot/src/share/vm/gc/serial/defNewGeneration.cpp index fc4181ef307..54243df64f9 100644 --- a/hotspot/src/share/vm/gc/serial/defNewGeneration.cpp +++ b/hotspot/src/share/vm/gc/serial/defNewGeneration.cpp @@ -134,7 +134,6 @@ void FastScanClosure::do_oop(oop* p) { FastScanClosure::do_oop_work(p); } void FastScanClosure::do_oop(narrowOop* p) { FastScanClosure::do_oop_work(p); } void KlassScanClosure::do_klass(Klass* klass) { -#ifndef PRODUCT if (TraceScavenge) { ResourceMark rm; gclog_or_tty->print_cr("KlassScanClosure::do_klass " PTR_FORMAT ", %s, dirty: %s", @@ -142,7 +141,6 @@ void KlassScanClosure::do_klass(Klass* klass) { klass->external_name(), klass->has_modified_oops() ? "true" : "false"); } -#endif // If the klass has not been dirtied we know that there's // no references into the young gen and we can skip it. diff --git a/hotspot/src/share/vm/gc/shared/gcTrace.cpp b/hotspot/src/share/vm/gc/shared/gcTrace.cpp index becf4b5b867..10da185584e 100644 --- a/hotspot/src/share/vm/gc/shared/gcTrace.cpp +++ b/hotspot/src/share/vm/gc/shared/gcTrace.cpp @@ -212,4 +212,34 @@ void G1NewTracer::report_evacuation_statistics(const G1EvacSummary& young_summar send_old_evacuation_statistics(old_summary); } +void G1NewTracer::report_basic_ihop_statistics(size_t threshold, + size_t target_ccupancy, + size_t current_occupancy, + size_t last_allocation_size, + double last_allocation_duration, + double last_marking_length) { + send_basic_ihop_statistics(threshold, + target_ccupancy, + current_occupancy, + last_allocation_size, + last_allocation_duration, + last_marking_length); +} + +void G1NewTracer::report_adaptive_ihop_statistics(size_t threshold, + size_t internal_target_occupancy, + size_t current_occupancy, + size_t additional_buffer_size, + double predicted_allocation_rate, + double predicted_marking_length, + bool prediction_active) { + send_adaptive_ihop_statistics(threshold, + internal_target_occupancy, + additional_buffer_size, + current_occupancy, + predicted_allocation_rate, + predicted_marking_length, + prediction_active); +} + #endif diff --git a/hotspot/src/share/vm/gc/shared/gcTrace.hpp b/hotspot/src/share/vm/gc/shared/gcTrace.hpp index e0db92dbe8f..bcacbb9b12b 100644 --- a/hotspot/src/share/vm/gc/shared/gcTrace.hpp +++ b/hotspot/src/share/vm/gc/shared/gcTrace.hpp @@ -253,6 +253,20 @@ class G1NewTracer : public YoungGCTracer { void report_evacuation_failed(EvacuationFailedInfo& ef_info); void report_evacuation_statistics(const G1EvacSummary& young_summary, const G1EvacSummary& old_summary) const; + + void report_basic_ihop_statistics(size_t threshold, + size_t target_occupancy, + size_t current_occupancy, + size_t last_allocation_size, + double last_allocation_duration, + double last_marking_length); + void report_adaptive_ihop_statistics(size_t threshold, + size_t internal_target_occupancy, + size_t current_occupancy, + size_t additional_buffer_size, + double predicted_allocation_rate, + double predicted_marking_length, + bool prediction_active); private: void send_g1_young_gc_event(); void send_evacuation_info_event(EvacuationInfo* info); @@ -260,6 +274,20 @@ class G1NewTracer : public YoungGCTracer { void send_young_evacuation_statistics(const G1EvacSummary& summary) const; void send_old_evacuation_statistics(const G1EvacSummary& summary) const; + + void send_basic_ihop_statistics(size_t threshold, + size_t target_occupancy, + size_t current_occupancy, + size_t last_allocation_size, + double last_allocation_duration, + double last_marking_length); + void send_adaptive_ihop_statistics(size_t threshold, + size_t internal_target_occupancy, + size_t current_occupancy, + size_t additional_buffer_size, + double predicted_allocation_rate, + double predicted_marking_length, + bool prediction_active); }; #endif diff --git a/hotspot/src/share/vm/gc/shared/gcTraceSend.cpp b/hotspot/src/share/vm/gc/shared/gcTraceSend.cpp index da72a232d78..5c6b5ad2284 100644 --- a/hotspot/src/share/vm/gc/shared/gcTraceSend.cpp +++ b/hotspot/src/share/vm/gc/shared/gcTraceSend.cpp @@ -35,6 +35,7 @@ #if INCLUDE_ALL_GCS #include "gc/g1/evacuationInfo.hpp" #include "gc/g1/g1YCTypes.hpp" +#include "tracefiles/traceEventClasses.hpp" #endif // All GC dependencies against the trace framework is contained within this file. @@ -265,6 +266,50 @@ void G1NewTracer::send_old_evacuation_statistics(const G1EvacSummary& summary) c old_evt.commit(); } } + +void G1NewTracer::send_basic_ihop_statistics(size_t threshold, + size_t target_occupancy, + size_t current_occupancy, + size_t last_allocation_size, + double last_allocation_duration, + double last_marking_length) { + EventGCG1BasicIHOP evt; + if (evt.should_commit()) { + evt.set_gcId(GCId::current()); + evt.set_threshold(threshold); + evt.set_targetOccupancy(target_occupancy); + evt.set_thresholdPercentage(target_occupancy > 0 ? threshold * 100.0 / target_occupancy : 0.0); + evt.set_currentOccupancy(current_occupancy); + evt.set_lastAllocationSize(last_allocation_size); + evt.set_lastAllocationDuration(last_allocation_duration); + evt.set_lastAllocationRate(last_allocation_duration != 0.0 ? last_allocation_size / last_allocation_duration : 0.0); + evt.set_lastMarkingLength(last_marking_length); + evt.commit(); + } +} + +void G1NewTracer::send_adaptive_ihop_statistics(size_t threshold, + size_t internal_target_occupancy, + size_t current_occupancy, + size_t additional_buffer_size, + double predicted_allocation_rate, + double predicted_marking_length, + bool prediction_active) { + EventGCG1AdaptiveIHOP evt; + if (evt.should_commit()) { + evt.set_gcId(GCId::current()); + evt.set_threshold(threshold); + evt.set_thresholdPercentage(internal_target_occupancy > 0 ? threshold * 100.0 / internal_target_occupancy : 0.0); + evt.set_internalTargetOccupancy(internal_target_occupancy); + evt.set_currentOccupancy(current_occupancy); + evt.set_additionalBufferSize(additional_buffer_size); + evt.set_predictedAllocationRate(predicted_allocation_rate); + evt.set_predictedMarkingLength(predicted_marking_length); + evt.set_predictionActive(prediction_active); + evt.commit(); + } +} + #endif static TraceStructVirtualSpace to_trace_struct(const VirtualSpaceSummary& summary) { diff --git a/hotspot/src/share/vm/interpreter/interpreter.cpp b/hotspot/src/share/vm/interpreter/interpreter.cpp index 1f4e08bcd43..0897270af93 100644 --- a/hotspot/src/share/vm/interpreter/interpreter.cpp +++ b/hotspot/src/share/vm/interpreter/interpreter.cpp @@ -300,7 +300,10 @@ AbstractInterpreter::MethodKind AbstractInterpreter::method_kind(methodHandle m) } // Accessor method? - if (m->is_accessor()) { + if (m->is_getter()) { + // TODO: We should have used ::is_accessor above, but fast accessors in Zero expect only getters. + // See CppInterpreter::accessor_entry in cppInterpreter_zero.cpp. This should be fixed in Zero, + // then the call above updated to ::is_accessor assert(m->size_of_parameters() == 1, "fast code for accessors assumes parameter size = 1"); return accessor; } diff --git a/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.cpp b/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.cpp index 3a46b2281fd..df84301f5bf 100644 --- a/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.cpp +++ b/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.cpp @@ -71,62 +71,97 @@ Method* getMethodFromHotSpotMethod(oop hotspot_method) { return CompilerToVM::asMethod(hotspot_method); } -VMReg getVMRegFromLocation(oop location, int total_frame_size) { - oop reg = code_Location::reg(location); +VMReg getVMRegFromLocation(Handle location, int total_frame_size, TRAPS) { + if (location.is_null()) { + THROW_NULL(vmSymbols::java_lang_NullPointerException()); + } + + Handle reg = code_Location::reg(location); jint offset = code_Location::offset(location); - if (reg != NULL) { + if (reg.not_null()) { // register jint number = code_Register::number(reg); - VMReg vmReg = CodeInstaller::get_hotspot_reg(number); - assert(offset % 4 == 0, "must be aligned"); - return vmReg->next(offset / 4); + VMReg vmReg = CodeInstaller::get_hotspot_reg(number, CHECK_NULL); + if (offset % 4 == 0) { + return vmReg->next(offset / 4); + } else { + JVMCI_ERROR_NULL("unaligned subregister offset %d in oop map", offset); + } } else { // stack slot - assert(offset % 4 == 0, "must be aligned"); - return VMRegImpl::stack2reg(offset / 4); + if (offset % 4 == 0) { + return VMRegImpl::stack2reg(offset / 4); + } else { + JVMCI_ERROR_NULL("unaligned stack offset %d in oop map", offset); + } } } // creates a HotSpot oop map out of the byte arrays provided by DebugInfo -OopMap* CodeInstaller::create_oop_map(oop debug_info) { - oop reference_map = DebugInfo::referenceMap(debug_info); +OopMap* CodeInstaller::create_oop_map(Handle debug_info, TRAPS) { + Handle reference_map = DebugInfo::referenceMap(debug_info); + if (reference_map.is_null()) { + THROW_NULL(vmSymbols::java_lang_NullPointerException()); + } + if (!reference_map->is_a(HotSpotReferenceMap::klass())) { + JVMCI_ERROR_NULL("unknown reference map: %s", reference_map->klass()->signature_name()); + } if (HotSpotReferenceMap::maxRegisterSize(reference_map) > 16) { _has_wide_vector = true; } OopMap* map = new OopMap(_total_frame_size, _parameter_count); - objArrayOop objects = HotSpotReferenceMap::objects(reference_map); - objArrayOop derivedBase = HotSpotReferenceMap::derivedBase(reference_map); - typeArrayOop sizeInBytes = HotSpotReferenceMap::sizeInBytes(reference_map); + objArrayHandle objects = HotSpotReferenceMap::objects(reference_map); + objArrayHandle derivedBase = HotSpotReferenceMap::derivedBase(reference_map); + typeArrayHandle sizeInBytes = HotSpotReferenceMap::sizeInBytes(reference_map); + if (objects.is_null() || derivedBase.is_null() || sizeInBytes.is_null()) { + THROW_NULL(vmSymbols::java_lang_NullPointerException()); + } + if (objects->length() != derivedBase->length() || objects->length() != sizeInBytes->length()) { + JVMCI_ERROR_NULL("arrays in reference map have different sizes: %d %d %d", objects->length(), derivedBase->length(), sizeInBytes->length()); + } for (int i = 0; i < objects->length(); i++) { - oop location = objects->obj_at(i); - oop baseLocation = derivedBase->obj_at(i); + Handle location = objects->obj_at(i); + Handle baseLocation = derivedBase->obj_at(i); int bytes = sizeInBytes->int_at(i); - VMReg vmReg = getVMRegFromLocation(location, _total_frame_size); - if (baseLocation != NULL) { + VMReg vmReg = getVMRegFromLocation(location, _total_frame_size, CHECK_NULL); + if (baseLocation.not_null()) { // derived oop - assert(bytes == 8, "derived oop can't be compressed"); - VMReg baseReg = getVMRegFromLocation(baseLocation, _total_frame_size); - map->set_derived_oop(vmReg, baseReg); +#ifdef _LP64 + if (bytes == 8) { +#else + if (bytes == 4) { +#endif + VMReg baseReg = getVMRegFromLocation(baseLocation, _total_frame_size, CHECK_NULL); + map->set_derived_oop(vmReg, baseReg); + } else { + JVMCI_ERROR_NULL("invalid derived oop size in ReferenceMap: %d", bytes); + } +#ifdef _LP64 } else if (bytes == 8) { // wide oop map->set_oop(vmReg); - } else { + } else if (bytes == 4) { // narrow oop - assert(bytes == 4, "wrong size"); map->set_narrowoop(vmReg); +#else + } else if (bytes == 4) { + map->set_oop(vmReg); +#endif + } else { + JVMCI_ERROR_NULL("invalid oop size in ReferenceMap: %d", bytes); } } - oop callee_save_info = (oop) DebugInfo::calleeSaveInfo(debug_info); - if (callee_save_info != NULL) { - objArrayOop registers = RegisterSaveLayout::registers(callee_save_info); - typeArrayOop slots = RegisterSaveLayout::slots(callee_save_info); + Handle callee_save_info = (oop) DebugInfo::calleeSaveInfo(debug_info); + if (callee_save_info.not_null()) { + objArrayHandle registers = RegisterSaveLayout::registers(callee_save_info); + typeArrayHandle slots = RegisterSaveLayout::slots(callee_save_info); for (jint i = 0; i < slots->length(); i++) { - oop jvmci_reg = registers->obj_at(i); + Handle jvmci_reg = registers->obj_at(i); jint jvmci_reg_number = code_Register::number(jvmci_reg); - VMReg hotspot_reg = CodeInstaller::get_hotspot_reg(jvmci_reg_number); + VMReg hotspot_reg = CodeInstaller::get_hotspot_reg(jvmci_reg_number, CHECK_NULL); // HotSpot stack slots are 4 bytes jint jvmci_slot = slots->int_at(i); jint hotspot_slot = jvmci_slot * VMRegImpl::slots_per_word; @@ -142,7 +177,7 @@ OopMap* CodeInstaller::create_oop_map(oop debug_info) { return map; } -Metadata* CodeInstaller::record_metadata_reference(Handle& constant) { +Metadata* CodeInstaller::record_metadata_reference(Handle constant, TRAPS) { oop obj = HotSpotMetaspaceConstantImpl::metaspaceObject(constant); if (obj->is_a(HotSpotResolvedObjectTypeImpl::klass())) { Klass* klass = java_lang_Class::as_Klass(HotSpotResolvedObjectTypeImpl::javaClass(obj)); @@ -157,16 +192,18 @@ Metadata* CodeInstaller::record_metadata_reference(Handle& constant) { TRACE_jvmci_3("metadata[%d of %d] = %s", index, _oop_recorder->metadata_count(), method->name()->as_C_string()); return method; } else { - fatal("unexpected metadata reference for constant of type %s", obj->klass()->name()->as_C_string()); - return NULL; + JVMCI_ERROR_NULL("unexpected metadata reference for constant of type %s", obj->klass()->signature_name()); } } #ifdef _LP64 -narrowKlass CodeInstaller::record_narrow_metadata_reference(Handle& constant) { +narrowKlass CodeInstaller::record_narrow_metadata_reference(Handle constant, TRAPS) { oop obj = HotSpotMetaspaceConstantImpl::metaspaceObject(constant); assert(HotSpotMetaspaceConstantImpl::compressed(constant), "unexpected uncompressed pointer"); - assert(obj->is_a(HotSpotResolvedObjectTypeImpl::klass()), "unexpected compressed pointer of type %s", obj->klass()->name()->as_C_string()); + + if (!obj->is_a(HotSpotResolvedObjectTypeImpl::klass())) { + JVMCI_ERROR_0("unexpected compressed pointer of type %s", obj->klass()->signature_name()); + } Klass* klass = java_lang_Class::as_Klass(HotSpotResolvedObjectTypeImpl::javaClass(obj)); int index = _oop_recorder->find_index(klass); @@ -175,9 +212,9 @@ narrowKlass CodeInstaller::record_narrow_metadata_reference(Handle& constant) { } #endif -Location::Type CodeInstaller::get_oop_type(oop value) { - oop lirKind = Value::lirKind(value); - oop platformKind = LIRKind::platformKind(lirKind); +Location::Type CodeInstaller::get_oop_type(Handle value) { + Handle lirKind = Value::lirKind(value); + Handle platformKind = LIRKind::platformKind(lirKind); assert(LIRKind::referenceMask(lirKind) == 1, "unexpected referenceMask"); if (platformKind == word_kind()) { @@ -187,24 +224,29 @@ Location::Type CodeInstaller::get_oop_type(oop value) { } } -ScopeValue* CodeInstaller::get_scope_value(oop value, BasicType type, GrowableArray* objects, ScopeValue* &second) { +ScopeValue* CodeInstaller::get_scope_value(Handle value, BasicType type, GrowableArray* objects, ScopeValue* &second, TRAPS) { second = NULL; - if (value == Value::ILLEGAL()) { - assert(type == T_ILLEGAL, "expected legal value"); + if (value.is_null()) { + THROW_NULL(vmSymbols::java_lang_NullPointerException()); + } else if (value == Value::ILLEGAL()) { + if (type != T_ILLEGAL) { + JVMCI_ERROR_NULL("unexpected illegal value, expected %s", basictype_to_str(type)); + } return _illegal_value; } else if (value->is_a(RegisterValue::klass())) { - oop reg = RegisterValue::reg(value); + Handle reg = RegisterValue::reg(value); jint number = code_Register::number(reg); - VMReg hotspotRegister = get_hotspot_reg(number); + VMReg hotspotRegister = get_hotspot_reg(number, CHECK_NULL); if (is_general_purpose_reg(hotspotRegister)) { Location::Type locationType; if (type == T_OBJECT) { locationType = get_oop_type(value); } else if (type == T_LONG) { locationType = Location::lng; - } else { - assert(type == T_INT || type == T_FLOAT || type == T_SHORT || type == T_CHAR || type == T_BYTE || type == T_BOOLEAN, "unexpected type in cpu register"); + } else if (type == T_INT || type == T_FLOAT || type == T_SHORT || type == T_CHAR || type == T_BYTE || type == T_BOOLEAN) { locationType = Location::int_in_long; + } else { + JVMCI_ERROR_NULL("unexpected type %s in cpu register", basictype_to_str(type)); } ScopeValue* value = new LocationValue(Location::new_reg_loc(locationType, hotspotRegister)); if (type == T_LONG) { @@ -212,13 +254,14 @@ ScopeValue* CodeInstaller::get_scope_value(oop value, BasicType type, GrowableAr } return value; } else { - assert(type == T_FLOAT || type == T_DOUBLE, "only float and double expected in xmm register"); Location::Type locationType; if (type == T_FLOAT) { // this seems weird, but the same value is used in c1_LinearScan locationType = Location::normal; - } else { + } else if (type == T_DOUBLE) { locationType = Location::dbl; + } else { + JVMCI_ERROR_NULL("unexpected type %s in floating point register", basictype_to_str(type)); } ScopeValue* value = new LocationValue(Location::new_reg_loc(locationType, hotspotRegister)); if (type == T_DOUBLE) { @@ -239,9 +282,10 @@ ScopeValue* CodeInstaller::get_scope_value(oop value, BasicType type, GrowableAr locationType = Location::lng; } else if (type == T_DOUBLE) { locationType = Location::dbl; - } else { - assert(type == T_INT || type == T_FLOAT || type == T_SHORT || type == T_CHAR || type == T_BYTE || type == T_BOOLEAN, "unexpected type in stack slot"); + } else if (type == T_INT || type == T_FLOAT || type == T_SHORT || type == T_CHAR || type == T_BYTE || type == T_BOOLEAN) { locationType = Location::normal; + } else { + JVMCI_ERROR_NULL("unexpected type %s in stack slot", basictype_to_str(type)); } ScopeValue* value = new LocationValue(Location::new_stk_loc(locationType, offset)); if (type == T_DOUBLE || type == T_LONG) { @@ -254,7 +298,10 @@ ScopeValue* CodeInstaller::get_scope_value(oop value, BasicType type, GrowableAr jlong prim = PrimitiveConstant::primitive(value); return new ConstantLongValue(prim); } else { - assert(type == JVMCIRuntime::kindToBasicType(JavaKind::typeChar(PrimitiveConstant::kind(value))), "primitive constant type doesn't match"); + BasicType constantType = JVMCIRuntime::kindToBasicType(PrimitiveConstant::kind(value), CHECK_NULL); + if (type != constantType) { + JVMCI_ERROR_NULL("primitive constant type doesn't match, expected %s but got %s", basictype_to_str(type), basictype_to_str(constantType)); + } if (type == T_INT || type == T_FLOAT) { jint prim = (jint)PrimitiveConstant::primitive(value); switch (prim) { @@ -264,53 +311,63 @@ ScopeValue* CodeInstaller::get_scope_value(oop value, BasicType type, GrowableAr case 2: return _int_2_scope_value; default: return new ConstantIntValue(prim); } - } else { - assert(type == T_LONG || type == T_DOUBLE, "unexpected primitive constant type"); + } else if (type == T_LONG || type == T_DOUBLE) { jlong prim = PrimitiveConstant::primitive(value); second = _int_1_scope_value; return new ConstantLongValue(prim); + } else { + JVMCI_ERROR_NULL("unexpected primitive constant type %s", basictype_to_str(type)); } } - } else { - assert(type == T_OBJECT, "unexpected object constant"); - if (value->is_a(NullConstant::klass()) || value->is_a(HotSpotCompressedNullConstant::klass())) { + } else if (value->is_a(NullConstant::klass()) || value->is_a(HotSpotCompressedNullConstant::klass())) { + if (type == T_OBJECT) { return _oop_null_scope_value; } else { - assert(value->is_a(HotSpotObjectConstantImpl::klass()), "unexpected constant type"); + JVMCI_ERROR_NULL("unexpected null constant, expected %s", basictype_to_str(type)); + } + } else if (value->is_a(HotSpotObjectConstantImpl::klass())) { + if (type == T_OBJECT) { oop obj = HotSpotObjectConstantImpl::object(value); - assert(obj != NULL, "null value must be in NullConstant"); + if (obj == NULL) { + JVMCI_ERROR_NULL("null value must be in NullConstant"); + } return new ConstantOopWriteValue(JNIHandles::make_local(obj)); + } else { + JVMCI_ERROR_NULL("unexpected object constant, expected %s", basictype_to_str(type)); } } } else if (value->is_a(VirtualObject::klass())) { - assert(type == T_OBJECT, "unexpected virtual object"); - int id = VirtualObject::id(value); - ScopeValue* object = objects->at(id); - assert(object != NULL, "missing value"); - return object; - } else { - value->klass()->print(); - value->print(); + if (type == T_OBJECT) { + int id = VirtualObject::id(value); + if (0 <= id && id < objects->length()) { + ScopeValue* object = objects->at(id); + if (object != NULL) { + return object; + } + } + JVMCI_ERROR_NULL("unknown virtual object id %d", id); + } else { + JVMCI_ERROR_NULL("unexpected virtual object, expected %s", basictype_to_str(type)); + } } - ShouldNotReachHere(); - return NULL; + + JVMCI_ERROR_NULL("unexpected value in scope: %s", value->klass()->signature_name()) } -void CodeInstaller::record_object_value(ObjectValue* sv, oop value, GrowableArray* objects) { - oop type = VirtualObject::type(value); +void CodeInstaller::record_object_value(ObjectValue* sv, Handle value, GrowableArray* objects, TRAPS) { + Handle type = VirtualObject::type(value); int id = VirtualObject::id(value); oop javaMirror = HotSpotResolvedObjectTypeImpl::javaClass(type); Klass* klass = java_lang_Class::as_Klass(javaMirror); bool isLongArray = klass == Universe::longArrayKlassObj(); - objArrayOop values = VirtualObject::values(value); - objArrayOop slotKinds = VirtualObject::slotKinds(value); + objArrayHandle values = VirtualObject::values(value); + objArrayHandle slotKinds = VirtualObject::slotKinds(value); for (jint i = 0; i < values->length(); i++) { ScopeValue* cur_second = NULL; - oop object = values->obj_at(i); - oop kind = slotKinds->obj_at(i); - BasicType type = JVMCIRuntime::kindToBasicType(JavaKind::typeChar(kind)); - ScopeValue* value = get_scope_value(object, type, objects, cur_second); + Handle object = values->obj_at(i); + BasicType type = JVMCIRuntime::kindToBasicType(slotKinds->obj_at(i), CHECK); + ScopeValue* value = get_scope_value(object, type, objects, cur_second, CHECK); if (isLongArray && cur_second == NULL) { // we're trying to put ints into a long array... this isn't really valid, but it's used for some optimizations. @@ -326,14 +383,19 @@ void CodeInstaller::record_object_value(ObjectValue* sv, oop value, GrowableArra } } -MonitorValue* CodeInstaller::get_monitor_value(oop value, GrowableArray* objects) { - guarantee(value->is_a(StackLockValue::klass()), "Monitors must be of type StackLockValue"); +MonitorValue* CodeInstaller::get_monitor_value(Handle value, GrowableArray* objects, TRAPS) { + if (value.is_null()) { + THROW_NULL(vmSymbols::java_lang_NullPointerException()); + } + if (!value->is_a(StackLockValue::klass())) { + JVMCI_ERROR_NULL("Monitors must be of type StackLockValue, got %s", value->klass()->signature_name()); + } ScopeValue* second = NULL; - ScopeValue* owner_value = get_scope_value(StackLockValue::owner(value), T_OBJECT, objects, second); + ScopeValue* owner_value = get_scope_value(StackLockValue::owner(value), T_OBJECT, objects, second, CHECK_NULL); assert(second == NULL, "monitor cannot occupy two stack slots"); - ScopeValue* lock_data_value = get_scope_value(StackLockValue::slot(value), T_LONG, objects, second); + ScopeValue* lock_data_value = get_scope_value(StackLockValue::slot(value), T_LONG, objects, second, CHECK_NULL); assert(second == lock_data_value, "monitor is LONG value that occupies two stack slots"); assert(lock_data_value->is_location(), "invalid monitor location"); Location lock_data_loc = ((LocationValue*)lock_data_value)->location(); @@ -346,7 +408,7 @@ MonitorValue* CodeInstaller::get_monitor_value(oop value, GrowableArrayis_Compiler_thread() ? thread->as_CompilerThread() : NULL; _oop_recorder = recorder; @@ -368,8 +430,7 @@ void CodeInstaller::initialize_dependencies(oop compiled_code, OopRecorder* reco } else if (assumption->klass() == Assumptions_CallSiteTargetValue::klass()) { assumption_CallSiteTargetValue(assumption); } else { - assumption->print(); - fatal("unexpected Assumption subclass"); + JVMCI_ERROR("unexpected Assumption subclass %s", assumption->klass()->signature_name()); } } } @@ -414,18 +475,19 @@ void RelocBuffer::ensure_size(size_t bytes) { _size = bytes; } -JVMCIEnv::CodeInstallResult CodeInstaller::gather_metadata(Handle target, Handle& compiled_code, CodeMetadata& metadata) { +JVMCIEnv::CodeInstallResult CodeInstaller::gather_metadata(Handle target, Handle compiled_code, CodeMetadata& metadata, TRAPS) { CodeBuffer buffer("JVMCI Compiler CodeBuffer for Metadata"); jobject compiled_code_obj = JNIHandles::make_local(compiled_code()); - initialize_dependencies(JNIHandles::resolve(compiled_code_obj), NULL); + initialize_dependencies(JNIHandles::resolve(compiled_code_obj), NULL, CHECK_OK); // Get instructions and constants CodeSections early because we need it. _instructions = buffer.insts(); _constants = buffer.consts(); - initialize_fields(target(), JNIHandles::resolve(compiled_code_obj)); - if (!initialize_buffer(buffer)) { - return JVMCIEnv::code_too_large; + initialize_fields(target(), JNIHandles::resolve(compiled_code_obj), CHECK_OK); + JVMCIEnv::CodeInstallResult result = initialize_buffer(buffer, CHECK_OK); + if (result != JVMCIEnv::ok) { + return result; } process_exception_handlers(); @@ -446,18 +508,18 @@ JVMCIEnv::CodeInstallResult CodeInstaller::gather_metadata(Handle target, Handle } // constructor used to create a method -JVMCIEnv::CodeInstallResult CodeInstaller::install(JVMCICompiler* compiler, Handle target, Handle& compiled_code, CodeBlob*& cb, Handle installed_code, Handle speculation_log) { +JVMCIEnv::CodeInstallResult CodeInstaller::install(JVMCICompiler* compiler, Handle target, Handle compiled_code, CodeBlob*& cb, Handle installed_code, Handle speculation_log, TRAPS) { CodeBuffer buffer("JVMCI Compiler CodeBuffer"); jobject compiled_code_obj = JNIHandles::make_local(compiled_code()); OopRecorder* recorder = new OopRecorder(&_arena, true); - initialize_dependencies(JNIHandles::resolve(compiled_code_obj), recorder); + initialize_dependencies(JNIHandles::resolve(compiled_code_obj), recorder, CHECK_OK); // Get instructions and constants CodeSections early because we need it. _instructions = buffer.insts(); _constants = buffer.consts(); - initialize_fields(target(), JNIHandles::resolve(compiled_code_obj)); - JVMCIEnv::CodeInstallResult result = initialize_buffer(buffer); + initialize_fields(target(), JNIHandles::resolve(compiled_code_obj), CHECK_OK); + JVMCIEnv::CodeInstallResult result = initialize_buffer(buffer, CHECK_OK); if (result != JVMCIEnv::ok) { return result; } @@ -500,7 +562,7 @@ JVMCIEnv::CodeInstallResult CodeInstaller::install(JVMCICompiler* compiler, Hand return result; } -void CodeInstaller::initialize_fields(oop target, oop compiled_code) { +void CodeInstaller::initialize_fields(oop target, oop compiled_code, TRAPS) { if (compiled_code->is_a(HotSpotCompiledNmethod::klass())) { Handle hotspotJavaMethod = HotSpotCompiledNmethod::method(compiled_code); methodHandle method = getMethodFromHotSpotMethod(hotspotJavaMethod()); @@ -521,7 +583,9 @@ void CodeInstaller::initialize_fields(oop target, oop compiled_code) { // Pre-calculate the constants section size. This is required for PC-relative addressing. _data_section_handle = JNIHandles::make_local(HotSpotCompiledCode::dataSection(compiled_code)); - guarantee(HotSpotCompiledCode::dataSectionAlignment(compiled_code) <= _constants->alignment(), "Alignment inside constants section is restricted by alignment of section begin"); + if ((_constants->alignment() % HotSpotCompiledCode::dataSectionAlignment(compiled_code)) != 0) { + JVMCI_ERROR("invalid data section alignment: %d", HotSpotCompiledCode::dataSectionAlignment(compiled_code)); + } _constants_size = data_section()->length(); _data_section_patches_handle = JNIHandles::make_local(HotSpotCompiledCode::dataSectionPatches(compiled_code)); @@ -538,16 +602,18 @@ void CodeInstaller::initialize_fields(oop target, oop compiled_code) { _word_kind_handle = JNIHandles::make_local(Architecture::wordKind(arch)); } -int CodeInstaller::estimate_stubs_size() { +int CodeInstaller::estimate_stubs_size(TRAPS) { // Estimate the number of static call stubs that might be emitted. int static_call_stubs = 0; objArrayOop sites = this->sites(); for (int i = 0; i < sites->length(); i++) { oop site = sites->obj_at(i); - if (site->is_a(CompilationResult_Mark::klass())) { + if (site != NULL && site->is_a(CompilationResult_Mark::klass())) { oop id_obj = CompilationResult_Mark::id(site); if (id_obj != NULL) { - assert(java_lang_boxing_object::is_instance(id_obj, T_INT), "Integer id expected"); + if (!java_lang_boxing_object::is_instance(id_obj, T_INT)) { + JVMCI_ERROR_0("expected Integer id, got %s", id_obj->klass()->signature_name()); + } jint id = id_obj->int_field(java_lang_boxing_object::value_offset_in_bytes(T_INT)); if (id == INVOKESTATIC || id == INVOKESPECIAL) { static_call_stubs++; @@ -559,7 +625,7 @@ int CodeInstaller::estimate_stubs_size() { } // perform data and call relocation on the CodeBuffer -JVMCIEnv::CodeInstallResult CodeInstaller::initialize_buffer(CodeBuffer& buffer) { +JVMCIEnv::CodeInstallResult CodeInstaller::initialize_buffer(CodeBuffer& buffer, TRAPS) { HandleMark hm; objArrayHandle sites = this->sites(); int locs_buffer_size = sites->length() * (relocInfo::length_limit + sizeof(relocInfo)); @@ -568,7 +634,7 @@ JVMCIEnv::CodeInstallResult CodeInstaller::initialize_buffer(CodeBuffer& buffer) // stubs. Stubs have extra relocs but they are managed by the stub // section itself so they don't need to be accounted for in the // locs_buffer above. - int stubs_size = estimate_stubs_size(); + int stubs_size = estimate_stubs_size(CHECK_OK); int total_size = round_to(_code_size, buffer.insts()->alignment()) + round_to(_constants_size, buffer.consts()->alignment()) + round_to(stubs_size, buffer.stubs()->alignment()); if (total_size > JVMCINMethodSizeLimit) { @@ -600,19 +666,30 @@ JVMCIEnv::CodeInstallResult CodeInstaller::initialize_buffer(CodeBuffer& buffer) for (int i = 0; i < data_section_patches()->length(); i++) { Handle patch = data_section_patches()->obj_at(i); + if (patch.is_null()) { + THROW_(vmSymbols::java_lang_NullPointerException(), JVMCIEnv::ok); + } Handle reference = CompilationResult_DataPatch::reference(patch); - assert(reference->is_a(CompilationResult_ConstantReference::klass()), "patch in data section must be a ConstantReference"); + if (reference.is_null()) { + THROW_(vmSymbols::java_lang_NullPointerException(), JVMCIEnv::ok); + } + if (!reference->is_a(CompilationResult_ConstantReference::klass())) { + JVMCI_ERROR_OK("invalid patch in data section: %s", reference->klass()->signature_name()); + } Handle constant = CompilationResult_ConstantReference::constant(reference); + if (constant.is_null()) { + THROW_(vmSymbols::java_lang_NullPointerException(), JVMCIEnv::ok); + } address dest = _constants->start() + CompilationResult_Site::pcOffset(patch); if (constant->is_a(HotSpotMetaspaceConstantImpl::klass())) { if (HotSpotMetaspaceConstantImpl::compressed(constant)) { #ifdef _LP64 - *((narrowKlass*) dest) = record_narrow_metadata_reference(constant); + *((narrowKlass*) dest) = record_narrow_metadata_reference(constant, CHECK_OK); #else - fatal("unexpected compressed Klass* in 32-bit mode"); + JVMCI_ERROR_OK("unexpected compressed Klass* in 32-bit mode"); #endif } else { - *((Metadata**) dest) = record_metadata_reference(constant); + *((Metadata**) dest) = record_metadata_reference(constant, CHECK_OK); } } else if (constant->is_a(HotSpotObjectConstantImpl::klass())) { Handle obj = HotSpotObjectConstantImpl::object(constant); @@ -623,48 +700,49 @@ JVMCIEnv::CodeInstallResult CodeInstaller::initialize_buffer(CodeBuffer& buffer) #ifdef _LP64 _constants->relocate(dest, oop_Relocation::spec(oop_index), relocInfo::narrow_oop_in_const); #else - fatal("unexpected compressed oop in 32-bit mode"); + JVMCI_ERROR_OK("unexpected compressed oop in 32-bit mode"); #endif } else { _constants->relocate(dest, oop_Relocation::spec(oop_index)); } } else { - ShouldNotReachHere(); + JVMCI_ERROR_OK("invalid constant in data section: %s", constant->klass()->signature_name()); } } jint last_pc_offset = -1; for (int i = 0; i < sites->length(); i++) { - { - No_Safepoint_Verifier no_safepoint; - oop site = sites->obj_at(i); - jint pc_offset = CompilationResult_Site::pcOffset(site); - - if (site->is_a(CompilationResult_Call::klass())) { - TRACE_jvmci_4("call at %i", pc_offset); - site_Call(buffer, pc_offset, site); - } else if (site->is_a(CompilationResult_Infopoint::klass())) { - // three reasons for infopoints denote actual safepoints - oop reason = CompilationResult_Infopoint::reason(site); - if (InfopointReason::SAFEPOINT() == reason || InfopointReason::CALL() == reason || InfopointReason::IMPLICIT_EXCEPTION() == reason) { - TRACE_jvmci_4("safepoint at %i", pc_offset); - site_Safepoint(buffer, pc_offset, site); - } else { - // if the infopoint is not an actual safepoint, it must have one of the other reasons - // (safeguard against new safepoint types that require handling above) - assert(InfopointReason::METHOD_START() == reason || InfopointReason::METHOD_END() == reason || InfopointReason::LINE_NUMBER() == reason, ""); - site_Infopoint(buffer, pc_offset, site); - } - } else if (site->is_a(CompilationResult_DataPatch::klass())) { - TRACE_jvmci_4("datapatch at %i", pc_offset); - site_DataPatch(buffer, pc_offset, site); - } else if (site->is_a(CompilationResult_Mark::klass())) { - TRACE_jvmci_4("mark at %i", pc_offset); - site_Mark(buffer, pc_offset, site); - } else { - fatal("unexpected Site subclass"); - } - last_pc_offset = pc_offset; + Handle site = sites->obj_at(i); + if (site.is_null()) { + THROW_(vmSymbols::java_lang_NullPointerException(), JVMCIEnv::ok); } + + jint pc_offset = CompilationResult_Site::pcOffset(site); + + if (site->is_a(CompilationResult_Call::klass())) { + TRACE_jvmci_4("call at %i", pc_offset); + site_Call(buffer, pc_offset, site, CHECK_OK); + } else if (site->is_a(CompilationResult_Infopoint::klass())) { + // three reasons for infopoints denote actual safepoints + oop reason = CompilationResult_Infopoint::reason(site); + if (InfopointReason::SAFEPOINT() == reason || InfopointReason::CALL() == reason || InfopointReason::IMPLICIT_EXCEPTION() == reason) { + TRACE_jvmci_4("safepoint at %i", pc_offset); + site_Safepoint(buffer, pc_offset, site, CHECK_OK); + } else if (InfopointReason::METHOD_START() == reason || InfopointReason::METHOD_END() == reason || InfopointReason::LINE_NUMBER() == reason) { + site_Infopoint(buffer, pc_offset, site, CHECK_OK); + } else { + JVMCI_ERROR_OK("unknown infopoint reason at %i", pc_offset); + } + } else if (site->is_a(CompilationResult_DataPatch::klass())) { + TRACE_jvmci_4("datapatch at %i", pc_offset); + site_DataPatch(buffer, pc_offset, site, CHECK_OK); + } else if (site->is_a(CompilationResult_Mark::klass())) { + TRACE_jvmci_4("mark at %i", pc_offset); + site_Mark(buffer, pc_offset, site, CHECK_OK); + } else { + JVMCI_ERROR_OK("unexpected site subclass: %s", site->klass()->signature_name()); + } + last_pc_offset = pc_offset; + if (CodeInstallSafepointChecks && SafepointSynchronize::do_call_back()) { // this is a hacky way to force a safepoint check but nothing else was jumping out at me. ThreadToNativeFromVM ttnfv(JavaThread::current()); @@ -673,7 +751,6 @@ JVMCIEnv::CodeInstallResult CodeInstaller::initialize_buffer(CodeBuffer& buffer) #ifndef PRODUCT if (comments() != NULL) { - No_Safepoint_Verifier no_safepoint; for (int i = 0; i < comments()->length(); i++) { oop comment = comments()->obj_at(i); assert(comment->is_a(HotSpotCompiledCode_Comment::klass()), "cce"); @@ -759,56 +836,61 @@ static bool bytecode_should_reexecute(Bytecodes::Code code) { return true; } -GrowableArray* CodeInstaller::record_virtual_objects(oop debug_info) { - objArrayOop virtualObjects = DebugInfo::virtualObjectMapping(debug_info); - if (virtualObjects == NULL) { +GrowableArray* CodeInstaller::record_virtual_objects(Handle debug_info, TRAPS) { + objArrayHandle virtualObjects = DebugInfo::virtualObjectMapping(debug_info); + if (virtualObjects.is_null()) { return NULL; } GrowableArray* objects = new GrowableArray(virtualObjects->length(), virtualObjects->length(), NULL); // Create the unique ObjectValues for (int i = 0; i < virtualObjects->length(); i++) { - oop value = virtualObjects->obj_at(i); + Handle value = virtualObjects->obj_at(i); int id = VirtualObject::id(value); - oop type = VirtualObject::type(value); + Handle type = VirtualObject::type(value); oop javaMirror = HotSpotResolvedObjectTypeImpl::javaClass(type); ObjectValue* sv = new ObjectValue(id, new ConstantOopWriteValue(JNIHandles::make_local(Thread::current(), javaMirror))); - assert(objects->at(id) == NULL, "once"); + if (id < 0 || id >= objects->length()) { + JVMCI_ERROR_NULL("virtual object id %d out of bounds", id); + } + if (objects->at(id) != NULL) { + JVMCI_ERROR_NULL("duplicate virtual object id %d", id); + } objects->at_put(id, sv); } // All the values which could be referenced by the VirtualObjects // exist, so now describe all the VirtualObjects themselves. for (int i = 0; i < virtualObjects->length(); i++) { - oop value = virtualObjects->obj_at(i); + Handle value = virtualObjects->obj_at(i); int id = VirtualObject::id(value); - record_object_value(objects->at(id)->as_ObjectValue(), value, objects); + record_object_value(objects->at(id)->as_ObjectValue(), value, objects, CHECK_NULL); } _debug_recorder->dump_object_pool(objects); return objects; } -void CodeInstaller::record_scope(jint pc_offset, oop debug_info) { - oop position = DebugInfo::bytecodePosition(debug_info); - if (position == NULL) { +void CodeInstaller::record_scope(jint pc_offset, Handle debug_info, TRAPS) { + Handle position = DebugInfo::bytecodePosition(debug_info); + if (position.is_null()) { // Stubs do not record scope info, just oop maps return; } - GrowableArray* objectMapping = record_virtual_objects(debug_info); - record_scope(pc_offset, position, objectMapping); + GrowableArray* objectMapping = record_virtual_objects(debug_info, CHECK); + record_scope(pc_offset, position, objectMapping, CHECK); } -void CodeInstaller::record_scope(jint pc_offset, oop position, GrowableArray* objects) { - oop frame = NULL; +void CodeInstaller::record_scope(jint pc_offset, Handle position, GrowableArray* objects, TRAPS) { + Handle frame; if (position->is_a(BytecodeFrame::klass())) { frame = position; } - oop caller_frame = BytecodePosition::caller(position); - if (caller_frame != NULL) { - record_scope(pc_offset, caller_frame, objects); + Handle caller_frame = BytecodePosition::caller(position); + if (caller_frame.not_null()) { + record_scope(pc_offset, caller_frame, objects, CHECK); } - oop hotspot_method = BytecodePosition::method(position); - Method* method = getMethodFromHotSpotMethod(hotspot_method); + Handle hotspot_method = BytecodePosition::method(position); + Method* method = getMethodFromHotSpotMethod(hotspot_method()); jint bci = BytecodePosition::bci(position); if (bci == BytecodeFrame::BEFORE_BCI()) { bci = SynchronizationEntryBCI; @@ -817,13 +899,13 @@ void CodeInstaller::record_scope(jint pc_offset, oop position, GrowableArrayname_and_sig_as_C_string()); bool reexecute = false; - if (frame != NULL) { + if (frame.not_null()) { if (bci == SynchronizationEntryBCI){ reexecute = false; } else { Bytecodes::Code code = Bytecodes::java_code_at(method, method->bcp_from(bci)); reexecute = bytecode_should_reexecute(code); - if (frame != NULL) { + if (frame.not_null()) { reexecute = (BytecodeFrame::duringCall(frame) == JNI_FALSE); } } @@ -834,15 +916,22 @@ void CodeInstaller::record_scope(jint pc_offset, oop position, GrowableArraylength(), "unexpected values length"); - assert(local_count + expression_count == slotKinds->length(), "unexpected slotKinds length"); + if (values.is_null() || slotKinds.is_null()) { + THROW(vmSymbols::java_lang_NullPointerException()); + } + if (local_count + expression_count + monitor_count != values->length()) { + JVMCI_ERROR("unexpected values length %d in scope (%d locals, %d expressions, %d monitors)", values->length(), local_count, expression_count, monitor_count); + } + if (local_count + expression_count != slotKinds->length()) { + JVMCI_ERROR("unexpected slotKinds length %d in scope (%d locals, %d expressions)", slotKinds->length(), local_count, expression_count); + } GrowableArray* locals = local_count > 0 ? new GrowableArray (local_count) : NULL; GrowableArray* expressions = expression_count > 0 ? new GrowableArray (expression_count) : NULL; @@ -853,30 +942,30 @@ void CodeInstaller::record_scope(jint pc_offset, oop position, GrowableArraylength(); i++) { ScopeValue* second = NULL; - oop value = values->obj_at(i); + Handle value = values->obj_at(i); if (i < local_count) { - oop kind = slotKinds->obj_at(i); - BasicType type = JVMCIRuntime::kindToBasicType(JavaKind::typeChar(kind)); - ScopeValue* first = get_scope_value(value, type, objects, second); + BasicType type = JVMCIRuntime::kindToBasicType(slotKinds->obj_at(i), CHECK); + ScopeValue* first = get_scope_value(value, type, objects, second, CHECK); if (second != NULL) { locals->append(second); } locals->append(first); } else if (i < local_count + expression_count) { - oop kind = slotKinds->obj_at(i); - BasicType type = JVMCIRuntime::kindToBasicType(JavaKind::typeChar(kind)); - ScopeValue* first = get_scope_value(value, type, objects, second); + BasicType type = JVMCIRuntime::kindToBasicType(slotKinds->obj_at(i), CHECK); + ScopeValue* first = get_scope_value(value, type, objects, second, CHECK); if (second != NULL) { expressions->append(second); } expressions->append(first); } else { - monitors->append(get_monitor_value(value, objects)); + MonitorValue *monitor = get_monitor_value(value, objects, CHECK); + monitors->append(monitor); } if (second != NULL) { i++; - assert(i < values->length(), "double-slot value not followed by Value.ILLEGAL"); - assert(values->obj_at(i) == Value::ILLEGAL(), "double-slot value not followed by Value.ILLEGAL"); + if (i >= values->length() || values->obj_at(i) != Value::ILLEGAL()) { + JVMCI_ERROR("double-slot value not followed by Value.ILLEGAL"); + } } } @@ -891,32 +980,37 @@ void CodeInstaller::record_scope(jint pc_offset, oop position, GrowableArraystart() + pc_offset; // jint next_pc_offset = Assembler::locate_next_instruction(instruction) - _instructions->start(); - _debug_recorder->add_safepoint(pc_offset, create_oop_map(debug_info)); - record_scope(pc_offset, debug_info); + OopMap *map = create_oop_map(debug_info, CHECK); + _debug_recorder->add_safepoint(pc_offset, map); + record_scope(pc_offset, debug_info, CHECK); _debug_recorder->end_safepoint(pc_offset); } -void CodeInstaller::site_Infopoint(CodeBuffer& buffer, jint pc_offset, oop site) { - oop debug_info = CompilationResult_Infopoint::debugInfo(site); - assert(debug_info != NULL, "debug info expected"); +void CodeInstaller::site_Infopoint(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS) { + Handle debug_info = CompilationResult_Infopoint::debugInfo(site); + if (debug_info.is_null()) { + JVMCI_ERROR("debug info expected at infopoint at %i", pc_offset); + } _debug_recorder->add_non_safepoint(pc_offset); - record_scope(pc_offset, debug_info); + record_scope(pc_offset, debug_info, CHECK); _debug_recorder->end_non_safepoint(pc_offset); } -void CodeInstaller::site_Call(CodeBuffer& buffer, jint pc_offset, oop site) { - oop target = CompilationResult_Call::target(site); +void CodeInstaller::site_Call(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS) { + Handle target = CompilationResult_Call::target(site); InstanceKlass* target_klass = InstanceKlass::cast(target->klass()); - oop hotspot_method = NULL; // JavaMethod - oop foreign_call = NULL; + Handle hotspot_method; // JavaMethod + Handle foreign_call; if (target_klass->is_subclass_of(SystemDictionary::HotSpotForeignCallTarget_klass())) { foreign_call = target; @@ -924,27 +1018,29 @@ void CodeInstaller::site_Call(CodeBuffer& buffer, jint pc_offset, oop site) { hotspot_method = target; } - oop debug_info = CompilationResult_Call::debugInfo(site); + Handle debug_info = CompilationResult_Call::debugInfo(site); - assert(!!hotspot_method ^ !!foreign_call, "Call site needs exactly one type"); + assert(hotspot_method.not_null() ^ foreign_call.not_null(), "Call site needs exactly one type"); NativeInstruction* inst = nativeInstruction_at(_instructions->start() + pc_offset); - jint next_pc_offset = CodeInstaller::pd_next_offset(inst, pc_offset, hotspot_method); + jint next_pc_offset = CodeInstaller::pd_next_offset(inst, pc_offset, hotspot_method, CHECK); - if (debug_info != NULL) { - _debug_recorder->add_safepoint(next_pc_offset, create_oop_map(debug_info)); - record_scope(next_pc_offset, debug_info); + if (debug_info.not_null()) { + OopMap *map = create_oop_map(debug_info, CHECK); + _debug_recorder->add_safepoint(next_pc_offset, map); + record_scope(next_pc_offset, debug_info, CHECK); } - if (foreign_call != NULL) { + if (foreign_call.not_null()) { jlong foreign_call_destination = HotSpotForeignCallTarget::address(foreign_call); - CodeInstaller::pd_relocate_ForeignCall(inst, foreign_call_destination); + CodeInstaller::pd_relocate_ForeignCall(inst, foreign_call_destination, CHECK); } else { // method != NULL - assert(hotspot_method != NULL, "unexpected JavaMethod"); - assert(debug_info != NULL, "debug info expected"); + if (debug_info.is_null()) { + JVMCI_ERROR("debug info expected at call at %i", pc_offset); + } TRACE_jvmci_3("method call"); - CodeInstaller::pd_relocate_JavaMethod(hotspot_method, pc_offset); + CodeInstaller::pd_relocate_JavaMethod(hotspot_method, pc_offset, CHECK); if (_next_call_type == INVOKESTATIC || _next_call_type == INVOKESPECIAL) { // Need a static call stub for transitions from compiled to interpreted. CompiledStaticCall::emit_to_interp_stub(buffer, _instructions->start() + pc_offset); @@ -953,38 +1049,45 @@ void CodeInstaller::site_Call(CodeBuffer& buffer, jint pc_offset, oop site) { _next_call_type = INVOKE_INVALID; - if (debug_info != NULL) { + if (debug_info.not_null()) { _debug_recorder->end_safepoint(next_pc_offset); } } -void CodeInstaller::site_DataPatch(CodeBuffer& buffer, jint pc_offset, oop site) { - oop reference = CompilationResult_DataPatch::reference(site); - if (reference->is_a(CompilationResult_ConstantReference::klass())) { +void CodeInstaller::site_DataPatch(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS) { + Handle reference = CompilationResult_DataPatch::reference(site); + if (reference.is_null()) { + THROW(vmSymbols::java_lang_NullPointerException()); + } else if (reference->is_a(CompilationResult_ConstantReference::klass())) { Handle constant = CompilationResult_ConstantReference::constant(reference); - if (constant->is_a(HotSpotObjectConstantImpl::klass())) { - pd_patch_OopConstant(pc_offset, constant); + if (constant.is_null()) { + THROW(vmSymbols::java_lang_NullPointerException()); + } else if (constant->is_a(HotSpotObjectConstantImpl::klass())) { + pd_patch_OopConstant(pc_offset, constant, CHECK); } else if (constant->is_a(HotSpotMetaspaceConstantImpl::klass())) { - pd_patch_MetaspaceConstant(pc_offset, constant); - } else if (constant->is_a(HotSpotSentinelConstant::klass())) { - fatal("sentinel constant unsupported"); + pd_patch_MetaspaceConstant(pc_offset, constant, CHECK); } else { - fatal("unknown constant type in data patch"); + JVMCI_ERROR("unknown constant type in data patch: %s", constant->klass()->signature_name()); } } else if (reference->is_a(CompilationResult_DataSectionReference::klass())) { int data_offset = CompilationResult_DataSectionReference::offset(reference); - assert(0 <= data_offset && data_offset < _constants_size, "data offset 0x%X points outside data section (size 0x%X)", data_offset, _constants_size); - pd_patch_DataSectionReference(pc_offset, data_offset); + if (0 <= data_offset && data_offset < _constants_size) { + pd_patch_DataSectionReference(pc_offset, data_offset); + } else { + JVMCI_ERROR("data offset 0x%X points outside data section (size 0x%X)", data_offset, _constants_size); + } } else { - fatal("unknown data patch type"); + JVMCI_ERROR("unknown data patch type: %s", reference->klass()->signature_name()); } } -void CodeInstaller::site_Mark(CodeBuffer& buffer, jint pc_offset, oop site) { - oop id_obj = CompilationResult_Mark::id(site); +void CodeInstaller::site_Mark(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS) { + Handle id_obj = CompilationResult_Mark::id(site); - if (id_obj != NULL) { - assert(java_lang_boxing_object::is_instance(id_obj, T_INT), "Integer id expected"); + if (id_obj.not_null()) { + if (!java_lang_boxing_object::is_instance(id_obj(), T_INT)) { + JVMCI_ERROR("expected Integer id, got %s", id_obj->klass()->signature_name()); + } jint id = id_obj->int_field(java_lang_boxing_object::value_offset_in_bytes(T_INT)); address pc = _instructions->start() + pc_offset; @@ -1017,7 +1120,7 @@ void CodeInstaller::site_Mark(CodeBuffer& buffer, jint pc_offset, oop site) { case POLL_FAR: case POLL_RETURN_NEAR: case POLL_RETURN_FAR: - pd_relocate_poll(pc, id); + pd_relocate_poll(pc, id, CHECK); break; case CARD_TABLE_SHIFT: case CARD_TABLE_ADDRESS: @@ -1027,7 +1130,7 @@ void CodeInstaller::site_Mark(CodeBuffer& buffer, jint pc_offset, oop site) { case CRC_TABLE_ADDRESS: break; default: - ShouldNotReachHere(); + JVMCI_ERROR("invalid mark id: %d", id); break; } } diff --git a/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.hpp b/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.hpp index 2ee2cbd742c..d2e40c86338 100644 --- a/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.hpp +++ b/hotspot/src/share/vm/jvmci/jvmciCodeInstaller.hpp @@ -154,13 +154,13 @@ private: static ConstantIntValue* _int_2_scope_value; static LocationValue* _illegal_value; - jint pd_next_offset(NativeInstruction* inst, jint pc_offset, oop method); - void pd_patch_OopConstant(int pc_offset, Handle& constant); - void pd_patch_MetaspaceConstant(int pc_offset, Handle& constant); + jint pd_next_offset(NativeInstruction* inst, jint pc_offset, Handle method, TRAPS); + void pd_patch_OopConstant(int pc_offset, Handle constant, TRAPS); + void pd_patch_MetaspaceConstant(int pc_offset, Handle constant, TRAPS); void pd_patch_DataSectionReference(int pc_offset, int data_offset); - void pd_relocate_ForeignCall(NativeInstruction* inst, jlong foreign_call_destination); - void pd_relocate_JavaMethod(oop method, jint pc_offset); - void pd_relocate_poll(address pc, jint mark); + void pd_relocate_ForeignCall(NativeInstruction* inst, jlong foreign_call_destination, TRAPS); + void pd_relocate_JavaMethod(Handle method, jint pc_offset, TRAPS); + void pd_relocate_poll(address pc, jint mark, TRAPS); objArrayOop sites() { return (objArrayOop) JNIHandles::resolve(_sites_handle); } arrayOop code() { return (arrayOop) JNIHandles::resolve(_code_handle); } @@ -177,33 +177,33 @@ public: CodeInstaller() : _arena(mtCompiler) {} - JVMCIEnv::CodeInstallResult gather_metadata(Handle target, Handle& compiled_code, CodeMetadata& metadata); - JVMCIEnv::CodeInstallResult install(JVMCICompiler* compiler, Handle target, Handle& compiled_code, CodeBlob*& cb, Handle installed_code, Handle speculation_log); + JVMCIEnv::CodeInstallResult gather_metadata(Handle target, Handle compiled_code, CodeMetadata& metadata, TRAPS); + JVMCIEnv::CodeInstallResult install(JVMCICompiler* compiler, Handle target, Handle compiled_code, CodeBlob*& cb, Handle installed_code, Handle speculation_log, TRAPS); static address runtime_call_target_address(oop runtime_call); - static VMReg get_hotspot_reg(jint jvmciRegisterNumber); + static VMReg get_hotspot_reg(jint jvmciRegisterNumber, TRAPS); static bool is_general_purpose_reg(VMReg hotspotRegister); const OopMapSet* oopMapSet() const { return _debug_recorder->_oopmaps; } protected: - Location::Type get_oop_type(oop value); - ScopeValue* get_scope_value(oop value, BasicType type, GrowableArray* objects, ScopeValue* &second); - MonitorValue* get_monitor_value(oop value, GrowableArray* objects); + Location::Type get_oop_type(Handle value); + ScopeValue* get_scope_value(Handle value, BasicType type, GrowableArray* objects, ScopeValue* &second, TRAPS); + MonitorValue* get_monitor_value(Handle value, GrowableArray* objects, TRAPS); - Metadata* record_metadata_reference(Handle& constant); + Metadata* record_metadata_reference(Handle constant, TRAPS); #ifdef _LP64 - narrowKlass record_narrow_metadata_reference(Handle& constant); + narrowKlass record_narrow_metadata_reference(Handle constant, TRAPS); #endif // extract the fields of the CompilationResult - void initialize_fields(oop target, oop target_method); - void initialize_dependencies(oop target_method, OopRecorder* oop_recorder); + void initialize_fields(oop target, oop target_method, TRAPS); + void initialize_dependencies(oop target_method, OopRecorder* oop_recorder, TRAPS); - int estimate_stubs_size(); + int estimate_stubs_size(TRAPS); // perform data and call relocation on the CodeBuffer - JVMCIEnv::CodeInstallResult initialize_buffer(CodeBuffer& buffer); + JVMCIEnv::CodeInstallResult initialize_buffer(CodeBuffer& buffer, TRAPS); void assumption_NoFinalizableSubclass(Handle assumption); void assumption_ConcreteSubtype(Handle assumption); @@ -211,19 +211,19 @@ protected: void assumption_ConcreteMethod(Handle assumption); void assumption_CallSiteTargetValue(Handle assumption); - void site_Safepoint(CodeBuffer& buffer, jint pc_offset, oop site); - void site_Infopoint(CodeBuffer& buffer, jint pc_offset, oop site); - void site_Call(CodeBuffer& buffer, jint pc_offset, oop site); - void site_DataPatch(CodeBuffer& buffer, jint pc_offset, oop site); - void site_Mark(CodeBuffer& buffer, jint pc_offset, oop site); + void site_Safepoint(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS); + void site_Infopoint(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS); + void site_Call(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS); + void site_DataPatch(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS); + void site_Mark(CodeBuffer& buffer, jint pc_offset, Handle site, TRAPS); - OopMap* create_oop_map(oop debug_info); + OopMap* create_oop_map(Handle debug_info, TRAPS); - void record_scope(jint pc_offset, oop debug_info); - void record_scope(jint pc_offset, oop code_pos, GrowableArray* objects); - void record_object_value(ObjectValue* sv, oop value, GrowableArray* objects); + void record_scope(jint pc_offset, Handle debug_info, TRAPS); + void record_scope(jint pc_offset, Handle code_pos, GrowableArray* objects, TRAPS); + void record_object_value(ObjectValue* sv, Handle value, GrowableArray* objects, TRAPS); - GrowableArray* record_virtual_objects(oop debug_info); + GrowableArray* record_virtual_objects(Handle debug_info, TRAPS); void process_exception_handlers(); int estimateStubSpace(int static_call_stubs); diff --git a/hotspot/src/share/vm/jvmci/jvmciCompiler.cpp b/hotspot/src/share/vm/jvmci/jvmciCompiler.cpp index 7fdf21e60ee..c07f9b4c250 100644 --- a/hotspot/src/share/vm/jvmci/jvmciCompiler.cpp +++ b/hotspot/src/share/vm/jvmci/jvmciCompiler.cpp @@ -112,7 +112,7 @@ void JVMCICompiler::bootstrap() { _bootstrapping = false; } -void JVMCICompiler::compile_method(methodHandle method, int entry_bci, JVMCIEnv* env) { +void JVMCICompiler::compile_method(const methodHandle& method, int entry_bci, JVMCIEnv* env) { JVMCI_EXCEPTION_CONTEXT bool is_osr = entry_bci != InvocationEntryBci; diff --git a/hotspot/src/share/vm/jvmci/jvmciCompiler.hpp b/hotspot/src/share/vm/jvmci/jvmciCompiler.hpp index 0b0a38d5afa..f1f5c079433 100644 --- a/hotspot/src/share/vm/jvmci/jvmciCompiler.hpp +++ b/hotspot/src/share/vm/jvmci/jvmciCompiler.hpp @@ -71,7 +71,7 @@ public: // Compilation entry point for methods virtual void compile_method(ciEnv* env, ciMethod* target, int entry_bci, DirectiveSet* directive); - void compile_method(methodHandle target, int entry_bci, JVMCIEnv* env); + void compile_method(const methodHandle& target, int entry_bci, JVMCIEnv* env); virtual bool is_trivial(Method* method); diff --git a/hotspot/src/share/vm/jvmci/jvmciCompilerToVM.cpp b/hotspot/src/share/vm/jvmci/jvmciCompilerToVM.cpp index c0f773e279b..558331e64a7 100644 --- a/hotspot/src/share/vm/jvmci/jvmciCompilerToVM.cpp +++ b/hotspot/src/share/vm/jvmci/jvmciCompilerToVM.cpp @@ -670,7 +670,7 @@ C2V_VMENTRY(jint, installCode, (JNIEnv *jniEnv, jobject, jobject target, jobject TraceTime install_time("installCode", JVMCICompiler::codeInstallTimer()); CodeInstaller installer; - JVMCIEnv::CodeInstallResult result = installer.install(compiler, target_handle, compiled_code_handle, cb, installed_code_handle, speculation_log_handle); + JVMCIEnv::CodeInstallResult result = installer.install(compiler, target_handle, compiled_code_handle, cb, installed_code_handle, speculation_log_handle, CHECK_0); if (PrintCodeCacheOnCompilation) { stringStream s; @@ -690,6 +690,7 @@ C2V_VMENTRY(jint, installCode, (JNIEnv *jniEnv, jobject, jobject target, jobject assert(installed_code_handle->is_a(InstalledCode::klass()), "wrong type"); CompilerToVM::invalidate_installed_code(installed_code_handle, CHECK_0); InstalledCode::set_address(installed_code_handle, (jlong) cb); + InstalledCode::set_version(installed_code_handle, InstalledCode::version(installed_code_handle) + 1); if (cb->is_nmethod()) { InstalledCode::set_entryPoint(installed_code_handle, (jlong) cb->as_nmethod_or_null()->verified_entry_point()); } else { @@ -726,7 +727,7 @@ C2V_VMENTRY(jint, getMetadata, (JNIEnv *jniEnv, jobject, jobject target, jobject CodeBlob *cb = NULL; CodeInstaller installer; - JVMCIEnv::CodeInstallResult result = installer.gather_metadata(target_handle, compiled_code_handle, code_metadata); //cb, pc_descs, nr_pc_descs, scopes_descs, scopes_size, reloc_buffer); + JVMCIEnv::CodeInstallResult result = installer.gather_metadata(target_handle, compiled_code_handle, code_metadata, CHECK_0); //cb, pc_descs, nr_pc_descs, scopes_descs, scopes_size, reloc_buffer); if (result != JVMCIEnv::ok) { return result; } diff --git a/hotspot/src/share/vm/jvmci/jvmciEnv.cpp b/hotspot/src/share/vm/jvmci/jvmciEnv.cpp index 348a28601be..4142f790c6c 100644 --- a/hotspot/src/share/vm/jvmci/jvmciEnv.cpp +++ b/hotspot/src/share/vm/jvmci/jvmciEnv.cpp @@ -161,7 +161,7 @@ KlassHandle JVMCIEnv::get_klass_by_name_impl(KlassHandle& accessing_klass, } // ------------------------------------------------------------------ -KlassHandle JVMCIEnv::get_klass_by_name(KlassHandle& accessing_klass, +KlassHandle JVMCIEnv::get_klass_by_name(KlassHandle accessing_klass, Symbol* klass_name, bool require_local) { ResourceMark rm; @@ -177,7 +177,7 @@ KlassHandle JVMCIEnv::get_klass_by_name(KlassHandle& accessing_klass, KlassHandle JVMCIEnv::get_klass_by_index_impl(const constantPoolHandle& cpool, int index, bool& is_accessible, - KlassHandle& accessor) { + KlassHandle accessor) { JVMCI_EXCEPTION_CONTEXT; KlassHandle klass (THREAD, ConstantPool::klass_at_if_loaded(cpool, index)); Symbol* klass_name = NULL; @@ -218,7 +218,7 @@ KlassHandle JVMCIEnv::get_klass_by_index_impl(const constantPoolHandle& cpool, KlassHandle JVMCIEnv::get_klass_by_index(const constantPoolHandle& cpool, int index, bool& is_accessible, - KlassHandle& accessor) { + KlassHandle accessor) { ResourceMark rm; KlassHandle result = get_klass_by_index_impl(cpool, index, is_accessible, accessor); return result; @@ -229,7 +229,7 @@ KlassHandle JVMCIEnv::get_klass_by_index(const constantPoolHandle& cpool, // // Implementation note: the results of field lookups are cached // in the accessor klass. -void JVMCIEnv::get_field_by_index_impl(instanceKlassHandle& klass, fieldDescriptor& field_desc, +void JVMCIEnv::get_field_by_index_impl(instanceKlassHandle klass, fieldDescriptor& field_desc, int index) { JVMCI_EXCEPTION_CONTEXT; @@ -270,7 +270,7 @@ void JVMCIEnv::get_field_by_index_impl(instanceKlassHandle& klass, fieldDescript // ------------------------------------------------------------------ // Get a field by index from a klass's constant pool. -void JVMCIEnv::get_field_by_index(instanceKlassHandle& accessor, fieldDescriptor& fd, int index) { +void JVMCIEnv::get_field_by_index(instanceKlassHandle accessor, fieldDescriptor& fd, int index) { ResourceMark rm; return get_field_by_index_impl(accessor, fd, index); } @@ -278,8 +278,8 @@ void JVMCIEnv::get_field_by_index(instanceKlassHandle& accessor, fieldDescriptor // ------------------------------------------------------------------ // Perform an appropriate method lookup based on accessor, holder, // name, signature, and bytecode. -methodHandle JVMCIEnv::lookup_method(instanceKlassHandle& h_accessor, - instanceKlassHandle& h_holder, +methodHandle JVMCIEnv::lookup_method(instanceKlassHandle h_accessor, + instanceKlassHandle h_holder, Symbol* name, Symbol* sig, Bytecodes::Code bc) { @@ -314,7 +314,7 @@ methodHandle JVMCIEnv::lookup_method(instanceKlassHandle& h_accessor, // ------------------------------------------------------------------ methodHandle JVMCIEnv::get_method_by_index_impl(const constantPoolHandle& cpool, int index, Bytecodes::Code bc, - instanceKlassHandle& accessor) { + instanceKlassHandle accessor) { if (bc == Bytecodes::_invokedynamic) { ConstantPoolCacheEntry* cpce = cpool->invokedynamic_cp_cache_entry_at(index); bool is_resolved = !cpce->is_f1_null(); @@ -379,7 +379,7 @@ methodHandle JVMCIEnv::get_method_by_index_impl(const constantPoolHandle& cpool, } // ------------------------------------------------------------------ -instanceKlassHandle JVMCIEnv::get_instance_klass_for_declared_method_holder(KlassHandle& method_holder) { +instanceKlassHandle JVMCIEnv::get_instance_klass_for_declared_method_holder(KlassHandle method_holder) { // For the case of .clone(), the method holder can be an ArrayKlass* // instead of an InstanceKlass*. For that case simply pretend that the // declared holder is Object.clone since that's where the call will bottom out. @@ -397,7 +397,7 @@ instanceKlassHandle JVMCIEnv::get_instance_klass_for_declared_method_holder(Klas // ------------------------------------------------------------------ methodHandle JVMCIEnv::get_method_by_index(const constantPoolHandle& cpool, int index, Bytecodes::Code bc, - instanceKlassHandle& accessor) { + instanceKlassHandle accessor) { ResourceMark rm; return get_method_by_index_impl(cpool, index, bc, accessor); } @@ -452,7 +452,7 @@ JVMCIEnv::CodeInstallResult JVMCIEnv::check_for_system_dictionary_modification(D // ------------------------------------------------------------------ JVMCIEnv::CodeInstallResult JVMCIEnv::register_method( - methodHandle& method, + const methodHandle& method, nmethod*& nm, int entry_bci, CodeOffsets* offsets, diff --git a/hotspot/src/share/vm/jvmci/jvmciEnv.hpp b/hotspot/src/share/vm/jvmci/jvmciEnv.hpp index f0291efde26..173729259fe 100644 --- a/hotspot/src/share/vm/jvmci/jvmciEnv.hpp +++ b/hotspot/src/share/vm/jvmci/jvmciEnv.hpp @@ -78,7 +78,7 @@ public: // The CI treats a klass as loaded if it is consistently defined in // another loader, even if it hasn't yet been loaded in all loaders // that could potentially see it via delegation. - static KlassHandle get_klass_by_name(KlassHandle& accessing_klass, + static KlassHandle get_klass_by_name(KlassHandle accessing_klass, Symbol* klass_name, bool require_local); @@ -86,12 +86,12 @@ public: static KlassHandle get_klass_by_index(const constantPoolHandle& cpool, int klass_index, bool& is_accessible, - KlassHandle& loading_klass); - static void get_field_by_index(instanceKlassHandle& loading_klass, fieldDescriptor& fd, + KlassHandle loading_klass); + static void get_field_by_index(instanceKlassHandle loading_klass, fieldDescriptor& fd, int field_index); static methodHandle get_method_by_index(const constantPoolHandle& cpool, int method_index, Bytecodes::Code bc, - instanceKlassHandle& loading_klass); + instanceKlassHandle loading_klass); JVMCIEnv(CompileTask* task, int system_dictionary_modification_counter); @@ -112,17 +112,17 @@ private: static KlassHandle get_klass_by_index_impl(const constantPoolHandle& cpool, int klass_index, bool& is_accessible, - KlassHandle& loading_klass); - static void get_field_by_index_impl(instanceKlassHandle& loading_klass, fieldDescriptor& fd, + KlassHandle loading_klass); + static void get_field_by_index_impl(instanceKlassHandle loading_klass, fieldDescriptor& fd, int field_index); static methodHandle get_method_by_index_impl(const constantPoolHandle& cpool, int method_index, Bytecodes::Code bc, - instanceKlassHandle& loading_klass); + instanceKlassHandle loading_klass); // Helper methods static bool check_klass_accessibility(KlassHandle accessing_klass, KlassHandle resolved_klass); - static methodHandle lookup_method(instanceKlassHandle& accessor, - instanceKlassHandle& holder, + static methodHandle lookup_method(instanceKlassHandle accessor, + instanceKlassHandle holder, Symbol* name, Symbol* sig, Bytecodes::Code bc); @@ -142,7 +142,7 @@ public: // Register the result of a compilation. static JVMCIEnv::CodeInstallResult register_method( - methodHandle& target, + const methodHandle& target, nmethod*& nm, int entry_bci, CodeOffsets* offsets, @@ -166,7 +166,7 @@ public: // InstanceKlass*. This is needed since the holder of a method in // the bytecodes could be an array type. Basically this converts // array types into java/lang/Object and other types stay as they are. - static instanceKlassHandle get_instance_klass_for_declared_method_holder(KlassHandle& klass); + static instanceKlassHandle get_instance_klass_for_declared_method_holder(KlassHandle klass); }; #endif // SHARE_VM_JVMCI_JVMCIENV_HPP diff --git a/hotspot/src/share/vm/jvmci/jvmciJavaClasses.cpp b/hotspot/src/share/vm/jvmci/jvmciJavaClasses.cpp index 190aaf419f1..3972a6c2e55 100644 --- a/hotspot/src/share/vm/jvmci/jvmciJavaClasses.cpp +++ b/hotspot/src/share/vm/jvmci/jvmciJavaClasses.cpp @@ -30,7 +30,7 @@ // This function is similar to javaClasses.cpp, it computes the field offset of a (static or instance) field. // It looks up the name and signature symbols without creating new ones, all the symbols of these classes need to be already loaded. -void compute_offset(int &dest_offset, Klass* klass, const char* name, const char* signature, bool static_field) { +void compute_offset(int &dest_offset, Klass* klass, const char* name, const char* signature, bool static_field, TRAPS) { InstanceKlass* ik = InstanceKlass::cast(klass); Symbol* name_symbol = SymbolTable::probe(name, (int)strlen(name)); Symbol* signature_symbol = SymbolTable::probe(signature, (int)strlen(signature)); @@ -49,6 +49,11 @@ void compute_offset(int &dest_offset, Klass* klass, const char* name, const char guarantee(fd.is_static() == static_field, "static/instance mismatch"); dest_offset = fd.offset(); assert(dest_offset != 0, "must be valid offset"); + if (static_field) { + // Must ensure classes for static fields are initialized as the + // accessor itself does not include a class initialization check. + ik->initialize(CHECK); + } } // This piece of macro magic creates the contents of the jvmci_compute_offsets method that initializes the field indices of all the access classes. @@ -57,7 +62,7 @@ void compute_offset(int &dest_offset, Klass* klass, const char* name, const char #define END_CLASS } -#define FIELD(klass, name, signature, static_field) compute_offset(klass::_##name##_offset, k, #name, signature, static_field); +#define FIELD(klass, name, signature, static_field) compute_offset(klass::_##name##_offset, k, #name, signature, static_field, CHECK); #define CHAR_FIELD(klass, name) FIELD(klass, name, "C", false) #define INT_FIELD(klass, name) FIELD(klass, name, "I", false) #define BOOLEAN_FIELD(klass, name) FIELD(klass, name, "Z", false) @@ -69,7 +74,7 @@ void compute_offset(int &dest_offset, Klass* klass, const char* name, const char #define STATIC_BOOLEAN_FIELD(klass, name) FIELD(klass, name, "Z", true) -void JVMCIJavaClasses::compute_offsets() { +void JVMCIJavaClasses::compute_offsets(TRAPS) { COMPILER_CLASSES_DO(START_CLASS, END_CLASS, CHAR_FIELD, INT_FIELD, BOOLEAN_FIELD, LONG_FIELD, FLOAT_FIELD, OOP_FIELD, OOP_FIELD, OOP_FIELD, STATIC_OOP_FIELD, STATIC_OOP_FIELD, STATIC_INT_FIELD, STATIC_BOOLEAN_FIELD) } diff --git a/hotspot/src/share/vm/jvmci/jvmciJavaClasses.hpp b/hotspot/src/share/vm/jvmci/jvmciJavaClasses.hpp index 22f6bede118..cf26827be8f 100644 --- a/hotspot/src/share/vm/jvmci/jvmciJavaClasses.hpp +++ b/hotspot/src/share/vm/jvmci/jvmciJavaClasses.hpp @@ -29,7 +29,7 @@ class JVMCIJavaClasses : AllStatic { public: - static void compute_offsets(); + static void compute_offsets(TRAPS); }; /* This macro defines the structure of the CompilationResult - classes. @@ -306,7 +306,7 @@ class name : AllStatic { assert(obj->is_a(SystemDictionary::name##_klass()), "wrong class, " #name " expected, found %s", obj->klass()->external_name()); \ assert(offset != 0, "must be valid offset"); \ } \ - static void compute_offsets(); \ + static void compute_offsets(TRAPS); \ public: \ static InstanceKlass* klass() { return SystemDictionary::name##_klass(); } @@ -315,10 +315,10 @@ class name : AllStatic { #define FIELD(name, type, accessor, cast) \ static int _##name##_offset; \ static type name(oop obj) { check(obj, #name, _##name##_offset); return cast obj->accessor(_##name##_offset); } \ - static type name(Handle& obj) { check(obj(), #name, _##name##_offset); return cast obj->accessor(_##name##_offset); } \ + static type name(Handle obj) { check(obj(), #name, _##name##_offset); return cast obj->accessor(_##name##_offset); } \ static type name(jobject obj) { check(JNIHandles::resolve(obj), #name, _##name##_offset); return cast JNIHandles::resolve(obj)->accessor(_##name##_offset); } \ static void set_##name(oop obj, type x) { check(obj, #name, _##name##_offset); obj->accessor##_put(_##name##_offset, x); } \ - static void set_##name(Handle& obj, type x) { check(obj(), #name, _##name##_offset); obj->accessor##_put(_##name##_offset, x); } \ + static void set_##name(Handle obj, type x) { check(obj(), #name, _##name##_offset); obj->accessor##_put(_##name##_offset, x); } \ static void set_##name(jobject obj, type x) { check(JNIHandles::resolve(obj), #name, _##name##_offset); JNIHandles::resolve(obj)->accessor##_put(_##name##_offset, x); } #define EMPTY_CAST @@ -392,6 +392,6 @@ COMPILER_CLASSES_DO(START_CLASS, END_CLASS, CHAR_FIELD, INT_FIELD, BOOLEAN_FIELD #undef STATIC_BOOLEAN_FIELD #undef EMPTY_CAST -void compute_offset(int &dest_offset, Klass* klass, const char* name, const char* signature, bool static_field); +void compute_offset(int &dest_offset, Klass* klass, const char* name, const char* signature, bool static_field, TRAPS); #endif // SHARE_VM_JVMCI_JVMCIJAVACLASSES_HPP diff --git a/hotspot/src/share/vm/jvmci/jvmciRuntime.cpp b/hotspot/src/share/vm/jvmci/jvmciRuntime.cpp index 55632f2acb9..afcbdf27f82 100644 --- a/hotspot/src/share/vm/jvmci/jvmciRuntime.cpp +++ b/hotspot/src/share/vm/jvmci/jvmciRuntime.cpp @@ -59,7 +59,11 @@ bool JVMCIRuntime::_shutdown_called = false; static const char* OPTION_PREFIX = "jvmci.option."; static const size_t OPTION_PREFIX_LEN = strlen(OPTION_PREFIX); -BasicType JVMCIRuntime::kindToBasicType(jchar ch) { +BasicType JVMCIRuntime::kindToBasicType(Handle kind, TRAPS) { + if (kind.is_null()) { + THROW_(vmSymbols::java_lang_NullPointerException(), T_ILLEGAL); + } + jchar ch = JavaKind::typeChar(kind); switch(ch) { case 'z': return T_BOOLEAN; case 'b': return T_BYTE; @@ -72,10 +76,8 @@ BasicType JVMCIRuntime::kindToBasicType(jchar ch) { case 'a': return T_OBJECT; case '-': return T_ILLEGAL; default: - fatal("unexpected Kind: %c", ch); - break; + JVMCI_ERROR_(T_ILLEGAL, "unexpected Kind: %c", ch); } - return T_ILLEGAL; } // Simple helper to see if the caller of a runtime stub which @@ -718,7 +720,7 @@ void JVMCIRuntime::initialize_well_known_classes(TRAPS) { if (JVMCIRuntime::_well_known_classes_initialized == false) { SystemDictionary::WKID scan = SystemDictionary::FIRST_JVMCI_WKID; SystemDictionary::initialize_wk_klasses_through(SystemDictionary::LAST_JVMCI_WKID, scan, CHECK); - JVMCIJavaClasses::compute_offsets(); + JVMCIJavaClasses::compute_offsets(CHECK); JVMCIRuntime::_well_known_classes_initialized = true; } } diff --git a/hotspot/src/share/vm/jvmci/jvmciRuntime.hpp b/hotspot/src/share/vm/jvmci/jvmciRuntime.hpp index 00eae524c15..9e69965f19d 100644 --- a/hotspot/src/share/vm/jvmci/jvmciRuntime.hpp +++ b/hotspot/src/share/vm/jvmci/jvmciRuntime.hpp @@ -29,6 +29,17 @@ #include "runtime/arguments.hpp" #include "runtime/deoptimization.hpp" +#define JVMCI_ERROR(...) \ + { Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbols::jdk_vm_ci_common_JVMCIError(), __VA_ARGS__); return; } + +#define JVMCI_ERROR_(ret, ...) \ + { Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbols::jdk_vm_ci_common_JVMCIError(), __VA_ARGS__); return ret; } + +#define JVMCI_ERROR_0(...) JVMCI_ERROR_(0, __VA_ARGS__) +#define JVMCI_ERROR_NULL(...) JVMCI_ERROR_(NULL, __VA_ARGS__) +#define JVMCI_ERROR_OK(...) JVMCI_ERROR_(JVMCIEnv::ok, __VA_ARGS__) +#define CHECK_OK CHECK_(JVMCIEnv::ok) + class ParseClosure : public StackObj { int _lineNo; char* _filename; @@ -171,7 +182,7 @@ class JVMCIRuntime: public AllStatic { } \ (void)(0 - static BasicType kindToBasicType(jchar ch); + static BasicType kindToBasicType(Handle kind, TRAPS); // The following routines are all called from compiled JVMCI code diff --git a/hotspot/src/share/vm/jvmci/vmSymbols_jvmci.hpp b/hotspot/src/share/vm/jvmci/vmSymbols_jvmci.hpp index 5502f2fa603..8d8af6bd79a 100644 --- a/hotspot/src/share/vm/jvmci/vmSymbols_jvmci.hpp +++ b/hotspot/src/share/vm/jvmci/vmSymbols_jvmci.hpp @@ -86,6 +86,7 @@ template(jdk_vm_ci_code_VirtualObject, "jdk/vm/ci/code/VirtualObject") \ template(jdk_vm_ci_code_RegisterSaveLayout, "jdk/vm/ci/code/RegisterSaveLayout") \ template(jdk_vm_ci_code_InvalidInstalledCodeException, "jdk/vm/ci/code/InvalidInstalledCodeException") \ + template(jdk_vm_ci_common_JVMCIError, "jdk/vm/ci/common/JVMCIError") \ template(compileMethod_name, "compileMethod") \ template(compileMethod_signature, "(Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethod;IJI)V") \ template(fromMetaspace_name, "fromMetaspace") \ diff --git a/hotspot/src/share/vm/logging/log.cpp b/hotspot/src/share/vm/logging/log.cpp index 1e9f94bec9d..eda1877c88c 100644 --- a/hotspot/src/share/vm/logging/log.cpp +++ b/hotspot/src/share/vm/logging/log.cpp @@ -37,10 +37,10 @@ void Test_log_length() { // Write long message to output file MutexLocker ml(LogConfiguration_lock); - LogConfiguration::parse_log_arguments("loglengthoutput.txt", "logging=develop", + LogConfiguration::parse_log_arguments("loglengthoutput.txt", "logging=trace", NULL, NULL, NULL); ResourceMark rm; - outputStream* logstream = LogHandle(logging)::develop_stream(); + outputStream* logstream = LogHandle(logging)::trace_stream(); logstream->print_cr("01:1234567890-" "02:1234567890-" "03:1234567890-" diff --git a/hotspot/src/share/vm/logging/log.hpp b/hotspot/src/share/vm/logging/log.hpp index 2f041afeb78..8b65f0cd2dd 100644 --- a/hotspot/src/share/vm/logging/log.hpp +++ b/hotspot/src/share/vm/logging/log.hpp @@ -49,11 +49,21 @@ #define log_info(...) (!log_is_enabled(Info, __VA_ARGS__)) ? (void)0 : Log::write #define log_debug(...) (!log_is_enabled(Debug, __VA_ARGS__)) ? (void)0 : Log::write #define log_trace(...) (!log_is_enabled(Trace, __VA_ARGS__)) ? (void)0 : Log::write + +// Macros for logging that should be excluded in product builds. +// Available for levels Info, Debug and Trace. Includes test macro that +// evaluates to false in product builds. #ifndef PRODUCT -#define log_develop(...) (!log_is_enabled(Develop, __VA_ARGS__)) ? (void)0 : Log::write +#define log_develop_info(...) (!log_is_enabled(Info, __VA_ARGS__)) ? (void)0 : Log::write +#define log_develop_debug(...) (!log_is_enabled(Debug, __VA_ARGS__)) ? (void)0 : Log::write +#define log_develop_trace(...) (!log_is_enabled(Trace, __VA_ARGS__)) ? (void)0 : Log::write +#define develop_log_is_enabled(level, ...) log_is_enabled(level, __VA_ARGS__) #else #define DUMMY_ARGUMENT_CONSUMER(...) -#define log_develop(...) DUMMY_ARGUMENT_CONSUMER +#define log_develop_info(...) DUMMY_ARGUMENT_CONSUMER +#define log_develop_debug(...) DUMMY_ARGUMENT_CONSUMER +#define log_develop_trace(...) DUMMY_ARGUMENT_CONSUMER +#define develop_log_is_enabled(...) false #endif // Convenience macro to test if the logging is enabled on the specified level for given tags. @@ -88,6 +98,11 @@ class Log VALUE_OBJ_CLASS_SPEC { // is not __NO_TAG, the number of tags given exceeds the maximum allowed. STATIC_ASSERT(GuardTag == LogTag::__NO_TAG); // Number of logging tags exceeds maximum supported! + // Empty constructor to avoid warnings on MSVC about unused variables + // when the log instance is only used for static functions. + Log() { + } + static bool is_level(LogLevelType level) { return LogTagSetMapping::tagset().is_level(level); } diff --git a/hotspot/src/share/vm/logging/logConfiguration.cpp b/hotspot/src/share/vm/logging/logConfiguration.cpp index d575a458249..9323c22d50c 100644 --- a/hotspot/src/share/vm/logging/logConfiguration.cpp +++ b/hotspot/src/share/vm/logging/logConfiguration.cpp @@ -33,6 +33,7 @@ #include "logging/logTagSet.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" +#include "runtime/mutexLocker.hpp" #include "runtime/os.inline.hpp" #include "utilities/globalDefinitions.hpp" @@ -44,6 +45,7 @@ void LogConfiguration::post_initialize() { LogDiagnosticCommand::registerCommand(); LogHandle(logging) log; log.info("Log configuration fully initialized."); + log_develop_info(logging)("Develop logging is available."); if (log.is_trace()) { ResourceMark rm; MutexLocker ml(LogConfiguration_lock); @@ -126,33 +128,72 @@ void LogConfiguration::delete_output(size_t idx) { void LogConfiguration::configure_output(size_t idx, const LogTagLevelExpression& tag_level_expression, const LogDecorators& decorators) { assert(idx < _n_outputs, "Invalid index, idx = " SIZE_FORMAT " and _n_outputs = " SIZE_FORMAT, idx, _n_outputs); LogOutput* output = _outputs[idx]; - output->set_decorators(decorators); - output->set_config_string(tag_level_expression.to_string()); + + // Clear the previous config description + output->clear_config_string(); + bool enabled = false; for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) { LogLevelType level = tag_level_expression.level_for(*ts); - if (level != LogLevel::Off) { - enabled = true; + + // Ignore tagsets that do not, and will not log on the output + if (!ts->has_output(output) && (level == LogLevel::NotMentioned || level == LogLevel::Off)) { + continue; + } + + // Update decorators before adding/updating output level, + // so that the tagset will have the necessary decorators when requiring them. + if (level != LogLevel::Off) { + ts->update_decorators(decorators); + } + + // Set the new level, if it changed + if (level != LogLevel::NotMentioned) { + ts->set_output_level(output, level); + } + + if (level != LogLevel::Off) { + // Keep track of whether or not the output is ever used by some tagset + enabled = true; + + if (level == LogLevel::NotMentioned) { + // Look up the previously set level for this output on this tagset + level = ts->level_for(output); + } + + // Update the config description with this tagset and level + output->add_to_config_string(ts, level); } - ts->update_decorators(decorators); - ts->set_output_level(output, level); } - // If the output is not used by any tagset it should be removed, unless it is stdout/stderr. - if (!enabled && idx > 1) { + // It is now safe to set the new decorators for the actual output + output->set_decorators(decorators); + + // Update the decorators on all tagsets to get rid of unused decorators + for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) { + ts->update_decorators(); + } + + if (enabled) { + assert(strlen(output->config_string()) > 0, + "Should always have a config description if the output is enabled."); + } else if (idx > 1) { + // Output is unused and should be removed. delete_output(idx); + } else { + // Output is either stdout or stderr, which means we can't remove it. + // Update the config description to reflect that the output is disabled. + output->set_config_string("all=off"); } } void LogConfiguration::disable_output(size_t idx) { LogOutput* out = _outputs[idx]; - LogDecorators empty_decorators; - empty_decorators.clear(); // Remove the output from all tagsets. for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) { ts->set_output_level(out, LogLevel::Off); - ts->update_decorators(empty_decorators); + ts->update_decorators(); } // Delete the output unless stdout/stderr @@ -171,6 +212,36 @@ void LogConfiguration::disable_logging() { } } +void LogConfiguration::configure_stdout(LogLevelType level, bool exact_match, ...) { + assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(), + "LogConfiguration lock must be held when calling this function"); + + size_t i; + va_list ap; + LogTagLevelExpression expr; + va_start(ap, exact_match); + for (i = 0; i < LogTag::MaxTags; i++) { + LogTagType tag = static_cast(va_arg(ap, int)); + expr.add_tag(tag); + if (tag == LogTag::__NO_TAG) { + assert(i > 0, "Must specify at least one tag!"); + break; + } + } + assert(i < LogTag::MaxTags || static_cast(va_arg(ap, int)) == LogTag::__NO_TAG, + "Too many tags specified! Can only have up to " SIZE_FORMAT " tags in a tag set.", LogTag::MaxTags); + va_end(ap); + + if (!exact_match) { + expr.set_allow_other_tags(); + } + expr.set_level(level); + expr.new_combination(); + + // Apply configuration to stdout (output #0), with the same decorators as before. + configure_output(0, expr, LogOutput::Stdout->decorators()); +} + bool LogConfiguration::parse_command_line_arguments(const char* opts) { char* copy = os::strdup_check_oom(opts, mtLogging); diff --git a/hotspot/src/share/vm/logging/logConfiguration.hpp b/hotspot/src/share/vm/logging/logConfiguration.hpp index 06f7dd5c6e9..dee0cc86bd8 100644 --- a/hotspot/src/share/vm/logging/logConfiguration.hpp +++ b/hotspot/src/share/vm/logging/logConfiguration.hpp @@ -24,6 +24,7 @@ #ifndef SHARE_VM_LOGGING_LOGCONFIGURATION_HPP #define SHARE_VM_LOGGING_LOGCONFIGURATION_HPP +#include "logging/logLevel.hpp" #include "memory/allocation.hpp" #include "utilities/globalDefinitions.hpp" @@ -70,6 +71,14 @@ class LogConfiguration : public AllStatic { // Disable all logging, equivalent to -Xlog:disable. static void disable_logging(); + // Configures logging on stdout for the given tags and level combination. + // Intended for mappings between -XX: flags and Unified Logging configuration. + // If exact_match is true, only tagsets with precisely the specified tags will be configured + // (exact_match=false is the same as "-Xlog:*=", and exact_match=true is "-Xlog:="). + // Tags should be specified using the LOG_TAGS macro, e.g. + // LogConfiguration::configure_stdout(LogLevel::, , LOG_TAGS()); + static void configure_stdout(LogLevelType level, bool exact_match, ...); + // Parse command line configuration. Parameter 'opts' is the string immediately following the -Xlog: argument ("gc" for -Xlog:gc). static bool parse_command_line_arguments(const char* opts = "all"); diff --git a/hotspot/src/share/vm/logging/logDecorators.cpp b/hotspot/src/share/vm/logging/logDecorators.cpp index 69e36b4bba8..335699714c2 100644 --- a/hotspot/src/share/vm/logging/logDecorators.cpp +++ b/hotspot/src/share/vm/logging/logDecorators.cpp @@ -25,6 +25,8 @@ #include "logging/logDecorators.hpp" #include "runtime/os.inline.hpp" +const LogDecorators LogDecorators::None = LogDecorators(0); + const char* LogDecorators::_name[][2] = { #define DECORATOR(n, a) {#n, #a}, DECORATOR_LIST diff --git a/hotspot/src/share/vm/logging/logDecorators.hpp b/hotspot/src/share/vm/logging/logDecorators.hpp index 0946c62bf56..56a4778b034 100644 --- a/hotspot/src/share/vm/logging/logDecorators.hpp +++ b/hotspot/src/share/vm/logging/logDecorators.hpp @@ -73,7 +73,12 @@ class LogDecorators VALUE_OBJ_CLASS_SPEC { return 1 << decorator; } + LogDecorators(uint mask) : _decorators(mask) { + } + public: + static const LogDecorators None; + LogDecorators() : _decorators(DefaultDecoratorsMask) { }; diff --git a/hotspot/src/share/vm/logging/logLevel.hpp b/hotspot/src/share/vm/logging/logLevel.hpp index 55ac3564d2c..4ea016b6f1e 100644 --- a/hotspot/src/share/vm/logging/logLevel.hpp +++ b/hotspot/src/share/vm/logging/logLevel.hpp @@ -29,14 +29,8 @@ // The list of log levels: // -// develop - A non-product level that is finer than trace. -// Should be used for really expensive and/or -// extensive logging, or logging that shouldn't -// or can't be included in a product build. -// -// trace - Finest level of logging in product builds. -// Use for extensive/noisy logging that can -// give slow-down when enabled. +// trace - Finest level of logging. Use for extensive/noisy +// logging that can give slow-down when enabled. // // debug - A finer level of logging. Use for semi-noisy // logging that is does not fit the info level. @@ -49,7 +43,6 @@ // error - Critical messages caused by errors. // #define LOG_LEVEL_LIST \ - NOT_PRODUCT(LOG_LEVEL(Develop, develop)) \ LOG_LEVEL(Trace, trace) \ LOG_LEVEL(Debug, debug) \ LOG_LEVEL(Info, info) \ @@ -65,6 +58,7 @@ class LogLevel : public AllStatic { #undef LOG_LEVEL Count, Invalid, + NotMentioned, First = Off + 1, Last = Error, Default = Warning, @@ -72,6 +66,7 @@ class LogLevel : public AllStatic { }; static const char *name(LogLevel::type level) { + assert(level >= 0 && level < LogLevel::Count, "Invalid level (enum value %d).", level); return _name[level]; } diff --git a/hotspot/src/share/vm/logging/logOutput.cpp b/hotspot/src/share/vm/logging/logOutput.cpp index ee59d587d8b..0b9a3bf0db9 100644 --- a/hotspot/src/share/vm/logging/logOutput.cpp +++ b/hotspot/src/share/vm/logging/logOutput.cpp @@ -24,7 +24,9 @@ #include "precompiled.hpp" #include "logging/logFileStreamOutput.hpp" #include "logging/logOutput.hpp" +#include "logging/logTagSet.hpp" #include "memory/allocation.inline.hpp" +#include "runtime/mutexLocker.hpp" #include "runtime/os.inline.hpp" LogOutput* const LogOutput::Stdout = &LogStdoutOutput::_instance; @@ -34,7 +36,54 @@ LogOutput::~LogOutput() { os::free(_config_string); } -void LogOutput::set_config_string(const char* string) { +void LogOutput::clear_config_string() { + assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(), + "Must hold configuration lock to modify config string"); + os::free(_config_string); - _config_string = os::strdup_check_oom(string, mtLogging); + _config_string_buffer_size = InitialConfigBufferSize; + _config_string = NEW_C_HEAP_ARRAY(char, _config_string_buffer_size, mtLogging); + _config_string[0] = '\0'; +} + +void LogOutput::set_config_string(const char* string) { + assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(), + "Must hold configuration lock to modify config string"); + + os::free(_config_string); + _config_string = os::strdup(string, mtLogging); + _config_string_buffer_size = strlen(_config_string) + 1; +} + +void LogOutput::add_to_config_string(const LogTagSet* ts, LogLevelType level) { + assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(), + "Must hold configuration lock to modify config string"); + + if (_config_string_buffer_size < InitialConfigBufferSize) { + _config_string_buffer_size = InitialConfigBufferSize; + _config_string = REALLOC_C_HEAP_ARRAY(char, _config_string, _config_string_buffer_size, mtLogging); + } + + size_t offset = strlen(_config_string); + for (;;) { + int ret = ts->label(_config_string + offset, _config_string_buffer_size - offset, "+"); + if (ret == -1) { + // Double the buffer size and retry + _config_string_buffer_size *= 2; + _config_string = REALLOC_C_HEAP_ARRAY(char, _config_string, _config_string_buffer_size, mtLogging); + continue; + } + break; + }; + + offset = strlen(_config_string); + for (;;) { + int ret = jio_snprintf(_config_string + offset, _config_string_buffer_size - offset, "=%s,", LogLevel::name(level)); + if (ret == -1) { + _config_string_buffer_size *= 2; + _config_string = REALLOC_C_HEAP_ARRAY(char, _config_string, _config_string_buffer_size, mtLogging); + continue; + } + break; + } } diff --git a/hotspot/src/share/vm/logging/logOutput.hpp b/hotspot/src/share/vm/logging/logOutput.hpp index cb9d52ef933..eaf02fb2fbd 100644 --- a/hotspot/src/share/vm/logging/logOutput.hpp +++ b/hotspot/src/share/vm/logging/logOutput.hpp @@ -25,18 +25,24 @@ #define SHARE_VM_LOGGING_LOGOUTPUT_HPP #include "logging/logDecorators.hpp" +#include "logging/logLevel.hpp" #include "memory/allocation.hpp" #include "utilities/globalDefinitions.hpp" class LogDecorations; +class LogTagSet; // The base class/interface for log outputs. // Keeps track of the latest configuration string, // and its selected decorators. class LogOutput : public CHeapObj { + private: + static const size_t InitialConfigBufferSize = 256; + char* _config_string; + size_t _config_string_buffer_size; + protected: LogDecorators _decorators; - char* _config_string; public: static LogOutput* const Stdout; @@ -54,10 +60,16 @@ class LogOutput : public CHeapObj { return _config_string; } - LogOutput() : _config_string(NULL) { + LogOutput() : _config_string(NULL), _config_string_buffer_size(0) { } virtual ~LogOutput(); + + // Clears any previous config description in preparation of reconfiguration. + void clear_config_string(); + // Adds the tagset on the given level to the config description (e.g. "tag1+tag2=level"). + void add_to_config_string(const LogTagSet* ts, LogLevelType level); + // Replaces the current config description with the given string. void set_config_string(const char* string); virtual const char* name() const = 0; diff --git a/hotspot/src/share/vm/logging/logOutputList.cpp b/hotspot/src/share/vm/logging/logOutputList.cpp index e30f077293c..e2f8a6f1559 100644 --- a/hotspot/src/share/vm/logging/logOutputList.cpp +++ b/hotspot/src/share/vm/logging/logOutputList.cpp @@ -59,7 +59,7 @@ void LogOutputList::set_output_level(LogOutput* output, LogLevelType level) { } } -LogOutputList::LogOutputNode* LogOutputList::find(LogOutput* output) { +LogOutputList::LogOutputNode* LogOutputList::find(const LogOutput* output) const { for (LogOutputNode* node = _level_start[LogLevel::Last]; node != NULL; node = node->_next) { if (output == node->_value) { return node; diff --git a/hotspot/src/share/vm/logging/logOutputList.hpp b/hotspot/src/share/vm/logging/logOutputList.hpp index 900b7d9fcdb..c20edd56bf0 100644 --- a/hotspot/src/share/vm/logging/logOutputList.hpp +++ b/hotspot/src/share/vm/logging/logOutputList.hpp @@ -56,7 +56,7 @@ class LogOutputList VALUE_OBJ_CLASS_SPEC { LogOutputNode* _level_start[LogLevel::Count]; volatile jint _active_readers; - LogOutputNode* find(LogOutput* output); + LogOutputNode* find(const LogOutput* output) const; void remove_output(LogOutputNode* node); void add_output(LogOutput* output, LogLevelType level); void update_output_level(LogOutputNode* node, LogLevelType level); @@ -69,10 +69,18 @@ class LogOutputList VALUE_OBJ_CLASS_SPEC { } // Test if the outputlist has an output for the given level. - bool is_level(LogLevelType level) { + bool is_level(LogLevelType level) const { return _level_start[level] != NULL; } + LogLevelType level_for(const LogOutput* output) const { + LogOutputNode* node = this->find(output); + if (node == NULL) { + return LogLevel::Off; + } + return node->_level; + } + // Set (add/update/remove) the output to the specified level. void set_output_level(LogOutput* output, LogLevelType level); diff --git a/hotspot/src/share/vm/logging/logTag.hpp b/hotspot/src/share/vm/logging/logTag.hpp index e5ca5de2e68..6f8cf888bde 100644 --- a/hotspot/src/share/vm/logging/logTag.hpp +++ b/hotspot/src/share/vm/logging/logTag.hpp @@ -33,8 +33,10 @@ #define LOG_TAG_LIST \ LOG_TAG(classinit) \ LOG_TAG(defaultmethods) \ + LOG_TAG(gc) \ LOG_TAG(logging) \ - LOG_TAG(safepoint) + LOG_TAG(safepoint) \ + LOG_TAG(vmoperation) #define PREFIX_LOG_TAG(T) (LogTag::T) diff --git a/hotspot/src/share/vm/logging/logTagLevelExpression.cpp b/hotspot/src/share/vm/logging/logTagLevelExpression.cpp index d3dd9b283f0..2213ee3e70b 100644 --- a/hotspot/src/share/vm/logging/logTagLevelExpression.cpp +++ b/hotspot/src/share/vm/logging/logTagLevelExpression.cpp @@ -29,10 +29,6 @@ const char* LogTagLevelExpression::DefaultExpressionString = "all"; -LogTagLevelExpression::~LogTagLevelExpression() { - os::free(_string); -} - void LogTagLevelExpression::clear() { _ntags = 0; _ncombinations = 0; @@ -43,8 +39,6 @@ void LogTagLevelExpression::clear() { _tags[combination][tag] = LogTag::__NO_TAG; } } - os::free(_string); - _string = NULL; } bool LogTagLevelExpression::parse(const char* str, outputStream* errstream) { @@ -131,15 +125,13 @@ bool LogTagLevelExpression::parse(const char* str, outputStream* errstream) { new_combination(); } - // Save the (unmodified) string for printing purposes. - _string = copy; - strcpy(_string, str); - + os::free(copy); return success; } LogLevelType LogTagLevelExpression::level_for(const LogTagSet& ts) const { - LogLevelType level = LogLevel::Off; + // Return NotMentioned if the given tagset isn't covered by this expression. + LogLevelType level = LogLevel::NotMentioned; for (size_t combination = 0; combination < _ncombinations; combination++) { bool contains_all = true; size_t tag_idx; diff --git a/hotspot/src/share/vm/logging/logTagLevelExpression.hpp b/hotspot/src/share/vm/logging/logTagLevelExpression.hpp index 5fae414e5a1..34163222989 100644 --- a/hotspot/src/share/vm/logging/logTagLevelExpression.hpp +++ b/hotspot/src/share/vm/logging/logTagLevelExpression.hpp @@ -24,6 +24,7 @@ #ifndef SHARE_VM_LOGGING_LOGTAGLEVELEXPRESSION_HPP #define SHARE_VM_LOGGING_LOGTAGLEVELEXPRESSION_HPP +#include "logging/logConfiguration.hpp" #include "logging/logLevel.hpp" #include "logging/logTag.hpp" #include "memory/allocation.hpp" @@ -35,6 +36,7 @@ class LogTagSet; // Class used to temporary encode a 'what'-expression during log configuration. // Consists of a combination of tags and levels, e.g. "tag1+tag2=level1,tag3*=level2". class LogTagLevelExpression : public StackObj { + friend void LogConfiguration::configure_stdout(LogLevelType, bool, ...); private: static const size_t MaxCombinations = 32; static const char* DefaultExpressionString; @@ -43,7 +45,6 @@ class LogTagLevelExpression : public StackObj { LogTagType _tags[MaxCombinations][LogTag::MaxTags]; LogLevelType _level[MaxCombinations]; bool _allow_other_tags[MaxCombinations]; - char* _string; void new_combination() { _ncombinations++; @@ -66,14 +67,9 @@ class LogTagLevelExpression : public StackObj { void clear(); public: - LogTagLevelExpression() : _ntags(0), _ncombinations(0), _string(NULL) { + LogTagLevelExpression() : _ntags(0), _ncombinations(0) { } - const char* to_string() const { - return _string; - } - - ~LogTagLevelExpression(); bool parse(const char* str, outputStream* errstream = NULL); LogLevelType level_for(const LogTagSet& ts) const; }; diff --git a/hotspot/src/share/vm/logging/logTagSet.cpp b/hotspot/src/share/vm/logging/logTagSet.cpp index 5a975c58073..7d22c480e32 100644 --- a/hotspot/src/share/vm/logging/logTagSet.cpp +++ b/hotspot/src/share/vm/logging/logTagSet.cpp @@ -49,7 +49,7 @@ LogTagSet::LogTagSet(LogTagType t0, LogTagType t1, LogTagType t2, LogTagType t3, _output_list.set_output_level(LogOutput::Stderr, LogLevel::Default); } -bool LogTagSet::is_level(LogLevelType level) { +bool LogTagSet::is_level(LogLevelType level) const { return _output_list.is_level(level); } @@ -77,11 +77,11 @@ void LogTagSet::log(LogLevelType level, const char* msg) { } } -int LogTagSet::label(char* buf, size_t len) { +int LogTagSet::label(char* buf, size_t len, const char* separator) const { int tot_written = 0; for (size_t i = 0; i < _ntags; i++) { int written = jio_snprintf(buf + tot_written, len - tot_written, "%s%s", - (i == 0 ? "" : ","), + (i == 0 ? "" : separator), LogTag::name(_tag[i])); if (written < 0) { return -1; diff --git a/hotspot/src/share/vm/logging/logTagSet.hpp b/hotspot/src/share/vm/logging/logTagSet.hpp index e25512f7630..3259e49eadb 100644 --- a/hotspot/src/share/vm/logging/logTagSet.hpp +++ b/hotspot/src/share/vm/logging/logTagSet.hpp @@ -77,17 +77,21 @@ class LogTagSet VALUE_OBJ_CLASS_SPEC { return false; } + LogLevelType level_for(const LogOutput* output) const { + return _output_list.level_for(output); + } + void set_output_level(LogOutput* output, LogLevelType level) { _output_list.set_output_level(output, level); } // Refresh the decorators for this tagset to contain the decorators for all // of its current outputs combined with the given decorators. - void update_decorators(const LogDecorators& decorator); + void update_decorators(const LogDecorators& decorator = LogDecorators::None); - int label(char *buf, size_t len); + int label(char *buf, size_t len, const char* separator = ",") const; bool has_output(const LogOutput* output); - bool is_level(LogLevelType level); + bool is_level(LogLevelType level) const; void log(LogLevelType level, const char* msg); }; diff --git a/hotspot/src/share/vm/memory/heap.cpp b/hotspot/src/share/vm/memory/heap.cpp index 2c4a661e359..1910ed49e18 100644 --- a/hotspot/src/share/vm/memory/heap.cpp +++ b/hotspot/src/share/vm/memory/heap.cpp @@ -47,7 +47,10 @@ CodeHeap::CodeHeap(const char* name, const int code_blob_type) _freelist_segments = 0; _freelist_length = 0; _max_allocated_capacity = 0; - _was_full = false; + _blob_count = 0; + _nmethod_count = 0; + _adapter_count = 0; + _full_count = 0; } @@ -185,6 +188,7 @@ void* CodeHeap::allocate(size_t instance_size) { assert(!block->free(), "must be marked free"); DEBUG_ONLY(memset((void*)block->allocated_space(), badCodeHeapNewVal, instance_size)); _max_allocated_capacity = MAX2(_max_allocated_capacity, allocated_capacity()); + _blob_count++; return block->allocated_space(); } @@ -198,6 +202,7 @@ void* CodeHeap::allocate(size_t instance_size) { _next_segment += number_of_segments; DEBUG_ONLY(memset((void *)b->allocated_space(), badCodeHeapNewVal, instance_size)); _max_allocated_capacity = MAX2(_max_allocated_capacity, allocated_capacity()); + _blob_count++; return b->allocated_space(); } else { return NULL; diff --git a/hotspot/src/share/vm/memory/heap.hpp b/hotspot/src/share/vm/memory/heap.hpp index 6f06e94f202..a34cd12ed85 100644 --- a/hotspot/src/share/vm/memory/heap.hpp +++ b/hotspot/src/share/vm/memory/heap.hpp @@ -100,7 +100,11 @@ class CodeHeap : public CHeapObj { const char* _name; // Name of the CodeHeap const int _code_blob_type; // CodeBlobType it contains - bool _was_full; // True if the code heap was full + int _blob_count; // Number of CodeBlobs + int _nmethod_count; // Number of nmethods + int _adapter_count; // Number of adapters + int _full_count; // Number of times the code heap was full + enum { free_sentinel = 0xFF }; @@ -179,8 +183,13 @@ class CodeHeap : public CHeapObj { // Debugging / Profiling const char* name() const { return _name; } - bool was_full() { return _was_full; } - void report_full() { _was_full = true; } + int blob_count() { return _blob_count; } + int nmethod_count() { return _nmethod_count; } + void set_nmethod_count(int count) { _nmethod_count = count; } + int adapter_count() { return _adapter_count; } + void set_adapter_count(int count) { _adapter_count = count; } + int full_count() { return _full_count; } + void report_full() { _full_count++; } private: size_t heap_unallocated_capacity() const; diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp index f4e40db3193..74ee0877ba9 100644 --- a/hotspot/src/share/vm/memory/metaspace.cpp +++ b/hotspot/src/share/vm/memory/metaspace.cpp @@ -3230,36 +3230,6 @@ void Metaspace::global_initialize() { SharedMiscDataSize = align_size_up(SharedMiscDataSize, max_alignment); SharedMiscCodeSize = align_size_up(SharedMiscCodeSize, max_alignment); - // make sure SharedReadOnlySize and SharedReadWriteSize are not less than - // the minimum values. - if (SharedReadOnlySize < MetaspaceShared::min_ro_size){ - report_out_of_shared_space(SharedReadOnly); - } - - if (SharedReadWriteSize < MetaspaceShared::min_rw_size){ - report_out_of_shared_space(SharedReadWrite); - } - - // the min_misc_data_size and min_misc_code_size estimates are based on - // MetaspaceShared::generate_vtable_methods(). - // The minimum size only accounts for the vtable methods. Any size less than the - // minimum required size would cause vm crash when allocating the vtable methods. - uint min_misc_data_size = align_size_up( - MetaspaceShared::num_virtuals * MetaspaceShared::vtbl_list_size * sizeof(void*), max_alignment); - - if (SharedMiscDataSize < min_misc_data_size) { - report_out_of_shared_space(SharedMiscData); - } - - uintx min_misc_code_size = align_size_up( - (MetaspaceShared::num_virtuals * MetaspaceShared::vtbl_list_size) * - (sizeof(void*) + MetaspaceShared::vtbl_method_size) + MetaspaceShared::vtbl_common_code_size, - max_alignment); - - if (SharedMiscCodeSize < min_misc_code_size) { - report_out_of_shared_space(SharedMiscCode); - } - // Initialize with the sum of the shared space sizes. The read-only // and read write metaspace chunks will be allocated out of this and the // remainder is the misc code and data chunks. diff --git a/hotspot/src/share/vm/memory/metaspaceShared.cpp b/hotspot/src/share/vm/memory/metaspaceShared.cpp index 0af64dbed96..a703ad4e607 100644 --- a/hotspot/src/share/vm/memory/metaspaceShared.cpp +++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp @@ -23,6 +23,8 @@ */ #include "precompiled.hpp" +#include "classfile/classListParser.hpp" +#include "classfile/classLoaderExt.hpp" #include "classfile/dictionary.hpp" #include "classfile/loaderConstraints.hpp" #include "classfile/placeholders.hpp" @@ -42,6 +44,7 @@ #include "runtime/signature.hpp" #include "runtime/vmThread.hpp" #include "runtime/vm_operations.hpp" +#include "utilities/defaultStream.hpp" #include "utilities/hashtable.inline.hpp" int MetaspaceShared::_max_alignment = 0; @@ -97,6 +100,10 @@ static void collect_classes(Klass* k) { } } +static void collect_classes2(Klass* k, ClassLoaderData* class_data) { + collect_classes(k); +} + static void remove_unshareable_in_classes() { for (int i = 0; i < _global_klass_objects->length(); i++) { Klass* k = _global_klass_objects->at(i); @@ -422,12 +429,15 @@ private: VirtualSpace _mc_vs; CompactHashtableWriter* _string_cht; GrowableArray *_string_regions; + char* _md_alloc_low; + char* _md_alloc_top; + char* _md_alloc_max; + static VM_PopulateDumpSharedSpace* _instance; public: VM_PopulateDumpSharedSpace(ClassLoaderData* loader_data, GrowableArray *class_promote_order) : _loader_data(loader_data) { - // Split up and initialize the misc code and data spaces ReservedSpace* shared_rs = MetaspaceShared::shared_rs(); size_t metadata_size = SharedReadOnlySize + SharedReadWriteSize; @@ -440,11 +450,43 @@ public: _md_vs.initialize(md_rs, SharedMiscDataSize); _mc_vs.initialize(mc_rs, SharedMiscCodeSize); _class_promote_order = class_promote_order; + + _md_alloc_low = _md_vs.low(); + _md_alloc_top = _md_alloc_low + sizeof(char*); + _md_alloc_max = _md_vs.low() + SharedMiscDataSize; + + assert(_instance == NULL, "must be singleton"); + _instance = this; + } + + ~VM_PopulateDumpSharedSpace() { + assert(_instance == this, "must be singleton"); + _instance = NULL; + } + + static VM_PopulateDumpSharedSpace* instance() { + assert(_instance != NULL, "sanity"); + return _instance; } VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; } void doit(); // outline because gdb sucks + char* misc_data_space_alloc(size_t num_bytes) { + size_t alignment = sizeof(char*); + num_bytes = align_size_up(num_bytes, alignment); + _md_alloc_top = (char*)align_ptr_up(_md_alloc_top, alignment); + if (_md_alloc_top + num_bytes > _md_alloc_max) { + report_out_of_shared_space(SharedMiscData); + } + + char* p = _md_alloc_top; + _md_alloc_top += num_bytes; + + memset(p, 0, num_bytes); + return p; + } + private: void handle_misc_data_space_failure(bool success) { if (!success) { @@ -453,6 +495,7 @@ private: } }; // class VM_PopulateDumpSharedSpace +VM_PopulateDumpSharedSpace* VM_PopulateDumpSharedSpace::_instance; void VM_PopulateDumpSharedSpace::doit() { Thread* THREAD = VMThread::vm_thread(); @@ -475,7 +518,11 @@ void VM_PopulateDumpSharedSpace::doit() { // that so we don't have to walk the SystemDictionary again. _global_klass_objects = new GrowableArray(1000); Universe::basic_type_classes_do(collect_classes); - SystemDictionary::classes_do(collect_classes); + + // Need to call SystemDictionary::classes_do(void f(Klass*, ClassLoaderData*)) + // as we may have some classes with NULL ClassLoaderData* in the dictionary. Other + // variants of SystemDictionary::classes_do will skip those classes. + SystemDictionary::classes_do(collect_classes2); tty->print_cr("Number of classes %d", _global_klass_objects->length()); { @@ -515,6 +562,10 @@ void VM_PopulateDumpSharedSpace::doit() { char* mc_top = mc_low; char* mc_end = _mc_vs.high(); + assert(_md_alloc_top != NULL, "sanity"); + *(char**)_md_alloc_low = _md_alloc_top; + md_top = _md_alloc_top; + // Reserve space for the list of Klass*s whose vtables are used // for patching others as needed. @@ -735,6 +786,7 @@ void MetaspaceShared::prepare_for_dumping() { void MetaspaceShared::preload_and_dump(TRAPS) { TraceTime timer("Dump Shared Spaces", TraceStartupTime); ResourceMark rm; + char class_list_path_str[JVM_MAXPATHLEN]; tty->print_cr("Allocated shared space: " SIZE_FORMAT " bytes at " PTR_FORMAT, MetaspaceShared::shared_rs()->size(), @@ -747,7 +799,6 @@ void MetaspaceShared::preload_and_dump(TRAPS) { // Construct the path to the class list (in jre/lib) // Walk up two directories from the location of the VM and // optionally tack on "lib" (depending on platform) - char class_list_path_str[JVM_MAXPATHLEN]; os::jvm_path(class_list_path_str, sizeof(class_list_path_str)); for (int i = 0; i < 3; i++) { char *end = strrchr(class_list_path_str, *os::file_separator()); @@ -785,6 +836,11 @@ void MetaspaceShared::preload_and_dump(TRAPS) { static const char map_entry_array_sig[] = "[Ljava/util/Map$Entry;"; SymbolTable::new_permanent_symbol(map_entry_array_sig, THREAD); + // Need to allocate the op here: + // op.misc_data_space_alloc() will be called during preload_and_dump(). + ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); + VM_PopulateDumpSharedSpace op(loader_data, class_promote_order); + tty->print_cr("Loading classes to share ..."); _has_error_classes = false; class_count += preload_and_dump(class_list_path, class_promote_order, @@ -809,44 +865,27 @@ void MetaspaceShared::preload_and_dump(TRAPS) { link_and_cleanup_shared_classes(CATCH); tty->print_cr("Rewriting and linking classes: done"); - // Create and dump the shared spaces. Everything so far is loaded - // with the null class loader. - ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); - VM_PopulateDumpSharedSpace op(loader_data, class_promote_order); VMThread::execute(&op); - // Since various initialization steps have been undone by this process, // it is not reasonable to continue running a java process. exit(0); } -int MetaspaceShared::preload_and_dump(const char * class_list_path, + +int MetaspaceShared::preload_and_dump(const char* class_list_path, GrowableArray* class_promote_order, TRAPS) { - FILE* file = fopen(class_list_path, "r"); - char class_name[256]; + ClassListParser parser(class_list_path); int class_count = 0; - if (file != NULL) { - while ((fgets(class_name, sizeof class_name, file)) != NULL) { - if (*class_name == '#') { // comment - continue; - } - // Remove trailing newline - size_t name_len = strlen(class_name); - if (class_name[name_len-1] == '\n') { - class_name[name_len-1] = '\0'; - } + while (parser.parse_one_line()) { + Klass* klass = ClassLoaderExt::load_one_class(&parser, THREAD); - // Got a class name - load it. - TempNewSymbol class_name_symbol = SymbolTable::new_permanent_symbol(class_name, THREAD); - guarantee(!HAS_PENDING_EXCEPTION, "Exception creating a symbol."); - Klass* klass = SystemDictionary::resolve_or_null(class_name_symbol, - THREAD); CLEAR_PENDING_EXCEPTION; if (klass != NULL) { if (PrintSharedSpaces && Verbose && WizardMode) { - tty->print_cr("Shared spaces preloaded: %s", class_name); + ResourceMark rm; + tty->print_cr("Shared spaces preloaded: %s", klass->external_name()); } InstanceKlass* ik = InstanceKlass::cast(klass); @@ -862,17 +901,8 @@ int MetaspaceShared::preload_and_dump(const char * class_list_path, guarantee(!HAS_PENDING_EXCEPTION, "exception in link_class"); class_count++; - } else { - //tty->print_cr("Preload failed: %s", class_name); } } - fclose(file); - } else { - char errmsg[JVM_MAXPATHLEN]; - os::lasterror(errmsg, JVM_MAXPATHLEN); - tty->print_cr("Loading classlist failed: %s", errmsg); - exit(1); - } return class_count; } @@ -908,6 +938,11 @@ bool MetaspaceShared::try_link_class(InstanceKlass* ik, TRAPS) { } } +// Allocate misc data blocks during dumping. +char* MetaspaceShared::misc_data_space_alloc(size_t num_bytes) { + return VM_PopulateDumpSharedSpace::instance()->misc_data_space_alloc(num_bytes); +} + // Closure for serializing initialization data in from a data area // (ptr_array) read from the shared file. @@ -1033,6 +1068,8 @@ void MetaspaceShared::initialize_shared_spaces() { char* buffer = mapinfo->header()->region_addr(md); + buffer = *((char**)buffer); // skip over the md_alloc'ed blocks + // Skip over (reserve space for) a list of addresses of C++ vtables // for Klass objects. They get filled in later. diff --git a/hotspot/src/share/vm/memory/metaspaceShared.hpp b/hotspot/src/share/vm/memory/metaspaceShared.hpp index dbb328ee4e8..bfefce6396a 100644 --- a/hotspot/src/share/vm/memory/metaspaceShared.hpp +++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp @@ -32,14 +32,55 @@ #include "utilities/exceptions.hpp" #include "utilities/macros.hpp" -#define LargeSharedArchiveSize (300*M) -#define HugeSharedArchiveSize (800*M) -#define ReadOnlyRegionPercentage 0.4 -#define ReadWriteRegionPercentage 0.55 -#define MiscDataRegionPercentage 0.03 -#define MiscCodeRegionPercentage 0.02 -#define LargeThresholdClassCount 5000 -#define HugeThresholdClassCount 40000 +#define DEFAULT_VTBL_LIST_SIZE (17) // number of entries in the shared space vtable list. +#define DEFAULT_VTBL_VIRTUALS_COUNT (200) // maximum number of virtual functions +// If virtual functions are added to Metadata, +// this number needs to be increased. Also, +// SharedMiscCodeSize will need to be increased. +// The following 2 sizes were based on +// MetaspaceShared::generate_vtable_methods() +#define DEFAULT_VTBL_METHOD_SIZE (16) // conservative size of the mov1 and jmp instructions +// for the x64 platform +#define DEFAULT_VTBL_COMMON_CODE_SIZE (1*K) // conservative size of the "common_code" for the x64 platform + +#define DEFAULT_SHARED_READ_WRITE_SIZE (NOT_LP64(12*M) LP64_ONLY(16*M)) +#define MIN_SHARED_READ_WRITE_SIZE (NOT_LP64(7*M) LP64_ONLY(12*M)) + +#define DEFAULT_SHARED_READ_ONLY_SIZE (NOT_LP64(12*M) LP64_ONLY(16*M)) +#define MIN_SHARED_READ_ONLY_SIZE (NOT_LP64(8*M) LP64_ONLY(9*M)) + +// the MIN_SHARED_MISC_DATA_SIZE and MIN_SHARED_MISC_CODE_SIZE estimates are based on +// MetaspaceShared::generate_vtable_methods(). +// The minimum size only accounts for the vtable methods. Any size less than the +// minimum required size would cause vm crash when allocating the vtable methods. +#define SHARED_MISC_SIZE_FOR(size) (DEFAULT_VTBL_VIRTUALS_COUNT*DEFAULT_VTBL_LIST_SIZE*size) + +#define DEFAULT_SHARED_MISC_DATA_SIZE (NOT_LP64(2*M) LP64_ONLY(4*M)) +#define MIN_SHARED_MISC_DATA_SIZE (SHARED_MISC_SIZE_FOR(sizeof(void*))) + +#define DEFAULT_SHARED_MISC_CODE_SIZE (120*K) +#define MIN_SHARED_MISC_CODE_SIZE (SHARED_MISC_SIZE_FOR(sizeof(void*))+SHARED_MISC_SIZE_FOR(DEFAULT_VTBL_METHOD_SIZE)+DEFAULT_VTBL_COMMON_CODE_SIZE) + +#define DEFAULT_COMBINED_SIZE (DEFAULT_SHARED_READ_WRITE_SIZE+DEFAULT_SHARED_READ_ONLY_SIZE+DEFAULT_SHARED_MISC_DATA_SIZE+DEFAULT_SHARED_MISC_CODE_SIZE) + +// the max size is the MAX size (ie. 0x7FFFFFFF) - the total size of +// the other 3 sections - page size (to avoid overflow in case the final +// size will get aligned up on page size) +#define SHARED_PAGE ((size_t)os::vm_page_size()) +#define MAX_SHARED_DELTA (0x7FFFFFFF) +#define MAX_SHARED_READ_WRITE_SIZE (MAX_SHARED_DELTA-(MIN_SHARED_READ_ONLY_SIZE+MIN_SHARED_MISC_DATA_SIZE+MIN_SHARED_MISC_CODE_SIZE)-SHARED_PAGE) +#define MAX_SHARED_READ_ONLY_SIZE (MAX_SHARED_DELTA-(MIN_SHARED_READ_WRITE_SIZE+MIN_SHARED_MISC_DATA_SIZE+MIN_SHARED_MISC_CODE_SIZE)-SHARED_PAGE) +#define MAX_SHARED_MISC_DATA_SIZE (MAX_SHARED_DELTA-(MIN_SHARED_READ_WRITE_SIZE+MIN_SHARED_READ_ONLY_SIZE+MIN_SHARED_MISC_CODE_SIZE)-SHARED_PAGE) +#define MAX_SHARED_MISC_CODE_SIZE (MAX_SHARED_DELTA-(MIN_SHARED_READ_WRITE_SIZE+MIN_SHARED_READ_ONLY_SIZE+MIN_SHARED_MISC_DATA_SIZE)-SHARED_PAGE) + +#define LargeSharedArchiveSize (300*M) +#define HugeSharedArchiveSize (800*M) +#define ReadOnlyRegionPercentage 0.4 +#define ReadWriteRegionPercentage 0.55 +#define MiscDataRegionPercentage 0.03 +#define MiscCodeRegionPercentage 0.02 +#define LargeThresholdClassCount 5000 +#define HugeThresholdClassCount 40000 #define SET_ESTIMATED_SIZE(type, region) \ Shared ##region## Size = FLAG_IS_DEFAULT(Shared ##region## Size) ? \ @@ -69,21 +110,10 @@ class MetaspaceShared : AllStatic { static bool _archive_loading_failed; public: enum { - vtbl_list_size = 17, // number of entries in the shared space vtable list. - num_virtuals = 200, // maximum number of virtual functions - // If virtual functions are added to Metadata, - // this number needs to be increased. Also, - // SharedMiscCodeSize will need to be increased. - // The following 2 sizes were based on - // MetaspaceShared::generate_vtable_methods() - vtbl_method_size = 16, // conservative size of the mov1 and jmp instructions - // for the x64 platform - vtbl_common_code_size = (1*K) // conservative size of the "common_code" for the x64 platform - }; - - enum { - min_ro_size = NOT_LP64(8*M) LP64_ONLY(9*M), // minimum ro and rw regions sizes based on dumping - min_rw_size = NOT_LP64(7*M) LP64_ONLY(12*M) // of a shared archive using the default classlist + vtbl_list_size = DEFAULT_VTBL_LIST_SIZE, + num_virtuals = DEFAULT_VTBL_VIRTUALS_COUNT, + vtbl_method_size = DEFAULT_VTBL_METHOD_SIZE, + vtbl_common_code_size = DEFAULT_VTBL_COMMON_CODE_SIZE }; enum { @@ -160,5 +190,8 @@ class MetaspaceShared : AllStatic { static int count_class(const char* classlist_file); static void estimate_regions_size() NOT_CDS_RETURN; + + // Allocate a block of memory from the "md" region. + static char* misc_data_space_alloc(size_t num_bytes); }; #endif // SHARE_VM_MEMORY_METASPACESHARED_HPP diff --git a/hotspot/src/share/vm/memory/universe.cpp b/hotspot/src/share/vm/memory/universe.cpp index 7ec9cfa3a99..d06a7fbddc3 100644 --- a/hotspot/src/share/vm/memory/universe.cpp +++ b/hotspot/src/share/vm/memory/universe.cpp @@ -115,6 +115,7 @@ LatestMethodCache* Universe::_finalizer_register_cache = NULL; LatestMethodCache* Universe::_loader_addClass_cache = NULL; LatestMethodCache* Universe::_pd_implies_cache = NULL; LatestMethodCache* Universe::_throw_illegal_access_error_cache = NULL; +LatestMethodCache* Universe::_do_stack_walk_cache = NULL; oop Universe::_out_of_memory_error_java_heap = NULL; oop Universe::_out_of_memory_error_metaspace = NULL; oop Universe::_out_of_memory_error_class_metaspace = NULL; @@ -240,6 +241,7 @@ void Universe::serialize(SerializeClosure* f, bool do_all) { _loader_addClass_cache->serialize(f); _pd_implies_cache->serialize(f); _throw_illegal_access_error_cache->serialize(f); + _do_stack_walk_cache->serialize(f); } void Universe::check_alignment(uintx size, uintx alignment, const char* name) { @@ -674,6 +676,7 @@ jint universe_init() { Universe::_loader_addClass_cache = new LatestMethodCache(); Universe::_pd_implies_cache = new LatestMethodCache(); Universe::_throw_illegal_access_error_cache = new LatestMethodCache(); + Universe::_do_stack_walk_cache = new LatestMethodCache(); if (UseSharedSpaces) { // Read the data structures supporting the shared spaces (shared @@ -1048,6 +1051,17 @@ bool universe_post_init() { SystemDictionary::ProtectionDomain_klass(), m); } + // Setup method for stack walking + InstanceKlass::cast(SystemDictionary::AbstractStackWalker_klass())->link_class(CHECK_false); + m = InstanceKlass::cast(SystemDictionary::AbstractStackWalker_klass())-> + find_method(vmSymbols::doStackWalk_name(), + vmSymbols::doStackWalk_signature()); + // Allow NULL which should only happen with bootstrapping. + if (m != NULL) { + Universe::_do_stack_walk_cache->init( + SystemDictionary::AbstractStackWalker_klass(), m); + } + // This needs to be done before the first scavenge/gc, since // it's an input to soft ref clearing policy. { diff --git a/hotspot/src/share/vm/memory/universe.hpp b/hotspot/src/share/vm/memory/universe.hpp index 854a818883c..f2dc42e6cfe 100644 --- a/hotspot/src/share/vm/memory/universe.hpp +++ b/hotspot/src/share/vm/memory/universe.hpp @@ -149,6 +149,7 @@ class Universe: AllStatic { static LatestMethodCache* _loader_addClass_cache; // method for registering loaded classes in class loader vector static LatestMethodCache* _pd_implies_cache; // method for checking protection domain attributes static LatestMethodCache* _throw_illegal_access_error_cache; // Unsafe.throwIllegalAccessError() method + static LatestMethodCache* _do_stack_walk_cache; // method for stack walker callback // preallocated error objects (no backtrace) static oop _out_of_memory_error_java_heap; @@ -314,6 +315,8 @@ class Universe: AllStatic { static Method* protection_domain_implies_method() { return _pd_implies_cache->get_method(); } static Method* throw_illegal_access_error() { return _throw_illegal_access_error_cache->get_method(); } + static Method* do_stack_walk_method() { return _do_stack_walk_cache->get_method(); } + static oop null_ptr_exception_instance() { return _null_ptr_exception_instance; } static oop arithmetic_exception_instance() { return _arithmetic_exception_instance; } static oop virtual_machine_error_instance() { return _virtual_machine_error_instance; } diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp index 9fe297aca04..a0c9a1be5e1 100644 --- a/hotspot/src/share/vm/oops/instanceKlass.cpp +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp @@ -27,6 +27,7 @@ #include "classfile/systemDictionary.hpp" #include "classfile/verifier.hpp" #include "classfile/vmSymbols.hpp" +#include "code/dependencyContext.hpp" #include "compiler/compileBroker.hpp" #include "gc/shared/collectedHeap.inline.hpp" #include "gc/shared/specialized_oop_closures.hpp" @@ -204,7 +205,6 @@ InstanceKlass::InstanceKlass(int vtable_len, int iksize = InstanceKlass::size(vtable_len, itable_len, nonstatic_oop_map_size, access_flags.is_interface(), is_anonymous); - set_vtable_length(vtable_len); set_itable_length(itable_len); set_static_field_size(static_field_size); @@ -233,7 +233,7 @@ InstanceKlass::InstanceKlass(int vtable_len, set_static_oop_field_count(0); set_nonstatic_field_size(0); set_is_marked_dependent(false); - set_has_unloaded_dependent(false); + _dep_context = DependencyContext::EMPTY; set_init_state(InstanceKlass::allocated); set_init_thread(NULL); set_reference_type(rt); @@ -247,7 +247,6 @@ InstanceKlass::InstanceKlass(int vtable_len, set_annotations(NULL); set_jvmti_cached_class_field_map(NULL); set_initial_method_idnum(0); - _dependencies = NULL; set_jvmti_cached_class_field_map(NULL); set_cached_class_file(NULL); set_initial_method_idnum(0); @@ -816,10 +815,13 @@ void InstanceKlass::initialize_impl(instanceKlassHandle this_k, TRAPS) { } } + // If C is an interface that declares a non-abstract, non-static method, + // the initialization of a class (not an interface) that implements C directly or + // indirectly. // Recursively initialize any superinterfaces that declare default methods // Only need to recurse if has_default_methods which includes declaring and // inheriting default methods - if (this_k->has_default_methods()) { + if (!this_k->is_interface() && this_k->has_default_methods()) { this_k->initialize_super_interfaces(this_k, CHECK); } @@ -1857,200 +1859,30 @@ jmethodID InstanceKlass::jmethod_id_or_null(Method* method) { return id; } -int nmethodBucket::decrement() { - return Atomic::add(-1, (volatile int *)&_count); +inline DependencyContext InstanceKlass::dependencies() { + DependencyContext dep_context(&_dep_context); + return dep_context; } -// -// Walk the list of dependent nmethods searching for nmethods which -// are dependent on the changes that were passed in and mark them for -// deoptimization. Returns the number of nmethods found. -// -int nmethodBucket::mark_dependent_nmethods(nmethodBucket* deps, DepChange& changes) { - assert_locked_or_safepoint(CodeCache_lock); - int found = 0; - for (nmethodBucket* b = deps; b != NULL; b = b->next()) { - nmethod* nm = b->get_nmethod(); - // since dependencies aren't removed until an nmethod becomes a zombie, - // the dependency list may contain nmethods which aren't alive. - if (b->count() > 0 && nm->is_alive() && !nm->is_marked_for_deoptimization() && nm->check_dependency_on(changes)) { - if (TraceDependencies) { - ResourceMark rm; - tty->print_cr("Marked for deoptimization"); - changes.print(); - nm->print(); - nm->print_dependencies(); - } - nm->mark_for_deoptimization(); - found++; - } - } - return found; -} - -// -// Add an nmethodBucket to the list of dependencies for this nmethod. -// It's possible that an nmethod has multiple dependencies on this klass -// so a count is kept for each bucket to guarantee that creation and -// deletion of dependencies is consistent. Returns new head of the list. -// -nmethodBucket* nmethodBucket::add_dependent_nmethod(nmethodBucket* deps, nmethod* nm) { - assert_locked_or_safepoint(CodeCache_lock); - for (nmethodBucket* b = deps; b != NULL; b = b->next()) { - if (nm == b->get_nmethod()) { - b->increment(); - return deps; - } - } - return new nmethodBucket(nm, deps); -} - -// -// Decrement count of the nmethod in the dependency list and remove -// the bucket completely when the count goes to 0. This method must -// find a corresponding bucket otherwise there's a bug in the -// recording of dependencies. Returns true if the bucket was deleted, -// or marked ready for reclaimation. -bool nmethodBucket::remove_dependent_nmethod(nmethodBucket** deps, nmethod* nm, bool delete_immediately) { - assert_locked_or_safepoint(CodeCache_lock); - - nmethodBucket* first = *deps; - nmethodBucket* last = NULL; - - for (nmethodBucket* b = first; b != NULL; b = b->next()) { - if (nm == b->get_nmethod()) { - int val = b->decrement(); - guarantee(val >= 0, "Underflow: %d", val); - if (val == 0) { - if (delete_immediately) { - if (last == NULL) { - *deps = b->next(); - } else { - last->set_next(b->next()); - } - delete b; - } - } - return true; - } - last = b; - } - -#ifdef ASSERT - tty->print_raw_cr("### can't find dependent nmethod"); - nm->print(); -#endif // ASSERT - ShouldNotReachHere(); - return false; -} - -// Convenience overload, for callers that don't want to delete the nmethodBucket entry. -bool nmethodBucket::remove_dependent_nmethod(nmethodBucket* deps, nmethod* nm) { - nmethodBucket** deps_addr = &deps; - return remove_dependent_nmethod(deps_addr, nm, false /* Don't delete */); -} - -// -// Reclaim all unused buckets. Returns new head of the list. -// -nmethodBucket* nmethodBucket::clean_dependent_nmethods(nmethodBucket* deps) { - nmethodBucket* first = deps; - nmethodBucket* last = NULL; - nmethodBucket* b = first; - - while (b != NULL) { - assert(b->count() >= 0, "bucket count: %d", b->count()); - nmethodBucket* next = b->next(); - if (b->count() == 0) { - if (last == NULL) { - first = next; - } else { - last->set_next(next); - } - delete b; - // last stays the same. - } else { - last = b; - } - b = next; - } - return first; -} - -#ifndef PRODUCT -void nmethodBucket::print_dependent_nmethods(nmethodBucket* deps, bool verbose) { - int idx = 0; - for (nmethodBucket* b = deps; b != NULL; b = b->next()) { - nmethod* nm = b->get_nmethod(); - tty->print("[%d] count=%d { ", idx++, b->count()); - if (!verbose) { - nm->print_on(tty, "nmethod"); - tty->print_cr(" } "); - } else { - nm->print(); - nm->print_dependencies(); - tty->print_cr("--- } "); - } - } -} - -bool nmethodBucket::is_dependent_nmethod(nmethodBucket* deps, nmethod* nm) { - for (nmethodBucket* b = deps; b != NULL; b = b->next()) { - if (nm == b->get_nmethod()) { -#ifdef ASSERT - int count = b->count(); - assert(count >= 0, "count shouldn't be negative: %d", count); -#endif - return true; - } - } - return false; -} -#endif //PRODUCT - int InstanceKlass::mark_dependent_nmethods(DepChange& changes) { - assert_locked_or_safepoint(CodeCache_lock); - return nmethodBucket::mark_dependent_nmethods(_dependencies, changes); -} - -void InstanceKlass::clean_dependent_nmethods() { - assert_locked_or_safepoint(CodeCache_lock); - - if (has_unloaded_dependent()) { - _dependencies = nmethodBucket::clean_dependent_nmethods(_dependencies); - set_has_unloaded_dependent(false); - } -#ifdef ASSERT - else { - // Verification - for (nmethodBucket* b = _dependencies; b != NULL; b = b->next()) { - assert(b->count() >= 0, "bucket count: %d", b->count()); - assert(b->count() != 0, "empty buckets need to be cleaned"); - } - } -#endif + return dependencies().mark_dependent_nmethods(changes); } void InstanceKlass::add_dependent_nmethod(nmethod* nm) { - assert_locked_or_safepoint(CodeCache_lock); - _dependencies = nmethodBucket::add_dependent_nmethod(_dependencies, nm); + dependencies().add_dependent_nmethod(nm); } void InstanceKlass::remove_dependent_nmethod(nmethod* nm, bool delete_immediately) { - assert_locked_or_safepoint(CodeCache_lock); - - if (nmethodBucket::remove_dependent_nmethod(&_dependencies, nm, delete_immediately)) { - set_has_unloaded_dependent(true); - } + dependencies().remove_dependent_nmethod(nm, delete_immediately); } #ifndef PRODUCT void InstanceKlass::print_dependent_nmethods(bool verbose) { - nmethodBucket::print_dependent_nmethods(_dependencies, verbose); + dependencies().print_dependent_nmethods(verbose); } bool InstanceKlass::is_dependent_nmethod(nmethod* nm) { - return nmethodBucket::is_dependent_nmethod(_dependencies, nm); + return dependencies().is_dependent_nmethod(nm); } #endif //PRODUCT @@ -2058,7 +1890,9 @@ void InstanceKlass::clean_weak_instanceklass_links(BoolObjectClosure* is_alive) clean_implementors_list(is_alive); clean_method_data(is_alive); - clean_dependent_nmethods(); + // Since GC iterates InstanceKlasses sequentially, it is safe to remove stale entries here. + DependencyContext dep_context(&_dep_context); + dep_context.expunge_stale_entries(); } void InstanceKlass::clean_implementors_list(BoolObjectClosure* is_alive) { @@ -2105,6 +1939,8 @@ void InstanceKlass::remove_unshareable_info() { constants()->remove_unshareable_info(); + assert(_dep_context == DependencyContext::EMPTY, "dependency context is not shareable"); + for (int i = 0; i < methods()->length(); i++) { Method* m = methods()->at(i); m->remove_unshareable_info(); @@ -2233,14 +2069,16 @@ void InstanceKlass::release_C_heap_structures() { } } - // release dependencies - nmethodBucket* b = _dependencies; - _dependencies = NULL; - while (b != NULL) { - nmethodBucket* next = b->next(); - delete b; - b = next; - } + // Release dependencies. + // It is desirable to use DC::remove_all_dependents() here, but, unfortunately, + // it is not safe (see JDK-8143408). The problem is that the klass dependency + // context can contain live dependencies, since there's a race between nmethod & + // klass unloading. If the klass is dead when nmethod unloading happens, relevant + // dependencies aren't removed from the context associated with the class (see + // nmethod::flush_dependencies). It ends up during klass unloading as seemingly + // live dependencies pointing to unloaded nmethods and causes a crash in + // DC::remove_all_dependents() when it touches unloaded nmethod. + dependencies().wipe(); // Deallocate breakpoint records if (breakpoints() != 0x0) { @@ -3561,199 +3399,3 @@ jint InstanceKlass::get_cached_class_file_len() { unsigned char * InstanceKlass::get_cached_class_file_bytes() { return VM_RedefineClasses::get_cached_class_file_bytes(_cached_class_file); } - - -/////////////// Unit tests /////////////// - -#ifndef PRODUCT - -class TestNmethodBucketContext { - public: - nmethod* _nmethodLast; - nmethod* _nmethodMiddle; - nmethod* _nmethodFirst; - - nmethodBucket* _bucketLast; - nmethodBucket* _bucketMiddle; - nmethodBucket* _bucketFirst; - - nmethodBucket* _bucketList; - - TestNmethodBucketContext() { - CodeCache_lock->lock_without_safepoint_check(); - - _nmethodLast = reinterpret_cast(0x8 * 0); - _nmethodMiddle = reinterpret_cast(0x8 * 1); - _nmethodFirst = reinterpret_cast(0x8 * 2); - - _bucketLast = new nmethodBucket(_nmethodLast, NULL); - _bucketMiddle = new nmethodBucket(_nmethodMiddle, _bucketLast); - _bucketFirst = new nmethodBucket(_nmethodFirst, _bucketMiddle); - - _bucketList = _bucketFirst; - } - - ~TestNmethodBucketContext() { - delete _bucketLast; - delete _bucketMiddle; - delete _bucketFirst; - - CodeCache_lock->unlock(); - } -}; - -class TestNmethodBucket { - public: - static void testRemoveDependentNmethodFirstDeleteImmediately() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(&c._bucketList, c._nmethodFirst, true /* delete */); - - assert(c._bucketList == c._bucketMiddle, "check"); - assert(c._bucketList->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next() == NULL, "check"); - - // Cleanup before context is deleted. - c._bucketFirst = NULL; - } - - static void testRemoveDependentNmethodMiddleDeleteImmediately() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(&c._bucketList, c._nmethodMiddle, true /* delete */); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next() == NULL, "check"); - - // Cleanup before context is deleted. - c._bucketMiddle = NULL; - } - - static void testRemoveDependentNmethodLastDeleteImmediately() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(&c._bucketList, c._nmethodLast, true /* delete */); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketMiddle, "check"); - assert(c._bucketList->next()->next() == NULL, "check"); - - // Cleanup before context is deleted. - c._bucketLast = NULL; - } - - static void testRemoveDependentNmethodFirstDeleteDeferred() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(&c._bucketList, c._nmethodFirst, false /* delete */); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketMiddle, "check"); - assert(c._bucketList->next()->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next()->next() == NULL, "check"); - - assert(c._bucketFirst->count() == 0, "check"); - assert(c._bucketMiddle->count() == 1, "check"); - assert(c._bucketLast->count() == 1, "check"); - } - - static void testRemoveDependentNmethodMiddleDeleteDeferred() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(&c._bucketList, c._nmethodMiddle, false /* delete */); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketMiddle, "check"); - assert(c._bucketList->next()->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next()->next() == NULL, "check"); - - assert(c._bucketFirst->count() == 1, "check"); - assert(c._bucketMiddle->count() == 0, "check"); - assert(c._bucketLast->count() == 1, "check"); - } - - static void testRemoveDependentNmethodLastDeleteDeferred() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(&c._bucketList, c._nmethodLast, false /* delete */); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketMiddle, "check"); - assert(c._bucketList->next()->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next()->next() == NULL, "check"); - - assert(c._bucketFirst->count() == 1, "check"); - assert(c._bucketMiddle->count() == 1, "check"); - assert(c._bucketLast->count() == 0, "check"); - } - - static void testRemoveDependentNmethodConvenienceFirst() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(c._bucketList, c._nmethodFirst); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketMiddle, "check"); - assert(c._bucketList->next()->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next()->next() == NULL, "check"); - - assert(c._bucketFirst->count() == 0, "check"); - assert(c._bucketMiddle->count() == 1, "check"); - assert(c._bucketLast->count() == 1, "check"); - } - - static void testRemoveDependentNmethodConvenienceMiddle() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(c._bucketList, c._nmethodMiddle); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketMiddle, "check"); - assert(c._bucketList->next()->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next()->next() == NULL, "check"); - - assert(c._bucketFirst->count() == 1, "check"); - assert(c._bucketMiddle->count() == 0, "check"); - assert(c._bucketLast->count() == 1, "check"); - } - - static void testRemoveDependentNmethodConvenienceLast() { - TestNmethodBucketContext c; - - nmethodBucket::remove_dependent_nmethod(c._bucketList, c._nmethodLast); - - assert(c._bucketList == c._bucketFirst, "check"); - assert(c._bucketList->next() == c._bucketMiddle, "check"); - assert(c._bucketList->next()->next() == c._bucketLast, "check"); - assert(c._bucketList->next()->next()->next() == NULL, "check"); - - assert(c._bucketFirst->count() == 1, "check"); - assert(c._bucketMiddle->count() == 1, "check"); - assert(c._bucketLast->count() == 0, "check"); - } - - static void testRemoveDependentNmethod() { - testRemoveDependentNmethodFirstDeleteImmediately(); - testRemoveDependentNmethodMiddleDeleteImmediately(); - testRemoveDependentNmethodLastDeleteImmediately(); - - testRemoveDependentNmethodFirstDeleteDeferred(); - testRemoveDependentNmethodMiddleDeleteDeferred(); - testRemoveDependentNmethodLastDeleteDeferred(); - - testRemoveDependentNmethodConvenienceFirst(); - testRemoveDependentNmethodConvenienceMiddle(); - testRemoveDependentNmethodConvenienceLast(); - } - - static void test() { - testRemoveDependentNmethod(); - } -}; - -void TestNmethodBucket_test() { - TestNmethodBucket::test(); -} - -#endif diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp index 9eebfcb7010..ca5262346a1 100644 --- a/hotspot/src/share/vm/oops/instanceKlass.hpp +++ b/hotspot/src/share/vm/oops/instanceKlass.hpp @@ -53,15 +53,15 @@ // forward declaration for class -- see below for definition -class SuperTypeClosure; -class JNIid; -class jniIdMapBase; class BreakpointInfo; -class fieldDescriptor; class DepChange; -class nmethodBucket; +class DependencyContext; +class fieldDescriptor; +class jniIdMapBase; +class JNIid; class JvmtiCachedClassFieldMap; class MemberNameTable; +class SuperTypeClosure; // This is used in iterators below. class FieldClosure: public StackObj { @@ -198,7 +198,6 @@ class InstanceKlass: public Klass { // _is_marked_dependent can be set concurrently, thus cannot be part of the // _misc_flags. bool _is_marked_dependent; // used for marking during flushing and deoptimization - bool _has_unloaded_dependent; // The low two bits of _misc_flags contains the kind field. // This can be used to quickly discriminate among the four kinds of @@ -235,7 +234,7 @@ class InstanceKlass: public Klass { MemberNameTable* _member_names; // Member names JNIid* _jni_ids; // First JNI identifier for static fields in this class jmethodID* _methods_jmethod_ids; // jmethodIDs corresponding to method_idnum, or NULL if none - nmethodBucket* _dependencies; // list of dependent nmethods + intptr_t _dep_context; // packed DependencyContext structure nmethod* _osr_nmethods_head; // Head of list of on-stack replacement nmethods for this class BreakpointInfo* _breakpoints; // bpt lists, managed by Method* // Linked instanceKlasses of previous versions @@ -468,9 +467,6 @@ class InstanceKlass: public Klass { bool is_marked_dependent() const { return _is_marked_dependent; } void set_is_marked_dependent(bool value) { _is_marked_dependent = value; } - bool has_unloaded_dependent() const { return _has_unloaded_dependent; } - void set_has_unloaded_dependent(bool value) { _has_unloaded_dependent = value; } - // initialization (virtuals from Klass) bool should_be_initialized() const; // means that initialize should be called void initialize(TRAPS); @@ -835,7 +831,8 @@ public: JNIid* jni_id_for(int offset); // maintenance of deoptimization dependencies - int mark_dependent_nmethods(DepChange& changes); + inline DependencyContext dependencies(); + int mark_dependent_nmethods(DepChange& changes); void add_dependent_nmethod(nmethod* nm); void remove_dependent_nmethod(nmethod* nm, bool delete_immediately); @@ -1027,7 +1024,6 @@ public: void clean_weak_instanceklass_links(BoolObjectClosure* is_alive); void clean_implementors_list(BoolObjectClosure* is_alive); void clean_method_data(BoolObjectClosure* is_alive); - void clean_dependent_nmethods(); // Explicit metaspace deallocation of fields // For RedefineClasses and class file parsing errors, we need to deallocate @@ -1320,48 +1316,6 @@ class JNIid: public CHeapObj { void verify(Klass* holder); }; - -// -// nmethodBucket is used to record dependent nmethods for -// deoptimization. nmethod dependencies are actually -// pairs but we really only care about the klass part for purposes of -// finding nmethods which might need to be deoptimized. Instead of -// recording the method, a count of how many times a particular nmethod -// was recorded is kept. This ensures that any recording errors are -// noticed since an nmethod should be removed as many times are it's -// added. -// -class nmethodBucket: public CHeapObj { - friend class VMStructs; - private: - nmethod* _nmethod; - int _count; - nmethodBucket* _next; - - public: - nmethodBucket(nmethod* nmethod, nmethodBucket* next) { - _nmethod = nmethod; - _next = next; - _count = 1; - } - int count() { return _count; } - int increment() { _count += 1; return _count; } - int decrement(); - nmethodBucket* next() { return _next; } - void set_next(nmethodBucket* b) { _next = b; } - nmethod* get_nmethod() { return _nmethod; } - - static int mark_dependent_nmethods(nmethodBucket* deps, DepChange& changes); - static nmethodBucket* add_dependent_nmethod(nmethodBucket* deps, nmethod* nm); - static bool remove_dependent_nmethod(nmethodBucket** deps, nmethod* nm, bool delete_immediately); - static bool remove_dependent_nmethod(nmethodBucket* deps, nmethod* nm); - static nmethodBucket* clean_dependent_nmethods(nmethodBucket* deps); -#ifndef PRODUCT - static void print_dependent_nmethods(nmethodBucket* deps, bool verbose); - static bool is_dependent_nmethod(nmethodBucket* deps, nmethod* nm); -#endif //PRODUCT -}; - // An iterator that's used to access the inner classes indices in the // InstanceKlass::_inner_classes array. class InnerClassesIterator : public StackObj { diff --git a/hotspot/src/share/vm/oops/method.cpp b/hotspot/src/share/vm/oops/method.cpp index 1c175b06f98..0ab31096982 100644 --- a/hotspot/src/share/vm/oops/method.cpp +++ b/hotspot/src/share/vm/oops/method.cpp @@ -579,12 +579,45 @@ bool Method::can_be_statically_bound() const { } bool Method::is_accessor() const { + return is_getter() || is_setter(); +} + +bool Method::is_getter() const { if (code_size() != 5) return false; if (size_of_parameters() != 1) return false; - if (java_code_at(0) != Bytecodes::_aload_0 ) return false; + if (java_code_at(0) != Bytecodes::_aload_0) return false; if (java_code_at(1) != Bytecodes::_getfield) return false; - if (java_code_at(4) != Bytecodes::_areturn && - java_code_at(4) != Bytecodes::_ireturn ) return false; + switch (java_code_at(4)) { + case Bytecodes::_ireturn: + case Bytecodes::_lreturn: + case Bytecodes::_freturn: + case Bytecodes::_dreturn: + case Bytecodes::_areturn: + break; + default: + return false; + } + return true; +} + +bool Method::is_setter() const { + if (code_size() != 6) return false; + if (java_code_at(0) != Bytecodes::_aload_0) return false; + switch (java_code_at(1)) { + case Bytecodes::_iload_1: + case Bytecodes::_aload_1: + case Bytecodes::_fload_1: + if (size_of_parameters() != 2) return false; + break; + case Bytecodes::_dload_1: + case Bytecodes::_lload_1: + if (size_of_parameters() != 3) return false; + break; + default: + return false; + } + if (java_code_at(2) != Bytecodes::_putfield) return false; + if (java_code_at(5) != Bytecodes::_return) return false; return true; } diff --git a/hotspot/src/share/vm/oops/method.hpp b/hotspot/src/share/vm/oops/method.hpp index b0cd50ec0c6..2b7bd268f5c 100644 --- a/hotspot/src/share/vm/oops/method.hpp +++ b/hotspot/src/share/vm/oops/method.hpp @@ -595,6 +595,12 @@ class Method : public Metadata { // returns true if the method is an accessor function (setter/getter). bool is_accessor() const; + // returns true if the method is a getter + bool is_getter() const; + + // returns true if the method is a setter + bool is_setter() const; + // returns true if the method does nothing but return a constant of primitive type bool is_constant_getter() const; diff --git a/hotspot/src/share/vm/opto/buildOopMap.cpp b/hotspot/src/share/vm/opto/buildOopMap.cpp index 3a76492fbe0..6c8981b2fe8 100644 --- a/hotspot/src/share/vm/opto/buildOopMap.cpp +++ b/hotspot/src/share/vm/opto/buildOopMap.cpp @@ -542,10 +542,11 @@ static void do_liveness(PhaseRegAlloc* regalloc, PhaseCFG* cfg, Block_List* work if (i == cfg->number_of_blocks()) { break; // Got 'em all } -#ifndef PRODUCT - if( PrintOpto && Verbose ) + + if (PrintOpto && Verbose) { tty->print_cr("retripping live calc"); -#endif + } + // Force the issue (expensively): recheck everybody for (i = 1; i < cfg->number_of_blocks(); i++) { worklist->push(cfg->get_block(i)); diff --git a/hotspot/src/share/vm/opto/c2_globals.hpp b/hotspot/src/share/vm/opto/c2_globals.hpp index ccb07bfe09a..ba3a535111d 100644 --- a/hotspot/src/share/vm/opto/c2_globals.hpp +++ b/hotspot/src/share/vm/opto/c2_globals.hpp @@ -186,9 +186,9 @@ "Maximum number of unrolls for main loop") \ range(0, max_jint) \ \ - product(bool, SuperWordLoopUnrollAnalysis, false, \ - "Map number of unrolls for main loop via " \ - "Superword Level Parallelism analysis") \ + product_pd(bool, SuperWordLoopUnrollAnalysis, \ + "Map number of unrolls for main loop via " \ + "Superword Level Parallelism analysis") \ \ notproduct(bool, TraceSuperWordLoopUnrollAnalysis, false, \ "Trace what Superword Level Parallelism analysis applies") \ diff --git a/hotspot/src/share/vm/opto/c2compiler.cpp b/hotspot/src/share/vm/opto/c2compiler.cpp index 60f1f200248..c8ff9804039 100644 --- a/hotspot/src/share/vm/opto/c2compiler.cpp +++ b/hotspot/src/share/vm/opto/c2compiler.cpp @@ -451,6 +451,7 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt case vmIntrinsics::_updateByteBufferAdler32: case vmIntrinsics::_profileBoolean: case vmIntrinsics::_isCompileConstant: + case vmIntrinsics::_Objects_checkIndex: break; default: return false; diff --git a/hotspot/src/share/vm/opto/callnode.cpp b/hotspot/src/share/vm/opto/callnode.cpp index 56fd109f7a5..c82a149d444 100644 --- a/hotspot/src/share/vm/opto/callnode.cpp +++ b/hotspot/src/share/vm/opto/callnode.cpp @@ -778,7 +778,7 @@ bool CallNode::may_modify(const TypeOopPtr *t_oop, PhaseTransform *phase) { } if (is_CallJava() && as_CallJava()->method() != NULL) { ciMethod* meth = as_CallJava()->method(); - if (meth->is_accessor()) { + if (meth->is_getter()) { return false; } // May modify (by reflection) if an boxing object is passed diff --git a/hotspot/src/share/vm/opto/cfgnode.cpp b/hotspot/src/share/vm/opto/cfgnode.cpp index 8cb6488a535..454a29636a6 100644 --- a/hotspot/src/share/vm/opto/cfgnode.cpp +++ b/hotspot/src/share/vm/opto/cfgnode.cpp @@ -984,7 +984,7 @@ const Type *PhiNode::Value( PhaseTransform *phase ) const { #ifdef ASSERT // The following logic has been moved into TypeOopPtr::filter. const Type* jt = t->join_speculative(_type); - if( jt->empty() ) { // Emptied out??? + if (jt->empty()) { // Emptied out??? // Check for evil case of 't' being a class and '_type' expecting an // interface. This can happen because the bytecodes do not contain @@ -995,14 +995,21 @@ const Type *PhiNode::Value( PhaseTransform *phase ) const { // be 'I' or 'j/l/O'. Thus we'll pick 'j/l/O'. If this then flows // into a Phi which "knows" it's an Interface type we'll have to // uplift the type. - if( !t->empty() && ttip && ttip->is_loaded() && ttip->klass()->is_interface() ) - { assert(ft == _type, ""); } // Uplift to interface - else if( !t->empty() && ttkp && ttkp->is_loaded() && ttkp->klass()->is_interface() ) - { assert(ft == _type, ""); } // Uplift to interface - // Otherwise it's something stupid like non-overlapping int ranges - // found on dying counted loops. - else - { assert(ft == Type::TOP, ""); } // Canonical empty value + if (!t->empty() && ttip && ttip->is_loaded() && ttip->klass()->is_interface()) { + assert(ft == _type, ""); // Uplift to interface + } else if (!t->empty() && ttkp && ttkp->is_loaded() && ttkp->klass()->is_interface()) { + assert(ft == _type, ""); // Uplift to interface + } else { + // We also have to handle 'evil cases' of interface- vs. class-arrays + Type::get_arrays_base_elements(jt, _type, NULL, &ttip); + if (!t->empty() && ttip != NULL && ttip->is_loaded() && ttip->klass()->is_interface()) { + assert(ft == _type, ""); // Uplift to array of interface + } else { + // Otherwise it's something stupid like non-overlapping int ranges + // found on dying counted loops. + assert(ft == Type::TOP, ""); // Canonical empty value + } + } } else { diff --git a/hotspot/src/share/vm/opto/cfgnode.hpp b/hotspot/src/share/vm/opto/cfgnode.hpp index 33a378e5be5..a87a70a41e6 100644 --- a/hotspot/src/share/vm/opto/cfgnode.hpp +++ b/hotspot/src/share/vm/opto/cfgnode.hpp @@ -270,7 +270,6 @@ class IfNode : public MultiBranchNode { virtual uint size_of() const { return sizeof(*this); } private: - ProjNode* range_check_trap_proj(int& flip, Node*& l, Node*& r); ProjNode* range_check_trap_proj() { int flip_test = 0; Node* l = NULL; @@ -283,7 +282,7 @@ private: bool is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn); bool has_shared_region(ProjNode* proj, ProjNode*& success, ProjNode*& fail); bool has_only_uncommon_traps(ProjNode* proj, ProjNode*& success, ProjNode*& fail, PhaseIterGVN* igvn); - static void merge_uncommon_traps(ProjNode* proj, ProjNode* success, ProjNode* fail, PhaseIterGVN* igvn); + Node* merge_uncommon_traps(ProjNode* proj, ProjNode* success, ProjNode* fail, PhaseIterGVN* igvn); static void improve_address_types(Node* l, Node* r, ProjNode* fail, PhaseIterGVN* igvn); bool is_cmp_with_loadrange(ProjNode* proj); bool is_null_check(ProjNode* proj, PhaseIterGVN* igvn); @@ -292,6 +291,12 @@ private: ProjNode* uncommon_trap_proj(CallStaticJavaNode*& call) const; bool fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* fail, PhaseIterGVN* igvn); +protected: + ProjNode* range_check_trap_proj(int& flip, Node*& l, Node*& r); + Node* Ideal_common(PhaseGVN *phase, bool can_reshape); + Node* dominated_by(Node* prev_dom, PhaseIterGVN* igvn); + Node* search_identical(int dist); + public: // Degrees of branch prediction probability by order of magnitude: @@ -375,8 +380,6 @@ public: virtual const Type *Value( PhaseTransform *phase ) const; virtual int required_outcnt() const { return 2; } virtual const RegMask &out_RegMask() const; - void dominated_by(Node* prev_dom, PhaseIterGVN* igvn); - int is_range_check(Node* &range, Node* &index, jint &offset); Node* fold_compares(PhaseIterGVN* phase); static Node* up_one_dom(Node* curr, bool linear_only = false); @@ -391,6 +394,20 @@ public: #endif }; +class RangeCheckNode : public IfNode { +private: + int is_range_check(Node* &range, Node* &index, jint &offset); + +public: + RangeCheckNode(Node* control, Node *b, float p, float fcnt) + : IfNode(control, b, p, fcnt) { + init_class_id(Class_RangeCheck); + } + + virtual int Opcode() const; + virtual Node* Ideal(PhaseGVN *phase, bool can_reshape); +}; + class IfProjNode : public CProjNode { public: IfProjNode(IfNode *ifnode, uint idx) : CProjNode(ifnode,idx) {} diff --git a/hotspot/src/share/vm/opto/classes.hpp b/hotspot/src/share/vm/opto/classes.hpp index 754c8f6a08f..0779c3b96a8 100644 --- a/hotspot/src/share/vm/opto/classes.hpp +++ b/hotspot/src/share/vm/opto/classes.hpp @@ -138,6 +138,7 @@ macro(Goto) macro(Halt) macro(HasNegatives) macro(If) +macro(RangeCheck) macro(IfFalse) macro(IfTrue) macro(Initialize) diff --git a/hotspot/src/share/vm/opto/compile.cpp b/hotspot/src/share/vm/opto/compile.cpp index 032fe0a6f6f..c96b6532a83 100644 --- a/hotspot/src/share/vm/opto/compile.cpp +++ b/hotspot/src/share/vm/opto/compile.cpp @@ -707,7 +707,7 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr _replay_inline_data = ciReplay::load_inline_data(method(), entry_bci(), ci_env->comp_level()); } #endif - set_print_inlining(directive->PrintInliningOption NOT_PRODUCT( || PrintOptoInlining)); + set_print_inlining(directive->PrintInliningOption || PrintOptoInlining); set_print_intrinsics(directive->PrintIntrinsicsOption); set_has_irreducible_loop(true); // conservative until build_loop_tree() reset it @@ -3181,6 +3181,13 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { n->set_req(MemBarNode::Precedent, top()); } break; + case Op_RangeCheck: { + RangeCheckNode* rc = n->as_RangeCheck(); + Node* iff = new IfNode(rc->in(0), rc->in(1), rc->_prob, rc->_fcnt); + n->subsume_by(iff, this); + frc._tests.push(iff); + break; + } default: assert( !n->is_Call(), "" ); assert( !n->is_Mem(), "" ); @@ -3189,8 +3196,9 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { } // Collect CFG split points - if (n->is_MultiBranch()) + if (n->is_MultiBranch() && !n->is_RangeCheck()) { frc._tests.push(n); + } } //------------------------------final_graph_reshaping_walk--------------------- @@ -3549,7 +3557,7 @@ void Compile::verify_graph_edges(bool no_dead_code) { void Compile::verify_barriers() { if (UseG1GC) { // Verify G1 pre-barriers - const int marking_offset = in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_active()); + const int marking_offset = in_bytes(JavaThread::satb_mark_queue_offset() + SATBMarkQueue::byte_offset_of_active()); ResourceArea *area = Thread::current()->resource_area(); Unique_Node_List visited(area); diff --git a/hotspot/src/share/vm/opto/doCall.cpp b/hotspot/src/share/vm/opto/doCall.cpp index 08801c70994..b8737560b07 100644 --- a/hotspot/src/share/vm/opto/doCall.cpp +++ b/hotspot/src/share/vm/opto/doCall.cpp @@ -45,7 +45,7 @@ void trace_type_profile(Compile* C, ciMethod *method, int depth, int bci, ciMeth if (TraceTypeProfile || C->print_inlining()) { outputStream* out = tty; if (!C->print_inlining()) { - if (NOT_PRODUCT(!PrintOpto &&) !PrintCompilation) { + if (!PrintOpto && !PrintCompilation) { method->print_short_name(); tty->cr(); } @@ -426,12 +426,10 @@ void Parse::do_call() { // uncommon-trap when callee is unloaded, uninitialized or will not link // bailout when too many arguments for register representation if (!will_link || can_not_compile_call_site(orig_callee, klass)) { -#ifndef PRODUCT if (PrintOpto && (Verbose || WizardMode)) { method()->print_name(); tty->print_cr(" can not compile call at bci %d to:", bci()); orig_callee->print_name(); tty->cr(); } -#endif return; } assert(holder_klass->is_loaded(), ""); @@ -634,12 +632,10 @@ void Parse::do_call() { // If the return type of the method is not loaded, assert that the // value we got is a null. Otherwise, we need to recompile. if (!rtype->is_loaded()) { -#ifndef PRODUCT if (PrintOpto && (Verbose || WizardMode)) { method()->print_name(); tty->print_cr(" asserting nullness of result at bci: %d", bci()); cg->method()->print_name(); tty->cr(); } -#endif if (C->log() != NULL) { C->log()->elem("assert_null reason='return' klass='%d'", C->log()->identify(rtype)); @@ -851,11 +847,9 @@ void Parse::catch_inline_exceptions(SafePointNode* ex_map) { if (remaining == 1) { push_ex_oop(ex_node); // Push exception oop for handler -#ifndef PRODUCT if (PrintOpto && WizardMode) { tty->print_cr(" Catching every inline exception bci:%d -> handler_bci:%d", bci(), handler_bci); } -#endif merge_exception(handler_bci); // jump to handler return; // No more handling to be done here! } @@ -882,13 +876,11 @@ void Parse::catch_inline_exceptions(SafePointNode* ex_map) { assert(klass->has_subklass() || tinst->klass_is_exact(), "lost exactness"); Node* ex_oop = _gvn.transform(new CheckCastPPNode(control(), ex_node, tinst)); push_ex_oop(ex_oop); // Push exception oop for handler -#ifndef PRODUCT if (PrintOpto && WizardMode) { tty->print(" Catching inline exception bci:%d -> handler_bci:%d -- ", bci(), handler_bci); klass->print_name(); tty->cr(); } -#endif merge_exception(handler_bci); } set_control(not_subtype_ctrl); @@ -1067,13 +1059,11 @@ ciMethod* Compile::optimize_inlining(ciMethod* caller, int bci, ciInstanceKlass* // such method can be changed when its class is redefined. ciMethod* exact_method = callee->resolve_invoke(calling_klass, actual_receiver); if (exact_method != NULL) { -#ifndef PRODUCT if (PrintOpto) { tty->print(" Calling method via exact type @%d --- ", bci); exact_method->print_name(); tty->cr(); } -#endif return exact_method; } } diff --git a/hotspot/src/share/vm/opto/escape.cpp b/hotspot/src/share/vm/opto/escape.cpp index fac8c4d67fe..92ed15ca3da 100644 --- a/hotspot/src/share/vm/opto/escape.cpp +++ b/hotspot/src/share/vm/opto/escape.cpp @@ -539,11 +539,11 @@ void ConnectionGraph::add_node_to_connection_graph(Node *n, Unique_Node_List *de if (tls->Opcode() == Op_ThreadLocal) { int offs = (int)igvn->find_intptr_t_con(adr->in(AddPNode::Offset), Type::OffsetBot); if (offs == in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_buf())) { + SATBMarkQueue::byte_offset_of_buf())) { break; // G1 pre barrier previous oop value store. } if (offs == in_bytes(JavaThread::dirty_card_queue_offset() + - PtrQueue::byte_offset_of_buf())) { + DirtyCardQueue::byte_offset_of_buf())) { break; // G1 post barrier card address store. } } diff --git a/hotspot/src/share/vm/opto/graphKit.cpp b/hotspot/src/share/vm/opto/graphKit.cpp index 397e6cb1c81..97ae61503a0 100644 --- a/hotspot/src/share/vm/opto/graphKit.cpp +++ b/hotspot/src/share/vm/opto/graphKit.cpp @@ -1457,18 +1457,22 @@ void GraphKit::set_all_memory_call(Node* call, bool separate_io_proj) { // factory methods in "int adr_idx" Node* GraphKit::make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, int adr_idx, - MemNode::MemOrd mo, LoadNode::ControlDependency control_dependency, bool require_atomic_access) { + MemNode::MemOrd mo, + LoadNode::ControlDependency control_dependency, + bool require_atomic_access, + bool unaligned, + bool mismatched) { assert(adr_idx != Compile::AliasIdxTop, "use other make_load factory" ); const TypePtr* adr_type = NULL; // debug-mode-only argument debug_only(adr_type = C->get_adr_type(adr_idx)); Node* mem = memory(adr_idx); Node* ld; if (require_atomic_access && bt == T_LONG) { - ld = LoadLNode::make_atomic(ctl, mem, adr, adr_type, t, mo, control_dependency); + ld = LoadLNode::make_atomic(ctl, mem, adr, adr_type, t, mo, control_dependency, unaligned, mismatched); } else if (require_atomic_access && bt == T_DOUBLE) { - ld = LoadDNode::make_atomic(ctl, mem, adr, adr_type, t, mo, control_dependency); + ld = LoadDNode::make_atomic(ctl, mem, adr, adr_type, t, mo, control_dependency, unaligned, mismatched); } else { - ld = LoadNode::make(_gvn, ctl, mem, adr, adr_type, t, bt, mo, control_dependency); + ld = LoadNode::make(_gvn, ctl, mem, adr, adr_type, t, bt, mo, control_dependency, unaligned, mismatched); } ld = _gvn.transform(ld); if ((bt == T_OBJECT) && C->do_escape_analysis() || C->eliminate_boxing()) { @@ -1481,7 +1485,9 @@ Node* GraphKit::make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, Node* GraphKit::store_to_memory(Node* ctl, Node* adr, Node *val, BasicType bt, int adr_idx, MemNode::MemOrd mo, - bool require_atomic_access) { + bool require_atomic_access, + bool unaligned, + bool mismatched) { assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); const TypePtr* adr_type = NULL; debug_only(adr_type = C->get_adr_type(adr_idx)); @@ -1494,6 +1500,12 @@ Node* GraphKit::store_to_memory(Node* ctl, Node* adr, Node *val, BasicType bt, } else { st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt, mo); } + if (unaligned) { + st->as_Store()->set_unaligned_access(); + } + if (mismatched) { + st->as_Store()->set_mismatched_access(); + } st = _gvn.transform(st); set_memory(st, adr_idx); // Back-to-back stores can only remove intermediate store with DU info @@ -1587,7 +1599,8 @@ Node* GraphKit::store_oop(Node* ctl, const TypeOopPtr* val_type, BasicType bt, bool use_precise, - MemNode::MemOrd mo) { + MemNode::MemOrd mo, + bool mismatched) { // Transformation of a value which could be NULL pointer (CastPP #NULL) // could be delayed during Parse (for example, in adjust_map_after_if()). // Execute transformation here to avoid barrier generation in such case. @@ -1607,7 +1620,7 @@ Node* GraphKit::store_oop(Node* ctl, NULL /* pre_val */, bt); - Node* store = store_to_memory(control(), adr, val, bt, adr_idx, mo); + Node* store = store_to_memory(control(), adr, val, bt, adr_idx, mo, mismatched); post_barrier(control(), store, obj, adr, adr_idx, val, bt, use_precise); return store; } @@ -1619,7 +1632,8 @@ Node* GraphKit::store_oop_to_unknown(Node* ctl, const TypePtr* adr_type, Node* val, BasicType bt, - MemNode::MemOrd mo) { + MemNode::MemOrd mo, + bool mismatched) { Compile::AliasType* at = C->alias_type(adr_type); const TypeOopPtr* val_type = NULL; if (adr_type->isa_instptr()) { @@ -1638,7 +1652,7 @@ Node* GraphKit::store_oop_to_unknown(Node* ctl, if (val_type == NULL) { val_type = TypeInstPtr::BOTTOM; } - return store_oop(ctl, obj, adr, adr_type, val, val_type, bt, true, mo); + return store_oop(ctl, obj, adr, adr_type, val, val_type, bt, true, mo, mismatched); } @@ -3986,16 +4000,16 @@ void GraphKit::g1_write_barrier_pre(bool do_load, float likely = PROB_LIKELY(0.999); float unlikely = PROB_UNLIKELY(0.999); - BasicType active_type = in_bytes(PtrQueue::byte_width_of_active()) == 4 ? T_INT : T_BYTE; - assert(in_bytes(PtrQueue::byte_width_of_active()) == 4 || in_bytes(PtrQueue::byte_width_of_active()) == 1, "flag width"); + BasicType active_type = in_bytes(SATBMarkQueue::byte_width_of_active()) == 4 ? T_INT : T_BYTE; + assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 4 || in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "flag width"); // Offsets into the thread const int marking_offset = in_bytes(JavaThread::satb_mark_queue_offset() + // 648 - PtrQueue::byte_offset_of_active()); + SATBMarkQueue::byte_offset_of_active()); const int index_offset = in_bytes(JavaThread::satb_mark_queue_offset() + // 656 - PtrQueue::byte_offset_of_index()); + SATBMarkQueue::byte_offset_of_index()); const int buffer_offset = in_bytes(JavaThread::satb_mark_queue_offset() + // 652 - PtrQueue::byte_offset_of_buf()); + SATBMarkQueue::byte_offset_of_buf()); // Now the actual pointers into the thread Node* marking_adr = __ AddP(no_base, tls, __ ConX(marking_offset)); @@ -4008,7 +4022,7 @@ void GraphKit::g1_write_barrier_pre(bool do_load, // if (!marking) __ if_then(marking, BoolTest::ne, zero, unlikely); { BasicType index_bt = TypeX_X->basic_type(); - assert(sizeof(size_t) == type2aelembytes(index_bt), "Loading G1 PtrQueue::_index with wrong size."); + assert(sizeof(size_t) == type2aelembytes(index_bt), "Loading G1 SATBMarkQueue::_index with wrong size."); Node* index = __ load(__ ctrl(), index_adr, TypeX_X, index_bt, Compile::AliasIdxRaw); if (do_load) { @@ -4196,9 +4210,9 @@ void GraphKit::g1_write_barrier_post(Node* oop_store, // Offsets into the thread const int index_offset = in_bytes(JavaThread::dirty_card_queue_offset() + - PtrQueue::byte_offset_of_index()); + DirtyCardQueue::byte_offset_of_index()); const int buffer_offset = in_bytes(JavaThread::dirty_card_queue_offset() + - PtrQueue::byte_offset_of_buf()); + DirtyCardQueue::byte_offset_of_buf()); // Pointers into the thread @@ -4373,7 +4387,8 @@ void GraphKit::inflate_string_slow(Node* src, Node* dst, Node* start, Node* coun set_memory(mem, TypeAryPtr::BYTES); Node* ch = load_array_element(control(), src, i_byte, TypeAryPtr::BYTES); Node* st = store_to_memory(control(), array_element_address(dst, i_char, T_BYTE), - AndI(ch, intcon(0xff)), T_CHAR, TypeAryPtr::BYTES, MemNode::unordered); + AndI(ch, intcon(0xff)), T_CHAR, TypeAryPtr::BYTES, MemNode::unordered, + false, false, true /* mismatched */); IfNode* iff = create_and_map_if(head, Bool(CmpI(i_byte, count), BoolTest::lt), PROB_FAIR, COUNT_UNKNOWN); head->init_req(2, IfTrue(iff)); diff --git a/hotspot/src/share/vm/opto/graphKit.hpp b/hotspot/src/share/vm/opto/graphKit.hpp index a47047d64ae..4b4be335cd3 100644 --- a/hotspot/src/share/vm/opto/graphKit.hpp +++ b/hotspot/src/share/vm/opto/graphKit.hpp @@ -513,23 +513,28 @@ class GraphKit : public Phase { // of volatile fields. Node* make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, MemNode::MemOrd mo, LoadNode::ControlDependency control_dependency = LoadNode::DependsOnlyOnTest, - bool require_atomic_access = false) { + bool require_atomic_access = false, bool unaligned = false, + bool mismatched = false) { // This version computes alias_index from bottom_type return make_load(ctl, adr, t, bt, adr->bottom_type()->is_ptr(), - mo, control_dependency, require_atomic_access); + mo, control_dependency, require_atomic_access, + unaligned, mismatched); } Node* make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, const TypePtr* adr_type, MemNode::MemOrd mo, LoadNode::ControlDependency control_dependency = LoadNode::DependsOnlyOnTest, - bool require_atomic_access = false) { + bool require_atomic_access = false, bool unaligned = false, + bool mismatched = false) { // This version computes alias_index from an address type assert(adr_type != NULL, "use other make_load factory"); return make_load(ctl, adr, t, bt, C->get_alias_index(adr_type), - mo, control_dependency, require_atomic_access); + mo, control_dependency, require_atomic_access, + unaligned, mismatched); } // This is the base version which is given an alias index. Node* make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, int adr_idx, MemNode::MemOrd mo, LoadNode::ControlDependency control_dependency = LoadNode::DependsOnlyOnTest, - bool require_atomic_access = false); + bool require_atomic_access = false, bool unaligned = false, + bool mismatched = false); // Create & transform a StoreNode and store the effect into the // parser's memory state. @@ -542,19 +547,24 @@ class GraphKit : public Phase { Node* store_to_memory(Node* ctl, Node* adr, Node* val, BasicType bt, const TypePtr* adr_type, MemNode::MemOrd mo, - bool require_atomic_access = false) { + bool require_atomic_access = false, + bool unaligned = false, + bool mismatched = false) { // This version computes alias_index from an address type assert(adr_type != NULL, "use other store_to_memory factory"); return store_to_memory(ctl, adr, val, bt, C->get_alias_index(adr_type), - mo, require_atomic_access); + mo, require_atomic_access, + unaligned, mismatched); } // This is the base version which is given alias index // Return the new StoreXNode Node* store_to_memory(Node* ctl, Node* adr, Node* val, BasicType bt, int adr_idx, MemNode::MemOrd, - bool require_atomic_access = false); + bool require_atomic_access = false, + bool unaligned = false, + bool mismatched = false); // All in one pre-barrier, store, post_barrier @@ -577,7 +587,8 @@ class GraphKit : public Phase { const TypeOopPtr* val_type, BasicType bt, bool use_precise, - MemNode::MemOrd mo); + MemNode::MemOrd mo, + bool mismatched = false); Node* store_oop_to_object(Node* ctl, Node* obj, // containing obj @@ -608,7 +619,8 @@ class GraphKit : public Phase { const TypePtr* adr_type, Node* val, BasicType bt, - MemNode::MemOrd mo); + MemNode::MemOrd mo, + bool mismatched = false); // For the few case where the barriers need special help void pre_barrier(bool do_load, Node* ctl, diff --git a/hotspot/src/share/vm/opto/idealKit.cpp b/hotspot/src/share/vm/opto/idealKit.cpp index cbba57eba95..94d7ef4033c 100644 --- a/hotspot/src/share/vm/opto/idealKit.cpp +++ b/hotspot/src/share/vm/opto/idealKit.cpp @@ -368,7 +368,8 @@ Node* IdealKit::load(Node* ctl, Node* IdealKit::store(Node* ctl, Node* adr, Node *val, BasicType bt, int adr_idx, - MemNode::MemOrd mo, bool require_atomic_access) { + MemNode::MemOrd mo, bool require_atomic_access, + bool mismatched) { assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory"); const TypePtr* adr_type = NULL; debug_only(adr_type = C->get_adr_type(adr_idx)); @@ -379,6 +380,9 @@ Node* IdealKit::store(Node* ctl, Node* adr, Node *val, BasicType bt, } else { st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt, mo); } + if (mismatched) { + st->as_Store()->set_mismatched_access(); + } st = transform(st); set_memory(st, adr_idx); diff --git a/hotspot/src/share/vm/opto/idealKit.hpp b/hotspot/src/share/vm/opto/idealKit.hpp index 73db4771509..20996c286d6 100644 --- a/hotspot/src/share/vm/opto/idealKit.hpp +++ b/hotspot/src/share/vm/opto/idealKit.hpp @@ -229,7 +229,8 @@ class IdealKit: public StackObj { BasicType bt, int adr_idx, MemNode::MemOrd mo, - bool require_atomic_access = false); + bool require_atomic_access = false, + bool mismatched = false); // Store a card mark ordered after store_oop Node* storeCM(Node* ctl, diff --git a/hotspot/src/share/vm/opto/ifnode.cpp b/hotspot/src/share/vm/opto/ifnode.cpp index 5d22abd6729..c8205690bb0 100644 --- a/hotspot/src/share/vm/opto/ifnode.cpp +++ b/hotspot/src/share/vm/opto/ifnode.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "ci/ciTypeFlow.hpp" #include "memory/allocation.inline.hpp" #include "opto/addnode.hpp" #include "opto/castnode.hpp" @@ -305,12 +306,16 @@ static Node* split_if(IfNode *iff, PhaseIterGVN *igvn) { Node *b_c = phase->transform(new BoolNode(cmp_c,b->_test._test)); Node *b_x = phase->transform(new BoolNode(cmp_x,b->_test._test)); // Make the IfNode - IfNode *iff_c = new IfNode(region_c,b_c,iff->_prob,iff->_fcnt); + IfNode* iff_c = iff->clone()->as_If(); + iff_c->set_req(0, region_c); + iff_c->set_req(1, b_c); igvn->set_type_bottom(iff_c); igvn->_worklist.push(iff_c); hook->init_req(2, iff_c); - IfNode *iff_x = new IfNode(region_x,b_x,iff->_prob, iff->_fcnt); + IfNode* iff_x = iff->clone()->as_If(); + iff_x->set_req(0, region_x); + iff_x->set_req(1, b_x); igvn->set_type_bottom(iff_x); igvn->_worklist.push(iff_x); hook->init_req(3, iff_x); @@ -480,7 +485,7 @@ ProjNode* IfNode::range_check_trap_proj(int& flip_test, Node*& l, Node*& r) { return NULL; } if (l->is_top()) return NULL; // Top input means dead test - if (r->Opcode() != Op_LoadRange) return NULL; + if (r->Opcode() != Op_LoadRange && !is_RangeCheck()) return NULL; // We have recognized one of these forms: // Flip 1: If (Bool[<] CmpU(l, LoadRange)) ... @@ -495,7 +500,7 @@ ProjNode* IfNode::range_check_trap_proj(int& flip_test, Node*& l, Node*& r) { // Return 0 if not a range check. Return 1 if a range check and set index and // offset. Return 2 if we had to negate the test. Index is NULL if the check // is versus a constant. -int IfNode::is_range_check(Node* &range, Node* &index, jint &offset) { +int RangeCheckNode::is_range_check(Node* &range, Node* &index, jint &offset) { int flip_test = 0; Node* l = NULL; Node* r = NULL; @@ -520,9 +525,9 @@ int IfNode::is_range_check(Node* &range, Node* &index, jint &offset) { return 0; } else if (l->Opcode() == Op_AddI) { if ((off = l->in(1)->find_int_con(0)) != 0) { - ind = l->in(2); + ind = l->in(2)->uncast(); } else if ((off = l->in(2)->find_int_con(0)) != 0) { - ind = l->in(1); + ind = l->in(1)->uncast(); } } else if ((off = l->find_int_con(-1)) >= 0) { // constant offset with no variable index @@ -723,7 +728,7 @@ bool IfNode::is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn) { return ctrl != NULL && ctrl->is_Proj() && ctrl->in(0) != NULL && - ctrl->in(0)->is_If() && + ctrl->in(0)->Opcode() == Op_If && ctrl->in(0)->outcnt() == 2 && ctrl->in(0)->as_If()->cmpi_folds(igvn) && // Must compare same value @@ -771,6 +776,11 @@ bool IfNode::has_only_uncommon_traps(ProjNode* proj, ProjNode*& success, ProjNod CallStaticJavaNode* dom_unc = otherproj->is_uncommon_trap_proj(Deoptimization::Reason_none); if (otherproj->outcnt() == 1 && dom_unc != NULL) { + // We need to re-execute the folded Ifs after deoptimization from the merged traps + if (!dom_unc->jvms()->should_reexecute()) { + return false; + } + CallStaticJavaNode* unc = NULL; ProjNode* unc_proj = uncommon_trap_proj(unc); if (unc_proj != NULL && unc_proj->outcnt() == 1) { @@ -784,12 +794,41 @@ bool IfNode::has_only_uncommon_traps(ProjNode* proj, ProjNode*& success, ProjNod } else if (dom_unc->in(0) != otherproj || unc->in(0) != unc_proj) { return false; } + + // Different methods and methods containing jsrs are not supported. + ciMethod* method = unc->jvms()->method(); + ciMethod* dom_method = dom_unc->jvms()->method(); + if (method != dom_method || method->has_jsrs()) { + return false; + } + // Check that both traps are in the same activation of the method (instead + // of two activations being inlined through different call sites) by verifying + // that the call stacks are equal for both JVMStates. + JVMState* dom_caller = dom_unc->jvms()->caller(); + JVMState* caller = unc->jvms()->caller(); + if ((dom_caller == NULL) != (caller == NULL)) { + // The current method must either be inlined into both dom_caller and + // caller or must not be inlined at all (top method). Bail out otherwise. + return false; + } else if (dom_caller != NULL && !dom_caller->same_calls_as(caller)) { + return false; + } + // Check that the bci of the dominating uncommon trap dominates the bci + // of the dominated uncommon trap. Otherwise we may not re-execute + // the dominated check after deoptimization from the merged uncommon trap. + ciTypeFlow* flow = dom_method->get_flow_analysis(); + int bci = unc->jvms()->bci(); + int dom_bci = dom_unc->jvms()->bci(); + if (!flow->is_dominated_by(bci, dom_bci)) { + return false; + } + // See merge_uncommon_traps: the reason of the uncommon trap // will be changed and the state of the dominating If will be // used. Checked that we didn't apply this transformation in a // previous compilation and it didn't cause too many traps - if (!igvn->C->too_many_traps(dom_unc->jvms()->method(), dom_unc->jvms()->bci(), Deoptimization::Reason_unstable_fused_if) && - !igvn->C->too_many_traps(dom_unc->jvms()->method(), dom_unc->jvms()->bci(), Deoptimization::Reason_range_check)) { + if (!igvn->C->too_many_traps(dom_method, dom_bci, Deoptimization::Reason_unstable_fused_if) && + !igvn->C->too_many_traps(dom_method, dom_bci, Deoptimization::Reason_range_check)) { success = unc_proj; fail = unc_proj->other_if_proj(); return true; @@ -941,8 +980,8 @@ bool IfNode::fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* f if (failtype->_lo > failtype->_hi) { // previous if determines the result of this if so // replace Bool with constant - igvn->hash_delete(this); - set_req(1, igvn->intcon(success->_con)); + igvn->_worklist.push(in(1)); + igvn->replace_input_of(this, 1, igvn->intcon(success->_con)); return true; } } @@ -961,7 +1000,8 @@ bool IfNode::fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* f Node* newbool = igvn->transform(new BoolNode(newcmp, cond)); igvn->replace_input_of(dom_iff, 1, igvn->intcon(proj->_con)); - set_req(1, newbool); + igvn->_worklist.push(in(1)); + igvn->replace_input_of(this, 1, newbool); return true; } @@ -971,7 +1011,10 @@ bool IfNode::fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* f // Merge the branches that trap for this If and the dominating If into // a single region that branches to the uncommon trap for the // dominating If -void IfNode::merge_uncommon_traps(ProjNode* proj, ProjNode* success, ProjNode* fail, PhaseIterGVN* igvn) { +Node* IfNode::merge_uncommon_traps(ProjNode* proj, ProjNode* success, ProjNode* fail, PhaseIterGVN* igvn) { + Node* res = this; + assert(success->in(0) == this, "bad projection"); + ProjNode* otherproj = proj->other_if_proj(); CallStaticJavaNode* unc = success->is_uncommon_trap_proj(Deoptimization::Reason_none); @@ -1007,6 +1050,8 @@ void IfNode::merge_uncommon_traps(ProjNode* proj, ProjNode* success, ProjNode* f trap_request = Deoptimization::make_trap_request(Deoptimization::Reason_range_check, action); improve_address_types(l, r, fail, igvn); + + res = igvn->transform(new RangeCheckNode(in(0), in(1), _prob, _fcnt)); } else if (unc != dom_unc) { // If we trap we won't know what CmpI would have caused the trap // so use a special trap reason to mark this pair of CmpI nodes as @@ -1016,6 +1061,7 @@ void IfNode::merge_uncommon_traps(ProjNode* proj, ProjNode* success, ProjNode* f trap_request = Deoptimization::make_trap_request(Deoptimization::Reason_unstable_fused_if, action); } igvn->replace_input_of(dom_unc, TypeFunc::Parms, igvn->intcon(trap_request)); + return res; } // If we are turning 2 CmpI nodes into a CmpU that follows the pattern @@ -1209,8 +1255,7 @@ Node* IfNode::fold_compares(PhaseIterGVN* igvn) { if (has_only_uncommon_traps(dom_cmp, success, fail, igvn) && // Next call modifies graph so must be last fold_compares_helper(dom_cmp, success, fail, igvn)) { - merge_uncommon_traps(dom_cmp, success, fail, igvn); - return this; + return merge_uncommon_traps(dom_cmp, success, fail, igvn); } return NULL; } else if (ctrl->in(0) != NULL && @@ -1229,8 +1274,7 @@ Node* IfNode::fold_compares(PhaseIterGVN* igvn) { // Next call modifies graph so must be last fold_compares_helper(dom_cmp, success, fail, igvn)) { reroute_side_effect_free_unc(other_cmp, dom_cmp, igvn); - merge_uncommon_traps(dom_cmp, success, fail, igvn); - return this; + return merge_uncommon_traps(dom_cmp, success, fail, igvn); } } } @@ -1311,14 +1355,10 @@ struct RangeCheck { jint off; }; -//------------------------------Ideal------------------------------------------ -// Return a node which is more "ideal" than the current node. Strip out -// control copies -Node *IfNode::Ideal(PhaseGVN *phase, bool can_reshape) { +Node* IfNode::Ideal_common(PhaseGVN *phase, bool can_reshape) { if (remove_dead_region(phase, can_reshape)) return this; // No Def-Use info? if (!can_reshape) return NULL; - PhaseIterGVN *igvn = phase->is_IterGVN(); // Don't bother trying to transform a dead if if (in(0)->is_top()) return NULL; @@ -1334,24 +1374,291 @@ Node *IfNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (idt_if != NULL) return idt_if; // Try to split the IF + PhaseIterGVN *igvn = phase->is_IterGVN(); Node *s = split_if(this, igvn); if (s != NULL) return s; + return NodeSentinel; +} + +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. Strip out +// control copies +Node* IfNode::Ideal(PhaseGVN *phase, bool can_reshape) { + Node* res = Ideal_common(phase, can_reshape); + if (res != NodeSentinel) { + return res; + } + // Check for people making a useless boolean: things like // if( (x < y ? true : false) ) { ... } // Replace with if( x < y ) { ... } Node *bol2 = remove_useless_bool(this, phase); if( bol2 ) return bol2; + if (in(0) == NULL) return NULL; // Dead loop? + + PhaseIterGVN *igvn = phase->is_IterGVN(); + Node* result = fold_compares(igvn); + if (result != NULL) { + return result; + } + + // Scan for an equivalent test + Node *cmp; + int dist = 0; // Cutoff limit for search + int op = Opcode(); + if( op == Op_If && + (cmp=in(1)->in(1))->Opcode() == Op_CmpP ) { + if( cmp->in(2) != NULL && // make sure cmp is not already dead + cmp->in(2)->bottom_type() == TypePtr::NULL_PTR ) { + dist = 64; // Limit for null-pointer scans + } else { + dist = 4; // Do not bother for random pointer tests + } + } else { + dist = 4; // Limit for random junky scans + } + + Node* prev_dom = search_identical(dist); + + if (prev_dom == NULL) { + return NULL; + } + + // Replace dominated IfNode + return dominated_by(prev_dom, igvn); +} + +//------------------------------dominated_by----------------------------------- +Node* IfNode::dominated_by(Node* prev_dom, PhaseIterGVN *igvn) { +#ifndef PRODUCT + if (TraceIterativeGVN) { + tty->print(" Removing IfNode: "); this->dump(); + } + if (VerifyOpto && !igvn->allow_progress()) { + // Found an equivalent dominating test, + // we can not guarantee reaching a fix-point for these during iterativeGVN + // since intervening nodes may not change. + return NULL; + } +#endif + + igvn->hash_delete(this); // Remove self to prevent spurious V-N + Node *idom = in(0); + // Need opcode to decide which way 'this' test goes + int prev_op = prev_dom->Opcode(); + Node *top = igvn->C->top(); // Shortcut to top + + // Loop predicates may have depending checks which should not + // be skipped. For example, range check predicate has two checks + // for lower and upper bounds. + ProjNode* unc_proj = proj_out(1 - prev_dom->as_Proj()->_con)->as_Proj(); + if (unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_predicate) != NULL) + prev_dom = idom; + + // Now walk the current IfNode's projections. + // Loop ends when 'this' has no more uses. + for (DUIterator_Last imin, i = last_outs(imin); i >= imin; --i) { + Node *ifp = last_out(i); // Get IfTrue/IfFalse + igvn->add_users_to_worklist(ifp); + // Check which projection it is and set target. + // Data-target is either the dominating projection of the same type + // or TOP if the dominating projection is of opposite type. + // Data-target will be used as the new control edge for the non-CFG + // nodes like Casts and Loads. + Node *data_target = (ifp->Opcode() == prev_op) ? prev_dom : top; + // Control-target is just the If's immediate dominator or TOP. + Node *ctrl_target = (ifp->Opcode() == prev_op) ? idom : top; + + // For each child of an IfTrue/IfFalse projection, reroute. + // Loop ends when projection has no more uses. + for (DUIterator_Last jmin, j = ifp->last_outs(jmin); j >= jmin; --j) { + Node* s = ifp->last_out(j); // Get child of IfTrue/IfFalse + if( !s->depends_only_on_test() ) { + // Find the control input matching this def-use edge. + // For Regions it may not be in slot 0. + uint l; + for( l = 0; s->in(l) != ifp; l++ ) { } + igvn->replace_input_of(s, l, ctrl_target); + } else { // Else, for control producers, + igvn->replace_input_of(s, 0, data_target); // Move child to data-target + } + } // End for each child of a projection + + igvn->remove_dead_node(ifp); + } // End for each IfTrue/IfFalse child of If + + // Kill the IfNode + igvn->remove_dead_node(this); + + // Must return either the original node (now dead) or a new node + // (Do not return a top here, since that would break the uniqueness of top.) + return new ConINode(TypeInt::ZERO); +} + +Node* IfNode::search_identical(int dist) { // Setup to scan up the CFG looking for a dominating test - Node *dom = in(0); - Node *prev_dom = this; + Node* dom = in(0); + Node* prev_dom = this; + int op = Opcode(); + // Search up the dominator tree for an If with an identical test + while( dom->Opcode() != op || // Not same opcode? + dom->in(1) != in(1) || // Not same input 1? + (req() == 3 && dom->in(2) != in(2)) || // Not same input 2? + prev_dom->in(0) != dom ) { // One path of test does not dominate? + if( dist < 0 ) return NULL; + + dist--; + prev_dom = dom; + dom = up_one_dom( dom ); + if( !dom ) return NULL; + } + + // Check that we did not follow a loop back to ourselves + if( this == dom ) + return NULL; + + if( dist > 2 ) // Add to count of NULL checks elided + explicit_null_checks_elided++; + + return prev_dom; +} + +//------------------------------Identity--------------------------------------- +// If the test is constant & we match, then we are the input Control +Node *IfProjNode::Identity(PhaseTransform *phase) { + // Can only optimize if cannot go the other way + const TypeTuple *t = phase->type(in(0))->is_tuple(); + if (t == TypeTuple::IFNEITHER || + // kill dead branch first otherwise the IfNode's control will + // have 2 control uses (the IfNode that doesn't go away because + // it still has uses and this branch of the + // If). Node::has_special_unique_user() will cause this node to + // be reprocessed once the dead branch is killed. + (always_taken(t) && in(0)->outcnt() == 1)) { + // IfNode control + return in(0)->in(0); + } + // no progress + return this; +} + +#ifndef PRODUCT +//-------------------------------related--------------------------------------- +// An IfProjNode's related node set consists of its input (an IfNode) including +// the IfNode's condition, plus all of its outputs at level 1. In compact mode, +// the restrictions for IfNode apply (see IfNode::rel). +void IfProjNode::related(GrowableArray *in_rel, GrowableArray *out_rel, bool compact) const { + Node* ifNode = this->in(0); + in_rel->append(ifNode); + if (compact) { + ifNode->collect_nodes(in_rel, 3, false, true); + } else { + ifNode->collect_nodes_in_all_data(in_rel, false); + } + this->collect_nodes(out_rel, -1, false, false); +} + +//------------------------------dump_spec-------------------------------------- +void IfNode::dump_spec(outputStream *st) const { + st->print("P=%f, C=%f",_prob,_fcnt); +} + +//-------------------------------related--------------------------------------- +// For an IfNode, the set of related output nodes is just the output nodes till +// depth 2, i.e, the IfTrue/IfFalse projection nodes plus the nodes they refer. +// The related input nodes contain no control nodes, but all data nodes +// pertaining to the condition. In compact mode, the input nodes are collected +// up to a depth of 3. +void IfNode::related(GrowableArray *in_rel, GrowableArray *out_rel, bool compact) const { + if (compact) { + this->collect_nodes(in_rel, 3, false, true); + } else { + this->collect_nodes_in_all_data(in_rel, false); + } + this->collect_nodes(out_rel, -2, false, false); +} +#endif + +//------------------------------idealize_test---------------------------------- +// Try to canonicalize tests better. Peek at the Cmp/Bool/If sequence and +// come up with a canonical sequence. Bools getting 'eq', 'gt' and 'ge' forms +// converted to 'ne', 'le' and 'lt' forms. IfTrue/IfFalse get swapped as +// needed. +static IfNode* idealize_test(PhaseGVN* phase, IfNode* iff) { + assert(iff->in(0) != NULL, "If must be live"); + + if (iff->outcnt() != 2) return NULL; // Malformed projections. + Node* old_if_f = iff->proj_out(false); + Node* old_if_t = iff->proj_out(true); + + // CountedLoopEnds want the back-control test to be TRUE, irregardless of + // whether they are testing a 'gt' or 'lt' condition. The 'gt' condition + // happens in count-down loops + if (iff->is_CountedLoopEnd()) return NULL; + if (!iff->in(1)->is_Bool()) return NULL; // Happens for partially optimized IF tests + BoolNode *b = iff->in(1)->as_Bool(); + BoolTest bt = b->_test; + // Test already in good order? + if( bt.is_canonical() ) + return NULL; + + // Flip test to be canonical. Requires flipping the IfFalse/IfTrue and + // cloning the IfNode. + Node* new_b = phase->transform( new BoolNode(b->in(1), bt.negate()) ); + if( !new_b->is_Bool() ) return NULL; + b = new_b->as_Bool(); + + PhaseIterGVN *igvn = phase->is_IterGVN(); + assert( igvn, "Test is not canonical in parser?" ); + + // The IF node never really changes, but it needs to be cloned + iff = iff->clone()->as_If(); + iff->set_req(1, b); + iff->_prob = 1.0-iff->_prob; + + Node *prior = igvn->hash_find_insert(iff); + if( prior ) { + igvn->remove_dead_node(iff); + iff = (IfNode*)prior; + } else { + // Cannot call transform on it just yet + igvn->set_type_bottom(iff); + } + igvn->_worklist.push(iff); + + // Now handle projections. Cloning not required. + Node* new_if_f = (Node*)(new IfFalseNode( iff )); + Node* new_if_t = (Node*)(new IfTrueNode ( iff )); + + igvn->register_new_node_with_optimizer(new_if_f); + igvn->register_new_node_with_optimizer(new_if_t); + // Flip test, so flip trailing control + igvn->replace_node(old_if_f, new_if_t); + igvn->replace_node(old_if_t, new_if_f); + + // Progress + return iff; +} + +Node* RangeCheckNode::Ideal(PhaseGVN *phase, bool can_reshape) { + Node* res = Ideal_common(phase, can_reshape); + if (res != NodeSentinel) { + return res; + } + + PhaseIterGVN *igvn = phase->is_IterGVN(); + // Setup to scan up the CFG looking for a dominating test + Node* prev_dom = this; // Check for range-check vs other kinds of tests - Node *index1, *range1; + Node* index1; + Node* range1; jint offset1; int flip1 = is_range_check(range1, index1, offset1); - if( flip1 ) { + if (flip1) { + Node* dom = in(0); // Try to remove extra range checks. All 'up_one_dom' gives up at merges // so all checks we inspect post-dominate the top-most check we find. // If we are going to fail the current check and we reach the top check @@ -1372,13 +1679,14 @@ Node *IfNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Scan for the top checks and collect range of offsets for (int dist = 0; dist < 999; dist++) { // Range-Check scan limit - if (dom->Opcode() == Op_If && // Not same opcode? + if (dom->Opcode() == Op_RangeCheck && // Not same opcode? prev_dom->in(0) == dom) { // One path of test does dominate? if (dom == this) return NULL; // dead loop // See if this is a range check - Node *index2, *range2; + Node* index2; + Node* range2; jint offset2; - int flip2 = dom->as_If()->is_range_check(range2, index2, offset2); + int flip2 = dom->as_RangeCheck()->is_range_check(range2, index2, offset2); // See if this is a _matching_ range check, checking against // the same array bounds. if (flip2 == flip1 && range2 == range1 && index2 == index1 && @@ -1486,237 +1794,14 @@ Node *IfNode::Ideal(PhaseGVN *phase, bool can_reshape) { prev_dom = rc0.ctl; } } + } else { + prev_dom = search_identical(4); - } else { // Scan for an equivalent test - - Node *cmp; - int dist = 0; // Cutoff limit for search - int op = Opcode(); - if( op == Op_If && - (cmp=in(1)->in(1))->Opcode() == Op_CmpP ) { - if( cmp->in(2) != NULL && // make sure cmp is not already dead - cmp->in(2)->bottom_type() == TypePtr::NULL_PTR ) { - dist = 64; // Limit for null-pointer scans - } else { - dist = 4; // Do not bother for random pointer tests - } - } else { - dist = 4; // Limit for random junky scans - } - - // Normal equivalent-test check. - if( !dom ) return NULL; // Dead loop? - - Node* result = fold_compares(igvn); - if (result != NULL) { - return result; - } - - // Search up the dominator tree for an If with an identical test - while( dom->Opcode() != op || // Not same opcode? - dom->in(1) != in(1) || // Not same input 1? - (req() == 3 && dom->in(2) != in(2)) || // Not same input 2? - prev_dom->in(0) != dom ) { // One path of test does not dominate? - if( dist < 0 ) return NULL; - - dist--; - prev_dom = dom; - dom = up_one_dom( dom ); - if( !dom ) return NULL; - } - - // Check that we did not follow a loop back to ourselves - if( this == dom ) + if (prev_dom == NULL) { return NULL; - - if( dist > 2 ) // Add to count of NULL checks elided - explicit_null_checks_elided++; - - } // End of Else scan for an equivalent test - - // Hit! Remove this IF -#ifndef PRODUCT - if( TraceIterativeGVN ) { - tty->print(" Removing IfNode: "); this->dump(); + } } - if( VerifyOpto && !phase->allow_progress() ) { - // Found an equivalent dominating test, - // we can not guarantee reaching a fix-point for these during iterativeGVN - // since intervening nodes may not change. - return NULL; - } -#endif // Replace dominated IfNode - dominated_by( prev_dom, igvn ); - - // Must return either the original node (now dead) or a new node - // (Do not return a top here, since that would break the uniqueness of top.) - return new ConINode(TypeInt::ZERO); -} - -//------------------------------dominated_by----------------------------------- -void IfNode::dominated_by( Node *prev_dom, PhaseIterGVN *igvn ) { - igvn->hash_delete(this); // Remove self to prevent spurious V-N - Node *idom = in(0); - // Need opcode to decide which way 'this' test goes - int prev_op = prev_dom->Opcode(); - Node *top = igvn->C->top(); // Shortcut to top - - // Loop predicates may have depending checks which should not - // be skipped. For example, range check predicate has two checks - // for lower and upper bounds. - ProjNode* unc_proj = proj_out(1 - prev_dom->as_Proj()->_con)->as_Proj(); - if (unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_predicate) != NULL) - prev_dom = idom; - - // Now walk the current IfNode's projections. - // Loop ends when 'this' has no more uses. - for (DUIterator_Last imin, i = last_outs(imin); i >= imin; --i) { - Node *ifp = last_out(i); // Get IfTrue/IfFalse - igvn->add_users_to_worklist(ifp); - // Check which projection it is and set target. - // Data-target is either the dominating projection of the same type - // or TOP if the dominating projection is of opposite type. - // Data-target will be used as the new control edge for the non-CFG - // nodes like Casts and Loads. - Node *data_target = (ifp->Opcode() == prev_op) ? prev_dom : top; - // Control-target is just the If's immediate dominator or TOP. - Node *ctrl_target = (ifp->Opcode() == prev_op) ? idom : top; - - // For each child of an IfTrue/IfFalse projection, reroute. - // Loop ends when projection has no more uses. - for (DUIterator_Last jmin, j = ifp->last_outs(jmin); j >= jmin; --j) { - Node* s = ifp->last_out(j); // Get child of IfTrue/IfFalse - if( !s->depends_only_on_test() ) { - // Find the control input matching this def-use edge. - // For Regions it may not be in slot 0. - uint l; - for( l = 0; s->in(l) != ifp; l++ ) { } - igvn->replace_input_of(s, l, ctrl_target); - } else { // Else, for control producers, - igvn->replace_input_of(s, 0, data_target); // Move child to data-target - } - } // End for each child of a projection - - igvn->remove_dead_node(ifp); - } // End for each IfTrue/IfFalse child of If - - // Kill the IfNode - igvn->remove_dead_node(this); -} - -//------------------------------Identity--------------------------------------- -// If the test is constant & we match, then we are the input Control -Node *IfProjNode::Identity(PhaseTransform *phase) { - // Can only optimize if cannot go the other way - const TypeTuple *t = phase->type(in(0))->is_tuple(); - if (t == TypeTuple::IFNEITHER || - // kill dead branch first otherwise the IfNode's control will - // have 2 control uses (the IfNode that doesn't go away because - // it still has uses and this branch of the - // If). Node::has_special_unique_user() will cause this node to - // be reprocessed once the dead branch is killed. - (always_taken(t) && in(0)->outcnt() == 1)) { - // IfNode control - return in(0)->in(0); - } - // no progress - return this; -} - -#ifndef PRODUCT -//-------------------------------related--------------------------------------- -// An IfProjNode's related node set consists of its input (an IfNode) including -// the IfNode's condition, plus all of its outputs at level 1. In compact mode, -// the restrictions for IfNode apply (see IfNode::rel). -void IfProjNode::related(GrowableArray *in_rel, GrowableArray *out_rel, bool compact) const { - Node* ifNode = this->in(0); - in_rel->append(ifNode); - if (compact) { - ifNode->collect_nodes(in_rel, 3, false, true); - } else { - ifNode->collect_nodes_in_all_data(in_rel, false); - } - this->collect_nodes(out_rel, -1, false, false); -} - -//------------------------------dump_spec-------------------------------------- -void IfNode::dump_spec(outputStream *st) const { - st->print("P=%f, C=%f",_prob,_fcnt); -} - -//-------------------------------related--------------------------------------- -// For an IfNode, the set of related output nodes is just the output nodes till -// depth 2, i.e, the IfTrue/IfFalse projection nodes plus the nodes they refer. -// The related input nodes contain no control nodes, but all data nodes -// pertaining to the condition. In compact mode, the input nodes are collected -// up to a depth of 3. -void IfNode::related(GrowableArray *in_rel, GrowableArray *out_rel, bool compact) const { - if (compact) { - this->collect_nodes(in_rel, 3, false, true); - } else { - this->collect_nodes_in_all_data(in_rel, false); - } - this->collect_nodes(out_rel, -2, false, false); -} -#endif - -//------------------------------idealize_test---------------------------------- -// Try to canonicalize tests better. Peek at the Cmp/Bool/If sequence and -// come up with a canonical sequence. Bools getting 'eq', 'gt' and 'ge' forms -// converted to 'ne', 'le' and 'lt' forms. IfTrue/IfFalse get swapped as -// needed. -static IfNode* idealize_test(PhaseGVN* phase, IfNode* iff) { - assert(iff->in(0) != NULL, "If must be live"); - - if (iff->outcnt() != 2) return NULL; // Malformed projections. - Node* old_if_f = iff->proj_out(false); - Node* old_if_t = iff->proj_out(true); - - // CountedLoopEnds want the back-control test to be TRUE, irregardless of - // whether they are testing a 'gt' or 'lt' condition. The 'gt' condition - // happens in count-down loops - if (iff->is_CountedLoopEnd()) return NULL; - if (!iff->in(1)->is_Bool()) return NULL; // Happens for partially optimized IF tests - BoolNode *b = iff->in(1)->as_Bool(); - BoolTest bt = b->_test; - // Test already in good order? - if( bt.is_canonical() ) - return NULL; - - // Flip test to be canonical. Requires flipping the IfFalse/IfTrue and - // cloning the IfNode. - Node* new_b = phase->transform( new BoolNode(b->in(1), bt.negate()) ); - if( !new_b->is_Bool() ) return NULL; - b = new_b->as_Bool(); - - PhaseIterGVN *igvn = phase->is_IterGVN(); - assert( igvn, "Test is not canonical in parser?" ); - - // The IF node never really changes, but it needs to be cloned - iff = new IfNode( iff->in(0), b, 1.0-iff->_prob, iff->_fcnt); - - Node *prior = igvn->hash_find_insert(iff); - if( prior ) { - igvn->remove_dead_node(iff); - iff = (IfNode*)prior; - } else { - // Cannot call transform on it just yet - igvn->set_type_bottom(iff); - } - igvn->_worklist.push(iff); - - // Now handle projections. Cloning not required. - Node* new_if_f = (Node*)(new IfFalseNode( iff )); - Node* new_if_t = (Node*)(new IfTrueNode ( iff )); - - igvn->register_new_node_with_optimizer(new_if_f); - igvn->register_new_node_with_optimizer(new_if_t); - // Flip test, so flip trailing control - igvn->replace_node(old_if_f, new_if_t); - igvn->replace_node(old_if_t, new_if_f); - - // Progress - return iff; + return dominated_by(prev_dom, igvn); } diff --git a/hotspot/src/share/vm/opto/lcm.cpp b/hotspot/src/share/vm/opto/lcm.cpp index 1dd92fa610f..57614f7cddf 100644 --- a/hotspot/src/share/vm/opto/lcm.cpp +++ b/hotspot/src/share/vm/opto/lcm.cpp @@ -498,9 +498,13 @@ Node* PhaseCFG::select( continue; // Schedule IV increment last. - if (e->is_Mach() && e->as_Mach()->ideal_Opcode() == Op_CountedLoopEnd && - e->in(1)->in(1) == n && n->is_iteratively_computed()) - continue; + if (e->is_Mach() && e->as_Mach()->ideal_Opcode() == Op_CountedLoopEnd) { + // Cmp might be matched into CountedLoopEnd node. + Node *cmp = (e->in(1)->ideal_reg() == Op_RegFlags) ? e->in(1) : e; + if (cmp->req() > 1 && cmp->in(1) == n && n->is_iteratively_computed()) { + continue; + } + } uint n_choice = 2; diff --git a/hotspot/src/share/vm/opto/library_call.cpp b/hotspot/src/share/vm/opto/library_call.cpp index c1f744c5be0..749c4680b23 100644 --- a/hotspot/src/share/vm/opto/library_call.cpp +++ b/hotspot/src/share/vm/opto/library_call.cpp @@ -238,7 +238,7 @@ class LibraryCallKit : public GraphKit { // Generates the guards that check whether the result of // Unsafe.getObject should be recorded in an SATB log buffer. void insert_pre_barrier(Node* base_oop, Node* offset, Node* pre_val, bool need_mem_bar); - bool inline_unsafe_access(bool is_native_ptr, bool is_store, BasicType type, bool is_volatile); + bool inline_unsafe_access(bool is_native_ptr, bool is_store, BasicType type, bool is_volatile, bool is_unaligned); static bool klass_needs_init_guard(Node* kls); bool inline_unsafe_allocate(); bool inline_unsafe_copyMemory(); @@ -256,6 +256,7 @@ class LibraryCallKit : public GraphKit { bool inline_native_getLength(); bool inline_array_copyOf(bool is_copyOfRange); bool inline_array_equals(StrIntrinsicNode::ArgEnc ae); + bool inline_objects_checkIndex(); void copy_to_clone(Node* obj, Node* alloc_obj, Node* obj_size, bool is_array, bool card_mark); bool inline_native_clone(bool is_virtual); bool inline_native_Reflection_getCallerClass(); @@ -544,72 +545,72 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_inflateStringC: case vmIntrinsics::_inflateStringB: return inline_string_copy(!is_compress); - case vmIntrinsics::_getObject: return inline_unsafe_access(!is_native_ptr, !is_store, T_OBJECT, !is_volatile); - case vmIntrinsics::_getBoolean: return inline_unsafe_access(!is_native_ptr, !is_store, T_BOOLEAN, !is_volatile); - case vmIntrinsics::_getByte: return inline_unsafe_access(!is_native_ptr, !is_store, T_BYTE, !is_volatile); - case vmIntrinsics::_getShort: return inline_unsafe_access(!is_native_ptr, !is_store, T_SHORT, !is_volatile); - case vmIntrinsics::_getChar: return inline_unsafe_access(!is_native_ptr, !is_store, T_CHAR, !is_volatile); - case vmIntrinsics::_getInt: return inline_unsafe_access(!is_native_ptr, !is_store, T_INT, !is_volatile); - case vmIntrinsics::_getLong: return inline_unsafe_access(!is_native_ptr, !is_store, T_LONG, !is_volatile); - case vmIntrinsics::_getFloat: return inline_unsafe_access(!is_native_ptr, !is_store, T_FLOAT, !is_volatile); - case vmIntrinsics::_getDouble: return inline_unsafe_access(!is_native_ptr, !is_store, T_DOUBLE, !is_volatile); - case vmIntrinsics::_putObject: return inline_unsafe_access(!is_native_ptr, is_store, T_OBJECT, !is_volatile); - case vmIntrinsics::_putBoolean: return inline_unsafe_access(!is_native_ptr, is_store, T_BOOLEAN, !is_volatile); - case vmIntrinsics::_putByte: return inline_unsafe_access(!is_native_ptr, is_store, T_BYTE, !is_volatile); - case vmIntrinsics::_putShort: return inline_unsafe_access(!is_native_ptr, is_store, T_SHORT, !is_volatile); - case vmIntrinsics::_putChar: return inline_unsafe_access(!is_native_ptr, is_store, T_CHAR, !is_volatile); - case vmIntrinsics::_putInt: return inline_unsafe_access(!is_native_ptr, is_store, T_INT, !is_volatile); - case vmIntrinsics::_putLong: return inline_unsafe_access(!is_native_ptr, is_store, T_LONG, !is_volatile); - case vmIntrinsics::_putFloat: return inline_unsafe_access(!is_native_ptr, is_store, T_FLOAT, !is_volatile); - case vmIntrinsics::_putDouble: return inline_unsafe_access(!is_native_ptr, is_store, T_DOUBLE, !is_volatile); + case vmIntrinsics::_getObject: return inline_unsafe_access(!is_native_ptr, !is_store, T_OBJECT, !is_volatile, false); + case vmIntrinsics::_getBoolean: return inline_unsafe_access(!is_native_ptr, !is_store, T_BOOLEAN, !is_volatile, false); + case vmIntrinsics::_getByte: return inline_unsafe_access(!is_native_ptr, !is_store, T_BYTE, !is_volatile, false); + case vmIntrinsics::_getShort: return inline_unsafe_access(!is_native_ptr, !is_store, T_SHORT, !is_volatile, false); + case vmIntrinsics::_getChar: return inline_unsafe_access(!is_native_ptr, !is_store, T_CHAR, !is_volatile, false); + case vmIntrinsics::_getInt: return inline_unsafe_access(!is_native_ptr, !is_store, T_INT, !is_volatile, false); + case vmIntrinsics::_getLong: return inline_unsafe_access(!is_native_ptr, !is_store, T_LONG, !is_volatile, false); + case vmIntrinsics::_getFloat: return inline_unsafe_access(!is_native_ptr, !is_store, T_FLOAT, !is_volatile, false); + case vmIntrinsics::_getDouble: return inline_unsafe_access(!is_native_ptr, !is_store, T_DOUBLE, !is_volatile, false); + case vmIntrinsics::_putObject: return inline_unsafe_access(!is_native_ptr, is_store, T_OBJECT, !is_volatile, false); + case vmIntrinsics::_putBoolean: return inline_unsafe_access(!is_native_ptr, is_store, T_BOOLEAN, !is_volatile, false); + case vmIntrinsics::_putByte: return inline_unsafe_access(!is_native_ptr, is_store, T_BYTE, !is_volatile, false); + case vmIntrinsics::_putShort: return inline_unsafe_access(!is_native_ptr, is_store, T_SHORT, !is_volatile, false); + case vmIntrinsics::_putChar: return inline_unsafe_access(!is_native_ptr, is_store, T_CHAR, !is_volatile, false); + case vmIntrinsics::_putInt: return inline_unsafe_access(!is_native_ptr, is_store, T_INT, !is_volatile, false); + case vmIntrinsics::_putLong: return inline_unsafe_access(!is_native_ptr, is_store, T_LONG, !is_volatile, false); + case vmIntrinsics::_putFloat: return inline_unsafe_access(!is_native_ptr, is_store, T_FLOAT, !is_volatile, false); + case vmIntrinsics::_putDouble: return inline_unsafe_access(!is_native_ptr, is_store, T_DOUBLE, !is_volatile, false); - case vmIntrinsics::_getByte_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_BYTE, !is_volatile); - case vmIntrinsics::_getShort_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_SHORT, !is_volatile); - case vmIntrinsics::_getChar_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_CHAR, !is_volatile); - case vmIntrinsics::_getInt_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_INT, !is_volatile); - case vmIntrinsics::_getLong_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_LONG, !is_volatile); - case vmIntrinsics::_getFloat_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_FLOAT, !is_volatile); - case vmIntrinsics::_getDouble_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_DOUBLE, !is_volatile); - case vmIntrinsics::_getAddress_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_ADDRESS, !is_volatile); + case vmIntrinsics::_getByte_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_BYTE, !is_volatile, false); + case vmIntrinsics::_getShort_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_SHORT, !is_volatile, false); + case vmIntrinsics::_getChar_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_CHAR, !is_volatile, false); + case vmIntrinsics::_getInt_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_INT, !is_volatile, false); + case vmIntrinsics::_getLong_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_LONG, !is_volatile, false); + case vmIntrinsics::_getFloat_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_FLOAT, !is_volatile, false); + case vmIntrinsics::_getDouble_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_DOUBLE, !is_volatile, false); + case vmIntrinsics::_getAddress_raw: return inline_unsafe_access( is_native_ptr, !is_store, T_ADDRESS, !is_volatile, false); - case vmIntrinsics::_putByte_raw: return inline_unsafe_access( is_native_ptr, is_store, T_BYTE, !is_volatile); - case vmIntrinsics::_putShort_raw: return inline_unsafe_access( is_native_ptr, is_store, T_SHORT, !is_volatile); - case vmIntrinsics::_putChar_raw: return inline_unsafe_access( is_native_ptr, is_store, T_CHAR, !is_volatile); - case vmIntrinsics::_putInt_raw: return inline_unsafe_access( is_native_ptr, is_store, T_INT, !is_volatile); - case vmIntrinsics::_putLong_raw: return inline_unsafe_access( is_native_ptr, is_store, T_LONG, !is_volatile); - case vmIntrinsics::_putFloat_raw: return inline_unsafe_access( is_native_ptr, is_store, T_FLOAT, !is_volatile); - case vmIntrinsics::_putDouble_raw: return inline_unsafe_access( is_native_ptr, is_store, T_DOUBLE, !is_volatile); - case vmIntrinsics::_putAddress_raw: return inline_unsafe_access( is_native_ptr, is_store, T_ADDRESS, !is_volatile); + case vmIntrinsics::_putByte_raw: return inline_unsafe_access( is_native_ptr, is_store, T_BYTE, !is_volatile, false); + case vmIntrinsics::_putShort_raw: return inline_unsafe_access( is_native_ptr, is_store, T_SHORT, !is_volatile, false); + case vmIntrinsics::_putChar_raw: return inline_unsafe_access( is_native_ptr, is_store, T_CHAR, !is_volatile, false); + case vmIntrinsics::_putInt_raw: return inline_unsafe_access( is_native_ptr, is_store, T_INT, !is_volatile, false); + case vmIntrinsics::_putLong_raw: return inline_unsafe_access( is_native_ptr, is_store, T_LONG, !is_volatile, false); + case vmIntrinsics::_putFloat_raw: return inline_unsafe_access( is_native_ptr, is_store, T_FLOAT, !is_volatile, false); + case vmIntrinsics::_putDouble_raw: return inline_unsafe_access( is_native_ptr, is_store, T_DOUBLE, !is_volatile, false); + case vmIntrinsics::_putAddress_raw: return inline_unsafe_access( is_native_ptr, is_store, T_ADDRESS, !is_volatile, false); - case vmIntrinsics::_getObjectVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_OBJECT, is_volatile); - case vmIntrinsics::_getBooleanVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_BOOLEAN, is_volatile); - case vmIntrinsics::_getByteVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_BYTE, is_volatile); - case vmIntrinsics::_getShortVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_SHORT, is_volatile); - case vmIntrinsics::_getCharVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_CHAR, is_volatile); - case vmIntrinsics::_getIntVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_INT, is_volatile); - case vmIntrinsics::_getLongVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_LONG, is_volatile); - case vmIntrinsics::_getFloatVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_FLOAT, is_volatile); - case vmIntrinsics::_getDoubleVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_DOUBLE, is_volatile); + case vmIntrinsics::_getObjectVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_OBJECT, is_volatile, false); + case vmIntrinsics::_getBooleanVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_BOOLEAN, is_volatile, false); + case vmIntrinsics::_getByteVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_BYTE, is_volatile, false); + case vmIntrinsics::_getShortVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_SHORT, is_volatile, false); + case vmIntrinsics::_getCharVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_CHAR, is_volatile, false); + case vmIntrinsics::_getIntVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_INT, is_volatile, false); + case vmIntrinsics::_getLongVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_LONG, is_volatile, false); + case vmIntrinsics::_getFloatVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_FLOAT, is_volatile, false); + case vmIntrinsics::_getDoubleVolatile: return inline_unsafe_access(!is_native_ptr, !is_store, T_DOUBLE, is_volatile, false); - case vmIntrinsics::_putObjectVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_OBJECT, is_volatile); - case vmIntrinsics::_putBooleanVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_BOOLEAN, is_volatile); - case vmIntrinsics::_putByteVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_BYTE, is_volatile); - case vmIntrinsics::_putShortVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_SHORT, is_volatile); - case vmIntrinsics::_putCharVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_CHAR, is_volatile); - case vmIntrinsics::_putIntVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_INT, is_volatile); - case vmIntrinsics::_putLongVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_LONG, is_volatile); - case vmIntrinsics::_putFloatVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_FLOAT, is_volatile); - case vmIntrinsics::_putDoubleVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_DOUBLE, is_volatile); + case vmIntrinsics::_putObjectVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_OBJECT, is_volatile, false); + case vmIntrinsics::_putBooleanVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_BOOLEAN, is_volatile, false); + case vmIntrinsics::_putByteVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_BYTE, is_volatile, false); + case vmIntrinsics::_putShortVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_SHORT, is_volatile, false); + case vmIntrinsics::_putCharVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_CHAR, is_volatile, false); + case vmIntrinsics::_putIntVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_INT, is_volatile, false); + case vmIntrinsics::_putLongVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_LONG, is_volatile, false); + case vmIntrinsics::_putFloatVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_FLOAT, is_volatile, false); + case vmIntrinsics::_putDoubleVolatile: return inline_unsafe_access(!is_native_ptr, is_store, T_DOUBLE, is_volatile, false); - case vmIntrinsics::_getShortUnaligned: return inline_unsafe_access(!is_native_ptr, !is_store, T_SHORT, !is_volatile); - case vmIntrinsics::_getCharUnaligned: return inline_unsafe_access(!is_native_ptr, !is_store, T_CHAR, !is_volatile); - case vmIntrinsics::_getIntUnaligned: return inline_unsafe_access(!is_native_ptr, !is_store, T_INT, !is_volatile); - case vmIntrinsics::_getLongUnaligned: return inline_unsafe_access(!is_native_ptr, !is_store, T_LONG, !is_volatile); + case vmIntrinsics::_getShortUnaligned: return inline_unsafe_access(!is_native_ptr, !is_store, T_SHORT, !is_volatile, true); + case vmIntrinsics::_getCharUnaligned: return inline_unsafe_access(!is_native_ptr, !is_store, T_CHAR, !is_volatile, true); + case vmIntrinsics::_getIntUnaligned: return inline_unsafe_access(!is_native_ptr, !is_store, T_INT, !is_volatile, true); + case vmIntrinsics::_getLongUnaligned: return inline_unsafe_access(!is_native_ptr, !is_store, T_LONG, !is_volatile, true); - case vmIntrinsics::_putShortUnaligned: return inline_unsafe_access(!is_native_ptr, is_store, T_SHORT, !is_volatile); - case vmIntrinsics::_putCharUnaligned: return inline_unsafe_access(!is_native_ptr, is_store, T_CHAR, !is_volatile); - case vmIntrinsics::_putIntUnaligned: return inline_unsafe_access(!is_native_ptr, is_store, T_INT, !is_volatile); - case vmIntrinsics::_putLongUnaligned: return inline_unsafe_access(!is_native_ptr, is_store, T_LONG, !is_volatile); + case vmIntrinsics::_putShortUnaligned: return inline_unsafe_access(!is_native_ptr, is_store, T_SHORT, !is_volatile, true); + case vmIntrinsics::_putCharUnaligned: return inline_unsafe_access(!is_native_ptr, is_store, T_CHAR, !is_volatile, true); + case vmIntrinsics::_putIntUnaligned: return inline_unsafe_access(!is_native_ptr, is_store, T_INT, !is_volatile, true); + case vmIntrinsics::_putLongUnaligned: return inline_unsafe_access(!is_native_ptr, is_store, T_LONG, !is_volatile, true); case vmIntrinsics::_compareAndSwapObject: return inline_unsafe_load_store(T_OBJECT, LS_cmpxchg); case vmIntrinsics::_compareAndSwapInt: return inline_unsafe_load_store(T_INT, LS_cmpxchg); @@ -647,6 +648,7 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_copyOfRange: return inline_array_copyOf(true); case vmIntrinsics::_equalsB: return inline_array_equals(StrIntrinsicNode::LL); case vmIntrinsics::_equalsC: return inline_array_equals(StrIntrinsicNode::UU); + case vmIntrinsics::_Objects_checkIndex: return inline_objects_checkIndex(); case vmIntrinsics::_clone: return inline_native_clone(intrinsic()->is_virtual()); case vmIntrinsics::_isAssignableFrom: return inline_native_subtype_check(); @@ -1045,6 +1047,54 @@ bool LibraryCallKit::inline_hasNegatives() { return true; } +bool LibraryCallKit::inline_objects_checkIndex() { + Node* index = argument(0); + Node* length = argument(1); + if (too_many_traps(Deoptimization::Reason_intrinsic) || too_many_traps(Deoptimization::Reason_range_check)) { + return false; + } + + Node* len_pos_cmp = _gvn.transform(new CmpINode(length, intcon(0))); + Node* len_pos_bol = _gvn.transform(new BoolNode(len_pos_cmp, BoolTest::ge)); + + { + BuildCutout unless(this, len_pos_bol, PROB_MAX); + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_make_not_entrant); + } + + if (stopped()) { + return false; + } + + Node* rc_cmp = _gvn.transform(new CmpUNode(index, length)); + BoolTest::mask btest = BoolTest::lt; + Node* rc_bool = _gvn.transform(new BoolNode(rc_cmp, btest)); + RangeCheckNode* rc = new RangeCheckNode(control(), rc_bool, PROB_MAX, COUNT_UNKNOWN); + _gvn.set_type(rc, rc->Value(&_gvn)); + if (!rc_bool->is_Con()) { + record_for_igvn(rc); + } + set_control(_gvn.transform(new IfTrueNode(rc))); + { + PreserveJVMState pjvms(this); + set_control(_gvn.transform(new IfFalseNode(rc))); + uncommon_trap(Deoptimization::Reason_range_check, + Deoptimization::Action_make_not_entrant); + } + + if (stopped()) { + return false; + } + + Node* result = new CastIINode(index, TypeInt::make(0, _gvn.type(length)->is_int()->_hi, Type::WidenMax)); + result->set_req(0, control()); + result = _gvn.transform(result); + set_result(result); + replace_in_map(index, result); + return true; +} + //------------------------------inline_string_indexOf------------------------ bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) { if (!Matcher::has_match_rule(Op_StrIndexOf) || !UseSSE42Intrinsics) { @@ -1453,9 +1503,11 @@ bool LibraryCallKit::inline_string_char_access(bool is_store) { Node* adr = array_element_address(value, index, T_CHAR); if (is_store) { - (void) store_to_memory(control(), adr, ch, T_CHAR, TypeAryPtr::BYTES, MemNode::unordered); + (void) store_to_memory(control(), adr, ch, T_CHAR, TypeAryPtr::BYTES, MemNode::unordered, + false, false, true /* mismatched */); } else { - ch = make_load(control(), adr, TypeInt::CHAR, T_CHAR, MemNode::unordered); + ch = make_load(control(), adr, TypeInt::CHAR, T_CHAR, MemNode::unordered, + LoadNode::DependsOnlyOnTest, false, false, true /* mismatched */); set_result(ch); } return true; @@ -2385,7 +2437,7 @@ const TypeOopPtr* LibraryCallKit::sharpen_unsafe_type(Compile::AliasType* alias_ return NULL; } -bool LibraryCallKit::inline_unsafe_access(bool is_native_ptr, bool is_store, BasicType type, bool is_volatile) { +bool LibraryCallKit::inline_unsafe_access(bool is_native_ptr, bool is_store, BasicType type, bool is_volatile, bool unaligned) { if (callee()->is_static()) return false; // caller must have the capability! #ifndef PRODUCT @@ -2527,7 +2579,28 @@ bool LibraryCallKit::inline_unsafe_access(bool is_native_ptr, bool is_store, Bas // of safe & unsafe memory. if (need_mem_bar) insert_mem_bar(Op_MemBarCPUOrder); - if (!is_store) { + assert(alias_type->adr_type() == TypeRawPtr::BOTTOM || alias_type->adr_type() == TypeOopPtr::BOTTOM || + alias_type->field() != NULL || alias_type->element() != NULL, "field, array element or unknown"); + bool mismatched = false; + if (alias_type->element() != NULL || alias_type->field() != NULL) { + BasicType bt; + if (alias_type->element() != NULL) { + const Type* element = alias_type->element(); + bt = element->isa_narrowoop() ? T_OBJECT : element->array_element_basic_type(); + } else { + bt = alias_type->field()->type()->basic_type(); + } + if (bt == T_ARRAY) { + // accessing an array field with getObject is not a mismatch + bt = T_OBJECT; + } + if (bt != type) { + mismatched = true; + } + } + assert(type != T_OBJECT || !unaligned, "unaligned access not supported with object type"); + + if (!is_store) { Node* p = NULL; // Try to constant fold a load from a constant field ciField* field = alias_type->field(); @@ -2543,7 +2616,7 @@ bool LibraryCallKit::inline_unsafe_access(bool is_native_ptr, bool is_store, Bas MemNode::MemOrd mo = is_volatile ? MemNode::acquire : MemNode::unordered; // To be valid, unsafe loads may depend on other conditions than // the one that guards them: pin the Load node - p = make_load(control(), adr, value_type, type, adr_type, mo, LoadNode::Pinned, is_volatile); + p = make_load(control(), adr, value_type, type, adr_type, mo, LoadNode::Pinned, is_volatile, unaligned, mismatched); // load value switch (type) { case T_BOOLEAN: @@ -2590,12 +2663,12 @@ bool LibraryCallKit::inline_unsafe_access(bool is_native_ptr, bool is_store, Bas MemNode::MemOrd mo = is_volatile ? MemNode::release : MemNode::unordered; if (type != T_OBJECT ) { - (void) store_to_memory(control(), adr, val, type, adr_type, mo, is_volatile); + (void) store_to_memory(control(), adr, val, type, adr_type, mo, is_volatile, unaligned, mismatched); } else { // Possibly an oop being stored to Java heap or native memory if (!TypePtr::NULL_PTR->higher_equal(_gvn.type(heap_base_oop))) { // oop to Java heap. - (void) store_oop_to_unknown(control(), heap_base_oop, adr, adr_type, val, type, mo); + (void) store_oop_to_unknown(control(), heap_base_oop, adr, adr_type, val, type, mo, mismatched); } else { // We can't tell at compile time if we are storing in the Java heap or outside // of it. So we need to emit code to conditionally do the proper type of @@ -2607,11 +2680,11 @@ bool LibraryCallKit::inline_unsafe_access(bool is_native_ptr, bool is_store, Bas __ if_then(heap_base_oop, BoolTest::ne, null(), PROB_UNLIKELY(0.999)); { // Sync IdealKit and graphKit. sync_kit(ideal); - Node* st = store_oop_to_unknown(control(), heap_base_oop, adr, adr_type, val, type, mo); + Node* st = store_oop_to_unknown(control(), heap_base_oop, adr, adr_type, val, type, mo, mismatched); // Update IdealKit memory. __ sync_kit(this); } __ else_(); { - __ store(__ ctrl(), adr, val, type, alias_type->index(), mo, is_volatile); + __ store(__ ctrl(), adr, val, type, alias_type->index(), mo, is_volatile, mismatched); } __ end_if(); // Final sync IdealKit and GraphKit. final_sync(ideal); diff --git a/hotspot/src/share/vm/opto/loopPredicate.cpp b/hotspot/src/share/vm/opto/loopPredicate.cpp index 8d202294807..9cc6961931e 100644 --- a/hotspot/src/share/vm/opto/loopPredicate.cpp +++ b/hotspot/src/share/vm/opto/loopPredicate.cpp @@ -91,7 +91,8 @@ void PhaseIdealLoop::register_control(Node* n, IdealLoopTree *loop, Node* pred) // The true projecttion (if_cont) of the new_iff is returned. // This code is also used to clone predicates to cloned loops. ProjNode* PhaseIdealLoop::create_new_if_for_predicate(ProjNode* cont_proj, Node* new_entry, - Deoptimization::DeoptReason reason) { + Deoptimization::DeoptReason reason, + int opcode) { assert(cont_proj->is_uncommon_trap_if_pattern(reason), "must be a uct if pattern!"); IfNode* iff = cont_proj->in(0)->as_If(); @@ -133,8 +134,13 @@ ProjNode* PhaseIdealLoop::create_new_if_for_predicate(ProjNode* cont_proj, Node* } // Create new_iff IdealLoopTree* lp = get_loop(entry); - IfNode *new_iff = iff->clone()->as_If(); - new_iff->set_req(0, entry); + IfNode* new_iff = NULL; + if (opcode == Op_If) { + new_iff = new IfNode(entry, iff->in(1), iff->_prob, iff->_fcnt); + } else { + assert(opcode == Op_RangeCheck, "no other if variant here"); + new_iff = new RangeCheckNode(entry, iff->in(1), iff->_prob, iff->_fcnt); + } register_control(new_iff, lp, entry); Node *if_cont = new IfTrueNode(new_iff); Node *if_uct = new IfFalseNode(new_iff); @@ -183,7 +189,8 @@ ProjNode* PhaseIdealLoop::create_new_if_for_predicate(ProjNode* cont_proj, Node* //------------------------------create_new_if_for_predicate------------------------ // Create a new if below new_entry for the predicate to be cloned (IGVN optimization) ProjNode* PhaseIterGVN::create_new_if_for_predicate(ProjNode* cont_proj, Node* new_entry, - Deoptimization::DeoptReason reason) { + Deoptimization::DeoptReason reason, + int opcode) { assert(new_entry != 0, "only used for clone predicate"); assert(cont_proj->is_uncommon_trap_if_pattern(reason), "must be a uct if pattern!"); IfNode* iff = cont_proj->in(0)->as_If(); @@ -208,8 +215,13 @@ ProjNode* PhaseIterGVN::create_new_if_for_predicate(ProjNode* cont_proj, Node* n } // Create new_iff in new location. - IfNode *new_iff = iff->clone()->as_If(); - new_iff->set_req(0, new_entry); + IfNode* new_iff = NULL; + if (opcode == Op_If) { + new_iff = new IfNode(new_entry, iff->in(1), iff->_prob, iff->_fcnt); + } else { + assert(opcode == Op_RangeCheck, "no other if variant here"); + new_iff = new RangeCheckNode(new_entry, iff->in(1), iff->_prob, iff->_fcnt); + } register_new_node_with_optimizer(new_iff); Node *if_cont = new IfTrueNode(new_iff); @@ -249,9 +261,9 @@ ProjNode* PhaseIdealLoop::clone_predicate(ProjNode* predicate_proj, Node* new_en PhaseIterGVN* igvn) { ProjNode* new_predicate_proj; if (loop_phase != NULL) { - new_predicate_proj = loop_phase->create_new_if_for_predicate(predicate_proj, new_entry, reason); + new_predicate_proj = loop_phase->create_new_if_for_predicate(predicate_proj, new_entry, reason, Op_If); } else { - new_predicate_proj = igvn->create_new_if_for_predicate(predicate_proj, new_entry, reason); + new_predicate_proj = igvn->create_new_if_for_predicate(predicate_proj, new_entry, reason, Op_If); } IfNode* iff = new_predicate_proj->in(0)->as_If(); Node* ctrl = iff->in(0); @@ -557,7 +569,7 @@ bool IdealLoopTree::is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, Invari return false; } Node* range = cmp->in(2); - if (range->Opcode() != Op_LoadRange) { + if (range->Opcode() != Op_LoadRange && !iff->is_RangeCheck()) { const TypeInt* tint = phase->_igvn.type(range)->isa_int(); if (tint == NULL || tint->empty() || tint->_lo < 0) { // Allow predication on positive values that aren't LoadRanges. @@ -714,7 +726,8 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree *loop) { while (current_proj != head) { if (loop == get_loop(current_proj) && // still in the loop ? current_proj->is_Proj() && // is a projection ? - current_proj->in(0)->Opcode() == Op_If) { // is a if projection ? + (current_proj->in(0)->Opcode() == Op_If || + current_proj->in(0)->Opcode() == Op_RangeCheck)) { // is a if projection ? if_proj_list.push(current_proj); } current_proj = idom(current_proj); @@ -753,7 +766,8 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree *loop) { if (invar.is_invariant(bol)) { // Invariant test new_predicate_proj = create_new_if_for_predicate(predicate_proj, NULL, - Deoptimization::Reason_predicate); + Deoptimization::Reason_predicate, + iff->Opcode()); Node* ctrl = new_predicate_proj->in(0)->as_If()->in(0); BoolNode* new_predicate_bol = invar.clone(bol, ctrl)->as_Bool(); @@ -797,8 +811,8 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree *loop) { // lower_bound test will dominate the upper bound test and all // cloned or created nodes will use the lower bound test as // their declared control. - ProjNode* lower_bound_proj = create_new_if_for_predicate(predicate_proj, NULL, Deoptimization::Reason_predicate); - ProjNode* upper_bound_proj = create_new_if_for_predicate(predicate_proj, NULL, Deoptimization::Reason_predicate); + ProjNode* lower_bound_proj = create_new_if_for_predicate(predicate_proj, NULL, Deoptimization::Reason_predicate, iff->Opcode()); + ProjNode* upper_bound_proj = create_new_if_for_predicate(predicate_proj, NULL, Deoptimization::Reason_predicate, iff->Opcode()); assert(upper_bound_proj->in(0)->as_If()->in(0) == lower_bound_proj, "should dominate"); Node *ctrl = lower_bound_proj->in(0)->as_If()->in(0); diff --git a/hotspot/src/share/vm/opto/loopTransform.cpp b/hotspot/src/share/vm/opto/loopTransform.cpp index 735a00dd1a1..41ebaffc838 100644 --- a/hotspot/src/share/vm/opto/loopTransform.cpp +++ b/hotspot/src/share/vm/opto/loopTransform.cpp @@ -290,7 +290,7 @@ bool IdealLoopTree::policy_peeling( PhaseIdealLoop *phase ) const { if (ctrl->is_top()) return false; // Found dead test on live IF? No peeling! // Standard IF only has one input value to check for loop invariance - assert( test->Opcode() == Op_If || test->Opcode() == Op_CountedLoopEnd, "Check this code when new subtype is added"); + assert(test->Opcode() == Op_If || test->Opcode() == Op_CountedLoopEnd || test->Opcode() == Op_RangeCheck, "Check this code when new subtype is added"); // Condition is not a member of this loop? if( !is_member(phase->get_loop(ctrl)) && is_loop_exit(test) ) @@ -792,8 +792,10 @@ bool IdealLoopTree::policy_unroll(PhaseIdealLoop *phase) { return false; } - if(cl->do_unroll_only()) { - NOT_PRODUCT(if (TraceSuperWordLoopUnrollAnalysis) tty->print_cr("policy_unroll passed vector loop(vlen=%d,factor = %d)\n", slp_max_unroll_factor, future_unroll_ct)); + if (cl->do_unroll_only()) { + if (TraceSuperWordLoopUnrollAnalysis) { + tty->print_cr("policy_unroll passed vector loop(vlen=%d,factor = %d)\n", slp_max_unroll_factor, future_unroll_ct); + } } // Unroll once! (Each trip will soon do double iterations) @@ -818,7 +820,9 @@ void IdealLoopTree::policy_unroll_slp_analysis(CountedLoopNode *cl, PhaseIdealLo if (slp_max_unroll_factor >= future_unroll_ct) { int new_limit = cl->node_count_before_unroll() * slp_max_unroll_factor; if (new_limit > LoopUnrollLimit) { - NOT_PRODUCT(if (TraceSuperWordLoopUnrollAnalysis) tty->print_cr("slp analysis unroll=%d, default limit=%d\n", new_limit, _local_loop_unroll_limit)); + if (TraceSuperWordLoopUnrollAnalysis) { + tty->print_cr("slp analysis unroll=%d, default limit=%d\n", new_limit, _local_loop_unroll_limit); + } _local_loop_unroll_limit = new_limit; } } @@ -856,7 +860,8 @@ bool IdealLoopTree::policy_range_check( PhaseIdealLoop *phase ) const { // loop-invariant. for (uint i = 0; i < _body.size(); i++) { Node *iff = _body[i]; - if (iff->Opcode() == Op_If) { // Test? + if (iff->Opcode() == Op_If || + iff->Opcode() == Op_RangeCheck) { // Test? // Comparing trip+off vs limit Node *bol = iff->in(1); @@ -2035,8 +2040,8 @@ void PhaseIdealLoop::do_range_check( IdealLoopTree *loop, Node_List &old_new ) { // loop-invariant. for( uint i = 0; i < loop->_body.size(); i++ ) { Node *iff = loop->_body[i]; - if( iff->Opcode() == Op_If ) { // Test? - + if (iff->Opcode() == Op_If || + iff->Opcode() == Op_RangeCheck) { // Test? // Test is an IfNode, has 2 projections. If BOTH are in the loop // we need loop unswitching instead of iteration splitting. Node *exit = loop->is_loop_exit(iff); @@ -2119,10 +2124,9 @@ void PhaseIdealLoop::do_range_check( IdealLoopTree *loop, Node_List &old_new ) { conditional_rc = !loop->dominates_backedge(iff) || RangeLimitCheck; } } else { -#ifndef PRODUCT - if( PrintOpto ) + if (PrintOpto) { tty->print_cr("missed RCE opportunity"); -#endif + } continue; // In release mode, ignore it } } else { // Otherwise work on normal compares @@ -2157,10 +2161,9 @@ void PhaseIdealLoop::do_range_check( IdealLoopTree *loop, Node_List &old_new ) { } break; default: -#ifndef PRODUCT - if( PrintOpto ) + if (PrintOpto) { tty->print_cr("missed RCE opportunity"); -#endif + } continue; // Unhandled case } } @@ -2504,9 +2507,7 @@ bool IdealLoopTree::iteration_split_impl( PhaseIdealLoop *phase, Node_List &old_ return false; } if (should_peel) { // Should we peel? -#ifndef PRODUCT - if (PrintOpto) tty->print_cr("should_peel"); -#endif + if (PrintOpto) { tty->print_cr("should_peel"); } phase->do_peeling(this,old_new); } else if (should_unswitch) { phase->do_unswitching(this, old_new); diff --git a/hotspot/src/share/vm/opto/loopUnswitch.cpp b/hotspot/src/share/vm/opto/loopUnswitch.cpp index 49e1052a2cd..991d339d58c 100644 --- a/hotspot/src/share/vm/opto/loopUnswitch.cpp +++ b/hotspot/src/share/vm/opto/loopUnswitch.cpp @@ -132,7 +132,7 @@ void PhaseIdealLoop::do_unswitching (IdealLoopTree *loop, Node_List &old_new) { head->as_CountedLoop()->set_normal_loop(); } - ProjNode* proj_true = create_slow_version_of_loop(loop, old_new); + ProjNode* proj_true = create_slow_version_of_loop(loop, old_new, unswitch_iff->Opcode()); #ifdef ASSERT Node* uniqc = proj_true->unique_ctrl_out(); @@ -222,7 +222,8 @@ void PhaseIdealLoop::do_unswitching (IdealLoopTree *loop, Node_List &old_new) { // and inserting an if to select fast-slow versions. // Return control projection of the entry to the fast version. ProjNode* PhaseIdealLoop::create_slow_version_of_loop(IdealLoopTree *loop, - Node_List &old_new) { + Node_List &old_new, + int opcode) { LoopNode* head = loop->_head->as_Loop(); bool counted_loop = head->is_CountedLoop(); Node* entry = head->in(LoopNode::EntryControl); @@ -235,7 +236,8 @@ ProjNode* PhaseIdealLoop::create_slow_version_of_loop(IdealLoopTree *loop, register_node(opq, outer_loop, entry, dom_depth(entry)); Node *bol = new Conv2BNode(opq); register_node(bol, outer_loop, entry, dom_depth(entry)); - IfNode* iff = new IfNode(entry, bol, PROB_MAX, COUNT_UNKNOWN); + IfNode* iff = (opcode == Op_RangeCheck) ? new RangeCheckNode(entry, bol, PROB_MAX, COUNT_UNKNOWN) : + new IfNode(entry, bol, PROB_MAX, COUNT_UNKNOWN); register_node(iff, outer_loop, entry, dom_depth(entry)); ProjNode* iffast = new IfTrueNode(iff); register_node(iffast, outer_loop, iff, dom_depth(iff)); @@ -359,16 +361,22 @@ bool CountedLoopReserveKit::create_reserve() { } if(!_lpt->_head->is_CountedLoop()) { - NOT_PRODUCT(if(TraceLoopOpts) {tty->print_cr("CountedLoopReserveKit::create_reserve: %d not counted loop", _lpt->_head->_idx);}) + if (TraceLoopOpts) { + tty->print_cr("CountedLoopReserveKit::create_reserve: %d not counted loop", _lpt->_head->_idx); + } return false; } CountedLoopNode *cl = _lpt->_head->as_CountedLoop(); if (!cl->is_valid_counted_loop()) { - NOT_PRODUCT(if(TraceLoopOpts) {tty->print_cr("CountedLoopReserveKit::create_reserve: %d not valid counted loop", cl->_idx);}) + if (TraceLoopOpts) { + tty->print_cr("CountedLoopReserveKit::create_reserve: %d not valid counted loop", cl->_idx); + } return false; // skip malformed counted loop } if (!cl->is_main_loop()) { - NOT_PRODUCT(if(TraceLoopOpts) {tty->print_cr("CountedLoopReserveKit::create_reserve: %d not main loop", cl->_idx);}) + if (TraceLoopOpts) { + tty->print_cr("CountedLoopReserveKit::create_reserve: %d not main loop", cl->_idx); + } return false; // skip normal, pre, and post loops } diff --git a/hotspot/src/share/vm/opto/loopnode.cpp b/hotspot/src/share/vm/opto/loopnode.cpp index eb09d62d23a..953364397ab 100644 --- a/hotspot/src/share/vm/opto/loopnode.cpp +++ b/hotspot/src/share/vm/opto/loopnode.cpp @@ -329,6 +329,9 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { Node* phi_incr = NULL; // Trip-counter increment must be commutative & associative. + if (incr->Opcode() == Op_CastII) { + incr = incr->in(1); + } if (incr->is_Phi()) { if (incr->as_Phi()->region() != x || incr->req() != 3) return false; // Not simple trip counter expression @@ -356,6 +359,9 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { xphi = stride; stride = tmp; } + if (xphi->Opcode() == Op_CastII) { + xphi = xphi->in(1); + } // Stride must be constant int stride_con = stride->get_int(); if (stride_con == 0) @@ -2397,11 +2403,9 @@ void PhaseIdealLoop::build_and_optimize(bool do_split_ifs, bool skip_loop_opts) // After that switch predicates off and do more loop optimizations. if (!C->major_progress() && (C->predicate_count() > 0)) { C->cleanup_loop_predicates(_igvn); -#ifndef PRODUCT if (TraceLoopOpts) { tty->print_cr("PredicatesOff"); } -#endif C->set_major_progress(); } diff --git a/hotspot/src/share/vm/opto/loopnode.hpp b/hotspot/src/share/vm/opto/loopnode.hpp index 98d8c2533b0..fd736bbf426 100644 --- a/hotspot/src/share/vm/opto/loopnode.hpp +++ b/hotspot/src/share/vm/opto/loopnode.hpp @@ -916,7 +916,8 @@ public: // Create a new if above the uncommon_trap_if_pattern for the predicate to be promoted ProjNode* create_new_if_for_predicate(ProjNode* cont_proj, Node* new_entry, - Deoptimization::DeoptReason reason); + Deoptimization::DeoptReason reason, + int opcode); void register_control(Node* n, IdealLoopTree *loop, Node* pred); // Clone loop predicates to cloned loops (peeled, unswitched) @@ -966,7 +967,8 @@ public: // Create a slow version of the loop by cloning the loop // and inserting an if to select fast-slow versions. ProjNode* create_slow_version_of_loop(IdealLoopTree *loop, - Node_List &old_new); + Node_List &old_new, + int opcode); // Clone a loop and return the clone head (clone_loop_head). // Added nodes include int(1), int(0) - disconnected, If, IfTrue, IfFalse, diff --git a/hotspot/src/share/vm/opto/loopopts.cpp b/hotspot/src/share/vm/opto/loopopts.cpp index 3256a06d8be..9be560add6a 100644 --- a/hotspot/src/share/vm/opto/loopopts.cpp +++ b/hotspot/src/share/vm/opto/loopopts.cpp @@ -199,14 +199,11 @@ Node *PhaseIdealLoop::split_thru_phi( Node *n, Node *region, int policy ) { // IGVN worklist for later cleanup. Move control-dependent data Nodes on the // live path up to the dominating control. void PhaseIdealLoop::dominated_by( Node *prevdom, Node *iff, bool flip, bool exclude_loop_predicate ) { -#ifndef PRODUCT - if (VerifyLoopOptimizations && PrintOpto) tty->print_cr("dominating test"); -#endif - + if (VerifyLoopOptimizations && PrintOpto) { tty->print_cr("dominating test"); } // prevdom is the dominating projection of the dominating test. assert( iff->is_If(), "" ); - assert( iff->Opcode() == Op_If || iff->Opcode() == Op_CountedLoopEnd, "Check this code when new subtype is added"); + assert(iff->Opcode() == Op_If || iff->Opcode() == Op_CountedLoopEnd || iff->Opcode() == Op_RangeCheck, "Check this code when new subtype is added"); int pop = prevdom->Opcode(); assert( pop == Op_IfFalse || pop == Op_IfTrue, "" ); if (flip) { @@ -617,9 +614,7 @@ Node *PhaseIdealLoop::conditional_move( Node *region ) { } } if (phi == NULL) break; -#ifndef PRODUCT - if (PrintOpto && VerifyLoopOptimizations) tty->print_cr("CMOV"); -#endif + if (PrintOpto && VerifyLoopOptimizations) { tty->print_cr("CMOV"); } // Move speculative ops for (uint j = 1; j < region->req(); j++) { Node *proj = region->in(j); @@ -963,10 +958,9 @@ static bool merge_point_too_heavy(Compile* C, Node* region) { } int nodes_left = C->max_node_limit() - C->live_nodes(); if (weight * 8 > nodes_left) { -#ifndef PRODUCT - if (PrintOpto) + if (PrintOpto) { tty->print_cr("*** Split-if bails out: %d nodes, region weight %d", C->unique(), weight); -#endif + } return true; } else { return false; @@ -1123,7 +1117,8 @@ void PhaseIdealLoop::split_if_with_blocks_post( Node *n ) { int n_op = n->Opcode(); // Check for an IF being dominated by another IF same test - if (n_op == Op_If) { + if (n_op == Op_If || + n_op == Op_RangeCheck) { Node *bol = n->in(1); uint max = bol->outcnt(); // Check for same test used more than once? @@ -1489,14 +1484,12 @@ void PhaseIdealLoop::sink_use( Node *use, Node *post_loop ) { void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd, Node* side_by_side_idom) { -#ifndef PRODUCT if (C->do_vector_loop() && PrintOpto) { const char* mname = C->method()->name()->as_quoted_ascii(); if (mname != NULL) { tty->print("PhaseIdealLoop::clone_loop: for vectorize method %s\n", mname); } } -#endif CloneMap& cm = C->clone_map(); Dict* dict = cm.dict(); @@ -1945,7 +1938,10 @@ ProjNode* PhaseIdealLoop::insert_if_before_proj(Node* left, bool Signed, BoolTes BoolNode* bol = new BoolNode(cmp, relop); register_node(bol, loop, proj2, ddepth); - IfNode* new_if = new IfNode(proj2, bol, iff->_prob, iff->_fcnt); + int opcode = iff->Opcode(); + assert(opcode == Op_If || opcode == Op_RangeCheck, "unexpected opcode"); + IfNode* new_if = (opcode == Op_If) ? new IfNode(proj2, bol, iff->_prob, iff->_fcnt): + new RangeCheckNode(proj2, bol, iff->_prob, iff->_fcnt); register_node(new_if, loop, proj2, ddepth); proj->set_req(0, new_if); // reattach diff --git a/hotspot/src/share/vm/opto/machnode.cpp b/hotspot/src/share/vm/opto/machnode.cpp index 514a9b5912e..c780d3f5340 100644 --- a/hotspot/src/share/vm/opto/machnode.cpp +++ b/hotspot/src/share/vm/opto/machnode.cpp @@ -433,36 +433,49 @@ bool MachNode::rematerialize() const { if (is_MachTemp()) return true; uint r = rule(); // Match rule - if( r < Matcher::_begin_rematerialize || - r >= Matcher::_end_rematerialize ) + if (r < Matcher::_begin_rematerialize || + r >= Matcher::_end_rematerialize) { return false; - - // For 2-address instructions, the input live range is also the output - // live range. Remateralizing does not make progress on the that live range. - if( two_adr() ) return false; - - // Check for rematerializing float constants, or not - if( !Matcher::rematerialize_float_constants ) { - int op = ideal_Opcode(); - if( op == Op_ConF || op == Op_ConD ) - return false; } - // Defining flags - can't spill these! Must remateralize. - if( ideal_reg() == Op_RegFlags ) + // For 2-address instructions, the input live range is also the output + // live range. Remateralizing does not make progress on the that live range. + if (two_adr()) return false; + + // Check for rematerializing float constants, or not + if (!Matcher::rematerialize_float_constants) { + int op = ideal_Opcode(); + if (op == Op_ConF || op == Op_ConD) { + return false; + } + } + + // Defining flags - can't spill these! Must remateralize. + if (ideal_reg() == Op_RegFlags) { return true; + } // Stretching lots of inputs - don't do it. - if( req() > 2 ) + if (req() > 2) { return false; + } + + if (req() == 2 && in(1) && in(1)->ideal_reg() == Op_RegFlags) { + // In(1) will be rematerialized, too. + // Stretching lots of inputs - don't do it. + if (in(1)->req() > 2) { + return false; + } + } // Don't remateralize somebody with bound inputs - it stretches a // fixed register lifetime. uint idx = oper_input_base(); if (req() > idx) { const RegMask &rm = in_RegMask(idx); - if (rm.is_bound(ideal_reg())) + if (rm.is_bound(ideal_reg())) { return false; + } } return true; diff --git a/hotspot/src/share/vm/opto/machnode.hpp b/hotspot/src/share/vm/opto/machnode.hpp index e30e23406c6..ca2ad70c264 100644 --- a/hotspot/src/share/vm/opto/machnode.hpp +++ b/hotspot/src/share/vm/opto/machnode.hpp @@ -578,8 +578,8 @@ public: #ifndef PRODUCT - virtual const char *Name() const { - switch (_spill_type) { + static const char *spill_type(SpillType st) { + switch (st) { case TwoAddress: return "TwoAddressSpillCopy"; case PhiInput: @@ -612,6 +612,10 @@ public: } } + virtual const char *Name() const { + return spill_type(_spill_type); + } + virtual void format( PhaseRegAlloc *, outputStream *st ) const; #endif }; diff --git a/hotspot/src/share/vm/opto/macro.cpp b/hotspot/src/share/vm/opto/macro.cpp index bd5ceaa9b7a..4551560a6e4 100644 --- a/hotspot/src/share/vm/opto/macro.cpp +++ b/hotspot/src/share/vm/opto/macro.cpp @@ -290,7 +290,7 @@ void PhaseMacroExpand::eliminate_card_mark(Node* p2x) { cmpx->in(1)->is_Load()) { Node* adr = cmpx->in(1)->as_Load()->in(MemNode::Address); const int marking_offset = in_bytes(JavaThread::satb_mark_queue_offset() + - PtrQueue::byte_offset_of_active()); + SATBMarkQueue::byte_offset_of_active()); if (adr->is_AddP() && adr->in(AddPNode::Base) == top() && adr->in(AddPNode::Address)->Opcode() == Op_ThreadLocal && adr->in(AddPNode::Offset) == MakeConX(marking_offset)) { diff --git a/hotspot/src/share/vm/opto/matcher.cpp b/hotspot/src/share/vm/opto/matcher.cpp index 117dd765ac8..acf3f32ece0 100644 --- a/hotspot/src/share/vm/opto/matcher.cpp +++ b/hotspot/src/share/vm/opto/matcher.cpp @@ -1569,13 +1569,11 @@ Node *Matcher::Label_Root( const Node *n, State *svec, Node *control, const Node // Can NOT include the match of a subtree when its memory state // is used by any of the other subtrees (input_mem == NodeSentinel) ) { -#ifndef PRODUCT // Print when we exclude matching due to different memory states at input-loads - if( PrintOpto && (Verbose && WizardMode) && (input_mem == NodeSentinel) - && !((mem!=(Node*)1) && m->is_Load() && m->in(MemNode::Memory) != mem) ) { + if (PrintOpto && (Verbose && WizardMode) && (input_mem == NodeSentinel) + && !((mem!=(Node*)1) && m->is_Load() && m->in(MemNode::Memory) != mem)) { tty->print_cr("invalid input_mem"); } -#endif // Switch to a register-only opcode; this value must be in a register // and cannot be subsumed as part of a larger instruction. s->DFA( m->ideal_reg(), m ); diff --git a/hotspot/src/share/vm/opto/matcher.hpp b/hotspot/src/share/vm/opto/matcher.hpp index fc0c66cf345..64cc48c65ec 100644 --- a/hotspot/src/share/vm/opto/matcher.hpp +++ b/hotspot/src/share/vm/opto/matcher.hpp @@ -269,6 +269,10 @@ public: // should generate this one. static const bool match_rule_supported(int opcode); + // identify extra cases that we might want to provide match rules for + // e.g. Op_ vector nodes and other intrinsics while guarding with vlen + static const bool match_rule_supported_vector(int opcode, int vlen); + // Some uarchs have different sized float register resources static const int float_pressure(int default_pressure_threshold); diff --git a/hotspot/src/share/vm/opto/memnode.cpp b/hotspot/src/share/vm/opto/memnode.cpp index d7da53ef871..2995b7dfade 100644 --- a/hotspot/src/share/vm/opto/memnode.cpp +++ b/hotspot/src/share/vm/opto/memnode.cpp @@ -72,8 +72,15 @@ void MemNode::dump_spec(outputStream *st) const { dump_adr_type(this, _adr_type, st); Compile* C = Compile::current(); - if( C->alias_type(_adr_type)->is_volatile() ) + if (C->alias_type(_adr_type)->is_volatile()) { st->print(" Volatile!"); + } + if (_unaligned_access) { + st->print(" unaligned"); + } + if (_mismatched_access) { + st->print(" mismatched"); + } } void MemNode::dump_adr_type(const Node* mem, const TypePtr* adr_type, outputStream *st) { @@ -754,7 +761,8 @@ bool LoadNode::is_immutable_value(Node* adr) { //----------------------------LoadNode::make----------------------------------- // Polymorphic factory method: -Node *LoadNode::make(PhaseGVN& gvn, Node *ctl, Node *mem, Node *adr, const TypePtr* adr_type, const Type *rt, BasicType bt, MemOrd mo, ControlDependency control_dependency) { +Node *LoadNode::make(PhaseGVN& gvn, Node *ctl, Node *mem, Node *adr, const TypePtr* adr_type, const Type *rt, BasicType bt, MemOrd mo, + ControlDependency control_dependency, bool unaligned, bool mismatched) { Compile* C = gvn.C; // sanity check the alias category against the created node type @@ -769,40 +777,68 @@ Node *LoadNode::make(PhaseGVN& gvn, Node *ctl, Node *mem, Node *adr, const TypeP // oop will be recorded in oop map if load crosses safepoint rt->isa_oopptr() || is_immutable_value(adr), "raw memory operations should have control edge"); + LoadNode* load = NULL; switch (bt) { - case T_BOOLEAN: return new LoadUBNode(ctl, mem, adr, adr_type, rt->is_int(), mo, control_dependency); - case T_BYTE: return new LoadBNode (ctl, mem, adr, adr_type, rt->is_int(), mo, control_dependency); - case T_INT: return new LoadINode (ctl, mem, adr, adr_type, rt->is_int(), mo, control_dependency); - case T_CHAR: return new LoadUSNode(ctl, mem, adr, adr_type, rt->is_int(), mo, control_dependency); - case T_SHORT: return new LoadSNode (ctl, mem, adr, adr_type, rt->is_int(), mo, control_dependency); - case T_LONG: return new LoadLNode (ctl, mem, adr, adr_type, rt->is_long(), mo, control_dependency); - case T_FLOAT: return new LoadFNode (ctl, mem, adr, adr_type, rt, mo, control_dependency); - case T_DOUBLE: return new LoadDNode (ctl, mem, adr, adr_type, rt, mo, control_dependency); - case T_ADDRESS: return new LoadPNode (ctl, mem, adr, adr_type, rt->is_ptr(), mo, control_dependency); + case T_BOOLEAN: load = new LoadUBNode(ctl, mem, adr, adr_type, rt->is_int(), mo, control_dependency); break; + case T_BYTE: load = new LoadBNode (ctl, mem, adr, adr_type, rt->is_int(), mo, control_dependency); break; + case T_INT: load = new LoadINode (ctl, mem, adr, adr_type, rt->is_int(), mo, control_dependency); break; + case T_CHAR: load = new LoadUSNode(ctl, mem, adr, adr_type, rt->is_int(), mo, control_dependency); break; + case T_SHORT: load = new LoadSNode (ctl, mem, adr, adr_type, rt->is_int(), mo, control_dependency); break; + case T_LONG: load = new LoadLNode (ctl, mem, adr, adr_type, rt->is_long(), mo, control_dependency); break; + case T_FLOAT: load = new LoadFNode (ctl, mem, adr, adr_type, rt, mo, control_dependency); break; + case T_DOUBLE: load = new LoadDNode (ctl, mem, adr, adr_type, rt, mo, control_dependency); break; + case T_ADDRESS: load = new LoadPNode (ctl, mem, adr, adr_type, rt->is_ptr(), mo, control_dependency); break; case T_OBJECT: #ifdef _LP64 if (adr->bottom_type()->is_ptr_to_narrowoop()) { - Node* load = gvn.transform(new LoadNNode(ctl, mem, adr, adr_type, rt->make_narrowoop(), mo, control_dependency)); - return new DecodeNNode(load, load->bottom_type()->make_ptr()); + load = new LoadNNode(ctl, mem, adr, adr_type, rt->make_narrowoop(), mo, control_dependency); } else #endif { assert(!adr->bottom_type()->is_ptr_to_narrowoop() && !adr->bottom_type()->is_ptr_to_narrowklass(), "should have got back a narrow oop"); - return new LoadPNode(ctl, mem, adr, adr_type, rt->is_oopptr(), mo, control_dependency); + load = new LoadPNode(ctl, mem, adr, adr_type, rt->is_oopptr(), mo, control_dependency); } + break; } - ShouldNotReachHere(); - return (LoadNode*)NULL; + assert(load != NULL, "LoadNode should have been created"); + if (unaligned) { + load->set_unaligned_access(); + } + if (mismatched) { + load->set_mismatched_access(); + } + if (load->Opcode() == Op_LoadN) { + Node* ld = gvn.transform(load); + return new DecodeNNode(ld, ld->bottom_type()->make_ptr()); + } + + return load; } -LoadLNode* LoadLNode::make_atomic(Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt, MemOrd mo, ControlDependency control_dependency) { +LoadLNode* LoadLNode::make_atomic(Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt, MemOrd mo, + ControlDependency control_dependency, bool unaligned, bool mismatched) { bool require_atomic = true; - return new LoadLNode(ctl, mem, adr, adr_type, rt->is_long(), mo, control_dependency, require_atomic); + LoadLNode* load = new LoadLNode(ctl, mem, adr, adr_type, rt->is_long(), mo, control_dependency, require_atomic); + if (unaligned) { + load->set_unaligned_access(); + } + if (mismatched) { + load->set_mismatched_access(); + } + return load; } -LoadDNode* LoadDNode::make_atomic(Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt, MemOrd mo, ControlDependency control_dependency) { +LoadDNode* LoadDNode::make_atomic(Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt, MemOrd mo, + ControlDependency control_dependency, bool unaligned, bool mismatched) { bool require_atomic = true; - return new LoadDNode(ctl, mem, adr, adr_type, rt, mo, control_dependency, require_atomic); + LoadDNode* load = new LoadDNode(ctl, mem, adr, adr_type, rt, mo, control_dependency, require_atomic); + if (unaligned) { + load->set_unaligned_access(); + } + if (mismatched) { + load->set_mismatched_access(); + } + return load; } @@ -2393,7 +2429,8 @@ Node *StoreNode::Ideal(PhaseGVN *phase, bool can_reshape) { st->Opcode() == Op_StoreVector || Opcode() == Op_StoreVector || phase->C->get_alias_index(adr_type()) == Compile::AliasIdxRaw || - (Opcode() == Op_StoreL && st->Opcode() == Op_StoreI), // expanded ClearArrayNode + (Opcode() == Op_StoreL && st->Opcode() == Op_StoreI) || // expanded ClearArrayNode + (is_mismatched_access() || st->as_Store()->is_mismatched_access()), "no mismatched stores, except on raw memory: %s %s", NodeClassNames[Opcode()], NodeClassNames[st->Opcode()]); if (st->in(MemNode::Address)->eqv_uncast(address) && @@ -3213,6 +3250,9 @@ bool InitializeNode::detect_init_independence(Node* n, int& count) { // within the initialized memory. intptr_t InitializeNode::can_capture_store(StoreNode* st, PhaseTransform* phase, bool can_reshape) { const int FAIL = 0; + if (st->is_unaligned_access()) { + return FAIL; + } if (st->req() != MemNode::ValueIn + 1) return FAIL; // an inscrutable StoreNode (card mark?) Node* ctl = st->in(MemNode::Control); diff --git a/hotspot/src/share/vm/opto/memnode.hpp b/hotspot/src/share/vm/opto/memnode.hpp index b9b5838eca4..cab41bcdf0d 100644 --- a/hotspot/src/share/vm/opto/memnode.hpp +++ b/hotspot/src/share/vm/opto/memnode.hpp @@ -39,11 +39,14 @@ class PhaseTransform; //------------------------------MemNode---------------------------------------- // Load or Store, possibly throwing a NULL pointer exception class MemNode : public Node { +private: + bool _unaligned_access; // Unaligned access from unsafe + bool _mismatched_access; // Mismatched access from unsafe: byte read in integer array for instance protected: #ifdef ASSERT const TypePtr* _adr_type; // What kind of memory is being addressed? #endif - virtual uint size_of() const; // Size is bigger (ASSERT only) + virtual uint size_of() const; public: enum { Control, // When is it safe to do this load? Memory, // Chunk of memory is being loaded from @@ -57,17 +60,17 @@ public: } MemOrd; protected: MemNode( Node *c0, Node *c1, Node *c2, const TypePtr* at ) - : Node(c0,c1,c2 ) { + : Node(c0,c1,c2 ), _unaligned_access(false), _mismatched_access(false) { init_class_id(Class_Mem); debug_only(_adr_type=at; adr_type();) } MemNode( Node *c0, Node *c1, Node *c2, const TypePtr* at, Node *c3 ) - : Node(c0,c1,c2,c3) { + : Node(c0,c1,c2,c3), _unaligned_access(false), _mismatched_access(false) { init_class_id(Class_Mem); debug_only(_adr_type=at; adr_type();) } MemNode( Node *c0, Node *c1, Node *c2, const TypePtr* at, Node *c3, Node *c4) - : Node(c0,c1,c2,c3,c4) { + : Node(c0,c1,c2,c3,c4), _unaligned_access(false), _mismatched_access(false) { init_class_id(Class_Mem); debug_only(_adr_type=at; adr_type();) } @@ -127,6 +130,11 @@ public: // the given memory state? (The state may or may not be in(Memory).) Node* can_see_stored_value(Node* st, PhaseTransform* phase) const; + void set_unaligned_access() { _unaligned_access = true; } + bool is_unaligned_access() const { return _unaligned_access; } + void set_mismatched_access() { _mismatched_access = true; } + bool is_mismatched_access() const { return _mismatched_access; } + #ifndef PRODUCT static void dump_adr_type(const Node* mem, const TypePtr* adr_type, outputStream *st); virtual void dump_spec(outputStream *st) const; @@ -190,9 +198,10 @@ public: } // Polymorphic factory method: - static Node* make(PhaseGVN& gvn, Node *c, Node *mem, Node *adr, - const TypePtr* at, const Type *rt, BasicType bt, - MemOrd mo, ControlDependency control_dependency = DependsOnlyOnTest); + static Node* make(PhaseGVN& gvn, Node *c, Node *mem, Node *adr, + const TypePtr* at, const Type *rt, BasicType bt, + MemOrd mo, ControlDependency control_dependency = DependsOnlyOnTest, + bool unaligned = false, bool mismatched = false); virtual uint hash() const; // Check the type @@ -367,7 +376,8 @@ public: virtual BasicType memory_type() const { return T_LONG; } bool require_atomic_access() const { return _require_atomic_access; } static LoadLNode* make_atomic(Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, - const Type* rt, MemOrd mo, ControlDependency control_dependency = DependsOnlyOnTest); + const Type* rt, MemOrd mo, ControlDependency control_dependency = DependsOnlyOnTest, + bool unaligned = false, bool mismatched = false); #ifndef PRODUCT virtual void dump_spec(outputStream *st) const { LoadNode::dump_spec(st); @@ -418,7 +428,8 @@ public: virtual BasicType memory_type() const { return T_DOUBLE; } bool require_atomic_access() const { return _require_atomic_access; } static LoadDNode* make_atomic(Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, - const Type* rt, MemOrd mo, ControlDependency control_dependency = DependsOnlyOnTest); + const Type* rt, MemOrd mo, ControlDependency control_dependency = DependsOnlyOnTest, + bool unaligned = false, bool mismatched = false); #ifndef PRODUCT virtual void dump_spec(outputStream *st) const { LoadNode::dump_spec(st); diff --git a/hotspot/src/share/vm/opto/movenode.cpp b/hotspot/src/share/vm/opto/movenode.cpp index 8fe9b0233be..ee797cf5d28 100644 --- a/hotspot/src/share/vm/opto/movenode.cpp +++ b/hotspot/src/share/vm/opto/movenode.cpp @@ -230,9 +230,7 @@ Node *CMoveINode::Ideal(PhaseGVN *phase, bool can_reshape) { // Convert to a bool (flipped) // Build int->bool conversion -#ifndef PRODUCT - if( PrintOpto ) tty->print_cr("CMOV to I2B"); -#endif + if (PrintOpto) { tty->print_cr("CMOV to I2B"); } Node *n = new Conv2BNode( cmp->in(1) ); if( flip ) n = new XorINode( phase->transform(n), phase->intcon(1) ); diff --git a/hotspot/src/share/vm/opto/multnode.cpp b/hotspot/src/share/vm/opto/multnode.cpp index 3648fef790e..83ef36b7621 100644 --- a/hotspot/src/share/vm/opto/multnode.cpp +++ b/hotspot/src/share/vm/opto/multnode.cpp @@ -44,14 +44,14 @@ Node *MultiNode::match( const ProjNode *proj, const Matcher *m ) { return proj-> //------------------------------proj_out--------------------------------------- // Get a named projection ProjNode* MultiNode::proj_out(uint which_proj) const { - assert(Opcode() != Op_If || which_proj == (uint)true || which_proj == (uint)false, "must be 1 or 0"); - assert(Opcode() != Op_If || outcnt() == 2, "bad if #1"); + assert((Opcode() != Op_If && Opcode() != Op_RangeCheck) || which_proj == (uint)true || which_proj == (uint)false, "must be 1 or 0"); + assert((Opcode() != Op_If && Opcode() != Op_RangeCheck) || outcnt() == 2, "bad if #1"); for( DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++ ) { Node *p = fast_out(i); if (p->is_Proj()) { ProjNode *proj = p->as_Proj(); if (proj->_con == which_proj) { - assert(Opcode() != Op_If || proj->Opcode() == (which_proj?Op_IfTrue:Op_IfFalse), "bad if #2"); + assert((Opcode() != Op_If && Opcode() != Op_RangeCheck) || proj->Opcode() == (which_proj ? Op_IfTrue : Op_IfFalse), "bad if #2"); return proj; } } else { diff --git a/hotspot/src/share/vm/opto/node.hpp b/hotspot/src/share/vm/opto/node.hpp index a779923db00..0117027d5d5 100644 --- a/hotspot/src/share/vm/opto/node.hpp +++ b/hotspot/src/share/vm/opto/node.hpp @@ -125,6 +125,7 @@ class PhaseValues; class PhiNode; class Pipeline; class ProjNode; +class RangeCheckNode; class RegMask; class RegionNode; class RootNode; @@ -584,6 +585,7 @@ public: DEFINE_CLASS_ID(Jump, PCTable, 1) DEFINE_CLASS_ID(If, MultiBranch, 1) DEFINE_CLASS_ID(CountedLoopEnd, If, 0) + DEFINE_CLASS_ID(RangeCheck, If, 1) DEFINE_CLASS_ID(NeverBranch, MultiBranch, 2) DEFINE_CLASS_ID(Start, Multi, 2) DEFINE_CLASS_ID(MemBar, Multi, 3) @@ -758,6 +760,7 @@ public: DEFINE_CLASS_QUERY(FastLock) DEFINE_CLASS_QUERY(FastUnlock) DEFINE_CLASS_QUERY(If) + DEFINE_CLASS_QUERY(RangeCheck) DEFINE_CLASS_QUERY(IfFalse) DEFINE_CLASS_QUERY(IfTrue) DEFINE_CLASS_QUERY(Initialize) diff --git a/hotspot/src/share/vm/opto/output.cpp b/hotspot/src/share/vm/opto/output.cpp index 7b0c074f686..7d90d638ed4 100644 --- a/hotspot/src/share/vm/opto/output.cpp +++ b/hotspot/src/share/vm/opto/output.cpp @@ -91,13 +91,10 @@ void Compile::Output() { } // Break before main entry point - if( (_method && C->directive()->BreakAtExecuteOption) -#ifndef PRODUCT - ||(OptoBreakpoint && is_method_compilation()) - ||(OptoBreakpointOSR && is_osr_compilation()) - ||(OptoBreakpointC2R && !_method) -#endif - ) { + if ((_method && C->directive()->BreakAtExecuteOption) || + (OptoBreakpoint && is_method_compilation()) || + (OptoBreakpointOSR && is_osr_compilation()) || + (OptoBreakpointC2R && !_method) ) { // checking for _method means that OptoBreakpoint does not apply to // runtime stubs or frame converters _cfg->insert( entry, 1, new MachBreakpointNode() ); diff --git a/hotspot/src/share/vm/opto/parse1.cpp b/hotspot/src/share/vm/opto/parse1.cpp index 99a3734ba46..f33cc2a2acc 100644 --- a/hotspot/src/share/vm/opto/parse1.cpp +++ b/hotspot/src/share/vm/opto/parse1.cpp @@ -958,25 +958,22 @@ void Parse::do_exits() { PPC64_ONLY(wrote_volatile() ||) (AlwaysSafeConstructors && wrote_fields()))) { _exits.insert_mem_bar(Op_MemBarRelease, alloc_with_final()); -#ifndef PRODUCT if (PrintOpto && (Verbose || WizardMode)) { method()->print_name(); tty->print_cr(" writes finals and needs a memory barrier"); } -#endif } - // Any method can write a @Stable field; insert memory barriers after - // those also. If there is a predecessor allocation node, bind the - // barrier there. + // Any method can write a @Stable field; insert memory barriers + // after those also. Can't bind predecessor allocation node (if any) + // with barrier because allocation doesn't always dominate + // MemBarRelease. if (wrote_stable()) { - _exits.insert_mem_bar(Op_MemBarRelease, alloc_with_final()); -#ifndef PRODUCT + _exits.insert_mem_bar(Op_MemBarRelease); if (PrintOpto && (Verbose || WizardMode)) { method()->print_name(); tty->print_cr(" writes @Stable and needs a memory barrier"); } -#endif } for (MergeMemStream mms(_exits.merged_memory()); mms.next_non_empty(); ) { @@ -991,13 +988,18 @@ void Parse::do_exits() { // In case of concurrent class loading, the type we set for the // ret_phi in build_exits() may have been too optimistic and the // ret_phi may be top now. -#ifdef ASSERT + // Otherwise, we've encountered an error and have to mark the method as + // not compilable. Just using an assertion instead would be dangerous + // as this could lead to an infinite compile loop in non-debug builds. { MutexLockerEx ml(Compile_lock, Mutex::_no_safepoint_check_flag); - assert(ret_type->isa_ptr() && C->env()->system_dictionary_modification_counter_changed(), "return value must be well defined"); + if (C->env()->system_dictionary_modification_counter_changed()) { + C->record_failure(C2Compiler::retry_class_loading_during_parsing()); + } else { + C->record_method_not_compilable("Can't determine return type."); + } } -#endif - C->record_failure(C2Compiler::retry_class_loading_during_parsing()); + return; } _exits.push_node(ret_type->basic_type(), ret_phi); } @@ -2147,15 +2149,24 @@ void Parse::return_current(Node* value) { // here. Node* phi = _exits.argument(0); const TypeInstPtr *tr = phi->bottom_type()->isa_instptr(); - if( tr && tr->klass()->is_loaded() && - tr->klass()->is_interface() ) { + if (tr && tr->klass()->is_loaded() && + tr->klass()->is_interface()) { const TypeInstPtr *tp = value->bottom_type()->isa_instptr(); if (tp && tp->klass()->is_loaded() && !tp->klass()->is_interface()) { // sharpen the type eagerly; this eases certain assert checking if (tp->higher_equal(TypeInstPtr::NOTNULL)) tr = tr->join_speculative(TypeInstPtr::NOTNULL)->is_instptr(); - value = _gvn.transform(new CheckCastPPNode(0,value,tr)); + value = _gvn.transform(new CheckCastPPNode(0, value, tr)); + } + } else { + // Also handle returns of oop-arrays to an arrays-of-interface return + const TypeInstPtr* phi_tip; + const TypeInstPtr* val_tip; + Type::get_arrays_base_elements(phi->bottom_type(), value->bottom_type(), &phi_tip, &val_tip); + if (phi_tip != NULL && phi_tip->is_loaded() && phi_tip->klass()->is_interface() && + val_tip != NULL && val_tip->is_loaded() && !val_tip->klass()->is_interface()) { + value = _gvn.transform(new CheckCastPPNode(0, value, phi->bottom_type())); } } phi->add_req(value); diff --git a/hotspot/src/share/vm/opto/parse2.cpp b/hotspot/src/share/vm/opto/parse2.cpp index cd68d761568..f6adb5d6241 100644 --- a/hotspot/src/share/vm/opto/parse2.cpp +++ b/hotspot/src/share/vm/opto/parse2.cpp @@ -136,8 +136,16 @@ Node* Parse::array_addressing(BasicType type, int vals, const Type* *result2) { BoolTest::mask btest = BoolTest::lt; tst = _gvn.transform( new BoolNode(chk, btest) ); } + RangeCheckNode* rc = new RangeCheckNode(control(), tst, PROB_MAX, COUNT_UNKNOWN); + _gvn.set_type(rc, rc->Value(&_gvn)); + if (!tst->is_Con()) { + record_for_igvn(rc); + } + set_control(_gvn.transform(new IfTrueNode(rc))); // Branch to failure if out of bounds - { BuildCutout unless(this, tst, PROB_MAX); + { + PreserveJVMState pjvms(this); + set_control(_gvn.transform(new IfFalseNode(rc))); if (C->allow_range_check_smearing()) { // Do not use builtin_throw, since range checks are sometimes // made more stringent by an optimistic transformation. @@ -940,13 +948,11 @@ bool Parse::seems_stable_comparison() const { //-------------------------------repush_if_args-------------------------------- // Push arguments of an "if" bytecode back onto the stack by adjusting _sp. inline int Parse::repush_if_args() { -#ifndef PRODUCT if (PrintOpto && WizardMode) { tty->print("defending against excessive implicit null exceptions on %s @%d in ", Bytecodes::name(iter().cur_bc()), iter().cur_bci()); method()->print_name(); tty->cr(); } -#endif int bc_depth = - Bytecodes::depth(iter().cur_bc()); assert(bc_depth == 1 || bc_depth == 2, "only two kinds of branches"); DEBUG_ONLY(sync_jvms()); // argument(n) requires a synced jvms @@ -967,10 +973,9 @@ void Parse::do_ifnull(BoolTest::mask btest, Node *c) { float prob = branch_prediction(cnt, btest, target_bci, c); if (prob == PROB_UNKNOWN) { // (An earlier version of do_ifnull omitted this trap for OSR methods.) -#ifndef PRODUCT - if (PrintOpto && Verbose) - tty->print_cr("Never-taken edge stops compilation at bci %d",bci()); -#endif + if (PrintOpto && Verbose) { + tty->print_cr("Never-taken edge stops compilation at bci %d", bci()); + } repush_if_args(); // to gather stats on loop // We need to mark this branch as taken so that if we recompile we will // see that it is possible. In the tiered system the interpreter doesn't @@ -1049,10 +1054,9 @@ void Parse::do_if(BoolTest::mask btest, Node* c) { float untaken_prob = 1.0 - prob; if (prob == PROB_UNKNOWN) { -#ifndef PRODUCT - if (PrintOpto && Verbose) - tty->print_cr("Never-taken edge stops compilation at bci %d",bci()); -#endif + if (PrintOpto && Verbose) { + tty->print_cr("Never-taken edge stops compilation at bci %d", bci()); + } repush_if_args(); // to gather stats on loop // We need to mark this branch as taken so that if we recompile we will // see that it is possible. In the tiered system the interpreter doesn't diff --git a/hotspot/src/share/vm/opto/parse3.cpp b/hotspot/src/share/vm/opto/parse3.cpp index f37e31e9458..d9ad4ced928 100644 --- a/hotspot/src/share/vm/opto/parse3.cpp +++ b/hotspot/src/share/vm/opto/parse3.cpp @@ -213,11 +213,9 @@ void Parse::do_get_xxx(Node* obj, ciField* field, bool is_field) { // not need to mention the class index, since the class will // already have been loaded if we ever see a non-null value.) // uncommon_trap(iter().get_field_signature_index()); -#ifndef PRODUCT if (PrintOpto && (Verbose || WizardMode)) { method()->print_name(); tty->print_cr(" asserting nullness of field at bci: %d", bci()); } -#endif if (C->log() != NULL) { C->log()->elem("assert_null reason='field' klass='%d'", C->log()->identify(field->type())); @@ -313,9 +311,8 @@ void Parse::do_put_xxx(Node* obj, ciField* field, bool is_field) { // Preserve allocation ptr to create precedent edge to it in membar // generated on exit from constructor. - if (C->eliminate_boxing() && - adr_type->isa_oopptr() && adr_type->is_oopptr()->is_ptr_to_boxed_value() && - AllocateNode::Ideal_allocation(obj, &_gvn) != NULL) { + // Can't bind stable with its allocation, only record allocation for final field. + if (field->is_final() && AllocateNode::Ideal_allocation(obj, &_gvn) != NULL) { set_alloc_with_final(obj); } } diff --git a/hotspot/src/share/vm/opto/phaseX.hpp b/hotspot/src/share/vm/opto/phaseX.hpp index a3e06c825a7..efd7b456853 100644 --- a/hotspot/src/share/vm/opto/phaseX.hpp +++ b/hotspot/src/share/vm/opto/phaseX.hpp @@ -521,7 +521,8 @@ public: Node* clone_loop_predicates(Node* old_entry, Node* new_entry, bool clone_limit_check); // Create a new if below new_entry for the predicate to be cloned ProjNode* create_new_if_for_predicate(ProjNode* cont_proj, Node* new_entry, - Deoptimization::DeoptReason reason); + Deoptimization::DeoptReason reason, + int opcode); void remove_speculative_types(); void check_no_speculative_types() { diff --git a/hotspot/src/share/vm/opto/reg_split.cpp b/hotspot/src/share/vm/opto/reg_split.cpp index 6eae4e574ad..5e14b9d5abc 100644 --- a/hotspot/src/share/vm/opto/reg_split.cpp +++ b/hotspot/src/share/vm/opto/reg_split.cpp @@ -55,13 +55,15 @@ static const char out_of_nodes[] = "out of nodes during split"; // Get a SpillCopy node with wide-enough masks. Use the 'wide-mask', the // wide ideal-register spill-mask if possible. If the 'wide-mask' does // not cover the input (or output), use the input (or output) mask instead. -Node *PhaseChaitin::get_spillcopy_wide(MachSpillCopyNode::SpillType spill_type, Node *def, Node *use, uint uidx ) { +Node *PhaseChaitin::get_spillcopy_wide(MachSpillCopyNode::SpillType spill_type, Node *def, Node *use, uint uidx) { // If ideal reg doesn't exist we've got a bad schedule happening // that is forcing us to spill something that isn't spillable. // Bail rather than abort int ireg = def->ideal_reg(); - if( ireg == 0 || ireg == Op_RegFlags ) { - assert(false, "attempted to spill a non-spillable item"); + if (ireg == 0 || ireg == Op_RegFlags) { + assert(false, "attempted to spill a non-spillable item: %d: %s <- %d: %s, ireg = %d, spill_type: %s", + def->_idx, def->Name(), use->_idx, use->Name(), ireg, + MachSpillCopyNode::spill_type(spill_type)); C->record_method_not_compilable("attempted to spill a non-spillable item"); return NULL; } @@ -308,14 +310,16 @@ Node* clone_node(Node* def, Block *b, Compile* C) { //------------------------------split_Rematerialize---------------------------- // Clone a local copy of the def. -Node *PhaseChaitin::split_Rematerialize( Node *def, Block *b, uint insidx, uint &maxlrg, GrowableArray splits, int slidx, uint *lrg2reach, Node **Reachblock, bool walkThru ) { +Node *PhaseChaitin::split_Rematerialize(Node *def, Block *b, uint insidx, uint &maxlrg, + GrowableArray splits, int slidx, uint *lrg2reach, + Node **Reachblock, bool walkThru) { // The input live ranges will be stretched to the site of the new // instruction. They might be stretched past a def and will thus // have the old and new values of the same live range alive at the // same time - a definite no-no. Split out private copies of // the inputs. - if( def->req() > 1 ) { - for( uint i = 1; i < def->req(); i++ ) { + if (def->req() > 1) { + for (uint i = 1; i < def->req(); i++) { Node *in = def->in(i); uint lidx = _lrg_map.live_range_id(in); // We do not need this for live ranges that are only defined once. @@ -327,12 +331,29 @@ Node *PhaseChaitin::split_Rematerialize( Node *def, Block *b, uint insidx, uint Block *b_def = _cfg.get_block_for_node(def); int idx_def = b_def->find_node(def); - Node *in_spill = get_spillcopy_wide(MachSpillCopyNode::InputToRematerialization, in, def, i ); - if( !in_spill ) return 0; // Bailed out - insert_proj(b_def,idx_def,in_spill,maxlrg++); - if( b_def == b ) - insidx++; - def->set_req(i,in_spill); + // Cannot spill Op_RegFlags. + Node *in_spill; + if (in->ideal_reg() != Op_RegFlags) { + in_spill = get_spillcopy_wide(MachSpillCopyNode::InputToRematerialization, in, def, i); + if (!in_spill) { return 0; } // Bailed out + insert_proj(b_def, idx_def, in_spill, maxlrg++); + if (b_def == b) { + insidx++; + } + def->set_req(i, in_spill); + } else { + // The 'in' defines a flag register. Flag registers can not be spilled. + // Register allocation handles live ranges with flag registers + // by rematerializing the def (in this case 'in'). Thus, this is not + // critical if the input can be rematerialized, too. + if (!in->rematerialize()) { + assert(false, "Can not rematerialize %d: %s. Prolongs RegFlags live" + " range and defining node %d: %s may not be rematerialized.", + def->_idx, def->Name(), in->_idx, in->Name()); + C->record_method_not_compilable("attempted to spill a non-spillable item with RegFlags input"); + return 0; // Bailed out + } + } } } @@ -506,10 +527,9 @@ uint PhaseChaitin::Split(uint maxlrg, ResourceArea* split_arena) { // Initialize the split counts to zero splits.append(0); #endif -#ifndef PRODUCT - if( PrintOpto && WizardMode && lrgs(bidx)._was_spilled1 ) + if (PrintOpto && WizardMode && lrgs(bidx)._was_spilled1) { tty->print_cr("Warning, 2nd spill of L%d",bidx); -#endif + } } } diff --git a/hotspot/src/share/vm/opto/split_if.cpp b/hotspot/src/share/vm/opto/split_if.cpp index 215832d68cf..1d345487966 100644 --- a/hotspot/src/share/vm/opto/split_if.cpp +++ b/hotspot/src/share/vm/opto/split_if.cpp @@ -390,13 +390,13 @@ void PhaseIdealLoop::handle_use( Node *use, Node *def, small_cache *cache, Node // Found an If getting its condition-code input from a Phi in the same block. // Split thru the Region. void PhaseIdealLoop::do_split_if( Node *iff ) { -#ifndef PRODUCT - if( PrintOpto && VerifyLoopOptimizations ) + if (PrintOpto && VerifyLoopOptimizations) { tty->print_cr("Split-if"); + } if (TraceLoopOpts) { tty->print_cr("SplitIf"); } -#endif + C->set_major_progress(); Node *region = iff->in(0); Node *region_dom = idom(region); diff --git a/hotspot/src/share/vm/opto/stringopts.cpp b/hotspot/src/share/vm/opto/stringopts.cpp index b11742f04b3..241384eccc6 100644 --- a/hotspot/src/share/vm/opto/stringopts.cpp +++ b/hotspot/src/share/vm/opto/stringopts.cpp @@ -1293,7 +1293,7 @@ void PhaseStringOpts::getChars(GraphKit& kit, Node* arg, Node* dst_array, BasicT Node* index = __ SubI(charPos, __ intcon((bt == T_BYTE) ? 1 : 2)); Node* ch = __ AddI(r, __ intcon('0')); Node* st = __ store_to_memory(kit.control(), kit.array_element_address(dst_array, index, T_BYTE), - ch, bt, byte_adr_idx, MemNode::unordered); + ch, bt, byte_adr_idx, MemNode::unordered, (bt != T_BYTE) /* mismatched */); iff = kit.create_and_map_if(head, __ Bool(__ CmpI(q, __ intcon(0)), BoolTest::ne), PROB_FAIR, COUNT_UNKNOWN); @@ -1331,7 +1331,7 @@ void PhaseStringOpts::getChars(GraphKit& kit, Node* arg, Node* dst_array, BasicT } else { Node* index = __ SubI(charPos, __ intcon((bt == T_BYTE) ? 1 : 2)); st = __ store_to_memory(kit.control(), kit.array_element_address(dst_array, index, T_BYTE), - sign, bt, byte_adr_idx, MemNode::unordered); + sign, bt, byte_adr_idx, MemNode::unordered, (bt != T_BYTE) /* mismatched */); final_merge->init_req(merge_index + 1, kit.control()); final_mem->init_req(merge_index + 1, st); @@ -1524,7 +1524,7 @@ void PhaseStringOpts::copy_constant_string(GraphKit& kit, IdealKit& ideal, ciTyp } else { val = readChar(src_array, i++); } - __ store(__ ctrl(), adr, __ ConI(val), T_CHAR, byte_adr_idx, MemNode::unordered); + __ store(__ ctrl(), adr, __ ConI(val), T_CHAR, byte_adr_idx, MemNode::unordered, true /* mismatched */); index = __ AddI(index, __ ConI(2)); } if (src_is_byte) { @@ -1612,7 +1612,7 @@ Node* PhaseStringOpts::copy_char(GraphKit& kit, Node* val, Node* dst_array, Node } if (!dcon || !dbyte) { // Destination is UTF16. Store a char. - __ store(__ ctrl(), adr, val, T_CHAR, byte_adr_idx, MemNode::unordered); + __ store(__ ctrl(), adr, val, T_CHAR, byte_adr_idx, MemNode::unordered, true /* mismatched */); __ set(end, __ AddI(start, __ ConI(2))); } if (!dcon) { diff --git a/hotspot/src/share/vm/opto/superword.cpp b/hotspot/src/share/vm/opto/superword.cpp index ba6e8192746..67d27e35bf8 100644 --- a/hotspot/src/share/vm/opto/superword.cpp +++ b/hotspot/src/share/vm/opto/superword.cpp @@ -276,7 +276,9 @@ void SuperWord::unrolling_analysis(int &local_loop_unroll_factor) { // stop looking, we already have the max vector to map to. if (cur_max_vector < local_loop_unroll_factor) { is_slp = false; - NOT_PRODUCT(if (TraceSuperWordLoopUnrollAnalysis) tty->print_cr("slp analysis fails: unroll limit greater than max vector\n")); + if (TraceSuperWordLoopUnrollAnalysis) { + tty->print_cr("slp analysis fails: unroll limit greater than max vector\n"); + } break; } @@ -389,11 +391,9 @@ void SuperWord::SLP_extract() { if (_do_vector_loop) { if (_packset.length() == 0) { -#ifndef PRODUCT if (TraceSuperWord) { tty->print_cr("\nSuperWord::_do_vector_loop DFA could not build packset, now trying to build anyway"); } -#endif pack_parallel(); } } @@ -558,9 +558,11 @@ void SuperWord::find_adjacent_refs() { assert(!same_velt_type(s, mem_ref), "sanity"); memops.push(s); } - MemNode* best_align_to_mem_ref = find_align_to_ref(memops); + best_align_to_mem_ref = find_align_to_ref(memops); if (best_align_to_mem_ref == NULL) { - NOT_PRODUCT(if (TraceSuperWord) tty->print_cr("SuperWord::find_adjacent_refs(): best_align_to_mem_ref == NULL");) + if (TraceSuperWord) { + tty->print_cr("SuperWord::find_adjacent_refs(): best_align_to_mem_ref == NULL"); + } break; } best_iv_adjustment = get_iv_adjustment(best_align_to_mem_ref); @@ -582,12 +584,10 @@ void SuperWord::find_adjacent_refs() { } // while (memops.size() != 0 set_align_to_ref(best_align_to_mem_ref); -#ifndef PRODUCT if (TraceSuperWord) { tty->print_cr("\nAfter find_adjacent_refs"); print_packset(); } -#endif } #ifndef PRODUCT @@ -874,7 +874,7 @@ void SuperWord::dependence_graph() { _dg.make_edge(s1, slice_sink); } } -#ifndef PRODUCT + if (TraceSuperWord) { tty->print_cr("\nDependence graph for slice: %d", n->_idx); for (int q = 0; q < _nlist.length(); q++) { @@ -882,11 +882,10 @@ void SuperWord::dependence_graph() { } tty->cr(); } -#endif + _nlist.clear(); } -#ifndef PRODUCT if (TraceSuperWord) { tty->print_cr("\ndisjoint_ptrs: %s", _disjoint_ptrs.length() > 0 ? "" : "NONE"); for (int r = 0; r < _disjoint_ptrs.length(); r++) { @@ -895,7 +894,7 @@ void SuperWord::dependence_graph() { } tty->cr(); } -#endif + } //---------------------------mem_slice_preds--------------------------- @@ -912,7 +911,9 @@ void SuperWord::mem_slice_preds(Node* start, Node* stop, GrowableArray &p if (out->is_Load()) { if (in_bb(out)) { preds.push(out); - NOT_PRODUCT(if (TraceSuperWord && Verbose) tty->print_cr("SuperWord::mem_slice_preds: added pred(%d)", out->_idx);) + if (TraceSuperWord && Verbose) { + tty->print_cr("SuperWord::mem_slice_preds: added pred(%d)", out->_idx); + } } } else { // FIXME @@ -931,7 +932,9 @@ void SuperWord::mem_slice_preds(Node* start, Node* stop, GrowableArray &p }//for if (n == stop) break; preds.push(n); - NOT_PRODUCT(if (TraceSuperWord && Verbose) tty->print_cr("SuperWord::mem_slice_preds: added pred(%d)", n->_idx);) + if (TraceSuperWord && Verbose) { + tty->print_cr("SuperWord::mem_slice_preds: added pred(%d)", n->_idx); + } prev = n; assert(n->is_Mem(), "unexpected node %s", n->Name()); n = n->in(MemNode::Memory); @@ -1123,12 +1126,10 @@ void SuperWord::extend_packlist() { } } -#ifndef PRODUCT if (TraceSuperWord) { tty->print_cr("\nAfter extend_packlist"); print_packset(); } -#endif } //------------------------------follow_use_defs--------------------------- @@ -1412,12 +1413,10 @@ void SuperWord::combine_packs() { } } -#ifndef PRODUCT if (TraceSuperWord) { tty->print_cr("\nAfter combine_packs"); print_packset(); } -#endif } //-----------------------------construct_my_pack_map-------------------------- @@ -2244,10 +2243,15 @@ void SuperWord::output() { if (cl->has_passed_slp()) { uint slp_max_unroll_factor = cl->slp_max_unroll(); if (slp_max_unroll_factor == max_vlen) { - NOT_PRODUCT(if (TraceSuperWordLoopUnrollAnalysis) tty->print_cr("vector loop(unroll=%d, len=%d)\n", max_vlen, max_vlen_in_bytes*BitsPerByte)); + if (TraceSuperWordLoopUnrollAnalysis) { + tty->print_cr("vector loop(unroll=%d, len=%d)\n", max_vlen, max_vlen_in_bytes*BitsPerByte); + } // For atomic unrolled loops which are vector mapped, instigate more unrolling. cl->set_notpassed_slp(); - C->set_major_progress(); + // if vector resources are limited, do not allow additional unrolling + if (FLOATPRESSURE > 8) { + C->set_major_progress(); + } cl->mark_do_unroll_only(); } } @@ -2650,10 +2654,10 @@ void SuperWord::compute_max_depth() { } ct++; } while (again); -#ifndef PRODUCT - if (TraceSuperWord && Verbose) + + if (TraceSuperWord && Verbose) { tty->print_cr("compute_max_depth iterated: %d times", ct); -#endif + } } //-------------------------compute_vector_element_type----------------------- @@ -2664,10 +2668,9 @@ void SuperWord::compute_max_depth() { // Normally the type of the add is integer, but for packed character // operations the type of the add needs to be char. void SuperWord::compute_vector_element_type() { -#ifndef PRODUCT - if (TraceSuperWord && Verbose) + if (TraceSuperWord && Verbose) { tty->print_cr("\ncompute_velt_type:"); -#endif + } // Initial type for (int i = 0; i < _block.length(); i++) { @@ -2758,7 +2761,9 @@ int SuperWord::memory_alignment(MemNode* s, int iv_adjust) { offset += iv_adjust*p.memory_size(); int off_rem = offset % vw; int off_mod = off_rem >= 0 ? off_rem : off_rem + vw; - NOT_PRODUCT(if(TraceSuperWord && Verbose) tty->print_cr("SWPointer::memory_alignment: off_rem = %d, off_mod = %d", off_rem, off_mod);) + if (TraceSuperWord && Verbose) { + tty->print_cr("SWPointer::memory_alignment: off_rem = %d, off_mod = %d", off_rem, off_mod); + } return off_mod; } @@ -4046,11 +4051,9 @@ int SuperWord::mark_generations() { }//for (int i... if (_ii_first == -1 || _ii_last == -1) { -#ifndef PRODUCT if (TraceSuperWord && Verbose) { tty->print_cr("SuperWord::mark_generations unknown error, something vent wrong"); } -#endif return -1; // something vent wrong } // collect nodes in the first and last generations @@ -4083,11 +4086,9 @@ int SuperWord::mark_generations() { }//for if (found == false) { -#ifndef PRODUCT if (TraceSuperWord && Verbose) { tty->print_cr("SuperWord::mark_generations: Cannot build order of iterations - no dependent Store for %d", nd->_idx); } -#endif _ii_order.clear(); return -1; } @@ -4153,11 +4154,10 @@ bool SuperWord::fix_commutative_inputs(Node* gold, Node* fix) { return true; } -#ifndef PRODUCT if (TraceSuperWord && Verbose) { tty->print_cr("SuperWord::fix_commutative_inputs: cannot fix node %d", fix->_idx); } -#endif + return false; } @@ -4224,11 +4224,9 @@ bool SuperWord::hoist_loads_in_graph() { for (int i = 0; i < _mem_slice_head.length(); i++) { Node* n = _mem_slice_head.at(i); if ( !in_bb(n) || !n->is_Phi() || n->bottom_type() != Type::MEMORY) { -#ifndef PRODUCT if (TraceSuperWord && Verbose) { tty->print_cr("SuperWord::hoist_loads_in_graph: skipping unexpected node n=%d", n->_idx); } -#endif continue; } @@ -4275,11 +4273,10 @@ bool SuperWord::hoist_loads_in_graph() { restart(); // invalidate all basic structures, since we rebuilt the graph -#ifndef PRODUCT if (TraceSuperWord && Verbose) { tty->print_cr("\nSuperWord::hoist_loads_in_graph() the graph was rebuilt, all structures invalidated and need rebuild"); } -#endif + return true; } diff --git a/hotspot/src/share/vm/opto/type.cpp b/hotspot/src/share/vm/opto/type.cpp index 8cf30c4fdfa..ec4c898afb2 100644 --- a/hotspot/src/share/vm/opto/type.cpp +++ b/hotspot/src/share/vm/opto/type.cpp @@ -150,6 +150,33 @@ BasicType Type::array_element_basic_type() const { return bt; } +// For two instance arrays of same dimension, return the base element types. +// Otherwise or if the arrays have different dimensions, return NULL. +void Type::get_arrays_base_elements(const Type *a1, const Type *a2, + const TypeInstPtr **e1, const TypeInstPtr **e2) { + + if (e1) *e1 = NULL; + if (e2) *e2 = NULL; + const TypeAryPtr* a1tap = (a1 == NULL) ? NULL : a1->isa_aryptr(); + const TypeAryPtr* a2tap = (a2 == NULL) ? NULL : a2->isa_aryptr(); + + if (a1tap != NULL && a2tap != NULL) { + // Handle multidimensional arrays + const TypePtr* a1tp = a1tap->elem()->make_ptr(); + const TypePtr* a2tp = a2tap->elem()->make_ptr(); + while (a1tp && a1tp->isa_aryptr() && a2tp && a2tp->isa_aryptr()) { + a1tap = a1tp->is_aryptr(); + a2tap = a2tp->is_aryptr(); + a1tp = a1tap->elem()->make_ptr(); + a2tp = a2tap->elem()->make_ptr(); + } + if (a1tp && a1tp->isa_instptr() && a2tp && a2tp->isa_instptr()) { + if (e1) *e1 = a1tp->is_instptr(); + if (e2) *e2 = a2tp->is_instptr(); + } + } +} + //---------------------------get_typeflow_type--------------------------------- // Import a type produced by ciTypeFlow. const Type* Type::get_typeflow_type(ciType* type) { @@ -2029,7 +2056,11 @@ const TypePtr* TypePtr::with_inline_depth(int depth) const { bool TypeAry::interface_vs_oop(const Type *t) const { const TypeAry* t_ary = t->is_ary(); if (t_ary) { - return _elem->interface_vs_oop(t_ary->_elem); + const TypePtr* this_ptr = _elem->make_ptr(); // In case we have narrow_oops + const TypePtr* t_ptr = t_ary->_elem->make_ptr(); + if(this_ptr != NULL && t_ptr != NULL) { + return this_ptr->interface_vs_oop(t_ptr); + } } return false; } @@ -3134,8 +3165,17 @@ const Type *TypeOopPtr::filter_helper(const Type *kills, bool include_speculativ // be 'I' or 'j/l/O'. Thus we'll pick 'j/l/O'. If this then flows // into a Phi which "knows" it's an Interface type we'll have to // uplift the type. - if (!empty() && ktip != NULL && ktip->is_loaded() && ktip->klass()->is_interface()) - return kills; // Uplift to interface + if (!empty()) { + if (ktip != NULL && ktip->is_loaded() && ktip->klass()->is_interface()) { + return kills; // Uplift to interface + } + // Also check for evil cases of 'this' being a class array + // and 'kills' expecting an array of interfaces. + Type::get_arrays_base_elements(ft, kills, NULL, &ktip); + if (ktip != NULL && ktip->is_loaded() && ktip->klass()->is_interface()) { + return kills; // Uplift to array of interface + } + } return Type::TOP; // Canonical empty value } diff --git a/hotspot/src/share/vm/opto/type.hpp b/hotspot/src/share/vm/opto/type.hpp index 2a3e530d7db..6636af716b4 100644 --- a/hotspot/src/share/vm/opto/type.hpp +++ b/hotspot/src/share/vm/opto/type.hpp @@ -368,6 +368,11 @@ public: return _const_basic_type[type]; } + // For two instance arrays of same dimension, return the base element types. + // Otherwise or if the arrays have different dimensions, return NULL. + static void get_arrays_base_elements(const Type *a1, const Type *a2, + const TypeInstPtr **e1, const TypeInstPtr **e2); + // Mapping to the array element's basic type. BasicType array_element_basic_type() const; diff --git a/hotspot/src/share/vm/opto/vectornode.cpp b/hotspot/src/share/vm/opto/vectornode.cpp index 9402cb14b90..3ba33385071 100644 --- a/hotspot/src/share/vm/opto/vectornode.cpp +++ b/hotspot/src/share/vm/opto/vectornode.cpp @@ -188,7 +188,7 @@ bool VectorNode::implemented(int opc, uint vlen, BasicType bt) { (vlen > 1) && is_power_of_2(vlen) && Matcher::vector_size_supported(bt, vlen)) { int vopc = VectorNode::opcode(opc, bt); - return vopc > 0 && Matcher::match_rule_supported(vopc) && (vopc != Op_CMoveD || vlen == 4); + return vopc > 0 && Matcher::match_rule_supported_vector(vopc, vlen); } return false; } diff --git a/hotspot/src/share/vm/prims/jni.cpp b/hotspot/src/share/vm/prims/jni.cpp index 996180e4713..b4a278faa3a 100644 --- a/hotspot/src/share/vm/prims/jni.cpp +++ b/hotspot/src/share/vm/prims/jni.cpp @@ -261,19 +261,6 @@ void jfieldIDWorkaround::verify_instance_jfieldID(Klass* k, jfieldID id) { Histogram* JNIHistogram; static volatile jint JNIHistogram_lock = 0; - class JNITraceWrapper : public StackObj { - public: - JNITraceWrapper(const char* format, ...) ATTRIBUTE_PRINTF(2, 3) { - if (TraceJNICalls) { - va_list ap; - va_start(ap, format); - tty->print("JNI "); - tty->vprint_cr(format, ap); - va_end(ap); - } - } - }; - class JNIHistogramElement : public HistogramElement { public: JNIHistogramElement(const char* name); @@ -305,7 +292,7 @@ void jfieldIDWorkaround::verify_instance_jfieldID(Klass* k, jfieldID id) { static JNIHistogramElement* e = new JNIHistogramElement(arg); \ /* There is a MT-race condition in VC++. So we need to make sure that that e has been initialized */ \ if (e != NULL) e->increment_count() - #define JNIWrapper(arg) JNICountWrapper(arg); JNITraceWrapper(arg) + #define JNIWrapper(arg) JNICountWrapper(arg); #else #define JNIWrapper(arg) #endif @@ -3759,7 +3746,7 @@ void copy_jni_function_table(const struct JNINativeInterface_ *new_jni_NativeInt void quicken_jni_functions() { // Replace GetField with fast versions if (UseFastJNIAccessors && !JvmtiExport::can_post_field_access() - && !VerifyJNIFields && !TraceJNICalls && !CountJNICalls && !CheckJNICalls + && !VerifyJNIFields && !CountJNICalls && !CheckJNICalls #if defined(_WINDOWS) && defined(IA32) && defined(COMPILER2) // windows x86 currently needs SEH wrapper and the gain of the fast // versions currently isn't certain for server vm on uniprocessor. @@ -3878,7 +3865,7 @@ _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_GetDefaultJavaVMInitArgs(void *args_) { unit_test_function_call // Forward declaration -void TestNmethodBucket_test(); +void TestDependencyContext_test(); void test_semaphore(); void TestOS_test(); void TestReservedSpace_test(); @@ -3902,7 +3889,8 @@ void TestG1BiasedArray_test(); void TestBufferingOopClosure_test(); void TestCodeCacheRemSet_test(); void FreeRegionList_test(); -void test_memset_with_concurrent_readers(); +void IHOP_test(); +void test_memset_with_concurrent_readers() NOT_DEBUG_RETURN; void TestPredictions_test(); void WorkerDataArray_test(); #endif @@ -3910,7 +3898,7 @@ void WorkerDataArray_test(); void execute_internal_vm_tests() { if (ExecuteInternalVMTests) { tty->print_cr("Running internal VM tests"); - run_unit_test(TestNmethodBucket_test()); + run_unit_test(TestDependencyContext_test()); run_unit_test(test_semaphore()); run_unit_test(TestOS_test()); run_unit_test(TestReservedSpace_test()); @@ -3950,6 +3938,7 @@ void execute_internal_vm_tests() { run_unit_test(TestCodeCacheRemSet_test()); if (UseG1GC) { run_unit_test(FreeRegionList_test()); + run_unit_test(IHOP_test()); } run_unit_test(test_memset_with_concurrent_readers()); run_unit_test(TestPredictions_test()); diff --git a/hotspot/src/share/vm/prims/jvm.cpp b/hotspot/src/share/vm/prims/jvm.cpp index d25485c051e..77ac9a44711 100644 --- a/hotspot/src/share/vm/prims/jvm.cpp +++ b/hotspot/src/share/vm/prims/jvm.cpp @@ -46,6 +46,7 @@ #include "prims/jvmtiThreadState.hpp" #include "prims/nativeLookup.hpp" #include "prims/privilegedStack.hpp" +#include "prims/stackwalk.hpp" #include "runtime/arguments.hpp" #include "runtime/atomic.inline.hpp" #include "runtime/handles.inline.hpp" @@ -223,19 +224,6 @@ void trace_class_resolution(Klass* to_class) { // Wrapper to trace JVM functions #ifdef ASSERT - class JVMTraceWrapper : public StackObj { - public: - JVMTraceWrapper(const char* format, ...) ATTRIBUTE_PRINTF(2, 3) { - if (TraceJVMCalls) { - va_list ap; - va_start(ap, format); - tty->print("JVM "); - tty->vprint_cr(format, ap); - va_end(ap); - } - } - }; - Histogram* JVMHistogram; volatile jint JVMHistogram_lock = 0; @@ -269,15 +257,9 @@ void trace_class_resolution(Klass* to_class) { static JVMHistogramElement* e = new JVMHistogramElement(arg); \ if (e != NULL) e->increment_count(); // Due to bug in VC++, we need a NULL check here eventhough it should never happen! - #define JVMWrapper(arg1) JVMCountWrapper(arg1); JVMTraceWrapper(arg1) - #define JVMWrapper2(arg1, arg2) JVMCountWrapper(arg1); JVMTraceWrapper(arg1, arg2) - #define JVMWrapper3(arg1, arg2, arg3) JVMCountWrapper(arg1); JVMTraceWrapper(arg1, arg2, arg3) - #define JVMWrapper4(arg1, arg2, arg3, arg4) JVMCountWrapper(arg1); JVMTraceWrapper(arg1, arg2, arg3, arg4) + #define JVMWrapper(arg) JVMCountWrapper(arg); #else - #define JVMWrapper(arg1) - #define JVMWrapper2(arg1, arg2) - #define JVMWrapper3(arg1, arg2, arg3) - #define JVMWrapper4(arg1, arg2, arg3, arg4) + #define JVMWrapper(arg) #endif @@ -547,6 +529,94 @@ JVM_ENTRY(jobject, JVM_GetStackTraceElement(JNIEnv *env, jobject throwable, jint JVM_END +// java.lang.StackWalker ////////////////////////////////////////////////////// + + +JVM_ENTRY(jobject, JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jlong mode, + jint skip_frames, jint frame_count, jint start_index, + jobjectArray classes, + jobjectArray frames)) + JVMWrapper("JVM_CallStackWalk"); + JavaThread* jt = (JavaThread*) THREAD; + if (!jt->is_Java_thread() || !jt->has_last_Java_frame()) { + THROW_MSG_(vmSymbols::java_lang_InternalError(), "doStackWalk: no stack trace", NULL); + } + + Handle stackStream_h(THREAD, JNIHandles::resolve_non_null(stackStream)); + objArrayOop ca = objArrayOop(JNIHandles::resolve_non_null(classes)); + objArrayHandle classes_array_h(THREAD, ca); + + // frames array is null when only getting caller reference + objArrayOop fa = objArrayOop(JNIHandles::resolve(frames)); + objArrayHandle frames_array_h(THREAD, fa); + + int limit = start_index + frame_count; + if (classes_array_h->length() < limit) { + THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), "not enough space in buffers", NULL); + } + + Handle result = StackWalk::walk(stackStream_h, mode, skip_frames, frame_count, + start_index, classes_array_h, + frames_array_h, CHECK_NULL); + return JNIHandles::make_local(env, result()); +JVM_END + + +JVM_ENTRY(jint, JVM_MoreStackWalk(JNIEnv *env, jobject stackStream, jlong mode, jlong anchor, + jint frame_count, jint start_index, + jobjectArray classes, + jobjectArray frames)) + JVMWrapper("JVM_MoreStackWalk"); + JavaThread* jt = (JavaThread*) THREAD; + objArrayOop ca = objArrayOop(JNIHandles::resolve_non_null(classes)); + objArrayHandle classes_array_h(THREAD, ca); + + // frames array is null when only getting caller reference + objArrayOop fa = objArrayOop(JNIHandles::resolve(frames)); + objArrayHandle frames_array_h(THREAD, fa); + + int limit = start_index+frame_count; + if (classes_array_h->length() < limit) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "not enough space in buffers"); + } + + Handle stackStream_h(THREAD, JNIHandles::resolve_non_null(stackStream)); + return StackWalk::moreFrames(stackStream_h, mode, anchor, frame_count, + start_index, classes_array_h, + frames_array_h, THREAD); +JVM_END + +JVM_ENTRY(void, JVM_FillStackFrames(JNIEnv *env, jclass stackStream, + jint start_index, + jobjectArray frames, + jint from_index, jint to_index)) + JVMWrapper("JVM_FillStackFrames"); + if (TraceStackWalk) { + tty->print("JVM_FillStackFrames() start_index=%d from_index=%d to_index=%d\n", + start_index, from_index, to_index); + } + + JavaThread* jt = (JavaThread*) THREAD; + + objArrayOop fa = objArrayOop(JNIHandles::resolve_non_null(frames)); + objArrayHandle frames_array_h(THREAD, fa); + + if (frames_array_h->length() < to_index) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "array length not matched"); + } + + for (int i = from_index; i < to_index; i++) { + Handle stackFrame(THREAD, frames_array_h->obj_at(i)); + java_lang_StackFrameInfo::fill_methodInfo(stackFrame, CHECK); + } +JVM_END + +JVM_ENTRY(void, JVM_SetMethodInfo(JNIEnv *env, jobject frame)) + JVMWrapper("JVM_SetMethodInfo"); + Handle stackFrame(THREAD, JNIHandles::resolve(frame)); + java_lang_StackFrameInfo::fill_methodInfo(stackFrame, THREAD); +JVM_END + // java.lang.Object /////////////////////////////////////////////// @@ -672,7 +742,7 @@ JVM_END // java.io.File /////////////////////////////////////////////////////////////// JVM_LEAF(char*, JVM_NativePath(char* path)) - JVMWrapper2("JVM_NativePath (%s)", path); + JVMWrapper("JVM_NativePath"); return os::native_path(path); JVM_END @@ -749,7 +819,7 @@ JVM_END // FindClassFromBootLoader is exported to the launcher for windows. JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env, const char* name)) - JVMWrapper2("JVM_FindClassFromBootLoader %s", name); + JVMWrapper("JVM_FindClassFromBootLoader"); // Java libraries should ensure that name is never null... if (name == NULL || (int)strlen(name) > Symbol::max_length()) { @@ -774,7 +844,7 @@ JVM_END JVM_ENTRY(jclass, JVM_FindClassFromCaller(JNIEnv* env, const char* name, jboolean init, jobject loader, jclass caller)) - JVMWrapper2("JVM_FindClassFromCaller %s throws ClassNotFoundException", name); + JVMWrapper("JVM_FindClassFromCaller throws ClassNotFoundException"); // Java libraries should ensure that name is never null... if (name == NULL || (int)strlen(name) > Symbol::max_length()) { // It's impossible to create this class; the name cannot fit @@ -809,7 +879,7 @@ JVM_END JVM_ENTRY(jclass, JVM_FindClassFromClass(JNIEnv *env, const char *name, jboolean init, jclass from)) - JVMWrapper2("JVM_FindClassFromClass %s", name); + JVMWrapper("JVM_FindClassFromClass"); if (name == NULL || (int)strlen(name) > Symbol::max_length()) { // It's impossible to create this class; the name cannot fit // into the constant pool. @@ -861,11 +931,10 @@ static void is_lock_held_by_thread(Handle loader, PerfCounter* counter, TRAPS) { } // common code for JVM_DefineClass() and JVM_DefineClassWithSource() -// and JVM_DefineClassWithSourceCond() static jclass jvm_define_class_common(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd, const char *source, - jboolean verify, TRAPS) { + TRAPS) { if (source == NULL) source = "__JVM_DefineClass__"; assert(THREAD->is_Java_thread(), "must be a JavaThread"); @@ -906,8 +975,7 @@ static jclass jvm_define_class_common(JNIEnv *env, const char *name, Handle protection_domain (THREAD, JNIHandles::resolve(pd)); Klass* k = SystemDictionary::resolve_from_stream(class_name, class_loader, protection_domain, &st, - verify != 0, - CHECK_NULL); + true, CHECK_NULL); if (TraceClassResolution && k != NULL) { trace_class_resolution(k); @@ -918,25 +986,16 @@ static jclass jvm_define_class_common(JNIEnv *env, const char *name, JVM_ENTRY(jclass, JVM_DefineClass(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd)) - JVMWrapper2("JVM_DefineClass %s", name); + JVMWrapper("JVM_DefineClass"); - return jvm_define_class_common(env, name, loader, buf, len, pd, NULL, true, THREAD); + return jvm_define_class_common(env, name, loader, buf, len, pd, NULL, THREAD); JVM_END JVM_ENTRY(jclass, JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd, const char *source)) - JVMWrapper2("JVM_DefineClassWithSource %s", name); + JVMWrapper("JVM_DefineClassWithSource"); - return jvm_define_class_common(env, name, loader, buf, len, pd, source, true, THREAD); -JVM_END - -JVM_ENTRY(jclass, JVM_DefineClassWithSourceCond(JNIEnv *env, const char *name, - jobject loader, const jbyte *buf, - jsize len, jobject pd, - const char *source, jboolean verify)) - JVMWrapper2("JVM_DefineClassWithSourceCond %s", name); - - return jvm_define_class_common(env, name, loader, buf, len, pd, source, verify, THREAD); + return jvm_define_class_common(env, name, loader, buf, len, pd, source, THREAD); JVM_END JVM_ENTRY(jclass, JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name)) @@ -3361,7 +3420,7 @@ JVM_END JVM_ENTRY_NO_ENV(void*, JVM_LoadLibrary(const char* name)) //%note jvm_ct - JVMWrapper2("JVM_LoadLibrary (%s)", name); + JVMWrapper("JVM_LoadLibrary"); char ebuf[1024]; void *load_result; { @@ -3393,7 +3452,7 @@ JVM_END JVM_LEAF(void*, JVM_FindLibraryEntry(void* handle, const char* name)) - JVMWrapper2("JVM_FindLibraryEntry (%s)", name); + JVMWrapper("JVM_FindLibraryEntry"); return os::dll_lookup(handle, name); JVM_END @@ -3401,7 +3460,7 @@ JVM_END // JNI version /////////////////////////////////////////////////////////////////////////////// JVM_LEAF(jboolean, JVM_IsSupportedJNIVersion(jint version)) - JVMWrapper2("JVM_IsSupportedJNIVersion (%d)", version); + JVMWrapper("JVM_IsSupportedJNIVersion"); return Threads::is_supported_jni_version_including_1_1(version); JVM_END @@ -3661,3 +3720,8 @@ JVM_ENTRY(void, JVM_GetVersionInfo(JNIEnv* env, jvm_version_info* info, size_t i info->is_attachable = AttachListener::is_attach_supported(); } JVM_END + +JVM_ENTRY_NO_ENV(jint, JVM_FindSignal(const char *name)) + return os::get_signal_number(name); +JVM_END + diff --git a/hotspot/src/share/vm/prims/jvm.h b/hotspot/src/share/vm/prims/jvm.h index beb5bc8f32a..021f55dccb3 100644 --- a/hotspot/src/share/vm/prims/jvm.h +++ b/hotspot/src/share/vm/prims/jvm.h @@ -200,6 +200,37 @@ JVM_GetStackTraceDepth(JNIEnv *env, jobject throwable); JNIEXPORT jobject JNICALL JVM_GetStackTraceElement(JNIEnv *env, jobject throwable, jint index); +/* + * java.lang.StackWalker + */ +enum { + JVM_STACKWALK_FILL_CLASS_REFS_ONLY = 0x2, + JVM_STACKWALK_FILTER_FILL_IN_STACK_TRACE = 0x10, + JVM_STACKWALK_SHOW_HIDDEN_FRAMES = 0x20, + JVM_STACKWALK_FILL_LIVE_STACK_FRAMES = 0x100 +}; + +JNIEXPORT jobject JNICALL +JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jlong mode, + jint skip_frames, jint frame_count, jint start_index, + jobjectArray classes, + jobjectArray frames); + +JNIEXPORT jint JNICALL +JVM_MoreStackWalk(JNIEnv *env, jobject stackStream, jlong mode, jlong anchor, + jint frame_count, jint start_index, + jobjectArray classes, + jobjectArray frames); + +JNIEXPORT void JNICALL +JVM_FillStackFrames(JNIEnv* env, jclass cls, + jint start_index, + jobjectArray frames, + jint from_index, jint toIndex); + +JNIEXPORT void JNICALL +JVM_SetMethodInfo(JNIEnv* env, jobject frame); + /* * java.lang.Thread */ @@ -378,17 +409,6 @@ JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd, const char *source); -/* Define a class with a source with conditional verification (added HSX 14) - * -Xverify:all will verify anyway, -Xverify:none will not verify, - * -Xverify:remote (default) will obey this conditional - * i.e. true = should_verify_class - */ -JNIEXPORT jclass JNICALL -JVM_DefineClassWithSourceCond(JNIEnv *env, const char *name, - jobject loader, const jbyte *buf, - jsize len, jobject pd, const char *source, - jboolean verify); - /* * Reflection support functions */ diff --git a/hotspot/src/share/vm/prims/methodHandles.cpp b/hotspot/src/share/vm/prims/methodHandles.cpp index 0f58dde7063..99e8a785f10 100644 --- a/hotspot/src/share/vm/prims/methodHandles.cpp +++ b/hotspot/src/share/vm/prims/methodHandles.cpp @@ -27,6 +27,7 @@ #include "classfile/stringTable.hpp" #include "code/codeCache.hpp" #include "code/codeCacheExtensions.hpp" +#include "code/dependencyContext.hpp" #include "compiler/compileBroker.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/oopMapCache.hpp" @@ -945,30 +946,33 @@ int MethodHandles::find_MemberNames(KlassHandle k, return rfill + overflow; } +// Is it safe to remove stale entries from a dependency list? +static bool safe_to_expunge() { + // Since parallel GC threads can concurrently iterate over a dependency + // list during safepoint, it is safe to remove entries only when + // CodeCache lock is held. + return CodeCache_lock->owned_by_self(); +} + void MethodHandles::add_dependent_nmethod(oop call_site, nmethod* nm) { assert_locked_or_safepoint(CodeCache_lock); oop context = java_lang_invoke_CallSite::context(call_site); - nmethodBucket* deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context); - - nmethodBucket* new_deps = nmethodBucket::add_dependent_nmethod(deps, nm); - if (deps != new_deps) { - java_lang_invoke_MethodHandleNatives_CallSiteContext::set_vmdependencies(context, new_deps); - } + DependencyContext deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context); + // Try to purge stale entries on updates. + // Since GC doesn't clean dependency contexts rooted at CallSiteContext objects, + // in order to avoid memory leak, stale entries are purged whenever a dependency list + // is changed (both on addition and removal). Though memory reclamation is delayed, + // it avoids indefinite memory usage growth. + deps.add_dependent_nmethod(nm, /*expunge_stale_entries=*/safe_to_expunge()); } void MethodHandles::remove_dependent_nmethod(oop call_site, nmethod* nm) { assert_locked_or_safepoint(CodeCache_lock); oop context = java_lang_invoke_CallSite::context(call_site); - nmethodBucket* deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context); - - if (nmethodBucket::remove_dependent_nmethod(deps, nm)) { - nmethodBucket* new_deps = nmethodBucket::clean_dependent_nmethods(deps); - if (deps != new_deps) { - java_lang_invoke_MethodHandleNatives_CallSiteContext::set_vmdependencies(context, new_deps); - } - } + DependencyContext deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context); + deps.remove_dependent_nmethod(nm, /*expunge_stale_entries=*/safe_to_expunge()); } void MethodHandles::flush_dependent_nmethods(Handle call_site, Handle target) { @@ -977,21 +981,15 @@ void MethodHandles::flush_dependent_nmethods(Handle call_site, Handle target) { int marked = 0; CallSiteDepChange changes(call_site(), target()); { + No_Safepoint_Verifier nsv; MutexLockerEx mu2(CodeCache_lock, Mutex::_no_safepoint_check_flag); oop context = java_lang_invoke_CallSite::context(call_site()); - nmethodBucket* deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context); - - marked = nmethodBucket::mark_dependent_nmethods(deps, changes); - if (marked > 0) { - nmethodBucket* new_deps = nmethodBucket::clean_dependent_nmethods(deps); - if (deps != new_deps) { - java_lang_invoke_MethodHandleNatives_CallSiteContext::set_vmdependencies(context, new_deps); - } - } + DependencyContext deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context); + marked = deps.mark_dependent_nmethods(changes); } if (marked > 0) { - // At least one nmethod has been marked for deoptimization + // At least one nmethod has been marked for deoptimization. VM_Deoptimize op; VMThread::execute(&op); } @@ -1331,6 +1329,8 @@ JVM_ENTRY(void, MHN_setCallSiteTargetVolatile(JNIEnv* env, jobject igcls, jobjec } JVM_END +// It is called by a Cleaner object which ensures that dropped CallSites properly +// deallocate their dependency information. JVM_ENTRY(void, MHN_clearCallSiteContext(JNIEnv* env, jobject igcls, jobject context_jh)) { Handle context(THREAD, JNIHandles::resolve_non_null(context_jh)); { @@ -1339,19 +1339,11 @@ JVM_ENTRY(void, MHN_clearCallSiteContext(JNIEnv* env, jobject igcls, jobject con int marked = 0; { + No_Safepoint_Verifier nsv; MutexLockerEx mu2(CodeCache_lock, Mutex::_no_safepoint_check_flag); - nmethodBucket* b = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context()); - while(b != NULL) { - nmethod* nm = b->get_nmethod(); - if (b->count() > 0 && nm->is_alive() && !nm->is_marked_for_deoptimization()) { - nm->mark_for_deoptimization(); - marked++; - } - nmethodBucket* next = b->next(); - delete b; - b = next; - } - java_lang_invoke_MethodHandleNatives_CallSiteContext::set_vmdependencies(context(), NULL); // reset context + assert(safe_to_expunge(), "removal is not safe"); + DependencyContext deps = java_lang_invoke_MethodHandleNatives_CallSiteContext::vmdependencies(context()); + marked = deps.remove_all_dependents(); } if (marked > 0) { // At least one nmethod has been marked for deoptimization diff --git a/hotspot/src/share/vm/prims/stackwalk.cpp b/hotspot/src/share/vm/prims/stackwalk.cpp new file mode 100644 index 00000000000..3e292641a79 --- /dev/null +++ b/hotspot/src/share/vm/prims/stackwalk.cpp @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +#include "precompiled.hpp" +#include "classfile/javaClasses.hpp" +#include "classfile/javaClasses.inline.hpp" +#include "classfile/vmSymbols.hpp" +#include "memory/oopFactory.hpp" +#include "oops/oop.inline.hpp" +#include "oops/objArrayOop.inline.hpp" +#include "prims/stackwalk.hpp" +#include "runtime/globals.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/vframe.hpp" +#include "utilities/globalDefinitions.hpp" + +// setup and cleanup actions +void StackWalkAnchor::setup_magic_on_entry(objArrayHandle classes_array) { + classes_array->obj_at_put(magic_pos, _thread->threadObj()); + _anchor = address_value(); + assert(check_magic(classes_array), "invalid magic"); +} + +bool StackWalkAnchor::check_magic(objArrayHandle classes_array) { + oop m1 = classes_array->obj_at(magic_pos); + jlong m2 = _anchor; + if (m1 == _thread->threadObj() && m2 == address_value()) return true; + return false; +} + +bool StackWalkAnchor::cleanup_magic_on_exit(objArrayHandle classes_array) { + bool ok = check_magic(classes_array); + classes_array->obj_at_put(magic_pos, NULL); + _anchor = 0L; + return ok; +} + +// Returns StackWalkAnchor for the current stack being traversed. +// +// Parameters: +// thread Current Java thread. +// magic Magic value used for each stack walking +// classes_array User-supplied buffers. The 0th element is reserved +// to this StackWalkAnchor to use +// +StackWalkAnchor* StackWalkAnchor::from_current(JavaThread* thread, jlong magic, + objArrayHandle classes_array) +{ + assert(thread != NULL && thread->is_Java_thread(), ""); + oop m1 = classes_array->obj_at(magic_pos); + if (m1 != thread->threadObj()) return NULL; + if (magic == 0L) return NULL; + StackWalkAnchor* anchor = (StackWalkAnchor*) (intptr_t) magic; + if (!anchor->is_valid_in(thread, classes_array)) return NULL; + return anchor; +} + +// Unpacks one or more frames into user-supplied buffers. +// Updates the end index, and returns the number of unpacked frames. +// Always start with the existing vfst.method and bci. +// Do not call vfst.next to advance over the last returned value. +// In other words, do not leave any stale data in the vfst. +// +// Parameters: +// mode Restrict which frames to be decoded. +// vfst vFrameStream. +// max_nframes Maximum number of frames to be filled. +// start_index Start index to the user-supplied buffers. +// classes_array Buffer to store classes in, starting at start_index. +// frames_array Buffer to store StackFrame in, starting at start_index. +// NULL if not used. +// end_index End index to the user-supplied buffers with unpacked frames. +// +// Returns the number of frames whose information was transferred into the buffers. +// +int StackWalk::fill_in_frames(jlong mode, vframeStream& vfst, + int max_nframes, int start_index, + objArrayHandle classes_array, + objArrayHandle frames_array, + int& end_index, TRAPS) { + if (TraceStackWalk) { + tty->print_cr("fill_in_frames limit=%d start=%d frames length=%d", + max_nframes, start_index, classes_array->length()); + } + assert(max_nframes > 0, "invalid max_nframes"); + assert(start_index + max_nframes <= classes_array->length(), "oob"); + + int frames_decoded = 0; + for (; !vfst.at_end(); vfst.next()) { + Method* method = vfst.method(); + int bci = vfst.bci(); + + if (method == NULL) continue; + if (!ShowHiddenFrames && StackWalk::skip_hidden_frames(mode)) { + if (method->is_hidden()) { + if (TraceStackWalk) { + tty->print(" hidden method: "); method->print_short_name(); + tty->print("\n"); + } + continue; + } + } + + int index = end_index++; + if (TraceStackWalk) { + tty->print(" %d: frame method: ", index); method->print_short_name(); + tty->print_cr(" bci=%d", bci); + } + + classes_array->obj_at_put(index, method->method_holder()->java_mirror()); + // fill in StackFrameInfo and initialize MemberName + if (live_frame_info(mode)) { + Handle stackFrame(frames_array->obj_at(index)); + fill_live_stackframe(stackFrame, method, bci, vfst.java_frame(), CHECK_0); + } else if (need_method_info(mode)) { + Handle stackFrame(frames_array->obj_at(index)); + fill_stackframe(stackFrame, method, bci); + } + if (++frames_decoded >= max_nframes) break; + } + return frames_decoded; +} + +static oop create_primitive_value_instance(StackValueCollection* values, int i, TRAPS) { + Klass* k = SystemDictionary::resolve_or_null(vmSymbols::java_lang_LiveStackFrameInfo(), CHECK_NULL); + instanceKlassHandle ik (THREAD, k); + + JavaValue result(T_OBJECT); + JavaCallArguments args; + Symbol* signature = NULL; + + // ## TODO: type is only available in LocalVariable table, if present. + // ## StackValue type is T_INT or T_OBJECT. + switch (values->at(i)->type()) { + case T_INT: + args.push_int(values->int_at(i)); + signature = vmSymbols::asPrimitive_int_signature(); + break; + + case T_LONG: + args.push_long(values->long_at(i)); + signature = vmSymbols::asPrimitive_long_signature(); + break; + + case T_FLOAT: + args.push_float(values->float_at(i)); + signature = vmSymbols::asPrimitive_float_signature(); + break; + + case T_DOUBLE: + args.push_double(values->double_at(i)); + signature = vmSymbols::asPrimitive_double_signature(); + break; + + case T_BYTE: + args.push_int(values->int_at(i)); + signature = vmSymbols::asPrimitive_byte_signature(); + break; + + case T_SHORT: + args.push_int(values->int_at(i)); + signature = vmSymbols::asPrimitive_short_signature(); + break; + + case T_CHAR: + args.push_int(values->int_at(i)); + signature = vmSymbols::asPrimitive_char_signature(); + break; + + case T_BOOLEAN: + args.push_int(values->int_at(i)); + signature = vmSymbols::asPrimitive_boolean_signature(); + break; + + case T_OBJECT: + return values->obj_at(i)(); + + case T_CONFLICT: + // put a non-null slot + args.push_int(0); + signature = vmSymbols::asPrimitive_int_signature(); + break; + + default: ShouldNotReachHere(); + } + JavaCalls::call_static(&result, + ik, + vmSymbols::asPrimitive_name(), + signature, + &args, + CHECK_NULL); + return (instanceOop) result.get_jobject(); +} + +static objArrayHandle values_to_object_array(StackValueCollection* values, TRAPS) { + objArrayHandle empty; + int length = values->size(); + objArrayOop array_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), + length, CHECK_(empty)); + objArrayHandle array_h(THREAD, array_oop); + for (int i = 0; i < values->size(); i++) { + StackValue* st = values->at(i); + oop obj = create_primitive_value_instance(values, i, CHECK_(empty)); + if (obj != NULL) + array_h->obj_at_put(i, obj); + } + return array_h; +} + +static objArrayHandle monitors_to_object_array(GrowableArray* monitors, TRAPS) { + int length = monitors->length(); + objArrayOop array_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), + length, CHECK_(objArrayHandle())); + objArrayHandle array_h(THREAD, array_oop); + for (int i = 0; i < length; i++) { + MonitorInfo* monitor = monitors->at(i); + array_h->obj_at_put(i, monitor->owner()); + } + return array_h; +} + +// Fill StackFrameInfo with declaringClass and bci and initialize memberName +void StackWalk::fill_stackframe(Handle stackFrame, const methodHandle& method, int bci) { + java_lang_StackFrameInfo::set_declaringClass(stackFrame(), method->method_holder()->java_mirror()); + java_lang_StackFrameInfo::set_method_and_bci(stackFrame(), method, bci); +} + +// Fill LiveStackFrameInfo with locals, monitors, and expressions +void StackWalk::fill_live_stackframe(Handle stackFrame, const methodHandle& method, + int bci, javaVFrame* jvf, TRAPS) { + fill_stackframe(stackFrame, method, bci); + if (jvf != NULL) { + StackValueCollection* locals = jvf->locals(); + StackValueCollection* expressions = jvf->expressions(); + GrowableArray* monitors = jvf->monitors(); + + if (!locals->is_empty()) { + objArrayHandle locals_h = values_to_object_array(locals, CHECK); + java_lang_LiveStackFrameInfo::set_locals(stackFrame(), locals_h()); + } + if (!expressions->is_empty()) { + objArrayHandle expressions_h = values_to_object_array(expressions, CHECK); + java_lang_LiveStackFrameInfo::set_operands(stackFrame(), expressions_h()); + } + if (monitors->length() > 0) { + objArrayHandle monitors_h = monitors_to_object_array(monitors, CHECK); + java_lang_LiveStackFrameInfo::set_monitors(stackFrame(), monitors_h()); + } + } +} + +// Begins stack walking. +// +// Parameters: +// stackStream StackStream object +// mode Stack walking mode. +// skip_frames Number of frames to be skipped. +// frame_count Number of frames to be traversed. +// start_index Start index to the user-supplied buffers. +// classes_array Buffer to store classes in, starting at start_index. +// frames_array Buffer to store StackFrame in, starting at start_index. +// NULL if not used. +// +// Returns Object returned from AbstractStackWalker::doStackWalk call. +// +oop StackWalk::walk(Handle stackStream, jlong mode, + int skip_frames, int frame_count, int start_index, + objArrayHandle classes_array, + objArrayHandle frames_array, + TRAPS) { + JavaThread* jt = (JavaThread*)THREAD; + if (TraceStackWalk) { + tty->print_cr("Start walking: mode " JLONG_FORMAT " skip %d frames batch size %d", + mode, skip_frames, frame_count); + } + + if (need_method_info(mode)) { + if (frames_array.is_null()) { + THROW_MSG_(vmSymbols::java_lang_NullPointerException(), "frames_array is NULL", NULL); + } + } + + Klass* stackWalker_klass = SystemDictionary::StackWalker_klass(); + Klass* abstractStackWalker_klass = SystemDictionary::AbstractStackWalker_klass(); + + methodHandle m_doStackWalk(THREAD, Universe::do_stack_walk_method()); + + // Open up a traversable stream onto my stack. + // This stream will be made available by *reference* to the inner Java call. + StackWalkAnchor anchor(jt); + vframeStream& vfst = anchor.vframe_stream(); + + { + // Skip all methods from AbstractStackWalker and StackWalk (enclosing method) + if (!fill_in_stacktrace(mode)) { + while (!vfst.at_end()) { + InstanceKlass* ik = vfst.method()->method_holder(); + if (ik != stackWalker_klass && + ik != abstractStackWalker_klass && ik->super() != abstractStackWalker_klass) { + break; + } + + if (TraceStackWalk) { + tty->print(" skip "); vfst.method()->print_short_name(); tty->print("\n"); + } + vfst.next(); + } + } + + // For exceptions, skip Throwable::fillInStackTrace and methods + // of the exception class and superclasses + if (fill_in_stacktrace(mode)) { + bool skip_to_fillInStackTrace = false; + bool skip_throwableInit_check = false; + while (!vfst.at_end() && !skip_throwableInit_check) { + InstanceKlass* ik = vfst.method()->method_holder(); + Method* method = vfst.method(); + if (!skip_to_fillInStackTrace) { + if (ik == SystemDictionary::Throwable_klass() && + method->name() == vmSymbols::fillInStackTrace_name()) { + // this frame will be skipped + skip_to_fillInStackTrace = true; + } + } else if (!(ik->is_subclass_of(SystemDictionary::Throwable_klass()) && + method->name() == vmSymbols::object_initializer_name())) { + // there are none or we've seen them all - either way stop checking + skip_throwableInit_check = true; + break; + } + + if (TraceStackWalk) { + tty->print("stack walk: skip "); vfst.method()->print_short_name(); tty->print("\n"); + } + vfst.next(); + } + } + + // stack frame has been traversed individually and resume stack walk + // from the stack frame at depth == skip_frames. + for (int n=0; n < skip_frames && !vfst.at_end(); vfst.next(), n++) { + if (TraceStackWalk) { + tty->print(" skip "); vfst.method()->print_short_name(); + tty->print_cr(" frame id: " PTR_FORMAT " pc: " PTR_FORMAT, + p2i(vfst.frame_id()), p2i(vfst.frame_pc())); + } + } + } + + // The Method* pointer in the vfst has a very short shelf life. Grab it now. + int end_index = start_index; + int numFrames = 0; + if (!vfst.at_end()) { + numFrames = fill_in_frames(mode, vfst, frame_count, start_index, classes_array, + frames_array, end_index, CHECK_NULL); + if (numFrames < 1) { + THROW_MSG_(vmSymbols::java_lang_InternalError(), "stack walk: decode failed", NULL); + } + } + + // JVM_CallStackWalk walks the stack and fills in stack frames, then calls to + // Java method java.lang.StackStreamFactory.AbstractStackWalker::doStackWalk + // which calls the implementation to consume the stack frames. + // When JVM_CallStackWalk returns, it invalidates the stack stream. + JavaValue result(T_OBJECT); + JavaCallArguments args(stackStream); + args.push_long(anchor.address_value()); + args.push_int(skip_frames); + args.push_int(frame_count); + args.push_int(start_index); + args.push_int(end_index); + + // Link the thread and vframe stream into the callee-visible object + anchor.setup_magic_on_entry(classes_array); + + JavaCalls::call(&result, m_doStackWalk, &args, THREAD); + + // Do this before anything else happens, to disable any lingering stream objects + bool ok = anchor.cleanup_magic_on_exit(classes_array); + + // Throw pending exception if we must + (void) (CHECK_NULL); + + if (!ok) { + THROW_MSG_(vmSymbols::java_lang_InternalError(), "doStackWalk: corrupted buffers on exit", NULL); + } + + // Return normally + return (oop)result.get_jobject(); + +} + +// Walk the next batch of stack frames +// +// Parameters: +// stackStream StackStream object +// mode Stack walking mode. +// magic Must be valid value to continue the stack walk +// frame_count Number of frames to be decoded. +// start_index Start index to the user-supplied buffers. +// classes_array Buffer to store classes in, starting at start_index. +// frames_array Buffer to store StackFrame in, starting at start_index. +// NULL if not used. +// +// Returns the end index of frame filled in the buffer. +// +jint StackWalk::moreFrames(Handle stackStream, jlong mode, jlong magic, + int frame_count, int start_index, + objArrayHandle classes_array, + objArrayHandle frames_array, + TRAPS) +{ + JavaThread* jt = (JavaThread*)THREAD; + StackWalkAnchor* existing_anchor = StackWalkAnchor::from_current(jt, magic, classes_array); + if (existing_anchor == NULL) { + THROW_MSG_(vmSymbols::java_lang_InternalError(), "doStackWalk: corrupted buffers", 0L); + } + + if ((need_method_info(mode) || live_frame_info(mode)) && frames_array.is_null()) { + THROW_MSG_(vmSymbols::java_lang_NullPointerException(), "frames_array is NULL", 0L); + } + + if (TraceStackWalk) { + tty->print_cr("StackWalk::moreFrames frame_count %d existing_anchor " PTR_FORMAT " start %d frames %d", + frame_count, p2i(existing_anchor), start_index, classes_array->length()); + } + int end_index = start_index; + if (frame_count <= 0) { + return end_index; // No operation. + } + + int count = frame_count + start_index; + assert (classes_array->length() >= count, "not enough space in buffers"); + + StackWalkAnchor& anchor = (*existing_anchor); + vframeStream& vfst = anchor.vframe_stream(); + if (!vfst.at_end()) { + vfst.next(); // this was the last frame decoded in the previous batch + if (!vfst.at_end()) { + int n = fill_in_frames(mode, vfst, frame_count, start_index, classes_array, + frames_array, end_index, CHECK_0); + if (n < 1) { + THROW_MSG_(vmSymbols::java_lang_InternalError(), "doStackWalk: later decode failed", 0L); + } + return end_index; + } + } + return end_index; +} diff --git a/hotspot/src/share/vm/prims/stackwalk.hpp b/hotspot/src/share/vm/prims/stackwalk.hpp new file mode 100644 index 00000000000..1fa906815be --- /dev/null +++ b/hotspot/src/share/vm/prims/stackwalk.hpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2015, 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. + * + */ + + +#ifndef SHARE_VM_PRIMS_STACKWALK_HPP +#define SHARE_VM_PRIMS_STACKWALK_HPP + +#include "oops/oop.hpp" +#include "runtime/vframe.hpp" + +class StackWalkAnchor : public StackObj { +private: + enum { + magic_pos = 0 + }; + + JavaThread* _thread; + vframeStream _vfst; + jlong _anchor; +public: + StackWalkAnchor(JavaThread* thread) + : _thread(thread), _vfst(thread), _anchor(0L) {} + + vframeStream& vframe_stream() { return _vfst; } + JavaThread* thread() { return _thread; } + + void setup_magic_on_entry(objArrayHandle classes_array); + bool check_magic(objArrayHandle classes_array); + bool cleanup_magic_on_exit(objArrayHandle classes_array); + + bool is_valid_in(Thread* thread, objArrayHandle classes_array) { + return (_thread == thread && check_magic(classes_array)); + } + + jlong address_value() { + return (jlong) castable_address(this); + } + + static StackWalkAnchor* from_current(JavaThread* thread, jlong anchor, objArrayHandle frames_array); +}; + +class StackWalk : public AllStatic { +private: + static int fill_in_frames(jlong mode, vframeStream& vfst, + int max_nframes, int start_index, + objArrayHandle classes_array, + objArrayHandle frames_array, + int& end_index, TRAPS); + + static void fill_stackframe(Handle stackFrame, const methodHandle& method, int bci); + + static void fill_live_stackframe(Handle stackFrame, const methodHandle& method, int bci, + javaVFrame* jvf, TRAPS); + + static inline bool skip_hidden_frames(int mode) { + return (mode & JVM_STACKWALK_SHOW_HIDDEN_FRAMES) == 0; + } + static inline bool need_method_info(int mode) { + return (mode & JVM_STACKWALK_FILL_CLASS_REFS_ONLY) == 0; + } + static inline bool live_frame_info(int mode) { + return (mode & JVM_STACKWALK_FILL_LIVE_STACK_FRAMES) != 0; + } + static inline bool fill_in_stacktrace(int mode) { + return (mode & JVM_STACKWALK_FILTER_FILL_IN_STACK_TRACE) != 0; + } + +public: + static oop walk(Handle stackStream, jlong mode, + int skip_frames, int frame_count, int start_index, + objArrayHandle classes_array, + objArrayHandle frames_array, + TRAPS); + + static jint moreFrames(Handle stackStream, jlong mode, jlong magic, + int frame_count, int start_index, + objArrayHandle classes_array, + objArrayHandle frames_array, + TRAPS); +}; +#endif // SHARE_VM_PRIMS_STACKWALK_HPP diff --git a/hotspot/src/share/vm/prims/whitebox.cpp b/hotspot/src/share/vm/prims/whitebox.cpp index a386a3f5313..6c68a00d29c 100644 --- a/hotspot/src/share/vm/prims/whitebox.cpp +++ b/hotspot/src/share/vm/prims/whitebox.cpp @@ -1271,6 +1271,10 @@ WB_ENTRY(void, WB_AssertMatchingSafepointCalls(JNIEnv* env, jobject o, jboolean attemptedNoSafepointValue == JNI_TRUE); WB_END +WB_ENTRY(jboolean, WB_IsSharedClass(JNIEnv* env, jobject wb, jclass clazz)) + return (jboolean)MetaspaceShared::is_in_shared_space(java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz))); +WB_END + WB_ENTRY(jboolean, WB_IsMonitorInflated(JNIEnv* env, jobject wb, jobject obj)) oop obj_oop = JNIHandles::resolve(obj); return (jboolean) obj_oop->mark()->has_monitor(); @@ -1471,6 +1475,7 @@ static JNINativeMethod methods[] = { {CC"runMemoryUnitTests", CC"()V", (void*)&WB_RunMemoryUnitTests}, {CC"readFromNoaccessArea",CC"()V", (void*)&WB_ReadFromNoaccessArea}, {CC"stressVirtualSpaceResize",CC"(JJJ)I", (void*)&WB_StressVirtualSpaceResize}, + {CC"isSharedClass", CC"(Ljava/lang/Class;)Z", (void*)&WB_IsSharedClass }, #if INCLUDE_ALL_GCS {CC"g1InConcurrentMark", CC"()Z", (void*)&WB_G1InConcurrentMark}, {CC"g1IsHumongous0", CC"(Ljava/lang/Object;)Z", (void*)&WB_G1IsHumongous }, diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp index ca98eb8e7f0..6fbc5f82719 100644 --- a/hotspot/src/share/vm/runtime/arguments.cpp +++ b/hotspot/src/share/vm/runtime/arguments.cpp @@ -459,7 +459,7 @@ const char* Arguments::real_flag_name(const char *flag_name) { return flag_name; } -#ifndef PRODUCT +#ifdef ASSERT static bool lookup_special_flag(const char *flag_name, size_t skip_index) { for (size_t i = 0; special_jvm_flags[i].name != NULL; i++) { if ((i != skip_index) && (strcmp(special_jvm_flags[i].name, flag_name) == 0)) { @@ -1468,24 +1468,6 @@ void Arguments::set_tiered_flags() { // Enable SegmentedCodeCache if TieredCompilation is enabled and ReservedCodeCacheSize >= 240M if (FLAG_IS_DEFAULT(SegmentedCodeCache) && ReservedCodeCacheSize >= 240*M) { FLAG_SET_ERGO(bool, SegmentedCodeCache, true); - - if (FLAG_IS_DEFAULT(ReservedCodeCacheSize)) { - // Multiply sizes by 5 but fix NonNMethodCodeHeapSize (distribute among non-profiled and profiled code heap) - if (FLAG_IS_DEFAULT(ProfiledCodeHeapSize)) { - FLAG_SET_ERGO(uintx, ProfiledCodeHeapSize, ProfiledCodeHeapSize * 5 + NonNMethodCodeHeapSize * 2); - } - if (FLAG_IS_DEFAULT(NonProfiledCodeHeapSize)) { - FLAG_SET_ERGO(uintx, NonProfiledCodeHeapSize, NonProfiledCodeHeapSize * 5 + NonNMethodCodeHeapSize * 2); - } - // Check consistency of code heap sizes - if ((NonNMethodCodeHeapSize + NonProfiledCodeHeapSize + ProfiledCodeHeapSize) != ReservedCodeCacheSize) { - jio_fprintf(defaultStream::error_stream(), - "Invalid code heap sizes: NonNMethodCodeHeapSize(%dK) + ProfiledCodeHeapSize(%dK) + NonProfiledCodeHeapSize(%dK) = %dK. Must be equal to ReservedCodeCacheSize = %uK.\n", - NonNMethodCodeHeapSize/K, ProfiledCodeHeapSize/K, NonProfiledCodeHeapSize/K, - (NonNMethodCodeHeapSize + ProfiledCodeHeapSize + NonProfiledCodeHeapSize)/K, ReservedCodeCacheSize/K); - vm_exit(1); - } - } } if (!UseInterpreter) { // -Xcomp Tier3InvokeNotifyFreqLog = 0; @@ -2535,18 +2517,11 @@ bool Arguments::check_vm_args_consistency() { "Invalid ReservedCodeCacheSize=%dM. Must be at most %uM.\n", ReservedCodeCacheSize/M, CODE_CACHE_SIZE_LIMIT/M); status = false; - } else if (NonNMethodCodeHeapSize < min_code_cache_size){ + } else if (NonNMethodCodeHeapSize < min_code_cache_size) { jio_fprintf(defaultStream::error_stream(), "Invalid NonNMethodCodeHeapSize=%dK. Must be at least %uK.\n", NonNMethodCodeHeapSize/K, min_code_cache_size/K); status = false; - } else if ((!FLAG_IS_DEFAULT(NonNMethodCodeHeapSize) || !FLAG_IS_DEFAULT(ProfiledCodeHeapSize) || !FLAG_IS_DEFAULT(NonProfiledCodeHeapSize)) - && (NonNMethodCodeHeapSize + NonProfiledCodeHeapSize + ProfiledCodeHeapSize) != ReservedCodeCacheSize) { - jio_fprintf(defaultStream::error_stream(), - "Invalid code heap sizes: NonNMethodCodeHeapSize(%dK) + ProfiledCodeHeapSize(%dK) + NonProfiledCodeHeapSize(%dK) = %dK. Must be equal to ReservedCodeCacheSize = %uK.\n", - NonNMethodCodeHeapSize/K, ProfiledCodeHeapSize/K, NonProfiledCodeHeapSize/K, - (NonNMethodCodeHeapSize + ProfiledCodeHeapSize + NonProfiledCodeHeapSize)/K, ReservedCodeCacheSize/K); - status = false; } if (!FLAG_IS_DEFAULT(CICompilerCount) && !FLAG_IS_DEFAULT(CICompilerCountPerCPU) && CICompilerCountPerCPU) { @@ -3425,7 +3400,7 @@ void Arguments::fix_appclasspath() { } if (!PrintSharedArchiveAndExit) { - ClassLoader::trace_class_path("[classpath: ", _java_class_path->value()); + ClassLoader::trace_class_path(tty, "[classpath: ", _java_class_path->value()); } } diff --git a/hotspot/src/share/vm/runtime/commandLineFlagConstraintList.cpp b/hotspot/src/share/vm/runtime/commandLineFlagConstraintList.cpp index 5371aa30da2..27ef604b6e7 100644 --- a/hotspot/src/share/vm/runtime/commandLineFlagConstraintList.cpp +++ b/hotspot/src/share/vm/runtime/commandLineFlagConstraintList.cpp @@ -223,7 +223,7 @@ void emit_constraint_double(const char* name, CommandLineFlagConstraintFunc_doub #define EMIT_CONSTRAINT_CHECK(func, type) , func, CommandLineFlagConstraint::type // the "name" argument must be a string literal -#define INITIAL_CONSTRAINTS_SIZE 45 +#define INITIAL_CONSTRAINTS_SIZE 69 GrowableArray* CommandLineFlagConstraintList::_constraints = NULL; CommandLineFlagConstraint::ConstraintType CommandLineFlagConstraintList::_validating_type = CommandLineFlagConstraint::AtParse; diff --git a/hotspot/src/share/vm/runtime/commandLineFlagRangeList.cpp b/hotspot/src/share/vm/runtime/commandLineFlagRangeList.cpp index b25f7ccdd28..a1a7cb76bb9 100644 --- a/hotspot/src/share/vm/runtime/commandLineFlagRangeList.cpp +++ b/hotspot/src/share/vm/runtime/commandLineFlagRangeList.cpp @@ -279,7 +279,7 @@ void emit_range_double(const char* name, double min, double max) { // Generate func argument to pass into emit_range_xxx functions #define EMIT_RANGE_CHECK(a, b) , a, b -#define INITIAL_RANGES_SIZE 204 +#define INITIAL_RANGES_SIZE 320 GrowableArray* CommandLineFlagRangeList::_ranges = NULL; // Check the ranges of all flags that have them diff --git a/hotspot/src/share/vm/runtime/commandLineFlagRangeList.hpp b/hotspot/src/share/vm/runtime/commandLineFlagRangeList.hpp index e4be9903214..a6777524890 100644 --- a/hotspot/src/share/vm/runtime/commandLineFlagRangeList.hpp +++ b/hotspot/src/share/vm/runtime/commandLineFlagRangeList.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_VM_RUNTIME_COMMANDLINEFLAGRANGELIST_HPP #define SHARE_VM_RUNTIME_COMMANDLINEFLAGRANGELIST_HPP +#include "memory/metaspaceShared.hpp" #include "runtime/globals.hpp" #include "utilities/growableArray.hpp" diff --git a/hotspot/src/share/vm/runtime/deoptimization.cpp b/hotspot/src/share/vm/runtime/deoptimization.cpp index ab733a9974f..f48a8fb7be2 100644 --- a/hotspot/src/share/vm/runtime/deoptimization.cpp +++ b/hotspot/src/share/vm/runtime/deoptimization.cpp @@ -68,7 +68,8 @@ Deoptimization::UnrollBlock::UnrollBlock(int size_of_deoptimized_frame, int number_of_frames, intptr_t* frame_sizes, address* frame_pcs, - BasicType return_type) { + BasicType return_type, + int exec_mode) { _size_of_deoptimized_frame = size_of_deoptimized_frame; _caller_adjustment = caller_adjustment; _caller_actual_parameters = caller_actual_parameters; @@ -80,10 +81,11 @@ Deoptimization::UnrollBlock::UnrollBlock(int size_of_deoptimized_frame, _initial_info = 0; // PD (x86 only) _counter_temp = 0; - _unpack_kind = 0; + _unpack_kind = exec_mode; _sender_sp_temp = 0; _total_frame_sizes = size_of_frames(); + assert(exec_mode >= 0 && exec_mode < Unpack_LIMIT, "Unexpected exec_mode"); } @@ -128,7 +130,7 @@ void Deoptimization::UnrollBlock::print() { // ResetNoHandleMark and HandleMark were removed from it. The actual reallocation // of previously eliminated objects occurs in realloc_objects, which is // called from the method fetch_unroll_info_helper below. -JRT_BLOCK_ENTRY(Deoptimization::UnrollBlock*, Deoptimization::fetch_unroll_info(JavaThread* thread)) +JRT_BLOCK_ENTRY(Deoptimization::UnrollBlock*, Deoptimization::fetch_unroll_info(JavaThread* thread, int exec_mode)) // It is actually ok to allocate handles in a leaf method. It causes no safepoints, // but makes the entry a little slower. There is however a little dance we have to // do in debug mode to get around the NoHandleMark code in the JRT_LEAF macro @@ -142,12 +144,12 @@ JRT_BLOCK_ENTRY(Deoptimization::UnrollBlock*, Deoptimization::fetch_unroll_info( } thread->inc_in_deopt_handler(); - return fetch_unroll_info_helper(thread); + return fetch_unroll_info_helper(thread, exec_mode); JRT_END // This is factored, since it is both called from a JRT_LEAF (deoptimization) and a JRT_ENTRY (uncommon_trap) -Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread* thread) { +Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread* thread, int exec_mode) { // Note: there is a safepoint safety issue here. No matter whether we enter // via vanilla deopt or uncommon trap we MUST NOT stop at a safepoint once @@ -186,6 +188,19 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread assert(vf->is_compiled_frame(), "Wrong frame type"); chunk->push(compiledVFrame::cast(vf)); + ScopeDesc* trap_scope = chunk->at(0)->scope(); + Handle exceptionObject; + if (trap_scope->rethrow_exception()) { + if (PrintDeoptimizationDetails) { + tty->print_cr("Exception to be rethrown in the interpreter for method %s::%s at bci %d", trap_scope->method()->method_holder()->name()->as_C_string(), trap_scope->method()->name()->as_C_string(), trap_scope->bci()); + } + GrowableArray* expressions = trap_scope->expressions(); + guarantee(expressions != NULL && expressions->length() > 0, "must have exception to throw"); + ScopeValue* topOfStack = expressions->top(); + exceptionObject = StackValue::create_stack_value(&deoptee, &map, topOfStack)->get_obj(); + assert(exceptionObject() != NULL, "exception oop can not be null"); + } + bool realloc_failures = false; #if defined(COMPILER2) || INCLUDE_JVMCI @@ -474,13 +489,21 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread assert(CodeCache::find_blob_unsafe(frame_pcs[0]) != NULL, "bad pc"); #endif // SHARK +#ifdef INCLUDE_JVMCI + if (exceptionObject() != NULL) { + thread->set_exception_oop(exceptionObject()); + exec_mode = Unpack_exception; + } +#endif + UnrollBlock* info = new UnrollBlock(array->frame_size() * BytesPerWord, caller_adjustment * BytesPerWord, caller_was_method_handle ? 0 : callee_parameters, number_of_frames, frame_sizes, frame_pcs, - return_type); + return_type, + exec_mode); // On some platforms, we need a way to pass some platform dependent // information to the unpacking code so the skeletal frames come out // correct (initial fp value, unextended sp, ...) @@ -1495,18 +1518,6 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* thread, jint tra #endif Bytecodes::Code trap_bc = trap_method->java_code_at(trap_bci); - - if (trap_scope->rethrow_exception()) { - if (PrintDeoptimizationDetails) { - tty->print_cr("Exception to be rethrown in the interpreter for method %s::%s at bci %d", trap_method->method_holder()->name()->as_C_string(), trap_method->name()->as_C_string(), trap_bci); - } - GrowableArray* expressions = trap_scope->expressions(); - guarantee(expressions != NULL, "must have exception to throw"); - ScopeValue* topOfStack = expressions->top(); - Handle topOfStackObj = StackValue::create_stack_value(&fr, ®_map, topOfStack)->get_obj(); - THREAD->set_pending_exception(topOfStackObj(), NULL, 0); - } - // Record this event in the histogram. gather_statistics(reason, action, trap_bc); @@ -1985,7 +1996,7 @@ Deoptimization::update_method_data_from_interpreter(MethodData* trap_mdo, int tr ignore_maybe_prior_recompile); } -Deoptimization::UnrollBlock* Deoptimization::uncommon_trap(JavaThread* thread, jint trap_request) { +Deoptimization::UnrollBlock* Deoptimization::uncommon_trap(JavaThread* thread, jint trap_request, jint exec_mode) { if (TraceDeoptimization) { tty->print("Uncommon trap "); } @@ -1994,7 +2005,7 @@ Deoptimization::UnrollBlock* Deoptimization::uncommon_trap(JavaThread* thread, j // This enters VM and may safepoint uncommon_trap_inner(thread, trap_request); } - return fetch_unroll_info_helper(thread); + return fetch_unroll_info_helper(thread, exec_mode); } // Local derived constants. diff --git a/hotspot/src/share/vm/runtime/deoptimization.hpp b/hotspot/src/share/vm/runtime/deoptimization.hpp index b61c3506668..7e63ab6bd38 100644 --- a/hotspot/src/share/vm/runtime/deoptimization.hpp +++ b/hotspot/src/share/vm/runtime/deoptimization.hpp @@ -123,7 +123,8 @@ class Deoptimization : AllStatic { Unpack_deopt = 0, // normal deoptimization, use pc computed in unpack_vframe_on_stack Unpack_exception = 1, // exception is pending Unpack_uncommon_trap = 2, // redo last byte code (C2 only) - Unpack_reexecute = 3 // reexecute bytecode (C1 only) + Unpack_reexecute = 3, // reexecute bytecode (C1 only) + Unpack_LIMIT = 4 }; // Checks all compiled methods. Invalid methods are deleted and @@ -179,13 +180,13 @@ JVMCI_ONLY(public:) intptr_t _initial_info; // Platform dependent data for the sender frame (was FP on x86) int _caller_actual_parameters; // The number of actual arguments at the // interpreted caller of the deoptimized frame + int _unpack_kind; // exec_mode that can be changed during fetch_unroll_info // The following fields are used as temps during the unpacking phase // (which is tight on registers, especially on x86). They really ought // to be PD variables but that involves moving this class into its own // file to use the pd include mechanism. Maybe in a later cleanup ... intptr_t _counter_temp; // SHOULD BE PD VARIABLE (x86 frame count temp) - intptr_t _unpack_kind; // SHOULD BE PD VARIABLE (x86 unpack kind) intptr_t _sender_sp_temp; // SHOULD BE PD VARIABLE (x86 sender_sp) public: // Constructor @@ -195,7 +196,8 @@ JVMCI_ONLY(public:) int number_of_frames, intptr_t* frame_sizes, address* frames_pcs, - BasicType return_type); + BasicType return_type, + int unpack_kind); ~UnrollBlock(); // Returns where a register is located. @@ -205,6 +207,7 @@ JVMCI_ONLY(public:) intptr_t* frame_sizes() const { return _frame_sizes; } int number_of_frames() const { return _number_of_frames; } address* frame_pcs() const { return _frame_pcs ; } + int unpack_kind() const { return _unpack_kind; } // Returns the total size of frames int size_of_frames() const; @@ -237,7 +240,7 @@ JVMCI_ONLY(public:) // deoptimized frame. // @argument thread. Thread where stub_frame resides. // @see OptoRuntime::deoptimization_fetch_unroll_info_C - static UnrollBlock* fetch_unroll_info(JavaThread* thread); + static UnrollBlock* fetch_unroll_info(JavaThread* thread, int exec_mode); //** Unpacks vframeArray onto execution stack // Called by assembly stub after execution has returned to @@ -262,7 +265,7 @@ JVMCI_ONLY(public:) //** Performs an uncommon trap for compiled code. // The top most compiler frame is converted into interpreter frames - static UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index); + static UnrollBlock* uncommon_trap(JavaThread* thread, jint unloaded_class_index, jint exec_mode); // Helper routine that enters the VM and may block static void uncommon_trap_inner(JavaThread* thread, jint unloaded_class_index); @@ -423,7 +426,7 @@ JVMCI_ONLY(public:) static void load_class_by_index(const constantPoolHandle& constant_pool, int index, TRAPS); static void load_class_by_index(const constantPoolHandle& constant_pool, int index); - static UnrollBlock* fetch_unroll_info_helper(JavaThread* thread); + static UnrollBlock* fetch_unroll_info_helper(JavaThread* thread, int exec_mode); static DeoptAction _unloaded_action; // == Action_reinterpret; static const char* _trap_reason_name[]; diff --git a/hotspot/src/share/vm/runtime/globals.cpp b/hotspot/src/share/vm/runtime/globals.cpp index 9d74dd36c69..638b15eaffc 100644 --- a/hotspot/src/share/vm/runtime/globals.cpp +++ b/hotspot/src/share/vm/runtime/globals.cpp @@ -528,59 +528,57 @@ const char* Flag::flag_error_str(Flag::Error error) { // 4991491 do not "optimize out" the was_set false values: omitting them // tickles a Microsoft compiler bug causing flagTable to be malformed -#define NAME(name) NOT_PRODUCT(&name) PRODUCT_ONLY(&CONST_##name) +#define RUNTIME_PRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_PRODUCT) }, +#define RUNTIME_PD_PRODUCT_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_PRODUCT | Flag::KIND_PLATFORM_DEPENDENT) }, +#define RUNTIME_DIAGNOSTIC_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_DIAGNOSTIC) }, +#define RUNTIME_EXPERIMENTAL_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_EXPERIMENTAL) }, +#define RUNTIME_MANAGEABLE_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_MANAGEABLE) }, +#define RUNTIME_PRODUCT_RW_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_PRODUCT | Flag::KIND_READ_WRITE) }, +#define RUNTIME_DEVELOP_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_DEVELOP) }, +#define RUNTIME_PD_DEVELOP_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_DEVELOP | Flag::KIND_PLATFORM_DEPENDENT) }, +#define RUNTIME_NOTPRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_NOT_PRODUCT) }, -#define RUNTIME_PRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_PRODUCT) }, -#define RUNTIME_PD_PRODUCT_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_PRODUCT | Flag::KIND_PLATFORM_DEPENDENT) }, -#define RUNTIME_DIAGNOSTIC_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_DIAGNOSTIC) }, -#define RUNTIME_EXPERIMENTAL_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_EXPERIMENTAL) }, -#define RUNTIME_MANAGEABLE_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_MANAGEABLE) }, -#define RUNTIME_PRODUCT_RW_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_PRODUCT | Flag::KIND_READ_WRITE) }, -#define RUNTIME_DEVELOP_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_DEVELOP) }, -#define RUNTIME_PD_DEVELOP_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_DEVELOP | Flag::KIND_PLATFORM_DEPENDENT) }, -#define RUNTIME_NOTPRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_NOT_PRODUCT) }, - -#define JVMCI_PRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_JVMCI | Flag::KIND_PRODUCT) }, -#define JVMCI_PD_PRODUCT_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_JVMCI | Flag::KIND_PRODUCT | Flag::KIND_PLATFORM_DEPENDENT) }, -#define JVMCI_DEVELOP_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_JVMCI | Flag::KIND_DEVELOP) }, -#define JVMCI_PD_DEVELOP_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_JVMCI | Flag::KIND_DEVELOP | Flag::KIND_PLATFORM_DEPENDENT) }, -#define JVMCI_DIAGNOSTIC_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_JVMCI | Flag::KIND_DIAGNOSTIC) }, -#define JVMCI_EXPERIMENTAL_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_JVMCI | Flag::KIND_EXPERIMENTAL) }, -#define JVMCI_NOTPRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_JVMCI | Flag::KIND_NOT_PRODUCT) }, +#define JVMCI_PRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_JVMCI | Flag::KIND_PRODUCT) }, +#define JVMCI_PD_PRODUCT_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_JVMCI | Flag::KIND_PRODUCT | Flag::KIND_PLATFORM_DEPENDENT) }, +#define JVMCI_DIAGNOSTIC_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_JVMCI | Flag::KIND_DIAGNOSTIC) }, +#define JVMCI_EXPERIMENTAL_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_JVMCI | Flag::KIND_EXPERIMENTAL) }, +#define JVMCI_DEVELOP_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_JVMCI | Flag::KIND_DEVELOP) }, +#define JVMCI_PD_DEVELOP_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_JVMCI | Flag::KIND_DEVELOP | Flag::KIND_PLATFORM_DEPENDENT) }, +#define JVMCI_NOTPRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_JVMCI | Flag::KIND_NOT_PRODUCT) }, #ifdef _LP64 -#define RUNTIME_LP64_PRODUCT_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_LP64_PRODUCT) }, +#define RUNTIME_LP64_PRODUCT_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_LP64_PRODUCT) }, #else #define RUNTIME_LP64_PRODUCT_FLAG_STRUCT(type, name, value, doc) /* flag is constant */ #endif // _LP64 -#define C1_PRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C1 | Flag::KIND_PRODUCT) }, -#define C1_PD_PRODUCT_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C1 | Flag::KIND_PRODUCT | Flag::KIND_PLATFORM_DEPENDENT) }, -#define C1_DIAGNOSTIC_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C1 | Flag::KIND_DIAGNOSTIC) }, -#define C1_DEVELOP_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C1 | Flag::KIND_DEVELOP) }, -#define C1_PD_DEVELOP_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C1 | Flag::KIND_DEVELOP | Flag::KIND_PLATFORM_DEPENDENT) }, -#define C1_NOTPRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C1 | Flag::KIND_NOT_PRODUCT) }, +#define C1_PRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C1 | Flag::KIND_PRODUCT) }, +#define C1_PD_PRODUCT_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C1 | Flag::KIND_PRODUCT | Flag::KIND_PLATFORM_DEPENDENT) }, +#define C1_DIAGNOSTIC_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C1 | Flag::KIND_DIAGNOSTIC) }, +#define C1_DEVELOP_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C1 | Flag::KIND_DEVELOP) }, +#define C1_PD_DEVELOP_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C1 | Flag::KIND_DEVELOP | Flag::KIND_PLATFORM_DEPENDENT) }, +#define C1_NOTPRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C1 | Flag::KIND_NOT_PRODUCT) }, -#define C2_PRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C2 | Flag::KIND_PRODUCT) }, -#define C2_PD_PRODUCT_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C2 | Flag::KIND_PRODUCT | Flag::KIND_PLATFORM_DEPENDENT) }, -#define C2_DIAGNOSTIC_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C2 | Flag::KIND_DIAGNOSTIC) }, -#define C2_EXPERIMENTAL_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C2 | Flag::KIND_EXPERIMENTAL) }, -#define C2_DEVELOP_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C2 | Flag::KIND_DEVELOP) }, -#define C2_PD_DEVELOP_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C2 | Flag::KIND_DEVELOP | Flag::KIND_PLATFORM_DEPENDENT) }, -#define C2_NOTPRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C2 | Flag::KIND_NOT_PRODUCT) }, +#define C2_PRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C2 | Flag::KIND_PRODUCT) }, +#define C2_PD_PRODUCT_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C2 | Flag::KIND_PRODUCT | Flag::KIND_PLATFORM_DEPENDENT) }, +#define C2_DIAGNOSTIC_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C2 | Flag::KIND_DIAGNOSTIC) }, +#define C2_EXPERIMENTAL_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C2 | Flag::KIND_EXPERIMENTAL) }, +#define C2_DEVELOP_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C2 | Flag::KIND_DEVELOP) }, +#define C2_PD_DEVELOP_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C2 | Flag::KIND_DEVELOP | Flag::KIND_PLATFORM_DEPENDENT) }, +#define C2_NOTPRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_C2 | Flag::KIND_NOT_PRODUCT) }, -#define ARCH_PRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_ARCH | Flag::KIND_PRODUCT) }, -#define ARCH_DIAGNOSTIC_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_ARCH | Flag::KIND_DIAGNOSTIC) }, -#define ARCH_EXPERIMENTAL_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_ARCH | Flag::KIND_EXPERIMENTAL) }, -#define ARCH_DEVELOP_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_ARCH | Flag::KIND_DEVELOP) }, -#define ARCH_NOTPRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_ARCH | Flag::KIND_NOT_PRODUCT) }, +#define ARCH_PRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_ARCH | Flag::KIND_PRODUCT) }, +#define ARCH_DIAGNOSTIC_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_ARCH | Flag::KIND_DIAGNOSTIC) }, +#define ARCH_EXPERIMENTAL_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_ARCH | Flag::KIND_EXPERIMENTAL) }, +#define ARCH_DEVELOP_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_ARCH | Flag::KIND_DEVELOP) }, +#define ARCH_NOTPRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_ARCH | Flag::KIND_NOT_PRODUCT) }, -#define SHARK_PRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_SHARK | Flag::KIND_PRODUCT) }, -#define SHARK_PD_PRODUCT_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_SHARK | Flag::KIND_PRODUCT | Flag::KIND_PLATFORM_DEPENDENT) }, -#define SHARK_DIAGNOSTIC_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_SHARK | Flag::KIND_DIAGNOSTIC) }, -#define SHARK_DEVELOP_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_SHARK | Flag::KIND_DEVELOP) }, -#define SHARK_PD_DEVELOP_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_SHARK | Flag::KIND_DEVELOP | Flag::KIND_PLATFORM_DEPENDENT) }, -#define SHARK_NOTPRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), NAME(name), NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_SHARK | Flag::KIND_NOT_PRODUCT) }, +#define SHARK_PRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_SHARK | Flag::KIND_PRODUCT) }, +#define SHARK_PD_PRODUCT_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_SHARK | Flag::KIND_PRODUCT | Flag::KIND_PLATFORM_DEPENDENT) }, +#define SHARK_DIAGNOSTIC_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_SHARK | Flag::KIND_DIAGNOSTIC) }, +#define SHARK_DEVELOP_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_SHARK | Flag::KIND_DEVELOP) }, +#define SHARK_PD_DEVELOP_FLAG_STRUCT( type, name, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_SHARK | Flag::KIND_DEVELOP | Flag::KIND_PLATFORM_DEPENDENT) }, +#define SHARK_NOTPRODUCT_FLAG_STRUCT( type, name, value, doc) { #type, XSTR(name), (void*) &name, NOT_PRODUCT_ARG(doc) Flag::Flags(Flag::DEFAULT | Flag::KIND_SHARK | Flag::KIND_NOT_PRODUCT) }, static Flag flagTable[] = { RUNTIME_FLAGS(RUNTIME_DEVELOP_FLAG_STRUCT, \ diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp index f33e6a52db3..9cf3ada09d4 100644 --- a/hotspot/src/share/vm/runtime/globals.hpp +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -1031,9 +1031,10 @@ public: product(bool, CreateCoredumpOnCrash, true, \ "Create core/mini dump on VM fatal error") \ \ - product(uintx, ErrorLogTimeout, 2 * 60, \ + product(uint64_t, ErrorLogTimeout, 2 * 60, \ "Timeout, in seconds, to limit the time spent on writing an " \ "error log in case of a crash.") \ + range(0, (uint64_t)max_jlong/1000) \ \ product_pd(bool, UseOSErrorReporting, \ "Let VM fatal error propagate to the OS (ie. WER on Windows)") \ @@ -1072,9 +1073,6 @@ public: develop(bool, BreakAtWarning, false, \ "Execute breakpoint upon encountering VM warning") \ \ - develop(bool, TraceVMOperation, false, \ - "Trace VM operations") \ - \ develop(bool, UseFakeTimers, false, \ "Tell whether the VM should use system time or a fake timer") \ \ @@ -1126,9 +1124,6 @@ public: diagnostic(bool, PrintNMethods, false, \ "Print assembly code for nmethods when generated") \ \ - diagnostic(intx, PrintNMethodsAtLevel, -1, \ - "Only print code for nmethods at the given compilation level") \ - \ diagnostic(bool, PrintNativeNMethods, false, \ "Print assembly code for native nmethods when generated") \ \ @@ -1428,18 +1423,9 @@ public: \ /* tracing */ \ \ - notproduct(bool, TraceRuntimeCalls, false, \ - "Trace run-time calls") \ - \ - develop(bool, TraceJNICalls, false, \ - "Trace JNI calls") \ - \ develop(bool, StressRewriter, false, \ "Stress linktime bytecode rewriting") \ \ - notproduct(bool, TraceJVMCalls, false, \ - "Trace JVM calls") \ - \ product(ccstr, TraceJVMTI, NULL, \ "Trace flags for JVMTI functions and events") \ \ @@ -3114,6 +3100,12 @@ public: "exceptions (0 means all)") \ range(0, max_jint/2) \ \ + develop(bool, TraceStackWalk, false, \ + "Trace stack walking") \ + \ + product(bool, MemberNameInStackFrame, true, \ + "Use MemberName in StackFrame") \ + \ /* notice: the max range value here is max_jint, not max_intx */ \ /* because of overflow issue */ \ NOT_EMBEDDED(diagnostic(intx, GuaranteedSafepointInterval, 1000, \ @@ -3551,7 +3543,7 @@ public: \ product_pd(intx, CompilerThreadStackSize, \ "Compiler Thread Stack Size (in Kbytes)") \ - range(0, max_intx) \ + range(0, max_intx /(1 * K)) \ \ develop_pd(size_t, JVMInvokeMethodSlack, \ "Stack space (bytes) required for JVM_InvokeMethod to complete") \ @@ -4106,21 +4098,26 @@ public: "If PrintSharedArchiveAndExit is true, also print the shared " \ "dictionary") \ \ - product(size_t, SharedReadWriteSize, NOT_LP64(12*M) LP64_ONLY(16*M), \ + product(size_t, SharedReadWriteSize, DEFAULT_SHARED_READ_WRITE_SIZE, \ "Size of read-write space for metadata (in bytes)") \ + range(MIN_SHARED_READ_WRITE_SIZE, MAX_SHARED_READ_WRITE_SIZE) \ \ - product(size_t, SharedReadOnlySize, NOT_LP64(12*M) LP64_ONLY(16*M), \ + product(size_t, SharedReadOnlySize, DEFAULT_SHARED_READ_ONLY_SIZE, \ "Size of read-only space for metadata (in bytes)") \ + range(MIN_SHARED_READ_ONLY_SIZE, MAX_SHARED_READ_ONLY_SIZE) \ \ - product(uintx, SharedMiscDataSize, NOT_LP64(2*M) LP64_ONLY(4*M), \ + product(size_t, SharedMiscDataSize, DEFAULT_SHARED_MISC_DATA_SIZE, \ "Size of the shared miscellaneous data area (in bytes)") \ + range(MIN_SHARED_MISC_DATA_SIZE, MAX_SHARED_MISC_DATA_SIZE) \ \ - product(uintx, SharedMiscCodeSize, 120*K, \ + product(size_t, SharedMiscCodeSize, DEFAULT_SHARED_MISC_CODE_SIZE, \ "Size of the shared miscellaneous code area (in bytes)") \ + range(MIN_SHARED_MISC_CODE_SIZE, MAX_SHARED_MISC_CODE_SIZE) \ \ - product(uintx, SharedBaseAddress, LP64_ONLY(32*G) \ + product(size_t, SharedBaseAddress, LP64_ONLY(32*G) \ NOT_LP64(LINUX_ONLY(2*G) NOT_LINUX(0)), \ "Address to allocate shared memory region for class data") \ + range(0, SIZE_MAX) \ \ product(uintx, SharedSymbolTableBucketSize, 4, \ "Average number of symbols per bucket in shared table") \ @@ -4280,9 +4277,9 @@ public: #define DECLARE_MANAGEABLE_FLAG(type, name, value, doc) extern "C" type name; #define DECLARE_PRODUCT_RW_FLAG(type, name, value, doc) extern "C" type name; #ifdef PRODUCT -#define DECLARE_DEVELOPER_FLAG(type, name, value, doc) extern "C" type CONST_##name; const type name = value; -#define DECLARE_PD_DEVELOPER_FLAG(type, name, doc) extern "C" type CONST_##name; const type name = pd_##name; -#define DECLARE_NOTPRODUCT_FLAG(type, name, value, doc) extern "C" type CONST_##name; +#define DECLARE_DEVELOPER_FLAG(type, name, value, doc) const type name = value; +#define DECLARE_PD_DEVELOPER_FLAG(type, name, doc) const type name = pd_##name; +#define DECLARE_NOTPRODUCT_FLAG(type, name, value, doc) const type name = value; #else #define DECLARE_DEVELOPER_FLAG(type, name, value, doc) extern "C" type name; #define DECLARE_PD_DEVELOPER_FLAG(type, name, doc) extern "C" type name; @@ -4303,9 +4300,9 @@ public: #define MATERIALIZE_MANAGEABLE_FLAG(type, name, value, doc) type name = value; #define MATERIALIZE_PRODUCT_RW_FLAG(type, name, value, doc) type name = value; #ifdef PRODUCT -#define MATERIALIZE_DEVELOPER_FLAG(type, name, value, doc) type CONST_##name = value; -#define MATERIALIZE_PD_DEVELOPER_FLAG(type, name, doc) type CONST_##name = pd_##name; -#define MATERIALIZE_NOTPRODUCT_FLAG(type, name, value, doc) type CONST_##name = value; +#define MATERIALIZE_DEVELOPER_FLAG(type, name, value, doc) +#define MATERIALIZE_PD_DEVELOPER_FLAG(type, name, doc) +#define MATERIALIZE_NOTPRODUCT_FLAG(type, name, value, doc) #else #define MATERIALIZE_DEVELOPER_FLAG(type, name, value, doc) type name = value; #define MATERIALIZE_PD_DEVELOPER_FLAG(type, name, doc) type name = pd_##name; diff --git a/hotspot/src/share/vm/runtime/init.cpp b/hotspot/src/share/vm/runtime/init.cpp index e4d8fe241ac..616d18dfb78 100644 --- a/hotspot/src/share/vm/runtime/init.cpp +++ b/hotspot/src/share/vm/runtime/init.cpp @@ -72,6 +72,7 @@ void vtableStubs_init(); void InlineCacheBuffer_init(); void compilerOracle_init(); bool compileBroker_init(); +void dependencyContext_init(); // Initialization after compiler initialization bool universe_post_init(); // must happen after compiler_init @@ -131,6 +132,8 @@ jint init_globals() { vtableStubs_init(); InlineCacheBuffer_init(); compilerOracle_init(); + dependencyContext_init(); + if (!compileBroker_init()) { return JNI_EINVAL; } diff --git a/hotspot/src/share/vm/runtime/interfaceSupport.cpp b/hotspot/src/share/vm/runtime/interfaceSupport.cpp index 9f19a5f18ca..a65fca64e40 100644 --- a/hotspot/src/share/vm/runtime/interfaceSupport.cpp +++ b/hotspot/src/share/vm/runtime/interfaceSupport.cpp @@ -70,10 +70,6 @@ RuntimeHistogramElement::RuntimeHistogramElement(const char* elementName) { Atomic::dec(&RuntimeHistogram_lock); } -void InterfaceSupport::trace(const char* result_type, const char* header) { - tty->print_cr("%6ld %s", _number_of_calls, header); -} - void InterfaceSupport::gc_alot() { Thread *thread = Thread::current(); if (!thread->is_Java_thread()) return; // Avoid concurrent calls diff --git a/hotspot/src/share/vm/runtime/interfaceSupport.hpp b/hotspot/src/share/vm/runtime/interfaceSupport.hpp index 206d101e945..fc5374df4fc 100644 --- a/hotspot/src/share/vm/runtime/interfaceSupport.hpp +++ b/hotspot/src/share/vm/runtime/interfaceSupport.hpp @@ -74,9 +74,6 @@ class InterfaceSupport: AllStatic { static long _number_of_calls; static long _fullgc_alot_invocation; - // tracing - static void trace(const char* result_type, const char* header); - // Helper methods used to implement +ScavengeALot and +FullGCALot static void check_gc_alot() { if (ScavengeALot || FullGCALot) gc_alot(); } static void gc_alot(); @@ -402,8 +399,6 @@ class RuntimeHistogramElement : public HistogramElement { #define TRACE_CALL(result_type, header) \ InterfaceSupport::_number_of_calls++; \ - if (TraceRuntimeCalls) \ - InterfaceSupport::trace(#result_type, #header); \ if (CountRuntimeCalls) { \ static RuntimeHistogramElement* e = new RuntimeHistogramElement(#header); \ if (e != NULL) e->increment_count(); \ diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp index 35bb7684be8..1ab815c7d52 100644 --- a/hotspot/src/share/vm/runtime/java.cpp +++ b/hotspot/src/share/vm/runtime/java.cpp @@ -49,6 +49,7 @@ #include "runtime/arguments.hpp" #include "runtime/biasedLocking.hpp" #include "runtime/compilationPolicy.hpp" +#include "runtime/deoptimization.hpp" #include "runtime/fprofiler.hpp" #include "runtime/init.hpp" #include "runtime/interfaceSupport.hpp" diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp index f9b920ee822..a26cc9ab3f9 100644 --- a/hotspot/src/share/vm/runtime/os.hpp +++ b/hotspot/src/share/vm/runtime/os.hpp @@ -476,6 +476,7 @@ class os: AllStatic { static ExtendedPC get_thread_pc(Thread *thread); static void breakpoint(); + static bool start_debugging(char *buf, int buflen); static address current_stack_pointer(); static address current_stack_base(); @@ -483,7 +484,7 @@ class os: AllStatic { static void verify_stack_alignment() PRODUCT_RETURN; - static int message_box(const char* title, const char* message); + static bool message_box(const char* title, const char* message); static char* do_you_want_to_debug(const char* message); // run cmd in a separate process and return its exit code; or -1 on failures @@ -641,6 +642,9 @@ class os: AllStatic { // returns NULL if exception_code is not an OS exception/signal. static const char* exception_name(int exception_code, char* buf, size_t buflen); + // Returns the signal number (e.g. 11) for a given signal name (SIGSEGV). + static int get_signal_number(const char* signal_name); + // Returns native Java library, loads if necessary static void* native_java_library(); diff --git a/hotspot/src/share/vm/runtime/perfData.hpp b/hotspot/src/share/vm/runtime/perfData.hpp index 9fc89caa722..1887c524113 100644 --- a/hotspot/src/share/vm/runtime/perfData.hpp +++ b/hotspot/src/share/vm/runtime/perfData.hpp @@ -424,6 +424,7 @@ class PerfLongVariant : public PerfLong { public: inline void inc() { (*(jlong*)_valuep)++; } inline void inc(jlong val) { (*(jlong*)_valuep) += val; } + inline void dec(jlong val) { inc(-val); } inline void add(jlong val) { (*(jlong*)_valuep) += val; } void clear_sample_helper() { _sample_helper = NULL; } }; diff --git a/hotspot/src/share/vm/runtime/sharedRuntime.cpp b/hotspot/src/share/vm/runtime/sharedRuntime.cpp index 2a78970f4b8..68440ee89cf 100644 --- a/hotspot/src/share/vm/runtime/sharedRuntime.cpp +++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp @@ -386,7 +386,7 @@ double SharedRuntime::dabs(double f) { #endif -#if defined(__SOFTFP__) || defined(PPC32) +#if defined(__SOFTFP__) || defined(PPC) double SharedRuntime::dsqrt(double f) { return sqrt(f); } diff --git a/hotspot/src/share/vm/runtime/sharedRuntime.hpp b/hotspot/src/share/vm/runtime/sharedRuntime.hpp index 1938c9dbb23..acf822f6eb1 100644 --- a/hotspot/src/share/vm/runtime/sharedRuntime.hpp +++ b/hotspot/src/share/vm/runtime/sharedRuntime.hpp @@ -141,7 +141,7 @@ class SharedRuntime: AllStatic { static double dabs(double f); #endif -#if defined(__SOFTFP__) || defined(PPC32) +#if defined(__SOFTFP__) || defined(PPC) static double dsqrt(double f); #endif diff --git a/hotspot/src/share/vm/runtime/sweeper.cpp b/hotspot/src/share/vm/runtime/sweeper.cpp index fad5edf0f9a..5a902266205 100644 --- a/hotspot/src/share/vm/runtime/sweeper.cpp +++ b/hotspot/src/share/vm/runtime/sweeper.cpp @@ -297,7 +297,7 @@ void NMethodSweeper::force_sweep() { void NMethodSweeper::handle_safepoint_request() { if (SafepointSynchronize::is_synchronizing()) { if (PrintMethodFlushing && Verbose) { - tty->print_cr("### Sweep at %d out of %d, yielding to safepoint", _seen, CodeCache::nof_nmethods()); + tty->print_cr("### Sweep at %d out of %d, yielding to safepoint", _seen, CodeCache::nmethod_count()); } MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); @@ -401,7 +401,7 @@ void NMethodSweeper::sweep_code_cache() { int flushed_c2_count = 0; if (PrintMethodFlushing && Verbose) { - tty->print_cr("### Sweep at %d out of %d", _seen, CodeCache::nof_nmethods()); + tty->print_cr("### Sweep at %d out of %d", _seen, CodeCache::nmethod_count()); } int swept_count = 0; diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp index de7c4139059..db0f5d9812c 100644 --- a/hotspot/src/share/vm/runtime/thread.cpp +++ b/hotspot/src/share/vm/runtime/thread.cpp @@ -1299,7 +1299,7 @@ void WatcherThread::run() { if (!ShowMessageBoxOnError && (OnError == NULL || OnError[0] == '\0') && Arguments::abort_hook() == NULL) { - os::sleep(this, ErrorLogTimeout * 60 * 1000, false); + os::sleep(this, (jlong)ErrorLogTimeout * 1000, false); // in seconds fdStream err(defaultStream::output_fd()); err.print_raw_cr("# [ timer expired, abort... ]"); // skip atexit/vm_exit/vm_abort hooks @@ -4022,13 +4022,6 @@ bool Threads::destroy_vm() { // will be stopped at native=>Java/VM barriers. Note that we can't // simply kill or suspend them, as it is inherently deadlock-prone. -#ifndef PRODUCT - // disable function tracing at JNI/JVM barriers - TraceJNICalls = false; - TraceJVMCalls = false; - TraceRuntimeCalls = false; -#endif - VM_Exit::set_vm_exited(); notify_vm_shutdown(); diff --git a/hotspot/src/share/vm/runtime/vframe.hpp b/hotspot/src/share/vm/runtime/vframe.hpp index 74c9b725811..654d6b823b1 100644 --- a/hotspot/src/share/vm/runtime/vframe.hpp +++ b/hotspot/src/share/vm/runtime/vframe.hpp @@ -317,10 +317,18 @@ class vframeStreamCommon : StackObj { intptr_t* frame_id() const { return _frame.id(); } address frame_pc() const { return _frame.pc(); } + javaVFrame* java_frame() { + vframe* vf = vframe::new_vframe(&_frame, &_reg_map, _thread); + if (vf->is_java_frame()) { + return (javaVFrame*)vf; + } + return NULL; + } + CodeBlob* cb() const { return _frame.cb(); } nmethod* nm() const { - assert( cb() != NULL && cb()->is_nmethod(), "usage"); - return (nmethod*) cb(); + assert( cb() != NULL && cb()->is_nmethod(), "usage"); + return (nmethod*) cb(); } // Frame type diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp index 784c8be3eb7..2818cc758e0 100644 --- a/hotspot/src/share/vm/runtime/vmStructs.cpp +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp @@ -343,10 +343,6 @@ typedef CompactHashtable SymbolCompactHashTable; nonstatic_field(InstanceKlass, _methods_jmethod_ids, jmethodID*) \ volatile_nonstatic_field(InstanceKlass, _idnum_allocated_count, u2) \ nonstatic_field(InstanceKlass, _annotations, Annotations*) \ - nonstatic_field(InstanceKlass, _dependencies, nmethodBucket*) \ - nonstatic_field(nmethodBucket, _nmethod, nmethod*) \ - nonstatic_field(nmethodBucket, _count, int) \ - nonstatic_field(nmethodBucket, _next, nmethodBucket*) \ nonstatic_field(InstanceKlass, _method_ordering, Array*) \ nonstatic_field(InstanceKlass, _default_vtable_indices, Array*) \ nonstatic_field(Klass, _super_check_offset, juint) \ @@ -969,6 +965,7 @@ typedef CompactHashtable SymbolCompactHashTable; nonstatic_field(Deoptimization::UnrollBlock, _caller_adjustment, int) \ nonstatic_field(Deoptimization::UnrollBlock, _number_of_frames, int) \ nonstatic_field(Deoptimization::UnrollBlock, _total_frame_sizes, int) \ + nonstatic_field(Deoptimization::UnrollBlock, _unpack_kind, int) \ nonstatic_field(Deoptimization::UnrollBlock, _frame_sizes, intptr_t*) \ nonstatic_field(Deoptimization::UnrollBlock, _frame_pcs, address*) \ nonstatic_field(Deoptimization::UnrollBlock, _register_block, intptr_t*) \ @@ -1378,10 +1375,6 @@ typedef CompactHashtable SymbolCompactHashTable; nonstatic_field(vframeArrayElement, _bci, int) \ nonstatic_field(vframeArrayElement, _method, Method*) \ \ - nonstatic_field(PtrQueue, _active, bool) \ - nonstatic_field(PtrQueue, _buf, void**) \ - nonstatic_field(PtrQueue, _index, size_t) \ - \ nonstatic_field(AccessFlags, _flags, jint) \ nonstatic_field(elapsedTimer, _counter, jlong) \ nonstatic_field(elapsedTimer, _active, bool) \ @@ -1554,7 +1547,6 @@ typedef CompactHashtable SymbolCompactHashTable; declare_toplevel_type(volatile Metadata*) \ \ declare_toplevel_type(DataLayout) \ - declare_toplevel_type(nmethodBucket) \ \ /********/ \ /* Oops */ \ @@ -2273,8 +2265,6 @@ typedef CompactHashtable SymbolCompactHashTable; /* Miscellaneous types */ \ /***************/ \ \ - declare_toplevel_type(PtrQueue) \ - \ /* freelist */ \ declare_toplevel_type(FreeChunk*) \ declare_toplevel_type(AdaptiveFreeList*) \ @@ -3066,6 +3056,9 @@ typedef CompactHashtable SymbolCompactHashTable; #define GENERATE_VM_INT_CONSTANT_ENTRY(name) \ { QUOTE(name), (int32_t) name }, +#define GENERATE_VM_INT_CONSTANT_WITH_VALUE_ENTRY(name, value) \ + { (name), (int32_t)(value) }, + #define GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY(name, value) \ { name, (int32_t) value }, @@ -3296,6 +3289,9 @@ VMIntConstantEntry VMStructs::localHotSpotVMIntConstants[] = { VM_INT_CONSTANTS_CMS(GENERATE_VM_INT_CONSTANT_ENTRY) VM_INT_CONSTANTS_PARNEW(GENERATE_VM_INT_CONSTANT_ENTRY) + + VM_INT_CONSTANTS_G1(GENERATE_VM_INT_CONSTANT_ENTRY, + GENERATE_VM_INT_CONSTANT_WITH_VALUE_ENTRY) #endif // INCLUDE_ALL_GCS #if INCLUDE_TRACE diff --git a/hotspot/src/share/vm/runtime/vm_operations.cpp b/hotspot/src/share/vm/runtime/vm_operations.cpp index ff402e1eb58..92e855b9e83 100644 --- a/hotspot/src/share/vm/runtime/vm_operations.cpp +++ b/hotspot/src/share/vm/runtime/vm_operations.cpp @@ -29,6 +29,7 @@ #include "code/codeCacheExtensions.hpp" #include "compiler/compileBroker.hpp" #include "gc/shared/isGCActiveMark.hpp" +#include "logging/log.hpp" #include "memory/heapInspection.hpp" #include "memory/resourceArea.hpp" #include "oops/symbol.hpp" @@ -55,13 +56,19 @@ void VM_Operation::set_calling_thread(Thread* thread, ThreadPriority priority) { void VM_Operation::evaluate() { ResourceMark rm; - if (TraceVMOperation) { - tty->print("["); - NOT_PRODUCT(print();) + outputStream* debugstream; + bool enabled = log_is_enabled(Debug, vmoperation); + if (enabled) { + debugstream = LogHandle(vmoperation)::debug_stream(); + debugstream->print("begin "); + print_on_error(debugstream); + debugstream->cr(); } doit(); - if (TraceVMOperation) { - tty->print_cr("]"); + if (enabled) { + debugstream->print("end "); + print_on_error(debugstream); + debugstream->cr(); } } diff --git a/hotspot/src/share/vm/shark/sharkRuntime.cpp b/hotspot/src/share/vm/shark/sharkRuntime.cpp index bf609bce455..c6fd028479b 100644 --- a/hotspot/src/share/vm/shark/sharkRuntime.cpp +++ b/hotspot/src/share/vm/shark/sharkRuntime.cpp @@ -213,8 +213,9 @@ int SharkRuntime::uncommon_trap(JavaThread* thread, int trap_request) { // Initiate the trap thread->set_last_Java_frame(); Deoptimization::UnrollBlock *urb = - Deoptimization::uncommon_trap(thread, trap_request); + Deoptimization::uncommon_trap(thread, trap_request, Deoptimization::Unpack_uncommon_trap); thread->reset_last_Java_frame(); + assert(urb->unpack_kind() == Deoptimization::Unpack_uncommon_trap, "expected Unpack_uncommon_trap"); // Pop our dummy frame and the frame being deoptimized thread->pop_zero_frame(); diff --git a/hotspot/src/share/vm/trace/trace.xml b/hotspot/src/share/vm/trace/trace.xml index 0329d9b725a..c1f4dfb88de 100644 --- a/hotspot/src/share/vm/trace/trace.xml +++ b/hotspot/src/share/vm/trace/trace.xml @@ -369,6 +369,32 @@ Declares a structure type that can be used in other events. + + + + + + + + + + + + + + + + + + + + + + + + +inline double percent_of(T numerator, T denominator) { + return denominator != 0 ? (double)numerator / denominator * 100.0 : 0.0; +} + //---------------------------------------------------------------------------------------------------- // Special casts // Cast floats into same-size integers and vice-versa w/o changing bit-pattern diff --git a/hotspot/src/share/vm/utilities/hashtable.cpp b/hotspot/src/share/vm/utilities/hashtable.cpp index d9044fb259c..b62c52b5e54 100644 --- a/hotspot/src/share/vm/utilities/hashtable.cpp +++ b/hotspot/src/share/vm/utilities/hashtable.cpp @@ -365,6 +365,7 @@ template class RehashableHashtable; template class RehashableHashtable; template class Hashtable; template class Hashtable; +template class Hashtable; template class Hashtable; #if defined(SOLARIS) || defined(CHECK_UNHANDLED_OOPS) template class Hashtable; @@ -378,6 +379,7 @@ template class HashtableEntry; template class BasicHashtableEntry; template class BasicHashtableEntry; template class BasicHashtable; +template class BasicHashtable; template class BasicHashtable; template class BasicHashtable; template class BasicHashtable; diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp index 6af19370b7c..c12e37526e5 100644 --- a/hotspot/src/share/vm/utilities/vmError.cpp +++ b/hotspot/src/share/vm/utilities/vmError.cpp @@ -1349,3 +1349,11 @@ void VMError::report_java_out_of_memory(const char* message) { VMThread::execute(&op); } } + +void VMError::show_message_box(char *buf, int buflen) { + bool yes; + do { + error_string(buf, buflen); + yes = os::start_debugging(buf,buflen); + } while (yes); +} diff --git a/hotspot/test/TEST.ROOT b/hotspot/test/TEST.ROOT index 46a0b4098a1..57f0e0ae061 100644 --- a/hotspot/test/TEST.ROOT +++ b/hotspot/test/TEST.ROOT @@ -30,7 +30,7 @@ keys=cte_test jcmd nmt regression gc stress groups=TEST.groups [closed/TEST.groups] -requires.properties=sun.arch.data.model +requires.properties=sun.arch.data.model java.version # Tests using jtreg 4.1 b12 features requiredVersion=4.1 b12 diff --git a/hotspot/test/compiler/arraycopy/TestArrayCopyNoInitDeopt.java b/hotspot/test/compiler/arraycopy/TestArrayCopyNoInitDeopt.java index c7caa21b237..dcfceb29185 100644 --- a/hotspot/test/compiler/arraycopy/TestArrayCopyNoInitDeopt.java +++ b/hotspot/test/compiler/arraycopy/TestArrayCopyNoInitDeopt.java @@ -25,7 +25,7 @@ * @test * @bug 8072016 * @summary Infinite deoptimization/recompilation cycles in case of arraycopy with tightly coupled allocation - * @library /testlibrary /test/lib /compiler/whitebox + * @library /testlibrary /test/lib /compiler/whitebox / * @modules java.base/sun.misc * java.management * @build TestArrayCopyNoInitDeopt @@ -42,6 +42,7 @@ import sun.hotspot.WhiteBox; import sun.hotspot.code.NMethod; import jdk.test.lib.Platform; import java.lang.reflect.*; +import compiler.whitebox.CompilerWhiteBoxTest; public class TestArrayCopyNoInitDeopt { diff --git a/hotspot/test/compiler/c2/8004741/Test8004741.java b/hotspot/test/compiler/c2/8004741/Test8004741.java index baacc34763d..7e64ff14d6e 100644 --- a/hotspot/test/compiler/c2/8004741/Test8004741.java +++ b/hotspot/test/compiler/c2/8004741/Test8004741.java @@ -25,8 +25,8 @@ * @test Test8004741.java * @bug 8004741 * @summary Missing compiled exception handle table entry for multidimensional array allocation - * @run main/othervm -Xmx64m -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -XX:+StressCompiledExceptionHandlers -XX:+SafepointALot -XX:GuaranteedSafepointInterval=100 Test8004741 - * @run main/othervm -Xmx64m -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -XX:+StressCompiledExceptionHandlers Test8004741 + * @run main/othervm -Xmx64m -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation -XX:+StressCompiledExceptionHandlers -XX:+SafepointALot -XX:GuaranteedSafepointInterval=100 Test8004741 + * @run main/othervm -Xmx64m -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation -XX:+StressCompiledExceptionHandlers Test8004741 */ import java.util.*; diff --git a/hotspot/test/compiler/compilercontrol/InlineMatcherTest.java b/hotspot/test/compiler/compilercontrol/InlineMatcherTest.java index 871f264baea..3dc057c448d 100644 --- a/hotspot/test/compiler/compilercontrol/InlineMatcherTest.java +++ b/hotspot/test/compiler/compilercontrol/InlineMatcherTest.java @@ -24,7 +24,8 @@ /* * @test InlineMatcherTest * @bug 8074095 - * @library /testlibrary /../../test/lib + * @library /testlibrary /test/lib + * @build sun.hotspot.WhiteBox * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI InlineMatcherTest diff --git a/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityBase.java b/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityBase.java index a058b2b65c1..9d01dac3243 100644 --- a/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityBase.java +++ b/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityBase.java @@ -30,7 +30,7 @@ * java.management * @build jdk.test.lib.* * @build jdk.test.lib.dcmd.* - * @build sun.hotspot.WhiteBox.* + * @build sun.hotspot.WhiteBox * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission * @run testng/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI TestCompilerDirectivesCompatibilityBase diff --git a/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityCommandOff.java b/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityCommandOff.java index 91329b1b29c..948b3249356 100644 --- a/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityCommandOff.java +++ b/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityCommandOff.java @@ -30,7 +30,7 @@ * java.management * @build jdk.test.lib.* * @build jdk.test.lib.dcmd.* - * @build sun.hotspot.WhiteBox.* + * @build sun.hotspot.WhiteBox * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission * @run testng/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions diff --git a/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityCommandOn.java b/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityCommandOn.java index bddbe6f4e3e..c2f10c8e8ef 100644 --- a/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityCommandOn.java +++ b/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityCommandOn.java @@ -30,7 +30,7 @@ * java.management * @build jdk.test.lib.* * @build jdk.test.lib.dcmd.* - * @build sun.hotspot.WhiteBox.* + * @build sun.hotspot.WhiteBox * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission * @run testng/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions diff --git a/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityFlag.java b/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityFlag.java index 9715b1e4662..2d6da4ad794 100644 --- a/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityFlag.java +++ b/hotspot/test/compiler/compilercontrol/TestCompilerDirectivesCompatibilityFlag.java @@ -30,7 +30,7 @@ * java.management * @build jdk.test.lib.* * @build jdk.test.lib.dcmd.* - * @build sun.hotspot.WhiteBox.* + * @build sun.hotspot.WhiteBox * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission * @run testng/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions diff --git a/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesFileStackTest.java b/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesFileStackTest.java index dc4546ddb95..aaccbc3b58e 100644 --- a/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesFileStackTest.java +++ b/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesFileStackTest.java @@ -26,7 +26,7 @@ * @bug 8137167 * @ignore 8140405 * @summary Tests jcmd to be able to clear directives added via options - * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / + * @library /testlibrary /test/lib /compiler/testlibrary ../share / * @build ClearDirectivesFileStackTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox @@ -73,9 +73,6 @@ public class ClearDirectivesFileStackTest extends AbstractTestBase { compileCommand.print(); builder.add(compileCommand); } - // print all directives before - builder.add(new JcmdCommand(Command.NONEXISTENT, null, null, - Scenario.Type.JCMD, Scenario.JcmdType.PRINT)); // clear the stack builder.add(new JcmdCommand(Command.NONEXISTENT, null, null, Scenario.Type.JCMD, Scenario.JcmdType.CLEAR)); diff --git a/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesStackTest.java b/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesStackTest.java index eac7b9a4786..0731e0ba8c2 100644 --- a/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesStackTest.java +++ b/hotspot/test/compiler/compilercontrol/jcmd/ClearDirectivesStackTest.java @@ -25,7 +25,7 @@ * @test * @bug 8137167 * @summary Tests clear JCMD command - * @library /testlibrary /../../test/lib /compiler/testlibrary ../share / + * @library /testlibrary /test/lib /compiler/testlibrary ../share / * @build ClearDirectivesStackTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* * @run main ClassFileInstaller sun.hotspot.WhiteBox @@ -68,9 +68,6 @@ public class ClearDirectivesStackTest extends AbstractTestBase { compileCommand.print(); builder.add(compileCommand); } - // print all directives before - builder.add(new JcmdCommand(Command.NONEXISTENT, null, null, - Scenario.Type.JCMD, Scenario.JcmdType.PRINT)); // clear the stack builder.add(new JcmdCommand(Command.NONEXISTENT, null, null, Scenario.Type.JCMD, Scenario.JcmdType.CLEAR)); diff --git a/hotspot/test/compiler/compilercontrol/jcmd/PrintDirectivesTest.java b/hotspot/test/compiler/compilercontrol/jcmd/PrintDirectivesTest.java new file mode 100644 index 00000000000..7e3225f6587 --- /dev/null +++ b/hotspot/test/compiler/compilercontrol/jcmd/PrintDirectivesTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8137167 + * @summary Tests jcmd to be able to add a directive to compile only specified methods + * @library /testlibrary /test/lib /compiler/testlibrary ../share / + * @build pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * compiler.testlibrary.CompilerUtils compiler.compilercontrol.share.actions.* + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm compiler.compilercontrol.jcmd.PrintDirectivesTest + */ + +package compiler.compilercontrol.jcmd; + +import compiler.compilercontrol.share.AbstractTestBase; +import compiler.compilercontrol.share.method.MethodDescriptor; +import compiler.compilercontrol.share.scenario.Command; +import compiler.compilercontrol.share.scenario.CommandGenerator; +import compiler.compilercontrol.share.scenario.CompileCommand; +import compiler.compilercontrol.share.scenario.JcmdCommand; +import compiler.compilercontrol.share.scenario.Scenario; +import jdk.test.lib.Utils; + +import java.lang.reflect.Executable; + +public class PrintDirectivesTest extends AbstractTestBase { + private static final int AMOUNT = Utils.getRandomInstance().nextInt( + Integer.getInteger("compiler.compilercontrol.jcmd." + + "PrintDirectivesTest.amount", 20)); + private final CommandGenerator cmdGen = new CommandGenerator(); + + public static void main(String[] args) { + new PrintDirectivesTest().test(); + } + + @Override + public void test() { + Scenario.Builder builder = Scenario.getBuilder(); + // Add some commands with directives file + for (int i = 0; i < AMOUNT; i++) { + Executable exec = Utils.getRandomElement(METHODS).first; + MethodDescriptor methodDescriptor = getValidMethodDescriptor(exec); + Command command = cmdGen.generateCommand(); + if (command == Command.NONEXISTENT) { + // skip invalid command + command = Command.COMPILEONLY; + } + CompileCommand compileCommand = new CompileCommand(command, + methodDescriptor, cmdGen.generateCompiler(), + Scenario.Type.DIRECTIVE); + compileCommand.print(); + builder.add(compileCommand); + } + // print all directives + builder.add(new JcmdCommand(Command.NONEXISTENT, null, null, + Scenario.Type.JCMD, Scenario.JcmdType.PRINT)); + Scenario scenario = builder.build(); + scenario.execute(); + } +} \ No newline at end of file diff --git a/hotspot/test/compiler/compilercontrol/jcmd/StressAddJcmdBase.java b/hotspot/test/compiler/compilercontrol/jcmd/StressAddJcmdBase.java new file mode 100644 index 00000000000..1da46dedf00 --- /dev/null +++ b/hotspot/test/compiler/compilercontrol/jcmd/StressAddJcmdBase.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2015, 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. + */ + +package compiler.compilercontrol.jcmd; + +import compiler.compilercontrol.parser.HugeDirectiveUtil; +import compiler.compilercontrol.share.AbstractTestBase; +import compiler.compilercontrol.share.method.MethodDescriptor; +import compiler.compilercontrol.share.scenario.Executor; +import jdk.test.lib.OutputAnalyzer; +import jdk.test.lib.TimeLimitedRunner; +import jdk.test.lib.Utils; +import pool.PoolHelper; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class StressAddJcmdBase { + private static final int DIRECTIVES_AMOUNT = Integer.getInteger( + "compiler.compilercontrol.jcmd.StressAddJcmdBase.directivesAmount", + 1000); + private static final int DIRECTIVE_FILES = Integer.getInteger( + "compiler.compilercontrol.jcmd.StressAddJcmdBase.directiveFiles", + 5); + private static final List DESCRIPTORS = new PoolHelper() + .getAllMethods().stream() + .map(pair -> AbstractTestBase + .getValidMethodDescriptor(pair.first)) + .collect(Collectors.toList()); + + /** + * Performs test + */ + public void test() { + List commands = prepareCommands(); + Executor executor = new TimeLimitedExecutor(commands); + List outputAnalyzers = executor.execute(); + outputAnalyzers.get(0).shouldHaveExitValue(0); + } + + /** + * Makes connection to the test VM + * + * @param pid a pid of the VM under test + * @param commands a list of jcmd commands to be executed + * @return true if the test should continue invocation of this method + */ + protected abstract boolean makeConnection(int pid, List commands); + + /** + * Finish test executions + */ + protected void finish() { } + + private List prepareCommands() { + String[] files = new String[DIRECTIVE_FILES]; + for (int i = 0; i < DIRECTIVE_FILES; i++) { + files[i] = "directives" + i + ".json"; + HugeDirectiveUtil.createHugeFile(DESCRIPTORS, files[i], + DIRECTIVES_AMOUNT); + } + return Stream.of(files) + .map(file -> "Compiler.directives_add " + file) + .collect(Collectors.toList()); + } + + private class TimeLimitedExecutor extends Executor { + private final List jcmdCommands; + + public TimeLimitedExecutor(List jcmdCommands) { + /* There are no need to check the state */ + super(true, null, null, jcmdCommands); + this.jcmdCommands = jcmdCommands; + } + + @Override + protected OutputAnalyzer[] executeJCMD(int pid) { + TimeLimitedRunner runner = new TimeLimitedRunner( + Utils.DEFAULT_TEST_TIMEOUT, + Utils.TIMEOUT_FACTOR, + () -> makeConnection(pid, jcmdCommands)); + try { + runner.call(); + } catch (Exception e) { + throw new Error("Exception during the execution: " + e, e); + } + finish(); + return new OutputAnalyzer[0]; + } + } +} diff --git a/hotspot/test/compiler/compilercontrol/jcmd/StressAddMultiThreadedTest.java b/hotspot/test/compiler/compilercontrol/jcmd/StressAddMultiThreadedTest.java new file mode 100644 index 00000000000..a955b47f4fa --- /dev/null +++ b/hotspot/test/compiler/compilercontrol/jcmd/StressAddMultiThreadedTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8137167 + * @summary Tests jcmd to be able to add a lot of huge directive files with + * parallel executed jcmds until timeout has reached + * @library /testlibrary /test/lib /compiler/testlibrary ../share / + * @build StressAddMultiThreadedTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * compiler.testlibrary.CompilerUtils + * compiler.compilercontrol.share.actions.* + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm/timeout=360 compiler.compilercontrol.jcmd.StressAddMultiThreadedTest + */ + +package compiler.compilercontrol.jcmd; + +import jdk.test.lib.dcmd.PidJcmdExecutor; + +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class StressAddMultiThreadedTest extends StressAddJcmdBase { + private static final int THREADS; + private final BlockingQueue queue; + private final ExecutorService executor; + + static { + THREADS = Runtime.getRuntime().availableProcessors() + * Integer.getInteger("compiler.compilercontrol.jcmd" + + ".StressAddMultiThreadedTest.threadFactor", 10); + } + + public StressAddMultiThreadedTest() { + queue = new ArrayBlockingQueue<>(THREADS); + executor = new ThreadPoolExecutor(THREADS, THREADS, 100, + TimeUnit.MILLISECONDS, queue, + new ThreadPoolExecutor.CallerRunsPolicy()); + } + + public static void main(String[] args) { + new StressAddMultiThreadedTest().test(); + } + + @Override + protected boolean makeConnection(int pid, List commands) { + commands.forEach(command -> { + if (!executor.isShutdown()) { + executor.submit(() -> new PidJcmdExecutor(String.valueOf(pid)) + .execute(command)); + } + }); + return !executor.isShutdown(); + } + + @Override + protected void finish() { + executor.shutdown(); + try { + executor.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new Error("Interrupted while awaiting for termination: " + e, + e); + } + executor.shutdownNow(); + } +} diff --git a/hotspot/test/compiler/compilercontrol/jcmd/StressAddSequentiallyTest.java b/hotspot/test/compiler/compilercontrol/jcmd/StressAddSequentiallyTest.java new file mode 100644 index 00000000000..071ecd3ddf4 --- /dev/null +++ b/hotspot/test/compiler/compilercontrol/jcmd/StressAddSequentiallyTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8137167 + * @summary Tests jcmd to be able to add a lot of huge directives + * @library /testlibrary /test/lib /compiler/testlibrary ../share / + * @build StressAddSequentiallyTest pool.sub.* pool.subpack.* sun.hotspot.WhiteBox + * compiler.testlibrary.CompilerUtils + * compiler.compilercontrol.share.actions.* + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm/timeout=300 compiler.compilercontrol.jcmd.StressAddSequentiallyTest + */ + +package compiler.compilercontrol.jcmd; + +import jdk.test.lib.dcmd.PidJcmdExecutor; + +import java.util.List; + +public class StressAddSequentiallyTest extends StressAddJcmdBase { + public static void main(String[] args) { + new StressAddSequentiallyTest().test(); + } + + @Override + protected boolean makeConnection(int pid, List commands) { + commands.forEach(command -> new PidJcmdExecutor(String.valueOf(pid)) + .execute(command)); + return true; + } +} diff --git a/hotspot/test/compiler/compilercontrol/parser/DirectiveParser.java b/hotspot/test/compiler/compilercontrol/parser/DirectiveParserTest.java similarity index 84% rename from hotspot/test/compiler/compilercontrol/parser/DirectiveParser.java rename to hotspot/test/compiler/compilercontrol/parser/DirectiveParserTest.java index 764e9ec708d..f0fc0bfe632 100644 --- a/hotspot/test/compiler/compilercontrol/parser/DirectiveParser.java +++ b/hotspot/test/compiler/compilercontrol/parser/DirectiveParserTest.java @@ -25,8 +25,8 @@ * @test * @bug 8137167 * @summary Tests directive json parser - * @library /testlibrary /../../test/lib ../share / - * @run driver compiler.compilercontrol.parser.DirectiveParser + * @library /testlibrary /test/lib ../share / + * @run driver compiler.compilercontrol.parser.DirectiveParserTest */ package compiler.compilercontrol.parser; @@ -37,7 +37,7 @@ import jdk.test.lib.OutputAnalyzer; import jdk.test.lib.ProcessTools; import jdk.test.lib.Utils; -public class DirectiveParser { +public class DirectiveParserTest { private static final String ERROR_MSG = "VM should exit with error " + "on incorrect JSON file: "; private static final String EXPECTED_ERROR_STRING = "Parsing of compiler" @@ -80,7 +80,7 @@ public class DirectiveParser { .end(); // end object file.end(); } - OutputAnalyzer output = execute(fileName); + OutputAnalyzer output = HugeDirectiveUtil.execute(fileName); output.shouldHaveExitValue(0); output.shouldNotContain(EXPECTED_ERROR_STRING); } @@ -95,7 +95,7 @@ public class DirectiveParser { .end(); // don't write matching } } - OutputAnalyzer output = execute(fileName); + OutputAnalyzer output = HugeDirectiveUtil.execute(fileName); Asserts.assertNE(output.getExitValue(), 0, ERROR_MSG + "non matching " + "brackets"); output.shouldContain(EXPECTED_ERROR_STRING); @@ -107,7 +107,7 @@ public class DirectiveParser { file.write(JSONFile.Element.ARRAY); file.end(); } - OutputAnalyzer output = execute(fileName); + OutputAnalyzer output = HugeDirectiveUtil.execute(fileName); Asserts.assertNE(output.getExitValue(), 0, ERROR_MSG + "empty array"); } @@ -117,7 +117,7 @@ public class DirectiveParser { file.write(JSONFile.Element.OBJECT); file.end(); } - OutputAnalyzer output = execute(fileName); + OutputAnalyzer output = HugeDirectiveUtil.execute(fileName); Asserts.assertNE(output.getExitValue(), 0, ERROR_MSG + "empty object " + "without any match"); output.shouldContain(EXPECTED_ERROR_STRING); @@ -128,33 +128,20 @@ public class DirectiveParser { try (JSONFile file = new JSONFile(fileName)) { // empty } - OutputAnalyzer output = execute(fileName); + OutputAnalyzer output = HugeDirectiveUtil.execute(fileName); Asserts.assertNE(output.getExitValue(), 0, ERROR_MSG + "empty file"); output.shouldContain(EXPECTED_ERROR_STRING); } private static void noFile() { - OutputAnalyzer output = execute("nonexistent.json"); + OutputAnalyzer output = HugeDirectiveUtil.execute("nonexistent.json"); Asserts.assertNE(output.getExitValue(), 0, ERROR_MSG + "non existing " + "file"); } private static void directory() { - OutputAnalyzer output = execute(Utils.TEST_SRC); + OutputAnalyzer output = HugeDirectiveUtil.execute(Utils.TEST_SRC); Asserts.assertNE(output.getExitValue(), 0, ERROR_MSG + "directory as " + "a name"); } - - private static OutputAnalyzer execute(String fileName) { - OutputAnalyzer output; - try { - output = ProcessTools.executeTestJvm( - "-XX:+UnlockDiagnosticVMOptions", - "-XX:CompilerDirectivesFile=" + fileName, - "-version"); - } catch (Throwable thr) { - throw new Error("Execution failed", thr); - } - return output; - } } diff --git a/hotspot/test/compiler/compilercontrol/parser/DirectiveStressTest.java b/hotspot/test/compiler/compilercontrol/parser/DirectiveStressTest.java new file mode 100644 index 00000000000..8e23273e643 --- /dev/null +++ b/hotspot/test/compiler/compilercontrol/parser/DirectiveStressTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8137167 + * @summary Stress directive json parser + * @library /testlibrary /test/lib ../share / + * @run driver compiler.compilercontrol.parser.DirectiveStressTest + */ + +package compiler.compilercontrol.parser; + +import compiler.compilercontrol.share.AbstractTestBase; +import compiler.compilercontrol.share.JSONFile; +import compiler.compilercontrol.share.method.MethodDescriptor; +import compiler.compilercontrol.share.scenario.DirectiveWriter; +import jdk.test.lib.OutputAnalyzer; +import pool.PoolHelper; + +import java.util.List; +import java.util.stream.Collectors; + +public class DirectiveStressTest { + private static final int AMOUNT = Integer.getInteger( + "compiler.compilercontrol.parser.DirectiveStressTest.amount", + Short.MAX_VALUE * 2 + 2); + private static final List DESCRIPTORS + = new PoolHelper().getAllMethods().stream() + .map(pair -> AbstractTestBase.getValidMethodDescriptor( + pair.first)) + .collect(Collectors.toList()); + private static final String EXPECTED_MESSAGE = " compiler directives added"; + + public static void main(String[] args) { + hugeFileTest(); + hugeObjectTest(); + } + + /* + * Creates file with AMOUNT of options in match block + */ + private static void hugeObjectTest() { + String fileName = "hugeObject.json"; + try (DirectiveWriter file = new DirectiveWriter(fileName)) { + file.write(JSONFile.Element.ARRAY); + HugeDirectiveUtil.createMatchObject(DESCRIPTORS, file, AMOUNT); + file.end(); // end array block + } + OutputAnalyzer output = HugeDirectiveUtil.execute(fileName); + output.shouldHaveExitValue(0); + output.shouldContain(1 + EXPECTED_MESSAGE); + output.shouldNotContain(HugeDirectiveUtil.EXPECTED_ERROR_STRING); + } + + /* + * Creates huge valid file with AMOUNT of match directives + */ + private static void hugeFileTest() { + String fileName = "hugeFile.json"; + HugeDirectiveUtil.createHugeFile(DESCRIPTORS, fileName, AMOUNT); + OutputAnalyzer output = HugeDirectiveUtil.execute(fileName); + output.shouldHaveExitValue(0); + output.shouldContain(AMOUNT + EXPECTED_MESSAGE); + output.shouldNotContain(HugeDirectiveUtil.EXPECTED_ERROR_STRING); + } +} diff --git a/hotspot/test/compiler/compilercontrol/parser/HugeDirectiveUtil.java b/hotspot/test/compiler/compilercontrol/parser/HugeDirectiveUtil.java new file mode 100644 index 00000000000..d5100ccc33d --- /dev/null +++ b/hotspot/test/compiler/compilercontrol/parser/HugeDirectiveUtil.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2015, 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. + */ + +package compiler.compilercontrol.parser; + +import compiler.compilercontrol.share.JSONFile; +import compiler.compilercontrol.share.method.MethodDescriptor; +import compiler.compilercontrol.share.scenario.DirectiveWriter; +import compiler.compilercontrol.share.scenario.Scenario; +import jdk.test.lib.OutputAnalyzer; +import jdk.test.lib.ProcessTools; +import jdk.test.lib.Utils; + +import java.util.EnumSet; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +/** + * Creates a huge directive file + */ +public final class HugeDirectiveUtil { + private static final Random RANDOM = Utils.getRandomInstance(); + protected static final String EXPECTED_ERROR_STRING = "Parsing of compiler " + + "directives failed"; + + private HugeDirectiveUtil() { } + + /** + * Creates huge file with specified amount of directives + * + * @param descriptors a list of descriptors to be randomly used + * in match and inline blocks + * @param fileName a directives file name to be created + * @param amount an amount of match objects + */ + public static void createHugeFile(List descriptors, + String fileName, int amount) { + try (DirectiveWriter file = new DirectiveWriter(fileName)) { + file.write(JSONFile.Element.ARRAY); + for (int i = 0; i < amount; i++) { + createMatchObject(descriptors, file, 1); + } + file.end(); + } + } + + /** + * Creates match object in the given file with specified size + * + * @param descriptors a list of method descriptors to be used + * @param file a directive file to write at + * @param objectSize a size of the match object + */ + public static void createMatchObject(List descriptors, + DirectiveWriter file, int objectSize) { + // get random amount of methods for the match + List methods = getRandomDescriptors(descriptors); + file.match(methods.toArray(new String[methods.size()])); + for (int i = 0; i < objectSize; i++) { + // emit compiler block + file.emitCompiler(Utils.getRandomElement( + Scenario.Compiler.values())); + // add option inside the compiler block + file.option(Utils.getRandomElement(DirectiveWriter.Option.values()), + RANDOM.nextBoolean()); + file.end(); // ends compiler block + + // add standalone option, enable can't be used standalone + EnumSet options = EnumSet.complementOf( + EnumSet.of(DirectiveWriter.Option.ENABLE)); + file.option(Utils.getRandomElement(options), RANDOM.nextBoolean()); + } + // add inline block with random inlinees + methods = getRandomDescriptors(descriptors).stream() + .map(s -> (RANDOM.nextBoolean() ? "+" : "-") + s) + .collect(Collectors.toList()); + file.inline(methods); + + // end match block + file.end(); + } + + private static List getRandomDescriptors( + List descriptors) { + int amount = 1 + RANDOM.nextInt(descriptors.size() - 1); + int skipAmount = RANDOM.nextInt(descriptors.size() - amount); + return descriptors.stream() + .skip(skipAmount) + .limit(amount) + .map(MethodDescriptor::getString) + .collect(Collectors.toList()); + } + + protected static OutputAnalyzer execute(String fileName) { + OutputAnalyzer output; + try { + output = ProcessTools.executeTestJvm( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:CompilerDirectivesFile=" + fileName, + "-version"); + } catch (Throwable thr) { + throw new Error("Execution failed with: " + thr, thr); + } + return output; + } +} diff --git a/hotspot/test/compiler/compilercontrol/share/actions/BaseAction.java b/hotspot/test/compiler/compilercontrol/share/actions/BaseAction.java index 911c000e7f0..15cb5604fb5 100644 --- a/hotspot/test/compiler/compilercontrol/share/actions/BaseAction.java +++ b/hotspot/test/compiler/compilercontrol/share/actions/BaseAction.java @@ -56,10 +56,27 @@ public class BaseAction { } public static void main(String[] args) { + new BaseAction().communicate(args); + } + + /* + * args[0] is a port to connect + * args[1] is an optional parameter that shows that the state map should be + * passed + */ + protected void communicate(String[] args) { if (args.length < 1) { throw new Error("TESTBUG: requires port as parameter: " + Arrays.toString(args)); } + boolean getStates = false; + if (args.length == 2) { + if ("states".equals(args[1])) { + getStates = true; + } else { + throw new Error("TESTBUG: incorrect argument: "+ args[1]); + } + } int pid; try { pid = ProcessTools.getProcessId(); @@ -78,14 +95,18 @@ public class BaseAction { // send own pid to execute jcmd if needed out.println(String.valueOf(pid)); out.flush(); - lines = in.lines().collect(Collectors.toList()); + if (getStates) { + lines = in.lines().collect(Collectors.toList()); + check(decodeMap(lines)); + } else { + in.readLine(); + } } catch (IOException e) { throw new Error("Error on performing network operation", e); } - check(decodeMap(lines)); } - private static Map decodeMap(List lines) { + private Map decodeMap(List lines) { if (lines == null || lines.size() == 0) { throw new Error("TESTBUG: unexpected lines list"); } @@ -113,7 +134,7 @@ public class BaseAction { return stateMap; } - protected static void check(Map methodStates) { + protected void check(Map methodStates) { // Check each method from the pool METHODS.forEach(pair -> { Executable x = pair.first; diff --git a/hotspot/test/compiler/compilercontrol/share/processors/CommandProcessor.java b/hotspot/test/compiler/compilercontrol/share/processors/CommandProcessor.java index b7926874346..6d28e1eaed3 100644 --- a/hotspot/test/compiler/compilercontrol/share/processors/CommandProcessor.java +++ b/hotspot/test/compiler/compilercontrol/share/processors/CommandProcessor.java @@ -24,8 +24,10 @@ package compiler.compilercontrol.share.processors; import compiler.compilercontrol.share.scenario.CompileCommand; +import jdk.test.lib.Asserts; import jdk.test.lib.OutputAnalyzer; +import java.util.Iterator; import java.util.List; import java.util.function.Consumer; @@ -33,26 +35,56 @@ import java.util.function.Consumer; * Checks that output contains a string with commands and full method pattern */ public class CommandProcessor implements Consumer { - protected final List commands; + private static final String INVALID_COMMAND_MSG = "CompileCommand: " + + "\\b(unrecognized command|Bad pattern|" + + "An error occurred during parsing)\\b"; + private final Iterator nonQuietedIterator; + private final Iterator quietedIterator; - public CommandProcessor(List commands) { - this.commands = commands; + public CommandProcessor(List nonQuieted, + List quieted) { + this.nonQuietedIterator = nonQuieted.iterator(); + this.quietedIterator = quieted.iterator(); } @Override public void accept(OutputAnalyzer outputAnalyzer) { - for (CompileCommand command : commands) { + try { + outputAnalyzer.asLines().stream() + .filter(s -> s.startsWith("CompileCommand:")) + .forEachOrdered(this::check); + } catch (Exception e) { + System.err.println(outputAnalyzer.getOutput()); + throw e; + } + } + + private void check(String input) { + if (nonQuietedIterator.hasNext()) { + CompileCommand command = nonQuietedIterator.next(); if (command.isValid()) { - outputAnalyzer.shouldContain("CompileCommand: " - + command.command.name + " " - + command.methodDescriptor.getCanonicalString()); - outputAnalyzer.shouldNotContain("CompileCommand: An error " - + "occurred during parsing"); + Asserts.assertTrue(input.contains(getOutputString(command)), + getOutputString(command) + "missing in output"); } else { - outputAnalyzer.shouldMatch("(CompileCommand: )" - + "(unrecognized command)|(Bad pattern)|" - + "(An error occurred during parsing)"); + Asserts.assertTrue(input.matches(INVALID_COMMAND_MSG), + "Error message missing for: " + getOutputString( + command)); + } + } else if (quietedIterator.hasNext()) { + CompileCommand command = quietedIterator.next(); + if (command.isValid()) { + Asserts.assertFalse(input.contains(getOutputString(command))); + } else { + Asserts.assertTrue(input.matches(INVALID_COMMAND_MSG), + "Error message missing for: " + getOutputString( + command)); } } } + + private String getOutputString(CompileCommand command) { + return "CompileCommand: " + + command.command.name + " " + + command.methodDescriptor.getCanonicalString(); + } } diff --git a/hotspot/test/compiler/compilercontrol/share/processors/PrintDirectivesProcessor.java b/hotspot/test/compiler/compilercontrol/share/processors/PrintDirectivesProcessor.java new file mode 100644 index 00000000000..6abc192c435 --- /dev/null +++ b/hotspot/test/compiler/compilercontrol/share/processors/PrintDirectivesProcessor.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2015, 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. + */ + +package compiler.compilercontrol.share.processors; + +import compiler.compilercontrol.share.method.MethodDescriptor; +import compiler.compilercontrol.share.scenario.CompileCommand; +import jdk.test.lib.Asserts; +import jdk.test.lib.OutputAnalyzer; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class PrintDirectivesProcessor + implements Consumer> { + private final List commands; + private static final Pattern MATCH_PATTERN + = Pattern.compile(" matching: (.*)"); + + public PrintDirectivesProcessor(List commands) { + this.commands = commands; + } + + @Override + public void accept(List outputAnalyzers) { + List directives = new ArrayList<>(); + outputAnalyzers.forEach(outputAnalyzer -> + directives.addAll(getDirectives(outputAnalyzer))); + List expectedDirectives = commands.stream() + .map(cc -> cc.methodDescriptor) + .map(MethodDescriptor::getCanonicalString) + .collect(Collectors.toList()); + + if (directives.size() != expectedDirectives.size()) { + printDirectives(directives, expectedDirectives); + throw new AssertionError(String.format("Different number of " + + "directives. Expected: %d, actual: %d", + expectedDirectives.size(), directives.size())); + } + for (int i = 0; i < directives.size(); i++) { + if (!directives.get(i).equals(expectedDirectives.get(i))) { + printDirectives(directives, expectedDirectives); + throw new AssertionError( + String.format("Directives differ at %d, expected:%s%n", + i, expectedDirectives.get(i))); + } + } + } + + private List getDirectives(OutputAnalyzer outputAnalyzer) { + List directives = new ArrayList<>(); + List inputStrings = outputAnalyzer.asLines(); + Iterator iterator = inputStrings.iterator(); + while (iterator.hasNext()) { + String input = iterator.next(); + if (input.equals("Directive:")) { + Asserts.assertTrue(iterator.hasNext(), "inconsistent directive" + + "printed into the output"); + String matchString = iterator.next(); + Matcher matcher = MATCH_PATTERN.matcher(matchString); + Asserts.assertTrue(matcher.matches(), "Incorrect matching " + + "string in directive"); + directives.add(matcher.group(1)); + } + } + return directives; + } + + private void printDirectives(List directives, + List expected) { + System.err.println("Actual directives: " + directives); + System.err.println("Expected directives: " + expected); + } +} diff --git a/hotspot/test/compiler/compilercontrol/share/scenario/DirectiveBuilder.java b/hotspot/test/compiler/compilercontrol/share/scenario/DirectiveBuilder.java index fc9990f9c46..af6c86dc3f2 100644 --- a/hotspot/test/compiler/compilercontrol/share/scenario/DirectiveBuilder.java +++ b/hotspot/test/compiler/compilercontrol/share/scenario/DirectiveBuilder.java @@ -36,6 +36,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.concurrent.Callable; +import java.util.stream.Collectors; /** * Directive file and state builder class @@ -45,9 +46,9 @@ public class DirectiveBuilder implements StateBuilder { = new PoolHelper().getAllMethods(); private final Map stateMap = new HashMap<>(); private final String fileName; - private Map> matchBlocks + private final Map> matchBlocks = new LinkedHashMap<>(); - private List inlineMatch = new ArrayList<>(); + private final List inlines = new ArrayList<>(); private boolean isFileValid = true; public DirectiveBuilder(String fileName) { @@ -66,7 +67,10 @@ public class DirectiveBuilder implements StateBuilder { @Override public List getCompileCommands() { - throw new Error("TESTBUG: isn't applicable for directives"); + return matchBlocks.keySet().stream() + // only method descriptor is required to check print_directives + .map(md -> new CompileCommand(null, md, null, null)) + .collect(Collectors.toList()); } @Override @@ -77,20 +81,36 @@ public class DirectiveBuilder implements StateBuilder { @Override public Map getStates() { + writeDirectiveFile(); + if (isFileValid) { + // Build states for each method according to match blocks + for (Pair> pair : METHODS) { + State state = getState(pair); + if (state != null) { + stateMap.put(pair.first, state); + } + } + return stateMap; + } else { + // return empty map because invalid file doesn't change states + return new HashMap<>(); + } + } + + private void writeDirectiveFile() { try (DirectiveWriter dirFile = new DirectiveWriter(fileName)) { for (MethodDescriptor matchDescriptor : matchBlocks.keySet()) { // Write match block with all options converted from commands dirFile.match(matchDescriptor); for (CompileCommand compileCommand : matchBlocks.get(matchDescriptor)) { - isFileValid &= compileCommand.isValid(); handleCommand(dirFile, compileCommand); } - if ("Inlinee.caller".matches((matchDescriptor.getRegexp()))) { + if ("Inlinee.caller()".matches(matchDescriptor.getRegexp()) + && !inlines.isEmpty()) { // Got a *.* match block, where inline would be written - dirFile.inline(inlineMatch.toArray( - new String[inlineMatch.size()])); - inlineMatch.clear(); + writeInlines(dirFile); + inlines.clear(); } dirFile.end(); // ends match block } @@ -100,12 +120,12 @@ public class DirectiveBuilder implements StateBuilder { * if we didn't do this before * Inlinee caller methods should match this block only */ - if (!inlineMatch.isEmpty()) { + if (!inlines.isEmpty()) { Pair> pair = METHODS.get(0); MethodDescriptor md = MethodGenerator.anyMatchDescriptor( pair.first); - CompileCommand cc = new CompileCommand(Command.QUIET, md, null, - Scenario.Type.DIRECTIVE); + CompileCommand cc = new CompileCommand(Command.QUIET, md, + null, Scenario.Type.DIRECTIVE); List commands = new ArrayList<>(); // Add appropriate "*.*" match block @@ -113,8 +133,7 @@ public class DirectiveBuilder implements StateBuilder { matchBlocks.put(md, commands); // Add match block for this descriptor with inlines dirFile.match(md); - dirFile.inline(inlineMatch.toArray( - new String[inlineMatch.size()])); + writeInlines(dirFile); dirFile.end(); } if (!matchBlocks.isEmpty()) { @@ -122,19 +141,6 @@ public class DirectiveBuilder implements StateBuilder { dirFile.end(); } - // Build states for each method according to match blocks - for (Pair> pair : METHODS) { - State state = getState(pair); - if (state != null) { - stateMap.put(pair.first, state); - } - } - } - if (isFileValid) { - return stateMap; - } else { - // return empty map because invalid file doesn't change states - return new HashMap<>(); } } @@ -154,7 +160,9 @@ public class DirectiveBuilder implements StateBuilder { * then apply commands from this match to the state */ for (CompileCommand cc : matchBlocks.get(matchDesc)) { - state = new State(); + if (state == null) { + state = new State(); + } if (!isMatchFound) { // this is a first found match, apply all commands state.apply(cc); @@ -184,16 +192,23 @@ public class DirectiveBuilder implements StateBuilder { case EXCLUDE: dirFile.excludeCompile(cmd.compiler, true); break; + case QUIET: + /* there are no appropriate directive for this, just make + match be enabled */ case INLINE: case DONTINLINE: - // Inline commands will be written later + /* Inline commands will be written later. + Just make this match be enabled */ + dirFile.emitCompiler(Scenario.Compiler.C1); + dirFile.option(DirectiveWriter.Option.ENABLE, true); + dirFile.end(); + dirFile.emitCompiler(Scenario.Compiler.C2); + dirFile.option(DirectiveWriter.Option.ENABLE, true); + dirFile.end(); break; case LOG: dirFile.option(DirectiveWriter.Option.LOG, true); break; - case QUIET: - // there are no appropriate directive for this - break; case PRINT: dirFile.option(DirectiveWriter.Option.PRINT_ASSEMBLY, true); break; @@ -210,16 +225,59 @@ public class DirectiveBuilder implements StateBuilder { } } + private void writeInlines(DirectiveWriter dirFile) { + List c1Block = new ArrayList<>(); + List c2Block = new ArrayList<>(); + List allBlock = new ArrayList<>(); + for (CompileCommand cc : inlines) { + String inlineMethodPattern; + switch (cc.command) { + case INLINE: + inlineMethodPattern = "+" + cc.methodDescriptor.getString(); + break; + case DONTINLINE: + inlineMethodPattern = "-" + cc.methodDescriptor.getString(); + break; + default: + throw new Error("TESTBUG: incorrect command got in " + + "the list: " + cc.command); + } + if (cc.compiler == Scenario.Compiler.C1) { + c1Block.add(inlineMethodPattern); + } else if (cc.compiler == Scenario.Compiler.C2) { + c2Block.add(inlineMethodPattern); + } else { + allBlock.add(inlineMethodPattern); + } + } + dirFile.emitCompiler(Scenario.Compiler.C1); + if (!c1Block.isEmpty()) { + dirFile.inline(c1Block); + } else { + dirFile.option(DirectiveWriter.Option.ENABLE, true); + } + dirFile.end(); + dirFile.emitCompiler(Scenario.Compiler.C2); + if (!c2Block.isEmpty()) { + dirFile.inline(c2Block); + } else { + dirFile.option(DirectiveWriter.Option.ENABLE, true); + } + dirFile.end(); + if (!allBlock.isEmpty()) { + dirFile.inline(allBlock); + } + } + @Override public void add(CompileCommand compileCommand) { + isFileValid &= compileCommand.isValid(); MethodDescriptor methodDescriptor = compileCommand.methodDescriptor; switch (compileCommand.command) { case INLINE: - inlineMatch.add("+" + methodDescriptor.getString()); - break; case DONTINLINE: - inlineMatch.add("-" + methodDescriptor.getString()); + inlines.add(compileCommand); break; } for (MethodDescriptor md: matchBlocks.keySet()) { diff --git a/hotspot/test/compiler/compilercontrol/share/scenario/DirectiveWriter.java b/hotspot/test/compiler/compilercontrol/share/scenario/DirectiveWriter.java index bd78eade820..414771a8edf 100644 --- a/hotspot/test/compiler/compilercontrol/share/scenario/DirectiveWriter.java +++ b/hotspot/test/compiler/compilercontrol/share/scenario/DirectiveWriter.java @@ -26,6 +26,8 @@ package compiler.compilercontrol.share.scenario; import compiler.compilercontrol.share.JSONFile; import compiler.compilercontrol.share.method.MethodDescriptor; +import java.util.List; + /** * Simple directive file writer. */ @@ -86,6 +88,20 @@ public class DirectiveWriter implements AutoCloseable { return this; } + /** + * Emits inline block with a given methods to be inlined or not. + * Each method should be prepended with + or - to show if it should be + * inlined or not. + * + * @param methods methods used for the inline + * @return this DirectiveWriter instance + */ + public DirectiveWriter inline(List methods) { + write(JSONFile.Element.PAIR, "inline"); + writeMethods(methods.toArray(new String[methods.size()])); + return this; + } + private void writeMethods(String[] methods) { if (methods.length == 0) { throw new IllegalArgumentException("ERROR: empty methods array"); @@ -111,16 +127,15 @@ public class DirectiveWriter implements AutoCloseable { */ public DirectiveWriter excludeCompile(Scenario.Compiler compiler, boolean exclude) { - if (compiler != null) { - emitCompiler(compiler); - option(Option.EXCLUDE, exclude); - end(); - } else { - for (Scenario.Compiler comp : Scenario.Compiler.values()) { - emitCompiler(comp); + for (Scenario.Compiler comp : Scenario.Compiler.values()) { + emitCompiler(comp); + if (comp == compiler || compiler == null) { option(Option.EXCLUDE, exclude); - end(); // end compiler block + } else { + // just make this block be enabled + option(Option.ENABLE, true); } + end(); // end compiler block } return this; } @@ -176,7 +191,8 @@ public class DirectiveWriter implements AutoCloseable { public enum Option { PRINT_ASSEMBLY("PrintAssembly"), LOG("Log"), - EXCLUDE("Exclude"); + EXCLUDE("Exclude"), + ENABLE("Enable"); public final String string; diff --git a/hotspot/test/compiler/compilercontrol/share/scenario/Executor.java b/hotspot/test/compiler/compilercontrol/share/scenario/Executor.java new file mode 100644 index 00000000000..bd387d9829e --- /dev/null +++ b/hotspot/test/compiler/compilercontrol/share/scenario/Executor.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2015, 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. + */ + +package compiler.compilercontrol.share.scenario; + +import compiler.compilercontrol.share.actions.BaseAction; +import jdk.test.lib.Asserts; +import jdk.test.lib.OutputAnalyzer; +import jdk.test.lib.ProcessTools; +import jdk.test.lib.dcmd.CommandExecutor; +import jdk.test.lib.dcmd.PidJcmdExecutor; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.lang.reflect.Executable; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class Executor { + private final boolean isValid; + private final List vmOptions; + private final Map states; + private final List jcmdCommands; + private final String execClass = System.getProperty("compiler." + + "compilercontrol.share.executor.executeClass", + BaseAction.class.getName()); + private OutputAnalyzer[] jcmdOutputAnalyzers; + + /** + * Constructor + * + * @param isValid shows that the input given to the VM is valid and + * VM shouldn't fail + * @param vmOptions a list of VM input options + * @param states a state map, or null for the non-checking execution + * @param jcmdCommands a list of diagnostic commands to be preformed + * on test VM + */ + public Executor(boolean isValid, List vmOptions, + Map states, List jcmdCommands) { + this.isValid = isValid; + if (vmOptions == null) { + this.vmOptions = new ArrayList<>(); + } else { + this.vmOptions = vmOptions; + } + this.states = states; + this.jcmdCommands = jcmdCommands; + } + + /** + * Executes separate VM a gets an OutputAnalyzer instance with the results + * of execution + */ + public List execute() { + // Add class name that would be executed in a separate VM + vmOptions.add(execClass); + OutputAnalyzer output; + try (ServerSocket serverSocket = new ServerSocket(0)) { + if (isValid) { + // Get port test VM will connect to + int port = serverSocket.getLocalPort(); + if (port == -1) { + throw new Error("Socket is not bound: " + port); + } + vmOptions.add(String.valueOf(port)); + if (states != null) { + // add flag to show that there should be state map passed + vmOptions.add("states"); + } + // Start separate thread to connect with test VM + new Thread(() -> connectTestVM(serverSocket)).start(); + } + // Start test VM + output = ProcessTools.executeTestJvmAllArgs( + vmOptions.toArray(new String[vmOptions.size()])); + } catch (Throwable thr) { + throw new Error("Execution failed: " + thr.getMessage(), thr); + } + + List outputList = new ArrayList<>(); + outputList.add(output); + if (jcmdOutputAnalyzers != null) { + Collections.addAll(outputList, jcmdOutputAnalyzers); + } + return outputList; + } + + /* + * Performs connection with a test VM, sends method states and performs + * JCMD operations on a test VM. + */ + private void connectTestVM(ServerSocket serverSocket) { + /* + * There are no way to prove that accept was invoked before we started + * test VM that connects to this serverSocket. Connection timeout is + * enough + */ + try ( + Socket socket = serverSocket.accept(); + PrintWriter pw = new PrintWriter(socket.getOutputStream(), + true); + BufferedReader in = new BufferedReader(new InputStreamReader( + socket.getInputStream()))) { + // Get pid of the executed process + int pid = Integer.parseInt(in.readLine()); + Asserts.assertNE(pid, 0, "Got incorrect pid"); + jcmdOutputAnalyzers = executeJCMD(pid); + if (states != null) { + // serialize and send state map + states.forEach((executable, state) -> { + pw.println("{"); + pw.println(executable.toGenericString()); + pw.println(state.toString()); + pw.println("}"); + }); + } else { + pw.println(); + } + } catch (IOException e) { + throw new Error("Failed to write data: " + e.getMessage(), e); + } + } + + // Executes all diagnostic commands + protected OutputAnalyzer[] executeJCMD(int pid) { + int size = jcmdCommands.size(); + OutputAnalyzer[] outputArray = new OutputAnalyzer[size]; + CommandExecutor jcmdExecutor = new PidJcmdExecutor(String.valueOf(pid)); + for (int i = 0; i < size; i++) { + outputArray[i] = jcmdExecutor.execute(jcmdCommands.get(i)); + } + return outputArray; + } +} diff --git a/hotspot/test/compiler/compilercontrol/share/scenario/JcmdStateBuilder.java b/hotspot/test/compiler/compilercontrol/share/scenario/JcmdStateBuilder.java index 4fed421e228..b799174d61b 100644 --- a/hotspot/test/compiler/compilercontrol/share/scenario/JcmdStateBuilder.java +++ b/hotspot/test/compiler/compilercontrol/share/scenario/JcmdStateBuilder.java @@ -36,15 +36,16 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; +import java.util.stream.Collectors; public class JcmdStateBuilder implements StateBuilder { private static final List>> METHODS = new PoolHelper().getAllMethods(); private final Map stateMap = new HashMap<>(); private final DirectiveBuilder directiveBuilder; - private Map> matchBlocks + private final Map> matchBlocks = new LinkedHashMap<>(); - private List commands = new ArrayList<>(); + private final List inlines = new ArrayList<>(); private boolean isFileValid = true; public JcmdStateBuilder(String fileName) { @@ -53,7 +54,6 @@ public class JcmdStateBuilder implements StateBuilder { @Override public void add(JcmdCommand compileCommand) { - commands.add(compileCommand); switch (compileCommand.jcmdType) { case ADD: directiveBuilder.add(compileCommand); @@ -64,6 +64,7 @@ public class JcmdStateBuilder implements StateBuilder { break; case CLEAR: matchBlocks.clear(); + inlines.clear(); break; case REMOVE: removeDirective(); @@ -73,15 +74,24 @@ public class JcmdStateBuilder implements StateBuilder { private void addCommand(JcmdCommand compileCommand) { isFileValid &= compileCommand.isValid(); + MethodDescriptor methodDescriptor = compileCommand.methodDescriptor; + + switch (compileCommand.command) { + case INLINE: + case DONTINLINE: + inlines.add(compileCommand); + break; + } for (MethodDescriptor md: matchBlocks.keySet()) { - if (compileCommand.methodDescriptor.getCanonicalString() - .matches(md.getRegexp())) { + if (methodDescriptor.getCanonicalString().matches(md.getRegexp())) { matchBlocks.get(md).add(compileCommand); } } - List commands = new ArrayList<>(); - commands.add(compileCommand); - matchBlocks.put(compileCommand.methodDescriptor, commands); + if (!matchBlocks.containsKey(compileCommand.methodDescriptor)) { + List commands = new ArrayList<>(); + commands.add(compileCommand); + matchBlocks.put(compileCommand.methodDescriptor, commands); + } } private void removeDirective() { @@ -101,14 +111,38 @@ public class JcmdStateBuilder implements StateBuilder { @Override public Map getStates() { directiveBuilder.getStates(); - // Build states for each method according to match blocks - for (Pair> pair : METHODS) { - State state = getState(pair); - if (state != null) { - stateMap.put(pair.first, state); + for (MethodDescriptor matchDescriptor : matchBlocks.keySet()) { + if ("Inlinee.caller()".matches(matchDescriptor.getRegexp()) + && !inlines.isEmpty()) { + // Got a *.* match block, where inline would be written + inlines.clear(); } } + /* + * Write inline directive in the end to the latest match block + * if we didn't do this before + * Inlinee caller methods should match this block only + */ + if (!inlines.isEmpty()) { + Pair> pair = METHODS.get(0); + MethodDescriptor md = MethodGenerator.anyMatchDescriptor( + pair.first); + CompileCommand cc = new CompileCommand(Command.QUIET, md, + null, Scenario.Type.DIRECTIVE); + List commands = new ArrayList<>(); + + // Add appropriate "*.*" match block + commands.add(cc); + matchBlocks.put(md, commands); + } if (isFileValid) { + // Build states for each method according to match blocks + for (Pair> pair : METHODS) { + State state = getState(pair); + if (state != null) { + stateMap.put(pair.first, state); + } + } return stateMap; } else { // return empty map because invalid file doesn't change states @@ -132,7 +166,9 @@ public class JcmdStateBuilder implements StateBuilder { * then apply commands from this match to the state */ for (CompileCommand cc : matchBlocks.get(matchDesc)) { - state = new State(); + if (state == null) { + state = new State(); + } if (!isMatchFound) { // this is a first found match, apply all commands state.apply(cc); @@ -159,6 +195,15 @@ public class JcmdStateBuilder implements StateBuilder { @Override public List getCompileCommands() { - return commands; + if (isFileValid) { + return matchBlocks.keySet().stream() + /* only method descriptor is required + to check print_directives */ + .map(md -> new JcmdCommand(null, md, null, null, + Scenario.JcmdType.ADD)) + .collect(Collectors.toList()); + } else { + return new ArrayList<>(); + } } } diff --git a/hotspot/test/compiler/compilercontrol/share/scenario/Scenario.java b/hotspot/test/compiler/compilercontrol/share/scenario/Scenario.java index a2205704f32..49109151d82 100644 --- a/hotspot/test/compiler/compilercontrol/share/scenario/Scenario.java +++ b/hotspot/test/compiler/compilercontrol/share/scenario/Scenario.java @@ -23,29 +23,18 @@ package compiler.compilercontrol.share.scenario; -import compiler.compilercontrol.share.actions.BaseAction; import compiler.compilercontrol.share.method.MethodDescriptor; import compiler.compilercontrol.share.processors.CommandProcessor; import compiler.compilercontrol.share.processors.LogProcessor; +import compiler.compilercontrol.share.processors.PrintDirectivesProcessor; import compiler.compilercontrol.share.processors.PrintProcessor; -import compiler.compilercontrol.share.processors.QuietProcessor; import jdk.test.lib.Asserts; import jdk.test.lib.OutputAnalyzer; import jdk.test.lib.Pair; -import jdk.test.lib.ProcessTools; -import jdk.test.lib.dcmd.CommandExecutorException; -import jdk.test.lib.dcmd.JcmdExecutor; import pool.PoolHelper; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; import java.lang.reflect.Executable; -import java.net.ServerSocket; -import java.net.Socket; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; @@ -60,18 +49,18 @@ import java.util.function.Consumer; */ public final class Scenario { private final boolean isValid; - private final List vmopts; private final Map states; private final List> processors; - private final List jcmdExecCommands; + private final Executor executor; + private final Consumer> jcmdProcessor; private Scenario(boolean isValid, List vmopts, Map states, List compileCommands, - List jcmdCommands) { + List jcmdCommands, + List directives) { this.isValid = isValid; - this.vmopts = vmopts; this.states = states; processors = new ArrayList<>(); processors.add(new LogProcessor(states)); @@ -87,10 +76,10 @@ public final class Scenario { nonQuieted.add(cc); } } - processors.add(new CommandProcessor(nonQuieted)); - processors.add(new QuietProcessor(quieted)); - jcmdExecCommands = new ArrayList<>(); + processors.add(new CommandProcessor(nonQuieted, quieted)); + List jcmdExecCommands = new ArrayList<>(); boolean addCommandMet = false; + boolean printCommandMet = false; for (JcmdCommand cmd : jcmdCommands) { switch (cmd.jcmdType) { case ADD: @@ -99,97 +88,40 @@ public final class Scenario { } addCommandMet = true; break; + case PRINT: + printCommandMet = true; + break; default: jcmdExecCommands.add(cmd.jcmdType.command); break; } } + // Add print command only in the end to get directives printed + if (printCommandMet) { + jcmdExecCommands.add(JcmdType.PRINT.command); + } + jcmdProcessor = new PrintDirectivesProcessor(directives); + executor = new Executor(isValid, vmopts, states, jcmdExecCommands); } /** * Executes scenario */ public void execute() { - // Construct execution command with CompileCommand and class - List argsList = new ArrayList<>(); - // Add VM options - argsList.addAll(vmopts); - // Add class name that would be executed in a separate VM - String classCmd = BaseAction.class.getName(); - argsList.add(classCmd); - OutputAnalyzer output; - try (ServerSocket serverSocket = new ServerSocket(0)) { - if (isValid) { - // Get port test VM will connect to - int port = serverSocket.getLocalPort(); - if (port == -1) { - throw new Error("Socket is not bound: " + port); - } - argsList.add(String.valueOf(port)); - // Start separate thread to connect with test VM - new Thread(() -> connectTestVM(serverSocket)).start(); - } - // Start test VM - output = ProcessTools.executeTestJvmAllArgs( - argsList.toArray(new String[argsList.size()])); - } catch (Throwable thr) { - throw new Error("Execution failed", thr); - } + List outputList = executor.execute(); + // The first one contains output from the test VM + OutputAnalyzer mainOuput = outputList.get(0); if (isValid) { - output.shouldHaveExitValue(0); - for (Consumer processor : processors) { - processor.accept(output); - } + mainOuput.shouldHaveExitValue(0); + processors.forEach(processor -> processor.accept(mainOuput)); + // only the last output contains directives got from print command + List last = new ArrayList<>(); + last.add(outputList.get(outputList.size() - 1)); + jcmdProcessor.accept(last); } else { - Asserts.assertNE(output.getExitValue(), 0, "VM should exit with " + Asserts.assertNE(mainOuput.getExitValue(), 0, "VM should exit with " + "error for incorrect directives"); - output.shouldContain("Parsing of compiler directives failed"); - } - } - - /* - * Performs connection with a test VM, sends method states and performs - * JCMD operations on a test VM. - */ - private void connectTestVM(ServerSocket serverSocket) { - /* - * There are no way to prove that accept was invoked before we started - * test VM that connects to this serverSocket. Connection timeout is - * enough - */ - try ( - Socket socket = serverSocket.accept(); - PrintWriter pw = new PrintWriter(socket.getOutputStream(), - true); - BufferedReader in = new BufferedReader(new InputStreamReader( - socket.getInputStream()))) { - // Get pid of the executed process - int pid = Integer.parseInt(in.readLine()); - Asserts.assertNE(pid, 0, "Got incorrect pid"); - executeJCMD(pid); - // serialize and send state map - for (Executable x : states.keySet()) { - pw.println("{"); - pw.println(x.toGenericString()); - pw.println(states.get(x).toString()); - pw.println("}"); - } - } catch (IOException e) { - throw new Error("Failed to write data", e); - } - } - - // Executes all diagnostic commands - private void executeJCMD(int pid) { - for (String command : jcmdExecCommands) { - new JcmdExecutor() { - @Override - protected List createCommandLine(String cmd) - throws CommandExecutorException { - return Arrays.asList(jcmdBinary, Integer.toString(pid), - cmd); - } - }.execute(command); + mainOuput.shouldContain("Parsing of compiler directives failed"); } } @@ -265,6 +197,7 @@ public final class Scenario { private final Map> builders = new HashMap<>(); private final JcmdStateBuilder jcmdStateBuilder; + private final List jcmdCommands = new ArrayList<>(); public Builder() { builders.put(Type.FILE, new CommandFileBuilder(Type.FILE.fileName)); @@ -279,6 +212,7 @@ public final class Scenario { Collections.addAll(vmopts, vmOptions); if (compileCommand.type == Type.JCMD) { jcmdStateBuilder.add((JcmdCommand) compileCommand); + jcmdCommands.add((JcmdCommand) compileCommand); } else { StateBuilder builder = builders.get( compileCommand.type); @@ -301,11 +235,9 @@ public final class Scenario { Map directiveFileStates = builders.get(Type.DIRECTIVE).getStates(); - // get all jcmd commands - List jcmdCommands = jcmdStateBuilder - .getCompileCommands(); + // check if directives stack was cleared by jcmd boolean isClearedState = false; - if (jcmdClearedState(jcmdCommands)) { + if (jcmdContainsCommand(JcmdType.CLEAR)) { isClearedState = true; } @@ -321,13 +253,13 @@ public final class Scenario { State st = State.merge(commandOptionState, commandFileState); if (!isClearedState) { State directiveState = directiveFileStates.get(x); - if (directiveState != null) { - st = directiveState; + State jcmdState = jcmdStates.get(x); + if (jcmdState != null) { + st = State.merge(st, jcmdState); + } else if (directiveState != null) { + st = State.merge(st, directiveState); } } - State jcmdState = jcmdStates.get(x); - st = State.merge(st, jcmdState); - finalStates.put(x, st); } @@ -339,6 +271,16 @@ public final class Scenario { ccList.addAll(builders.get(Type.OPTION).getCompileCommands()); ccList.addAll(builders.get(Type.FILE).getCompileCommands()); + // Create a list of directives to check which one was printed + List directives = new ArrayList<>(); + if (jcmdContainsCommand(JcmdType.PRINT)) { + if (!isClearedState) { + directives.addAll(builders.get(Type.DIRECTIVE) + .getCompileCommands()); + } + directives.addAll(jcmdStateBuilder.getCompileCommands()); + } + // Get all VM options after we build all states and files List options = new ArrayList<>(); options.addAll(vmopts); @@ -348,13 +290,13 @@ public final class Scenario { } options.addAll(jcmdStateBuilder.getOptions()); return new Scenario(isValid, options, finalStates, ccList, - jcmdCommands); + jcmdCommands, directives); } - // shows if jcmd have passed a clear command - private boolean jcmdClearedState(List jcmdCommands) { + // shows if jcmd have passed a specified jcmd command type + private boolean jcmdContainsCommand(JcmdType type) { for (JcmdCommand jcmdCommand : jcmdCommands) { - if (jcmdCommand.jcmdType == JcmdType.CLEAR) { + if (jcmdCommand.jcmdType == type) { return true; } } diff --git a/hotspot/test/compiler/compilercontrol/share/scenario/State.java b/hotspot/test/compiler/compilercontrol/share/scenario/State.java index 8be68d9b62a..b9a81984b69 100644 --- a/hotspot/test/compiler/compilercontrol/share/scenario/State.java +++ b/hotspot/test/compiler/compilercontrol/share/scenario/State.java @@ -182,11 +182,13 @@ public class State { } public boolean isC1Inlinable() { - return ! dontInline[Scenario.Compiler.C1.ordinal()].orElse(false); + return ! dontInline[Scenario.Compiler.C1.ordinal()].orElse(false) + && isC1Compilable(); } public boolean isC2Inlinable() { - return ! dontInline[Scenario.Compiler.C2.ordinal()].orElse(false); + return ! dontInline[Scenario.Compiler.C2.ordinal()].orElse(false) + && isC2Compilable(); } public boolean isInlinable() { @@ -206,11 +208,13 @@ public class State { } public boolean isC1ForceInline() { - return forceInline[Scenario.Compiler.C1.ordinal()].orElse(false); + return forceInline[Scenario.Compiler.C1.ordinal()].orElse(false) + && isC1Compilable(); } public boolean isC2ForceInline() { - return forceInline[Scenario.Compiler.C2.ordinal()].orElse(false); + return forceInline[Scenario.Compiler.C2.ordinal()].orElse(false) + && isC2Compilable(); } public boolean isForceInline() { @@ -229,7 +233,7 @@ public class State { if (value && isC2Compilable()) { setForceInline(Scenario.Compiler.C2.ordinal()); } else { - setDontInline(Scenario.Compiler.C1.ordinal()); + setDontInline(Scenario.Compiler.C2.ordinal()); } } diff --git a/hotspot/test/compiler/floatingpoint/TestPow2.java b/hotspot/test/compiler/floatingpoint/TestPow2.java index ba019a8532a..a443062b9b4 100644 --- a/hotspot/test/compiler/floatingpoint/TestPow2.java +++ b/hotspot/test/compiler/floatingpoint/TestPow2.java @@ -25,7 +25,7 @@ * @test * @bug 8063086 * @summary X^2 special case for C2 yields different result than interpreter - * @library /testlibrary /test/lib /compiler/whitebox + * @library /testlibrary /test/lib /compiler/whitebox / * @modules java.management * @build TestPow2 * @run main ClassFileInstaller sun.hotspot.WhiteBox @@ -36,6 +36,7 @@ import java.lang.reflect.*; import sun.hotspot.WhiteBox; +import compiler.whitebox.CompilerWhiteBoxTest; public class TestPow2 { diff --git a/hotspot/test/compiler/inlining/InlineAccessors.java b/hotspot/test/compiler/inlining/InlineAccessors.java new file mode 100644 index 00000000000..60f8986a829 --- /dev/null +++ b/hotspot/test/compiler/inlining/InlineAccessors.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015, 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. + * + */ +/** + * @test + * @bug 8140650 + * @summary Method::is_accessor should cover getters and setters for all types + * @library /testlibrary + * @run main/othervm InlineAccessors + */ +import java.lang.invoke.*; +import jdk.test.lib.*; +import static jdk.test.lib.Asserts.*; + +public class InlineAccessors { + public static void main(String[] args) throws Exception { + // try some sanity checks first + doTest(); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+IgnoreUnrecognizedVMOptions", "-showversion", + "-server", "-XX:-TieredCompilation", "-Xbatch", "-Xcomp", + "-XX:+PrintCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining", + "InlineAccessors$Launcher"); + + OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); + + analyzer.shouldHaveExitValue(0); + + // The test is applicable only to C2 (present in Server VM). + if (analyzer.getStderr().contains("Server VM")) { + analyzer.shouldContain("InlineAccessors::setBool (6 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::setByte (6 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::setChar (6 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::setShort (6 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::setInt (6 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::setFloat (6 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::setLong (6 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::setDouble (6 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::setObject (6 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::setArray (6 bytes) accessor"); + + analyzer.shouldContain("InlineAccessors::getBool (5 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::getByte (5 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::getChar (5 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::getShort (5 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::getInt (5 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::getFloat (5 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::getLong (5 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::getDouble (5 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::getObject (5 bytes) accessor"); + analyzer.shouldContain("InlineAccessors::getArray (5 bytes) accessor"); + } + } + + boolean bool; + byte b; + char c; + short s; + int i; + float f; + long l; + double d; + Object o; + Object[] a; + + public void setBool(boolean v) { bool = v; } + public void setByte(byte v) { b = v; } + public void setChar(char v) { c = v; } + public void setShort(short v) { s = v; } + public void setInt(int v) { i = v; } + public void setFloat(float v) { f = v; } + public void setLong(long v) { l = v; } + public void setDouble(double v) { d = v; } + public void setObject(Object v) { o = v; } + public void setArray(Object[] v) { a = v; } + + public boolean getBool() { return bool; } + public byte getByte() { return b; } + public char getChar() { return c; } + public short getShort() { return s; } + public int getInt() { return i; } + public float getFloat() { return f; } + public long getLong() { return l; } + public double getDouble() { return d; } + public Object getObject() { return o; } + public Object[] getArray() { return a; } + + static void doTest() { + InlineAccessors o = new InlineAccessors(); + o.setBool(false); + o.setByte((byte)0); + o.setChar('a'); + o.setShort((short)0); + o.setInt(0); + o.setFloat(0F); + o.setLong(0L); + o.setDouble(0D); + o.setObject(new Object()); + o.setArray(new Object[1]); + + o.getBool(); + o.getByte(); + o.getChar(); + o.getShort(); + o.getInt(); + o.getFloat(); + o.getLong(); + o.getDouble(); + o.getObject(); + o.getArray(); + } + + static class Launcher { + public static void main(String[] args) throws Exception { + for (int c = 0; c < 20_000; c++) { + doTest(); + } + } + } +} diff --git a/hotspot/test/compiler/intrinsics/IntrinsicAvailableTest.java b/hotspot/test/compiler/intrinsics/IntrinsicAvailableTest.java index 8d85afe8466..d48702e6da0 100644 --- a/hotspot/test/compiler/intrinsics/IntrinsicAvailableTest.java +++ b/hotspot/test/compiler/intrinsics/IntrinsicAvailableTest.java @@ -23,10 +23,11 @@ import java.lang.reflect.Executable; import java.util.concurrent.Callable; import java.util.Objects; +import compiler.whitebox.CompilerWhiteBoxTest; /* * @test * @bug 8130832 - * @library /testlibrary /test/lib /compiler/whitebox /compiler/testlibrary + * @library /testlibrary /test/lib /compiler/whitebox /compiler/testlibrary / * @build IntrinsicAvailableTest * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission diff --git a/hotspot/test/compiler/intrinsics/bmi/TestAndnI.java b/hotspot/test/compiler/intrinsics/bmi/TestAndnI.java index cbf08bc465d..0f74509722d 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestAndnI.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestAndnI.java @@ -61,15 +61,27 @@ public class TestAndnI { } public int intExpr(int src1, Expr.MemI src2) { - return ~src1 & src2.value; + if (src2 != null) { + return ~src1 & src2.value; + } else { + return 0; + } } public int intExpr(Expr.MemI src1, int src2) { - return ~src1.value & src2; + if (src1 != null) { + return ~src1.value & src2; + } else { + return 0; + } } public int intExpr(Expr.MemI src1, Expr.MemI src2) { - return ~src1.value & src2.value; + if (src1 != null && src2 != null) { + return ~src1.value & src2.value; + } else { + return 0; + } } } @@ -80,15 +92,27 @@ public class TestAndnI { } public int intExpr(int src1, Expr.MemI src2) { - return src1 & ~src2.value; + if (src2 != null) { + return src1 & ~src2.value; + } else { + return 0; + } } public int intExpr(Expr.MemI src1, int src2) { - return src1.value & ~src2; + if (src1 != null) { + return src1.value & ~src2; + } else { + return 0; + } } public int intExpr(Expr.MemI src1, Expr.MemI src2) { - return src1.value & ~src2.value; + if (src1 != null && src2 != null) { + return src1.value & ~src2.value; + } else { + return 0; + } } } } diff --git a/hotspot/test/compiler/intrinsics/bmi/TestAndnL.java b/hotspot/test/compiler/intrinsics/bmi/TestAndnL.java index 088d537f633..c8bf93344cc 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestAndnL.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestAndnL.java @@ -61,15 +61,27 @@ public class TestAndnL { } public long longExpr(long src1, Expr.MemL src2) { - return ~src1 & src2.value; + if (src2 != null) { + return ~src1 & src2.value; + } else { + return 0; + } } public long longExpr(Expr.MemL src1, long src2) { - return ~src1.value & src2; + if (src1 != null) { + return ~src1.value & src2; + } else { + return 0; + } } public long longExpr(Expr.MemL src1, Expr.MemL src2) { - return ~src1.value & src2.value; + if (src1 != null && src2 != null) { + return ~src1.value & src2.value; + } else { + return 0; + } } @@ -82,15 +94,27 @@ public class TestAndnL { } public long longExpr(long src1, Expr.MemL src2) { - return src1 & ~src2.value; + if (src2 != null) { + return src1 & ~src2.value; + } else { + return 0; + } } public long longExpr(Expr.MemL src1, long src2) { - return src1.value & ~src2; + if (src1 != null) { + return src1.value & ~src2; + } else { + return 0; + } } public long longExpr(Expr.MemL src1, Expr.MemL src2) { - return src1.value & ~src2.value; + if (src1 != null && src2 != null) { + return src1.value & ~src2.value; + } else { + return 0; + } } } diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/AddnTestI.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/AndnTestI.java similarity index 79% rename from hotspot/test/compiler/intrinsics/bmi/verifycode/AddnTestI.java rename to hotspot/test/compiler/intrinsics/bmi/verifycode/AndnTestI.java index f71f2153671..c6897440012 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/AddnTestI.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/AndnTestI.java @@ -24,21 +24,21 @@ /* * @test * @bug 8031321 - * @library /testlibrary /test/lib /compiler/whitebox .. + * @library /testlibrary /test/lib /compiler/whitebox / .. * @modules java.base/sun.misc * java.management - * @build AddnTestI + * @build AndnTestI * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission - * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions AddnTestI + * @run main/bootclasspath -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions AndnTestI */ import java.lang.reflect.Method; -public class AddnTestI extends BmiIntrinsicBase.BmiTestCase { +public class AndnTestI extends BmiIntrinsicBase.BmiTestCase { - protected AddnTestI(Method method) { + protected AndnTestI(Method method) { super(method); // from intel manual VEX.NDS.LZ.0F38.W0 F2 /r, example c4e260f2c2 instrMask = new byte[]{ @@ -54,7 +54,7 @@ public class AddnTestI extends BmiIntrinsicBase.BmiTestCase { } public static void main(String[] args) throws Exception { - BmiIntrinsicBase.verifyTestCase(AddnTestI::new, TestAndnI.AndnIExpr.class.getDeclaredMethods()); - BmiIntrinsicBase.verifyTestCase(AddnTestI::new, TestAndnI.AndnICommutativeExpr.class.getDeclaredMethods()); + BmiIntrinsicBase.verifyTestCase(AndnTestI::new, TestAndnI.AndnIExpr.class.getDeclaredMethods()); + BmiIntrinsicBase.verifyTestCase(AndnTestI::new, TestAndnI.AndnICommutativeExpr.class.getDeclaredMethods()); } } diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/AddnTestL.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/AndnTestL.java similarity index 76% rename from hotspot/test/compiler/intrinsics/bmi/verifycode/AddnTestL.java rename to hotspot/test/compiler/intrinsics/bmi/verifycode/AndnTestL.java index 9b64d8e4fbe..8725cb7f0bb 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/AddnTestL.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/AndnTestL.java @@ -24,27 +24,27 @@ /* * @test * @bug 8031321 - * @library /testlibrary /test/lib /compiler/whitebox .. + * @library /testlibrary /test/lib /compiler/whitebox / .. * @modules java.base/sun.misc * java.management - * @build AddnTestL + * @build AndnTestL * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission - * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions AddnTestL + * @run main/bootclasspath -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions AndnTestL */ import java.lang.reflect.Method; -public class AddnTestL extends AddnTestI { +public class AndnTestL extends AndnTestI { - protected AddnTestL(Method method) { + protected AndnTestL(Method method) { super(method); isLongOperation = true; } public static void main(String[] args) throws Exception { - BmiIntrinsicBase.verifyTestCase(AddnTestL::new, TestAndnL.AndnLExpr.class.getDeclaredMethods()); - BmiIntrinsicBase.verifyTestCase(AddnTestL::new, TestAndnL.AndnLCommutativeExpr.class.getDeclaredMethods()); + BmiIntrinsicBase.verifyTestCase(AndnTestL::new, TestAndnL.AndnLExpr.class.getDeclaredMethods()); + BmiIntrinsicBase.verifyTestCase(AndnTestL::new, TestAndnL.AndnLCommutativeExpr.class.getDeclaredMethods()); } } diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestI.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestI.java index f01b460b661..7b71a7706d5 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestI.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @library /testlibrary /test/lib /compiler/whitebox .. + * @library /testlibrary /test/lib /compiler/whitebox / .. * @modules java.base/sun.misc * java.management * @build BlsiTestI diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestL.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestL.java index ba9b027e20f..7f8d5e37398 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestL.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @library /testlibrary /test/lib /compiler/whitebox .. + * @library /testlibrary /test/lib /compiler/whitebox / .. * @modules java.base/sun.misc * java.management * @build BlsiTestL diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java index 1e83c2db06a..0e8158c9eb8 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @library /testlibrary /test/lib /compiler/whitebox .. + * @library /testlibrary /test/lib /compiler/whitebox / .. * @modules java.base/sun.misc * java.management * @build BlsmskTestI diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java index 0a51b8a4761..798b2dc4eca 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @library /testlibrary /test/lib /compiler/whitebox .. + * @library /testlibrary /test/lib /compiler/whitebox / .. * @modules java.base/sun.misc * java.management * @build BlsmskTestL diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestI.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestI.java index b358d8b7741..79d1ba7da20 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestI.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @library /testlibrary /test/lib /compiler/whitebox .. + * @library /testlibrary /test/lib /compiler/whitebox / .. * @modules java.base/sun.misc * java.management * @build BlsrTestI diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestL.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestL.java index 750ec7782b4..29fa2ad239c 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestL.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @library /testlibrary /test/lib /compiler/whitebox .. + * @library /testlibrary /test/lib /compiler/whitebox / .. * @modules java.base/sun.misc * java.management * @build BlsrTestL diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/BmiIntrinsicBase.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/BmiIntrinsicBase.java index c4c307ac3cc..8ecb518c34a 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/BmiIntrinsicBase.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/BmiIntrinsicBase.java @@ -32,6 +32,7 @@ import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.util.concurrent.Callable; import java.util.function.Function; +import compiler.whitebox.CompilerWhiteBoxTest; public class BmiIntrinsicBase extends CompilerWhiteBoxTest { diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestI.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestI.java index c8a334dd923..7436044e78e 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestI.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @library /testlibrary /test/lib /compiler/whitebox .. + * @library /testlibrary /test/lib /compiler/whitebox / .. * @modules java.base/sun.misc * java.management * @build LZcntTestI @@ -50,6 +50,8 @@ public class LZcntTestI extends BmiIntrinsicBase.BmiTestCase_x64 { public static void main(String[] args) throws Exception { // j.l.Integer and Long should be loaded to allow a compilation of the methods that use their methods System.out.println("class java.lang.Integer should be loaded. Proof: " + Integer.class); + // Avoid uncommon traps. + System.out.println("Num leading zeroes: " + new TestLzcntI.LzcntIExpr().intExpr(12341341)); BmiIntrinsicBase.verifyTestCase(LZcntTestI::new, TestLzcntI.LzcntIExpr.class.getDeclaredMethods()); } diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestL.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestL.java index e897eae1e0a..c96df728df8 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestL.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @library /testlibrary /test/lib /compiler/whitebox .. + * @library /testlibrary /test/lib /compiler/whitebox / .. * @modules java.base/sun.misc * java.management * @build LZcntTestL @@ -46,6 +46,8 @@ public class LZcntTestL extends LZcntTestI { public static void main(String[] args) throws Exception { // j.l.Integer and Long should be loaded to allow a compilation of the methods that use their methods System.out.println("classes java.lang.Long should be loaded. Proof: " + Long.class); + // Avoid uncommon traps. + System.out.println("Num leading zeroes: " + new TestLzcntL.LzcntLExpr().longExpr(12341341)); BmiIntrinsicBase.verifyTestCase(LZcntTestL::new, TestLzcntL.LzcntLExpr.class.getDeclaredMethods()); } } diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestI.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestI.java index 7b04578ae24..25f90f2e89e 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestI.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestI.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @library /testlibrary /test/lib /compiler/whitebox .. + * @library /testlibrary /test/lib /compiler/whitebox / .. * @modules java.base/sun.misc * java.management * @build TZcntTestI @@ -50,6 +50,8 @@ public class TZcntTestI extends BmiIntrinsicBase.BmiTestCase_x64 { public static void main(String[] args) throws Exception { // j.l.Integer and Long should be loaded to allow a compilation of the methods that use their methods System.out.println("class java.lang.Integer should be loaded. Proof: " + Integer.class); + // Avoid uncommon traps. + System.out.println("Num trailing zeroes: " + new TestTzcntI.TzcntIExpr().intExpr(12341341)); BmiIntrinsicBase.verifyTestCase(TZcntTestI::new, TestTzcntI.TzcntIExpr.class.getDeclaredMethods()); } diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestL.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestL.java index 568e3fa4e0b..812123d422a 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestL.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestL.java @@ -24,7 +24,7 @@ /* * @test * @bug 8031321 - * @library /testlibrary /test/lib /compiler/whitebox .. + * @library /testlibrary /test/lib /compiler/whitebox / .. * @modules java.base/sun.misc * java.management * @build TZcntTestL @@ -46,6 +46,8 @@ public class TZcntTestL extends TZcntTestI { public static void main(String[] args) throws Exception { // j.l.Integer and Long should be loaded to allow a compilation of the methods that use their methods System.out.println("classes java.lang.Long should be loaded. Proof: " + Long.class); + // Avoid uncommon traps. + System.out.println("Num trailing zeroes: " + new TestTzcntL.TzcntLExpr().longExpr(12341341)); BmiIntrinsicBase.verifyTestCase(TZcntTestL::new, TestTzcntL.TzcntLExpr.class.getDeclaredMethods()); } } diff --git a/hotspot/test/compiler/intrinsics/crc32/TestCRC32.java b/hotspot/test/compiler/intrinsics/crc32/TestCRC32.java new file mode 100644 index 00000000000..f7ecc241508 --- /dev/null +++ b/hotspot/test/compiler/intrinsics/crc32/TestCRC32.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2015, 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. + */ + +/** + * @test + * @bug 8143012 + * @summary CRC32 Intrinsics support on SPARC + * + * @run main/othervm/timeout=720 -Xbatch TestCRC32 -m + */ + +import java.nio.ByteBuffer; +import java.util.zip.Checksum; +import java.util.zip.CRC32; + +public class TestCRC32 { + public static void main(String[] args) { + int offset = Integer.getInteger("offset", 0); + int msgSize = Integer.getInteger("msgSize", 512); + boolean multi = false; + int iters = 20000; + int warmupIters = 20000; + + if (args.length > 0) { + if (args[0].equals("-m")) { + multi = true; + } else { + iters = Integer.valueOf(args[0]); + } + if (args.length > 1) { + warmupIters = Integer.valueOf(args[1]); + } + } + + if (multi) { + test_multi(warmupIters); + return; + } + + System.out.println(" offset = " + offset); + System.out.println("msgSize = " + msgSize + " bytes"); + System.out.println(" iters = " + iters); + + byte[] b = initializedBytes(msgSize, offset); + + CRC32 crc0 = new CRC32(); + CRC32 crc1 = new CRC32(); + CRC32 crc2 = new CRC32(); + + crc0.update(b, offset, msgSize); + + System.out.println("-------------------------------------------------------"); + + /* warm up */ + for (int i = 0; i < warmupIters; i++) { + crc1.reset(); + crc1.update(b, offset, msgSize); + } + + /* measure performance */ + long start = System.nanoTime(); + for (int i = 0; i < iters; i++) { + crc1.reset(); + crc1.update(b, offset, msgSize); + } + long end = System.nanoTime(); + double total = (double)(end - start)/1e9; // in seconds + double thruput = (double)msgSize*iters/1e6/total; // in MB/s + System.out.println("CRC32.update(byte[]) runtime = " + total + " seconds"); + System.out.println("CRC32.update(byte[]) throughput = " + thruput + " MB/s"); + + /* check correctness */ + for (int i = 0; i < iters; i++) { + crc1.reset(); + crc1.update(b, offset, msgSize); + if (!check(crc0, crc1)) break; + } + report("CRCs", crc0, crc1); + + System.out.println("-------------------------------------------------------"); + + ByteBuffer buf = ByteBuffer.allocateDirect(msgSize); + buf.put(b, offset, msgSize); + buf.flip(); + + /* warm up */ + for (int i = 0; i < warmupIters; i++) { + crc2.reset(); + crc2.update(buf); + buf.rewind(); + } + + /* measure performance */ + start = System.nanoTime(); + for (int i = 0; i < iters; i++) { + crc2.reset(); + crc2.update(buf); + buf.rewind(); + } + end = System.nanoTime(); + total = (double)(end - start)/1e9; // in seconds + thruput = (double)msgSize*iters/1e6/total; // in MB/s + System.out.println("CRC32.update(ByteBuffer) runtime = " + total + " seconds"); + System.out.println("CRC32.update(ByteBuffer) throughput = " + thruput + " MB/s"); + + /* check correctness */ + for (int i = 0; i < iters; i++) { + crc2.reset(); + crc2.update(buf); + buf.rewind(); + if (!check(crc0, crc2)) break; + } + report("CRCs", crc0, crc2); + + System.out.println("-------------------------------------------------------"); + } + + private static void report(String s, Checksum crc0, Checksum crc1) { + System.out.printf("%s: crc0 = %08x, crc1 = %08x\n", + s, crc0.getValue(), crc1.getValue()); + } + + private static boolean check(Checksum crc0, Checksum crc1) { + if (crc0.getValue() != crc1.getValue()) { + System.err.printf("ERROR: crc0 = %08x, crc1 = %08x\n", + crc0.getValue(), crc1.getValue()); + return false; + } + return true; + } + + private static byte[] initializedBytes(int M, int offset) { + byte[] bytes = new byte[M + offset]; + for (int i = 0; i < offset; i++) { + bytes[i] = (byte) i; + } + for (int i = offset; i < bytes.length; i++) { + bytes[i] = (byte) (i - offset); + } + return bytes; + } + + private static void test_multi(int iters) { + int len1 = 8; // the 8B/iteration loop + int len2 = 32; // the 32B/iteration loop + int len3 = 4096; // the 4KB/iteration loop + + byte[] b = initializedBytes(len3*16, 0); + int[] offsets = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 16, 32, 64, 128, 256, 512 }; + int[] sizes = { 0, 1, 2, 3, 4, 5, 6, 7, + len1, len1+1, len1+2, len1+3, len1+4, len1+5, len1+6, len1+7, + len1*2, len1*2+1, len1*2+3, len1*2+5, len1*2+7, + len2, len2+1, len2+3, len2+5, len2+7, + len2*2, len2*4, len2*8, len2*16, len2*32, len2*64, + len3, len3+1, len3+3, len3+5, len3+7, + len3*2, len3*4, len3*8, + len1+len2, len1+len2+1, len1+len2+3, len1+len2+5, len1+len2+7, + len1+len3, len1+len3+1, len1+len3+3, len1+len3+5, len1+len3+7, + len2+len3, len2+len3+1, len2+len3+3, len2+len3+5, len2+len3+7, + len1+len2+len3, len1+len2+len3+1, len1+len2+len3+3, + len1+len2+len3+5, len1+len2+len3+7, + (len1+len2+len3)*2, (len1+len2+len3)*2+1, (len1+len2+len3)*2+3, + (len1+len2+len3)*2+5, (len1+len2+len3)*2+7, + (len1+len2+len3)*3, (len1+len2+len3)*3-1, (len1+len2+len3)*3-3, + (len1+len2+len3)*3-5, (len1+len2+len3)*3-7 }; + CRC32[] crc0 = new CRC32[offsets.length*sizes.length]; + CRC32[] crc1 = new CRC32[offsets.length*sizes.length]; + int i, j, k; + + System.out.printf("testing %d cases ...\n", offsets.length*sizes.length); + + /* set the result from interpreter as reference */ + for (i = 0; i < offsets.length; i++) { + for (j = 0; j < sizes.length; j++) { + crc0[i*sizes.length + j] = new CRC32(); + crc1[i*sizes.length + j] = new CRC32(); + crc0[i*sizes.length + j].update(b, offsets[i], sizes[j]); + } + } + + /* warm up the JIT compiler and get result */ + for (k = 0; k < iters; k++) { + for (i = 0; i < offsets.length; i++) { + for (j = 0; j < sizes.length; j++) { + crc1[i*sizes.length + j].reset(); + crc1[i*sizes.length + j].update(b, offsets[i], sizes[j]); + } + } + } + + /* check correctness */ + for (i = 0; i < offsets.length; i++) { + for (j = 0; j < sizes.length; j++) { + if (!check(crc0[i*sizes.length + j], crc1[i*sizes.length + j])) { + System.out.printf("offsets[%d] = %d", i, offsets[i]); + System.out.printf("\tsizes[%d] = %d\n", j, sizes[j]); + } + } + } + } +} diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactIntTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactIntTest.java index 62ba1ead758..89ad29a5fc4 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactIntTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactIntTest.java @@ -23,8 +23,7 @@ /* * @test - * @library /testlibrary /test/lib /compiler/whitebox - * /compiler/testlibrary + * @library /testlibrary /test/lib /compiler/whitebox / /compiler/testlibrary * @modules java.base/sun.misc * java.management * @build AddExactIntTest diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactLongTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactLongTest.java index 3cb79cdb3e0..a5cae206498 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactLongTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactLongTest.java @@ -23,8 +23,7 @@ /* * @test - * @library /testlibrary /test/lib /compiler/whitebox - * /compiler/testlibrary + * @library /testlibrary /test/lib /compiler/whitebox / /compiler/testlibrary * @modules java.base/sun.misc * java.management * @build AddExactLongTest diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactIntTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactIntTest.java index 7fb5e0f29ab..6011d913025 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactIntTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactIntTest.java @@ -23,8 +23,7 @@ /* * @test - * @library /testlibrary /test/lib /compiler/whitebox - * /compiler/testlibrary + * @library /testlibrary /test/lib /compiler/whitebox / /compiler/testlibrary * @modules java.base/sun.misc * java.management * @build DecrementExactIntTest diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactLongTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactLongTest.java index 2b60cb03ee8..973403bc055 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactLongTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactLongTest.java @@ -23,8 +23,7 @@ /* * @test - * @library /testlibrary /test/lib /compiler/whitebox - * /compiler/testlibrary + * @library /testlibrary /test/lib /compiler/whitebox / /compiler/testlibrary * @modules java.base/sun.misc * java.management * @build DecrementExactLongTest diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactIntTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactIntTest.java index 66cc39170c3..bddaeb92351 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactIntTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactIntTest.java @@ -23,8 +23,7 @@ /* * @test - * @library /testlibrary /test/lib /compiler/whitebox - * /compiler/testlibrary + * @library /testlibrary /test/lib /compiler/whitebox / /compiler/testlibrary * @modules java.base/sun.misc * java.management * @build IncrementExactIntTest diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactLongTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactLongTest.java index b52ff29e171..7f32d312b50 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactLongTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactLongTest.java @@ -23,8 +23,7 @@ /* * @test - * @library /testlibrary /test/lib /compiler/whitebox - * /compiler/testlibrary + * @library /testlibrary /test/lib /compiler/whitebox / /compiler/testlibrary * @modules java.base/sun.misc * java.management * @build IncrementExactLongTest diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java index 1fcd33a7dd1..e271f9fd49f 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java @@ -27,6 +27,7 @@ import intrinsics.Verifier; import java.io.FileOutputStream; import java.lang.reflect.Executable; import java.util.Properties; +import compiler.whitebox.CompilerWhiteBoxTest; public abstract class IntrinsicBase extends CompilerWhiteBoxTest { protected String javaVmName; diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/MathIntrinsic.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/MathIntrinsic.java index 29d5e9916e8..7148a561336 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/MathIntrinsic.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/MathIntrinsic.java @@ -23,6 +23,7 @@ import java.lang.reflect.Executable; import java.util.concurrent.Callable; +import compiler.whitebox.CompilerWhiteBoxTest; public class MathIntrinsic { diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactIntTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactIntTest.java index 998ebda8108..c02bbe1c2c8 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactIntTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactIntTest.java @@ -23,8 +23,7 @@ /* * @test - * @library /testlibrary /test/lib /compiler/whitebox - * /compiler/testlibrary + * @library /testlibrary /test/lib /compiler/whitebox / /compiler/testlibrary * @modules java.base/sun.misc * java.management * @build MultiplyExactIntTest diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactLongTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactLongTest.java index dd5948f626d..3a480147ead 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactLongTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactLongTest.java @@ -23,8 +23,7 @@ /* * @test - * @library /testlibrary /test/lib /compiler/whitebox - * /compiler/testlibrary + * @library /testlibrary /test/lib /compiler/whitebox / /compiler/testlibrary * @modules java.base/sun.misc * java.management * @build MultiplyExactLongTest diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactIntTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactIntTest.java index de01cd3d1f6..ed99f769e15 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactIntTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactIntTest.java @@ -23,8 +23,7 @@ /* * @test - * @library /testlibrary /test/lib /compiler/whitebox - * /compiler/testlibrary + * @library /testlibrary /test/lib /compiler/whitebox / /compiler/testlibrary * @modules java.base/sun.misc * java.management * @build NegateExactIntTest diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactLongTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactLongTest.java index 7f773e8fa2a..946da7ed44f 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactLongTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactLongTest.java @@ -23,8 +23,7 @@ /* * @test - * @library /testlibrary /test/lib /compiler/whitebox - * /compiler/testlibrary + * @library /testlibrary /test/lib /compiler/whitebox / /compiler/testlibrary * @modules java.base/sun.misc * java.management * @build NegateExactLongTest diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactIntTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactIntTest.java index f7e6589b2b8..3681ffe35d9 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactIntTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactIntTest.java @@ -23,8 +23,7 @@ /* * @test - * @library /testlibrary /test/lib /compiler/whitebox - * /compiler/testlibrary + * @library /testlibrary /test/lib /compiler/whitebox / /compiler/testlibrary * @modules java.base/sun.misc * java.management * @build SubtractExactIntTest diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactLongTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactLongTest.java index eeefb81c8aa..f0a8903131a 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactLongTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactLongTest.java @@ -23,8 +23,7 @@ /* * @test - * @library /testlibrary /test/lib /compiler/whitebox - * /compiler/testlibrary + * @library /testlibrary /test/lib /compiler/whitebox / /compiler/testlibrary * @modules java.base/sun.misc * java.management * @build SubtractExactLongTest diff --git a/hotspot/test/compiler/intrinsics/unsafe/TestUnsafeMismatchedArrayFieldAccess.java b/hotspot/test/compiler/intrinsics/unsafe/TestUnsafeMismatchedArrayFieldAccess.java new file mode 100644 index 00000000000..2c60a719827 --- /dev/null +++ b/hotspot/test/compiler/intrinsics/unsafe/TestUnsafeMismatchedArrayFieldAccess.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +/** + * @test + * @bug 8142386 + * @library /testlibrary /test/lib + * @summary Unsafe access to an array is wrongly marked as mismatched + * @run main/othervm -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -XX:-TieredCompilation TestUnsafeMismatchedArrayFieldAccess + * + */ + +import java.lang.reflect.*; +import jdk.test.lib.Utils; +import sun.misc.Unsafe; + +public class TestUnsafeMismatchedArrayFieldAccess { + + private static final Unsafe UNSAFE = Utils.getUnsafe(); + + static { + try { + array_offset = UNSAFE.objectFieldOffset(TestUnsafeMismatchedArrayFieldAccess.class.getDeclaredField("array")); + } + catch (Exception e) { + throw new AssertionError(e); + } + } + + int[] array; + static final long array_offset; + + void m() { + UNSAFE.getObject(this, array_offset); + } + + static public void main(String[] args) { + TestUnsafeMismatchedArrayFieldAccess test = new TestUnsafeMismatchedArrayFieldAccess(); + + for (int i = 0; i < 20000; i++) { + test.m(); + } + } +} diff --git a/hotspot/test/compiler/intrinsics/unsafe/TestUnsafeUnalignedMismatchedAccesses.java b/hotspot/test/compiler/intrinsics/unsafe/TestUnsafeUnalignedMismatchedAccesses.java new file mode 100644 index 00000000000..b6a64e13a67 --- /dev/null +++ b/hotspot/test/compiler/intrinsics/unsafe/TestUnsafeUnalignedMismatchedAccesses.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +/** + * @test + * @bug 8136473 + * @summary Mismatched stores on same slice possible with Unsafe.Put*Unaligned methods + * @run main/othervm -XX:-UseOnStackReplacement -XX:-BackgroundCompilation TestUnsafeUnalignedMismatchedAccesses + * @run main/othervm -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -XX:+UnlockDiagnosticVMOptions -XX:-UseUnalignedAccesses TestUnsafeUnalignedMismatchedAccesses + * + */ + +import java.lang.reflect.*; +import jdk.internal.misc.Unsafe; + +public class TestUnsafeUnalignedMismatchedAccesses { + + private static final Unsafe UNSAFE; + + static { + try { + Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + UNSAFE = (Unsafe) unsafeField.get(null); + } + catch (Exception e) { + throw new AssertionError(e); + } + } + + static void test1(byte[] array) { + array[0] = 0; + UNSAFE.putIntUnaligned(array, UNSAFE.ARRAY_BYTE_BASE_OFFSET, 0); + array[0] = 0; + } + + static void test2(byte[] array) { + array[0] = 0; + UNSAFE.putIntUnaligned(array, UNSAFE.ARRAY_BYTE_BASE_OFFSET+1, 0); + array[0] = 0; + } + + static void test3(byte[] array) { + array[0] = 0; + UNSAFE.putIntUnaligned(array, UNSAFE.ARRAY_BYTE_BASE_OFFSET+2, 0); + array[0] = 0; + } + + static void test4(byte[] array) { + array[0] = 0; + UNSAFE.putIntUnaligned(array, UNSAFE.ARRAY_BYTE_BASE_OFFSET+3, 0); + array[0] = 0; + } + + static void test5(byte[] array) { + array[0] = 0; + UNSAFE.putInt(array, UNSAFE.ARRAY_BYTE_BASE_OFFSET, 0); + array[0] = 0; + } + + // unaligned access and non escaping allocation + static void test6() { + byte[] array = new byte[10]; + UNSAFE.putIntUnaligned(array, UNSAFE.ARRAY_BYTE_BASE_OFFSET+1, -1); + array[0] = 0; + } + + // unaligned access and non escaping allocation + static int test7() { + byte[] array = new byte[10]; + UNSAFE.putIntUnaligned(array, UNSAFE.ARRAY_BYTE_BASE_OFFSET+1, -1); + array[0] = 0; + array[2] = 0; + return array[0] + array[1] + array[2] + array[3] + array[4]; + } + + // unaligned access with vectorization + static void test8(int[] src1, int[] src2, int[] dst) { + for (int i = 0; i < dst.length-1; i++) { + int res = src1[i] + src2[i]; + UNSAFE.putIntUnaligned(dst, UNSAFE.ARRAY_INT_BASE_OFFSET + i*4+1, res); + } + } + + static public void main(String[] args) throws Exception { + byte[] byte_array = new byte[100]; + int[] int_array = new int[100]; + Object[] obj_array = new Object[100]; + TestUnsafeUnalignedMismatchedAccesses test = new TestUnsafeUnalignedMismatchedAccesses(); + for (int i = 0; i < 20000; i++) { + test1(byte_array); + test2(byte_array); + test3(byte_array); + test4(byte_array); + test5(byte_array); + test6(); + test7(); + test8(int_array, int_array, int_array); + } + } +} diff --git a/hotspot/test/compiler/jvmci/common/CTVMUtilities.java b/hotspot/test/compiler/jvmci/common/CTVMUtilities.java index 2563b35e5a5..67a1aef7a19 100644 --- a/hotspot/test/compiler/jvmci/common/CTVMUtilities.java +++ b/hotspot/test/compiler/jvmci/common/CTVMUtilities.java @@ -23,10 +23,25 @@ package compiler.jvmci.common; +import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Executable; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +import jdk.internal.org.objectweb.asm.ClassReader; +import jdk.internal.org.objectweb.asm.ClassVisitor; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.Label; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; +import jdk.internal.org.objectweb.asm.tree.ClassNode; +import jdk.test.lib.Utils; import jdk.vm.ci.code.InstalledCode; import jdk.vm.ci.hotspot.CompilerToVMHelper; import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; @@ -71,4 +86,73 @@ public class CTVMUtilities { this.entryPoint = entryPoint; } } + public static Map getBciToLineNumber(Executable method) { + Map lineNumbers = new TreeMap<>(); + try { + ClassReader cr = new ClassReader(method.getDeclaringClass() + .getName()); + ClassNode cn = new ClassNode(); + cr.accept(cn, ClassReader.EXPAND_FRAMES); + + Map labels = new HashMap<>(); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + ClassVisitor cv = new ClassVisitorForLabels(cw, labels, method); + cr.accept(cv, ClassReader.EXPAND_FRAMES); + labels.forEach((k, v) -> lineNumbers.put(k.getOffset(), v)); + } catch (IOException e) { + throw new Error("TEST BUG " + e, e); + } + boolean isEmptyMethod = Modifier.isAbstract(method.getModifiers()) + || Modifier.isNative(method.getModifiers()); + if (lineNumbers.isEmpty() && !isEmptyMethod) { + throw new Error(method + " doesn't contains the line numbers table " + +"(the method marked neither abstract nor native)"); + } + return lineNumbers; + } + + private static class ClassVisitorForLabels extends ClassVisitor { + private final Map lineNumbers; + private final String targetName; + private final String targetDesc; + + public ClassVisitorForLabels(ClassWriter cw, Map lines, + Executable target) { + super(Opcodes.ASM5, cw); + this.lineNumbers = lines; + + StringBuilder builder = new StringBuilder("("); + for (Parameter parameter : target.getParameters()) { + builder.append(Utils.toJVMTypeSignature(parameter.getType())); + } + builder.append(")"); + if (target instanceof Constructor) { + targetName = ""; + builder.append("V"); + } else { + targetName = target.getName(); + builder.append(Utils.toJVMTypeSignature( + ((Method) target).getReturnType())); + } + targetDesc = builder.toString(); + } + + @Override + public final MethodVisitor visitMethod(int access, String name, + String desc, String signature, + String[] exceptions) { + MethodVisitor mv = cv.visitMethod(access, name, desc, signature, + exceptions); + if (targetDesc.equals(desc) && targetName.equals(name)) { + return new MethodVisitor(Opcodes.ASM5, mv) { + @Override + public void visitLineNumber(int i, Label label) { + super.visitLineNumber(i, label); + lineNumbers.put(label, i); + } + }; + } + return mv; + } + } } diff --git a/hotspot/test/compiler/jvmci/compilerToVM/AllocateCompileIdTest.java b/hotspot/test/compiler/jvmci/compilerToVM/AllocateCompileIdTest.java index 7d36796ecaf..5e28832df8b 100644 --- a/hotspot/test/compiler/jvmci/compilerToVM/AllocateCompileIdTest.java +++ b/hotspot/test/compiler/jvmci/compilerToVM/AllocateCompileIdTest.java @@ -34,7 +34,6 @@ * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. * -XX:-BackgroundCompilation - -XX:+LogCompilation * compiler.jvmci.compilerToVM.AllocateCompileIdTest */ @@ -45,22 +44,21 @@ import compiler.jvmci.common.CTVMUtilities; import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.HashSet; +import java.util.stream.Collectors; +import java.util.stream.Stream; -import compiler.jvmci.common.testcases.TestCase; import jdk.vm.ci.hotspot.CompilerToVMHelper; import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; import jdk.test.lib.Asserts; import jdk.test.lib.Pair; import jdk.test.lib.Utils; -import sun.hotspot.WhiteBox; import sun.hotspot.code.NMethod; public class AllocateCompileIdTest { + private static final int SOME_REPEAT_VALUE = 5; private final HashSet ids = new HashSet<>(); public static void main(String[] args) { @@ -69,7 +67,6 @@ public class AllocateCompileIdTest { createTestCasesIncorrectBci().forEach(test::runSanityIncorrectTest); } - private static List createTestCasesCorrectBci() { List result = new ArrayList<>(); try { @@ -84,29 +81,29 @@ public class AllocateCompileIdTest { return result; } - private static List>> createTestCasesIncorrectBci() { List>> result = new ArrayList<>(); - try { Class aClass = DummyClass.class; Object receiver = new DummyClass(); Method method = aClass.getMethod("dummyInstanceFunction"); // greater than bytecode.length - int[] bcis = new int[] {30, 50, 200}; - for (int bci : bcis) { - result.add(new Pair<>( - new CompileCodeTestCase(receiver, method, bci), - IllegalArgumentException.class)); - } - bcis = new int[] {-4, -50, -200}; - for (int bci : bcis) { - result.add(new Pair<>( - new CompileCodeTestCase(receiver, method, bci), - IllegalArgumentException.class)); - } + byte[] bytecode = CompilerToVMHelper.getBytecode(CTVMUtilities + .getResolvedMethod(method)); + Stream.of( + // greater than bytecode.length + bytecode.length + 4, + bytecode.length + 50, + bytecode.length + 200, + // negative cases + -4, -50, -200) + .map(bci -> new Pair>( + new CompileCodeTestCase(receiver, method, bci), + IllegalArgumentException.class)) + .collect(Collectors.toList()); } catch (NoSuchMethodException e) { throw new Error("TEST BUG : " + e.getMessage(), e); } @@ -117,27 +114,20 @@ public class AllocateCompileIdTest { System.out.println(testCase); Executable aMethod = testCase.executable; // to generate ciTypeFlow - System.out.println(testCase.invoke(Utils.getNullValues(aMethod.getParameterTypes()))); + testCase.invoke(Utils.getNullValues(aMethod.getParameterTypes())); int bci = testCase.bci; HotSpotResolvedJavaMethod method = CTVMUtilities .getResolvedMethod(aMethod); - int wbCompileID = getWBCompileID(testCase); - int id = CompilerToVMHelper.allocateCompileId(method, bci); - Asserts.assertNE(id, 0, testCase + " : zero compile id"); - - if (wbCompileID > 0) { + for (int i = 0; i < SOME_REPEAT_VALUE; ++i) { + int wbCompileID = getWBCompileID(testCase); + int id = CompilerToVMHelper.allocateCompileId(method, bci); + Asserts.assertNE(id, 0, testCase + " : zero compile id"); Asserts.assertGT(id, wbCompileID, testCase + " : allocated 'compile id' not greater than existed"); - if (!ids.add(wbCompileID)) { - throw new AssertionError(String.format( - "%s : vm compilation allocated existed id -- %d", - testCase, id)); - } - } - if (!ids.add(id)) { - throw new AssertionError(String.format( - "%s : allocateCompileId returned existed id %d", - testCase, id)); + Asserts.assertTrue(ids.add(wbCompileID), testCase + + " : vm compilation allocated existing id " + id); + Asserts.assertTrue(ids.add(id), testCase + + " : allocateCompileId returned existing id " + id); } } @@ -156,8 +146,8 @@ public class AllocateCompileIdTest { private int getWBCompileID(CompileCodeTestCase testCase) { NMethod nm = testCase.deoptimizeAndCompile(); - if (nm == null) { - throw new Error("[TEST BUG] cannot compile method " + testCase); + if (nm == null || nm.compile_id <= 0) { + throw new Error("TEST BUG : cannot compile method " + testCase); } return nm.compile_id; } diff --git a/hotspot/test/compiler/jvmci/compilerToVM/GetLineNumberTableTest.java b/hotspot/test/compiler/jvmci/compilerToVM/GetLineNumberTableTest.java index c609d017cc8..9755b2a4564 100644 --- a/hotspot/test/compiler/jvmci/compilerToVM/GetLineNumberTableTest.java +++ b/hotspot/test/compiler/jvmci/compilerToVM/GetLineNumberTableTest.java @@ -40,25 +40,11 @@ import compiler.jvmci.common.CTVMUtilities; import compiler.jvmci.common.testcases.TestCase; import jdk.vm.ci.hotspot.CompilerToVMHelper; import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.internal.org.objectweb.asm.ClassVisitor; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.Label; -import jdk.internal.org.objectweb.asm.MethodVisitor; -import jdk.internal.org.objectweb.asm.Opcodes; -import jdk.internal.org.objectweb.asm.tree.ClassNode; import jdk.test.lib.Asserts; -import jdk.test.lib.Utils; -import java.io.IOException; -import java.lang.reflect.Constructor; import java.lang.reflect.Executable; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; import java.util.Arrays; -import java.util.HashMap; import java.util.Map; -import java.util.TreeMap; public class GetLineNumberTableTest { public static void main(String[] args) { @@ -80,79 +66,19 @@ public class GetLineNumberTableTest { } public static long[] getExpectedLineNumbers(Executable aMethod) { - try { - ClassReader cr = new ClassReader(aMethod.getDeclaringClass() - .getName()); - ClassNode cn = new ClassNode(); - cr.accept(cn, ClassReader.EXPAND_FRAMES); - - Map lineNumbers = new HashMap<>(); - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - ClassVisitor cv = new ClassVisitorForLabels(cw, lineNumbers, - aMethod); - cr.accept(cv, ClassReader.EXPAND_FRAMES); - - long[] result = null; - if (!lineNumbers.isEmpty()) { - Map labels = new TreeMap<>(); - lineNumbers.forEach((k, v) -> labels.put(k.getOffset(), v)); - - result = new long[2 * labels.size()]; - int i = 0; - for (Integer key : labels.keySet()) { - result[i++] = key.longValue(); - result[i++] = labels.get(key).longValue(); - } + Map bciToLine = CTVMUtilities + .getBciToLineNumber(aMethod); + long[] result = null; + if (!bciToLine.isEmpty()) { + result = new long[2 * bciToLine.size()]; + int i = 0; + for (Integer key : bciToLine.keySet()) { + result[i++] = key.longValue(); + result[i++] = bciToLine.get(key).longValue(); } - // compilerToVM::getLineNumberTable returns null in case empty table - return result; - } catch (IOException e) { - throw new Error("TEST BUG " + e, e); } + // compilerToVM::getLineNumberTable returns null in case empty table + return result; } - private static class ClassVisitorForLabels extends ClassVisitor { - private final Map lineNumbers; - private final String targetName; - private final String targetDesc; - - public ClassVisitorForLabels(ClassWriter cw, Map lines, - Executable target) { - super(Opcodes.ASM5, cw); - this.lineNumbers = lines; - - StringBuilder builder = new StringBuilder("("); - for (Parameter parameter : target.getParameters()) { - builder.append(Utils.toJVMTypeSignature(parameter.getType())); - } - builder.append(")"); - if (target instanceof Constructor) { - targetName = ""; - builder.append("V"); - } else { - targetName = target.getName(); - builder.append(Utils.toJVMTypeSignature( - ((Method) target).getReturnType())); - } - targetDesc = builder.toString(); - } - - @Override - public final MethodVisitor visitMethod(int access, String name, - String desc, String signature, - String[] exceptions) { - MethodVisitor mv = cv.visitMethod(access, name, desc, signature, - exceptions); - if (targetDesc.equals(desc) && targetName.equals(name)) { - return new MethodVisitor(Opcodes.ASM5, mv) { - @Override - public void visitLineNumber(int i, Label label) { - super.visitLineNumber(i, label); - lineNumbers.put(label, i); - } - }; - } - return mv; - } - } } diff --git a/hotspot/test/compiler/jvmci/compilerToVM/GetStackTraceElementTest.java b/hotspot/test/compiler/jvmci/compilerToVM/GetStackTraceElementTest.java index 3bbb5b29a72..9888931402a 100644 --- a/hotspot/test/compiler/jvmci/compilerToVM/GetStackTraceElementTest.java +++ b/hotspot/test/compiler/jvmci/compilerToVM/GetStackTraceElementTest.java @@ -41,6 +41,8 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; + +import compiler.jvmci.common.testcases.TestCase; import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; import jdk.vm.ci.hotspot.CompilerToVMHelper; import jdk.test.lib.Asserts; @@ -56,22 +58,49 @@ public class GetStackTraceElementTest { HotSpotResolvedJavaMethod method = CTVMUtilities .getResolvedMethod(aMethod); String className = aMethod.getDeclaringClass().getName(); + String methodName = aMethod.getName().equals(className) + ? "" + : aMethod.getName(); + String fileName = getFileName(className); + Map bciWithLineNumber = CTVMUtilities + .getBciToLineNumber(aMethod); + boolean isNative = Modifier.isNative(aMethod.getModifiers()); + int lineNumber = -1; + for (int bci : bcis) { + StackTraceElement ste = CompilerToVMHelper + .getStackTraceElement(method, bci); + Asserts.assertNotNull(ste, aMethod + " : got null StackTraceElement" + + " at bci " + bci); + Asserts.assertEQ(className, ste.getClassName(), aMethod + + " : unexpected class name"); + Asserts.assertEQ(fileName, ste.getFileName(), aMethod + + " : unexpected filename"); + Asserts.assertEQ(methodName, ste.getMethodName(), aMethod + + " : unexpected method name"); + Asserts.assertEQ(isNative, ste.isNativeMethod(), aMethod + + " : unexpected 'isNative' value"); + if (bciWithLineNumber.size() > 0) { + if (bciWithLineNumber.containsKey(bci)) { + lineNumber = bciWithLineNumber.get(bci); + } + Asserts.assertEQ(lineNumber, ste.getLineNumber(), aMethod + + " : unexpected line number"); + } else { + // native and abstract function + Asserts.assertGT(0, ste.getLineNumber(), + aMethod + " : unexpected line number for abstract " + + "or native method"); + } + } + + } + + private static String getFileName(String className) { int lastDot = className.lastIndexOf('.'); int firstDol = className.contains("$") ? className.indexOf('$') : className.length(); - String fileName = className.substring(lastDot + 1, firstDol) + ".java"; - for (int bci : bcis) { - StackTraceElement ste = CompilerToVMHelper - .getStackTraceElement(method, bci); - Asserts.assertNotNull(ste); - Asserts.assertEQ(ste.getClassName(), className); - Asserts.assertEQ(ste.getFileName(), fileName); - Asserts.assertEQ(ste.getMethodName(), aMethod.getName()); - Asserts.assertEQ(ste.isNativeMethod(), Modifier - .isNative(aMethod.getModifiers())); - } - + return className.substring(lastDot + 1, firstDol) + ".java"; } private static Map createTestCases() { @@ -86,6 +115,13 @@ public class GetStackTraceElementTest { aMethod = aClass.getDeclaredMethod("dummyEmptyFunction"); bci = new int[] {0}; testCases.put(aMethod, bci); + + aMethod = aClass.getDeclaredMethod("nativeFunction"); + bci = new int[] {0}; + testCases.put(aMethod, bci); + + TestCase.getAllExecutables() + .forEach(c -> testCases.put(c, new int[] {0})); } catch (NoSuchMethodException e) { throw new Error("TEST BUG : test method not found", e); } @@ -102,5 +138,7 @@ public class GetStackTraceElementTest { } public void dummyEmptyFunction() {} + + public native void nativeFunction(); } } diff --git a/hotspot/test/compiler/jvmci/compilerToVM/IsMatureTest.java b/hotspot/test/compiler/jvmci/compilerToVM/IsMatureTest.java index b4f89d67877..77f51447628 100644 --- a/hotspot/test/compiler/jvmci/compilerToVM/IsMatureTest.java +++ b/hotspot/test/compiler/jvmci/compilerToVM/IsMatureTest.java @@ -35,9 +35,11 @@ * -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI * compiler.jvmci.compilerToVM.IsMatureTest */ + package compiler.jvmci.compilerToVM; import compiler.jvmci.common.testcases.SimpleClass; +import compiler.whitebox.CompilerWhiteBoxTest; import jdk.vm.ci.hotspot.CompilerToVMHelper; import jdk.test.lib.Asserts; import sun.hotspot.WhiteBox; @@ -46,6 +48,10 @@ import java.lang.reflect.Executable; public class IsMatureTest { private static final WhiteBox WB = WhiteBox.getWhiteBox(); + private static final boolean IS_XCOMP + = System.getProperty("java.vm.info").contains("compiled mode"); + private static final boolean TIERED + = WB.getBooleanVMFlag("TieredCompilation"); public static void main(String[] args) throws Exception { new IsMatureTest().test(); @@ -54,23 +60,21 @@ public class IsMatureTest { public void test() throws Exception { SimpleClass sclass = new SimpleClass(); Executable method = SimpleClass.class.getDeclaredMethod("testMethod"); - long metaspaceMethodData = WB.getMethodData(method); - Asserts.assertEQ(metaspaceMethodData, 0L, "MDO should be null for " - + "never invoked method"); - boolean isMature = CompilerToVMHelper.isMature(metaspaceMethodData); - Asserts.assertFalse(isMature, "null MDO can't be mature"); - for (int i = 0; i < 1000; i++) { + long methodData = WB.getMethodData(method); + boolean isMature = CompilerToVMHelper.isMature(methodData); + Asserts.assertEQ(methodData, 0L, + "Never invoked method can't have method data"); + Asserts.assertFalse(isMature, "Never invoked method can't be mature"); + for (int i = 0; i < CompilerWhiteBoxTest.THRESHOLD; i++) { sclass.testMethod(); } - // warmed up, mdo should be ready for now - metaspaceMethodData = WB.getMethodData(method); - Asserts.assertNE(metaspaceMethodData, 0L, - "MDO should be available after 1000 calls"); - for (int i = 0; i < 100_000; i++) { - sclass.testMethod(); - } - isMature = CompilerToVMHelper.isMature(metaspaceMethodData); - Asserts.assertTrue(isMature, - "a 100_000 times invoked method should be mature"); + methodData = WB.getMethodData(method); + isMature = CompilerToVMHelper.isMature(methodData); + Asserts.assertNE(methodData, 0L, + "Multiple times invoked method should have method data"); + /* a method is not mature for -Xcomp and -Tiered, + see NonTieredCompPolicy::is_mature */ + Asserts.assertEQ(isMature, !(IS_XCOMP && !TIERED), + "Unexpected isMature state for multiple times invoked method"); } } diff --git a/hotspot/test/compiler/jvmci/compilerToVM/ReprofileTest.java b/hotspot/test/compiler/jvmci/compilerToVM/ReprofileTest.java index d010e67e21f..2a81af5bfc4 100644 --- a/hotspot/test/compiler/jvmci/compilerToVM/ReprofileTest.java +++ b/hotspot/test/compiler/jvmci/compilerToVM/ReprofileTest.java @@ -45,18 +45,15 @@ import compiler.jvmci.common.CTVMUtilities; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; -import java.util.Random; + +import compiler.whitebox.CompilerWhiteBoxTest; import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; import jdk.vm.ci.hotspot.CompilerToVMHelper; import jdk.vm.ci.meta.ProfilingInfo; import jdk.test.lib.Asserts; -import jdk.test.lib.Utils; -import sun.hotspot.WhiteBox; public class ReprofileTest { - private static final WhiteBox WB = WhiteBox.getWhiteBox(); - public static void main(String[] args) { List testCases = createTestCases(); testCases.forEach(ReprofileTest::runSanityTest); @@ -67,10 +64,10 @@ public class ReprofileTest { try { Class aClass = DummyClass.class; - testCases.add(aClass.getMethod("withLoop")); + testCases.add(aClass.getMethod("dummyInstanceFunction")); aClass = DummyClass.class; - testCases.add(aClass.getDeclaredMethod("dummyFunction")); + testCases.add(aClass.getMethod("dummyFunction")); } catch (NoSuchMethodException e) { throw new Error("TEST BUG " + e.getMessage(), e); } @@ -78,17 +75,17 @@ public class ReprofileTest { } private static void runSanityTest(Method aMethod) { + System.out.println(aMethod); HotSpotResolvedJavaMethod method = CTVMUtilities .getResolvedMethod(aMethod); ProfilingInfo startProfile = method.getProfilingInfo(); Asserts.assertFalse(startProfile.isMature(), aMethod - + " : profiling info is mature in the begging"); + + " : profiling info is mature in the beginning"); - long compileThreshold = (Long) WB.getVMFlag("CompileThreshold"); // make interpreter to profile this method try { Object obj = aMethod.getDeclaringClass().newInstance(); - for (long i = 0; i < compileThreshold; i++) { + for (long i = 0; i < CompilerWhiteBoxTest.THRESHOLD; i++) { aMethod.invoke(obj); } } catch (ReflectiveOperationException e) { @@ -99,10 +96,10 @@ public class ReprofileTest { Asserts.assertNE(startProfile.toString(), compProfile.toString(), String.format("%s : profiling info wasn't changed after " + "%d invocations", - aMethod, compileThreshold)); + aMethod, CompilerWhiteBoxTest.THRESHOLD)); Asserts.assertTrue(compProfile.isMature(), String.format("%s is not mature after %d invocations", - aMethod, compileThreshold)); + aMethod, CompilerWhiteBoxTest.THRESHOLD)); CompilerToVMHelper.reprofile(method); ProfilingInfo reprofiledProfile = method.getProfilingInfo(); diff --git a/hotspot/test/compiler/jvmci/errors/CodeInstallerTest.java b/hotspot/test/compiler/jvmci/errors/CodeInstallerTest.java new file mode 100644 index 00000000000..56b32cd10c6 --- /dev/null +++ b/hotspot/test/compiler/jvmci/errors/CodeInstallerTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015, 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. + */ + +package compiler.jvmci.errors; + +import java.lang.reflect.Method; + +import jdk.vm.ci.code.Architecture; +import jdk.vm.ci.code.CodeCacheProvider; +import jdk.vm.ci.code.CompilationResult; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.PlatformKind; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.hotspot.HotSpotConstantReflectionProvider; +import jdk.vm.ci.runtime.JVMCI; +import jdk.vm.ci.runtime.JVMCIBackend; + +import org.junit.Assert; + +public class CodeInstallerTest { + + protected final Architecture arch; + protected final CodeCacheProvider codeCache; + protected final MetaAccessProvider metaAccess; + protected final HotSpotConstantReflectionProvider constantReflection; + + protected final ResolvedJavaMethod dummyMethod; + + public static void dummyMethod() { + } + + protected CodeInstallerTest() { + JVMCIBackend backend = JVMCI.getRuntime().getHostJVMCIBackend(); + metaAccess = backend.getMetaAccess(); + codeCache = backend.getCodeCache(); + constantReflection = (HotSpotConstantReflectionProvider) backend.getConstantReflection(); + arch = codeCache.getTarget().arch; + + Method method = null; + try { + method = CodeInstallerTest.class.getMethod("dummyMethod"); + } catch (NoSuchMethodException e) { + Assert.fail(); + } + + dummyMethod = metaAccess.lookupJavaMethod(method); + } + + protected void installCode(CompilationResult result) { + codeCache.addCode(dummyMethod, result, null, null); + } + + protected CompilationResult createEmptyCompilationResult() { + CompilationResult ret = new CompilationResult(); + ret.setTotalFrameSize(0); + return ret; + } + + protected Register getRegister(PlatformKind kind, int index) { + Register[] allRegs = arch.getAvailableValueRegisters(); + for (int i = 0; i < allRegs.length; i++) { + if (arch.canStoreValue(allRegs[i].getRegisterCategory(), kind)) { + if (index-- == 0) { + return allRegs[i]; + } + } + } + return null; + } +} diff --git a/hotspot/test/compiler/jvmci/errors/TestInvalidCompilationResult.java b/hotspot/test/compiler/jvmci/errors/TestInvalidCompilationResult.java new file mode 100644 index 00000000000..89d6114cccd --- /dev/null +++ b/hotspot/test/compiler/jvmci/errors/TestInvalidCompilationResult.java @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2015, 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. + */ + +/** + * @test + * @requires (os.simpleArch == "x64" | os.simpleArch == "sparcv9") & os.arch != "aarch64" + * @compile CodeInstallerTest.java + * @run junit/othervm -da:jdk.vm.ci... -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI compiler.jvmci.errors.TestInvalidCompilationResult + */ + +package compiler.jvmci.errors; + +import static jdk.vm.ci.code.CompilationResult.ConstantReference; +import static jdk.vm.ci.code.CompilationResult.DataPatch; +import static jdk.vm.ci.code.CompilationResult.DataSectionReference; +import static jdk.vm.ci.code.CompilationResult.Infopoint; +import static jdk.vm.ci.code.CompilationResult.Reference; +import static jdk.vm.ci.code.DataSection.Data; +import static jdk.vm.ci.code.DataSection.DataBuilder; +import static jdk.vm.ci.meta.Assumptions.Assumption; + +import jdk.vm.ci.code.CompilationResult; +import jdk.vm.ci.code.InfopointReason; +import jdk.vm.ci.common.JVMCIError; +import jdk.vm.ci.hotspot.HotSpotConstant; +import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.VMConstant; + +import org.junit.Test; + +/** + * Tests for errors in the code installer. + */ +public class TestInvalidCompilationResult extends CodeInstallerTest { + + private static class InvalidAssumption extends Assumption { + } + + private static class InvalidVMConstant implements VMConstant { + + public boolean isDefaultForKind() { + return false; + } + + public String toValueString() { + return null; + } + } + + private static class InvalidReference extends Reference { + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object obj) { + return false; + } + } + + @Test(expected = JVMCIError.class) + public void testInvalidAssumption() { + CompilationResult result = createEmptyCompilationResult(); + result.setAssumptions(new Assumption[]{new InvalidAssumption()}); + installCode(result); + } + + @Test(expected = JVMCIError.class) + public void testInvalidAlignment() { + CompilationResult result = createEmptyCompilationResult(); + result.getDataSection().insertData(new Data(7, 1, DataBuilder.zero(1))); + installCode(result); + } + + @Test(expected = NullPointerException.class) + public void testNullDataPatchInDataSection() { + CompilationResult result = createEmptyCompilationResult(); + Data data = new Data(1, 1, (buffer, patch) -> { + patch.accept(null); + buffer.put((byte) 0); + }); + result.getDataSection().insertData(data); + installCode(result); + } + + @Test(expected = NullPointerException.class) + public void testNullReferenceInDataSection() { + CompilationResult result = createEmptyCompilationResult(); + Data data = new Data(1, 1, (buffer, patch) -> { + patch.accept(new DataPatch(buffer.position(), null)); + buffer.put((byte) 0); + }); + result.getDataSection().insertData(data); + installCode(result); + } + + @Test(expected = JVMCIError.class) + public void testInvalidDataSectionReference() { + CompilationResult result = createEmptyCompilationResult(); + DataSectionReference ref = result.getDataSection().insertData(new Data(1, 1, DataBuilder.zero(1))); + Data data = new Data(1, 1, (buffer, patch) -> { + patch.accept(new DataPatch(buffer.position(), ref)); + buffer.put((byte) 0); + }); + result.getDataSection().insertData(data); + installCode(result); + } + + @Test(expected = JVMCIError.class) + public void testInvalidNarrowMethodInDataSection() { + CompilationResult result = createEmptyCompilationResult(); + HotSpotConstant c = (HotSpotConstant) dummyMethod.getEncoding(); + Data data = new Data(4, 4, (buffer, patch) -> { + patch.accept(new DataPatch(buffer.position(), new ConstantReference((VMConstant) c.compress()))); + buffer.putInt(0); + }); + result.getDataSection().insertData(data); + installCode(result); + } + + @Test(expected = NullPointerException.class) + public void testNullConstantInDataSection() { + CompilationResult result = createEmptyCompilationResult(); + Data data = new Data(1, 1, (buffer, patch) -> { + patch.accept(new DataPatch(buffer.position(), new ConstantReference(null))); + }); + result.getDataSection().insertData(data); + installCode(result); + } + + @Test(expected = JVMCIError.class) + public void testInvalidConstantInDataSection() { + CompilationResult result = createEmptyCompilationResult(); + Data data = new Data(1, 1, (buffer, patch) -> { + patch.accept(new DataPatch(buffer.position(), new ConstantReference(new InvalidVMConstant()))); + }); + result.getDataSection().insertData(data); + installCode(result); + } + + @Test(expected = NullPointerException.class) + public void testNullReferenceInCode() { + CompilationResult result = createEmptyCompilationResult(); + result.recordDataPatch(0, null); + installCode(result); + } + + @Test(expected = NullPointerException.class) + public void testNullConstantInCode() { + CompilationResult result = createEmptyCompilationResult(); + result.recordDataPatch(0, new ConstantReference(null)); + installCode(result); + } + + @Test(expected = JVMCIError.class) + public void testInvalidConstantInCode() { + CompilationResult result = createEmptyCompilationResult(); + result.recordDataPatch(0, new ConstantReference(new InvalidVMConstant())); + installCode(result); + } + + @Test(expected = JVMCIError.class) + public void testInvalidReference() { + CompilationResult result = createEmptyCompilationResult(); + result.recordDataPatch(0, new InvalidReference()); + installCode(result); + } + + @Test(expected = JVMCIError.class) + public void testOutOfBoundsDataSectionReference() { + CompilationResult result = createEmptyCompilationResult(); + DataSectionReference ref = new DataSectionReference(); + ref.setOffset(0x1000); + result.recordDataPatch(0, ref); + installCode(result); + } + + @Test(expected = JVMCIError.class) + public void testInvalidMark() { + CompilationResult result = createEmptyCompilationResult(); + result.recordMark(0, new Object()); + installCode(result); + } + + @Test(expected = JVMCIError.class) + public void testInvalidMarkInt() { + CompilationResult result = createEmptyCompilationResult(); + result.recordMark(0, -1); + installCode(result); + } + + @Test(expected = NullPointerException.class) + public void testNullInfopoint() { + CompilationResult result = createEmptyCompilationResult(); + result.addInfopoint(null); + installCode(result); + } + + @Test(expected = JVMCIError.class) + public void testUnknownInfopointReason() { + CompilationResult result = createEmptyCompilationResult(); + result.addInfopoint(new Infopoint(0, null, InfopointReason.UNKNOWN)); + installCode(result); + } + + @Test(expected = JVMCIError.class) + public void testInfopointMissingDebugInfo() { + CompilationResult result = createEmptyCompilationResult(); + result.addInfopoint(new Infopoint(0, null, InfopointReason.METHOD_START)); + installCode(result); + } + + @Test(expected = JVMCIError.class) + public void testSafepointMissingDebugInfo() { + CompilationResult result = createEmptyCompilationResult(); + result.addInfopoint(new Infopoint(0, null, InfopointReason.SAFEPOINT)); + installCode(result); + } +} diff --git a/hotspot/test/compiler/jvmci/errors/TestInvalidDebugInfo.java b/hotspot/test/compiler/jvmci/errors/TestInvalidDebugInfo.java new file mode 100644 index 00000000000..cc387ed195f --- /dev/null +++ b/hotspot/test/compiler/jvmci/errors/TestInvalidDebugInfo.java @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2015, 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. + */ + +/** + * @test + * @requires (os.simpleArch == "x64" | os.simpleArch == "sparcv9") & os.arch != "aarch64" + * @compile CodeInstallerTest.java + * @run junit/othervm -da:jdk.vm.ci... -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI compiler.jvmci.errors.TestInvalidDebugInfo + */ + +package compiler.jvmci.errors; + +import static jdk.vm.ci.code.CompilationResult.Infopoint; + +import jdk.vm.ci.code.BytecodeFrame; +import jdk.vm.ci.code.CompilationResult; +import jdk.vm.ci.code.DebugInfo; +import jdk.vm.ci.code.InfopointReason; +import jdk.vm.ci.code.Location; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.code.VirtualObject; +import jdk.vm.ci.hotspot.HotSpotReferenceMap; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.JavaValue; +import jdk.vm.ci.meta.LIRKind; +import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.Value; +import jdk.vm.ci.common.JVMCIError; + +import org.junit.Test; + +/** + * Tests for errors in debug info. + */ +public class TestInvalidDebugInfo extends CodeInstallerTest { + + private static class UnknownJavaValue implements JavaValue { + } + + private void test(JavaValue[] values, JavaKind[] slotKinds, int locals, int stack, int locks) { + test(null, values, slotKinds, locals, stack, locks); + } + + private void test(VirtualObject[] vobj, JavaValue[] values, JavaKind[] slotKinds, int locals, int stack, int locks) { + BytecodeFrame frame = new BytecodeFrame(null, dummyMethod, 0, false, false, values, slotKinds, locals, stack, locks); + DebugInfo info = new DebugInfo(frame, vobj); + info.setReferenceMap(new HotSpotReferenceMap(new Location[0], new Location[0], new int[0], 8)); + + CompilationResult result = createEmptyCompilationResult(); + result.addInfopoint(new Infopoint(0, info, InfopointReason.SAFEPOINT)); + installCode(result); + } + + @Test(expected = NullPointerException.class) + public void testNullValues() { + test(null, new JavaKind[0], 0, 0, 0); + } + + @Test(expected = NullPointerException.class) + public void testNullSlotKinds() { + test(new JavaValue[0], null, 0, 0, 0); + } + + @Test(expected = JVMCIError.class) + public void testUnexpectedScopeValuesLength() { + test(new JavaValue[]{JavaConstant.FALSE}, new JavaKind[0], 0, 0, 0); + } + + @Test(expected = JVMCIError.class) + public void testUnexpectedScopeSlotKindsLength() { + test(new JavaValue[0], new JavaKind[]{JavaKind.Boolean}, 0, 0, 0); + } + + @Test(expected = NullPointerException.class) + public void testNullValue() { + test(new JavaValue[]{null}, new JavaKind[]{JavaKind.Int}, 1, 0, 0); + } + + @Test(expected = NullPointerException.class) + public void testNullSlotKind() { + test(new JavaValue[]{JavaConstant.INT_0}, new JavaKind[]{null}, 1, 0, 0); + } + + @Test(expected = NullPointerException.class) + public void testNullMonitor() { + test(new JavaValue[]{null}, new JavaKind[0], 0, 0, 1); + } + + @Test(expected = JVMCIError.class) + public void testWrongMonitorType() { + test(new JavaValue[]{JavaConstant.INT_0}, new JavaKind[0], 0, 0, 1); + } + + @Test(expected = JVMCIError.class) + public void testUnexpectedIllegalValue() { + test(new JavaValue[]{Value.ILLEGAL}, new JavaKind[]{JavaKind.Int}, 1, 0, 0); + } + + @Test(expected = JVMCIError.class) + public void testUnexpectedTypeInCPURegister() { + Register reg = getRegister(arch.getPlatformKind(JavaKind.Int), 0); + test(new JavaValue[]{reg.asValue()}, new JavaKind[]{JavaKind.Illegal}, 1, 0, 0); + } + + @Test(expected = JVMCIError.class) + public void testUnexpectedTypeInFloatRegister() { + Register reg = getRegister(arch.getPlatformKind(JavaKind.Float), 0); + test(new JavaValue[]{reg.asValue()}, new JavaKind[]{JavaKind.Illegal}, 1, 0, 0); + } + + @Test(expected = JVMCIError.class) + public void testUnexpectedTypeOnStack() { + LIRKind kind = codeCache.getTarget().getLIRKind(JavaKind.Int); + StackSlot value = StackSlot.get(kind, 8, false); + test(new JavaValue[]{value}, new JavaKind[]{JavaKind.Illegal}, 1, 0, 0); + } + + @Test(expected = JVMCIError.class) + public void testWrongConstantType() { + test(new JavaValue[]{JavaConstant.INT_0}, new JavaKind[]{JavaKind.Object}, 1, 0, 0); + } + + @Test(expected = JVMCIError.class) + public void testUnsupportedConstantType() { + test(new JavaValue[]{JavaConstant.forShort((short) 0)}, new JavaKind[]{JavaKind.Short}, 1, 0, 0); + } + + @Test(expected = JVMCIError.class) + public void testUnexpectedNull() { + test(new JavaValue[]{JavaConstant.NULL_POINTER}, new JavaKind[]{JavaKind.Int}, 1, 0, 0); + } + + @Test(expected = JVMCIError.class) + public void testUnexpectedObject() { + JavaValue wrapped = constantReflection.forObject(this); + test(new JavaValue[]{wrapped}, new JavaKind[]{JavaKind.Int}, 1, 0, 0); + } + + @Test(expected = JVMCIError.class) + public void testUnknownJavaValue() { + test(new JavaValue[]{new UnknownJavaValue()}, new JavaKind[]{JavaKind.Int}, 1, 0, 0); + } + + @Test(expected = JVMCIError.class) + public void testMissingIllegalAfterDouble() { + test(new JavaValue[]{JavaConstant.DOUBLE_0, JavaConstant.INT_0}, new JavaKind[]{JavaKind.Double, JavaKind.Int}, 2, 0, 0); + } + + @Test(expected = JVMCIError.class) + public void testInvalidVirtualObjectId() { + ResolvedJavaType obj = metaAccess.lookupJavaType(Object.class); + VirtualObject o = VirtualObject.get(obj, 5); + o.setValues(new JavaValue[0], new JavaKind[0]); + + test(new VirtualObject[]{o}, new JavaValue[0], new JavaKind[0], 0, 0, 0); + } + + @Test(expected = JVMCIError.class) + public void testDuplicateVirtualObject() { + ResolvedJavaType obj = metaAccess.lookupJavaType(Object.class); + VirtualObject o1 = VirtualObject.get(obj, 0); + o1.setValues(new JavaValue[0], new JavaKind[0]); + + VirtualObject o2 = VirtualObject.get(obj, 0); + o2.setValues(new JavaValue[0], new JavaKind[0]); + + test(new VirtualObject[]{o1, o2}, new JavaValue[0], new JavaKind[0], 0, 0, 0); + } + + @Test(expected = JVMCIError.class) + public void testUnexpectedVirtualObject() { + ResolvedJavaType obj = metaAccess.lookupJavaType(Object.class); + VirtualObject o = VirtualObject.get(obj, 0); + o.setValues(new JavaValue[0], new JavaKind[0]); + + test(new VirtualObject[]{o}, new JavaValue[]{o}, new JavaKind[]{JavaKind.Int}, 1, 0, 0); + } + + @Test(expected = JVMCIError.class) + public void testUndefinedVirtualObject() { + ResolvedJavaType obj = metaAccess.lookupJavaType(Object.class); + VirtualObject o0 = VirtualObject.get(obj, 0); + o0.setValues(new JavaValue[0], new JavaKind[0]); + + VirtualObject o1 = VirtualObject.get(obj, 1); + o1.setValues(new JavaValue[0], new JavaKind[0]); + + test(new VirtualObject[]{o0}, new JavaValue[]{o1}, new JavaKind[]{JavaKind.Object}, 1, 0, 0); + } +} diff --git a/hotspot/test/compiler/jvmci/errors/TestInvalidOopMap.java b/hotspot/test/compiler/jvmci/errors/TestInvalidOopMap.java new file mode 100644 index 00000000000..6291c06a7c2 --- /dev/null +++ b/hotspot/test/compiler/jvmci/errors/TestInvalidOopMap.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2015, 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. + */ + +/** + * @test + * @requires (os.simpleArch == "x64" | os.simpleArch == "sparcv9") & os.arch != "aarch64" + * @compile CodeInstallerTest.java + * @run junit/othervm -da:jdk.vm.ci... -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI compiler.jvmci.errors.TestInvalidOopMap + */ + +package compiler.jvmci.errors; + +import static jdk.vm.ci.code.CompilationResult.Infopoint; + +import jdk.vm.ci.code.BytecodePosition; +import jdk.vm.ci.code.CompilationResult; +import jdk.vm.ci.code.DebugInfo; +import jdk.vm.ci.code.InfopointReason; +import jdk.vm.ci.code.Location; +import jdk.vm.ci.code.ReferenceMap; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.hotspot.HotSpotReferenceMap; +import jdk.vm.ci.hotspot.HotSpotVMConfig; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.LIRKind; +import jdk.vm.ci.meta.PlatformKind; +import jdk.vm.ci.common.JVMCIError; + +import org.junit.Test; + +/** + * Tests for errors in oop maps. + */ +public class TestInvalidOopMap extends CodeInstallerTest { + + private static class InvalidReferenceMap extends ReferenceMap { + } + + private void test(ReferenceMap refMap) { + BytecodePosition pos = new BytecodePosition(null, dummyMethod, 0); + DebugInfo info = new DebugInfo(pos); + info.setReferenceMap(refMap); + + CompilationResult result = createEmptyCompilationResult(); + result.addInfopoint(new Infopoint(0, info, InfopointReason.SAFEPOINT)); + installCode(result); + } + + @Test(expected = NullPointerException.class) + public void testMissingReferenceMap() { + test(null); + } + + @Test(expected = JVMCIError.class) + public void testInvalidReferenceMap() { + test(new InvalidReferenceMap()); + } + + @Test(expected = NullPointerException.class) + public void testNullOops() { + test(new HotSpotReferenceMap(null, new Location[0], new int[0], 8)); + } + + @Test(expected = NullPointerException.class) + public void testNullBase() { + test(new HotSpotReferenceMap(new Location[0], null, new int[0], 8)); + } + + @Test(expected = NullPointerException.class) + public void testNullSize() { + test(new HotSpotReferenceMap(new Location[0], new Location[0], null, 8)); + } + + @Test(expected = JVMCIError.class) + public void testInvalidLength() { + test(new HotSpotReferenceMap(new Location[1], new Location[2], new int[3], 8)); + } + + @Test(expected = JVMCIError.class) + public void testInvalidShortOop() { + PlatformKind kind = arch.getPlatformKind(JavaKind.Short); + Register reg = getRegister(kind, 0); + + Location[] oops = new Location[]{Location.register(reg)}; + Location[] base = new Location[]{null}; + int[] size = new int[]{kind.getSizeInBytes()}; + + test(new HotSpotReferenceMap(oops, base, size, 8)); + } + + @Test(expected = JVMCIError.class) + public void testInvalidNarrowDerivedOop() { + if (!HotSpotVMConfig.config().useCompressedOops) { + throw new JVMCIError("skipping test"); + } + + PlatformKind kind = arch.getPlatformKind(JavaKind.Int); + Register reg = getRegister(kind, 0); + Register baseReg = getRegister(arch.getPlatformKind(JavaKind.Object), 1); + + Location[] oops = new Location[]{Location.register(reg)}; + Location[] base = new Location[]{Location.register(baseReg)}; + int[] size = new int[]{kind.getSizeInBytes()}; + + test(new HotSpotReferenceMap(oops, base, size, 8)); + } +} diff --git a/hotspot/test/compiler/jvmci/events/JvmciNotifyInstallEventTest.java b/hotspot/test/compiler/jvmci/events/JvmciNotifyInstallEventTest.java index 7ad73d87912..89a81b19990 100644 --- a/hotspot/test/compiler/jvmci/events/JvmciNotifyInstallEventTest.java +++ b/hotspot/test/compiler/jvmci/events/JvmciNotifyInstallEventTest.java @@ -111,6 +111,8 @@ public class JvmciNotifyInstallEventTest implements HotSpotVMEventListener { Asserts.assertEQ(gotInstallNotification, 1, "Got unexpected event count after 1st install attempt"); // since "empty" compilation result is ok, a second attempt should be ok + compResult = new CompilationResult(METHOD_NAME); // create another instance with fresh state + compResult.setTotalFrameSize(0); codeCache.installCode(compRequest, compResult, /* installedCode = */ null, /* speculationLog = */ null, /* isDefault = */ false); Asserts.assertEQ(gotInstallNotification, 2, diff --git a/hotspot/test/compiler/loopopts/TestCastIINoLoopLimitCheck.java b/hotspot/test/compiler/loopopts/TestCastIINoLoopLimitCheck.java index c9c985ab4ac..57a61459192 100644 --- a/hotspot/test/compiler/loopopts/TestCastIINoLoopLimitCheck.java +++ b/hotspot/test/compiler/loopopts/TestCastIINoLoopLimitCheck.java @@ -26,7 +26,7 @@ * @test * @bug 8073184 * @summary CastII that guards counted loops confuses range check elimination with LoopLimitCheck off - * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-LoopLimitCheck -XX:CompileOnly=TestCastIINoLoopLimitCheck.m -Xcomp TestCastIINoLoopLimitCheck + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:-LoopLimitCheck -XX:CompileOnly=TestCastIINoLoopLimitCheck.m -Xcomp TestCastIINoLoopLimitCheck * */ diff --git a/hotspot/test/compiler/loopopts/superword/TestBestAlign.java b/hotspot/test/compiler/loopopts/superword/TestBestAlign.java new file mode 100644 index 00000000000..9063798e63a --- /dev/null +++ b/hotspot/test/compiler/loopopts/superword/TestBestAlign.java @@ -0,0 +1,56 @@ +/* + * Copyright 2015 SAP AG. 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. + */ + +/* + * @test + * @bug 8141624 + * @summary Limit calculation of pre loop during super word optimization is wrong + * @run main/othervm TestBestAlign + * @author gunter.haug@sap.com + */ + +public class TestBestAlign { + + static final int initVal = -1; + static int intArray []; + static boolean boolArray[]; + static int limit; + static public void clear() { + for (int i = 0; i < limit; i++) { + boolArray[1] = true; + intArray[i] = initVal; + boolArray[2] = true; + } + } + + public static void main(String argv[]) throws Exception { + limit = 64; + boolArray = new boolean[8]; + intArray = new int[limit + 4]; + for (int i = 0; i < 10000000; ++i) { + if(i % 1000000 == 0) + System.out.println(i); + clear(); + } + } +} diff --git a/hotspot/test/compiler/rangechecks/TestExplicitRangeChecks.java b/hotspot/test/compiler/rangechecks/TestExplicitRangeChecks.java index bd54edef523..0053ffac156 100644 --- a/hotspot/test/compiler/rangechecks/TestExplicitRangeChecks.java +++ b/hotspot/test/compiler/rangechecks/TestExplicitRangeChecks.java @@ -25,7 +25,7 @@ * @test * @bug 8073480 * @summary explicit range checks should be recognized by C2 - * @library /testlibrary /test/lib /compiler/whitebox + * @library /testlibrary /test/lib /compiler/whitebox / * @build TestExplicitRangeChecks * @run main ClassFileInstaller sun.hotspot.WhiteBox * @run main ClassFileInstaller jdk.test.lib.Platform @@ -41,6 +41,7 @@ import sun.hotspot.WhiteBox; import sun.hotspot.code.NMethod; import jdk.test.lib.Platform; import sun.misc.Unsafe; +import compiler.whitebox.CompilerWhiteBoxTest; public class TestExplicitRangeChecks { diff --git a/hotspot/test/compiler/rangechecks/TestRangeCheckSmearing.java b/hotspot/test/compiler/rangechecks/TestRangeCheckSmearing.java index c66fac104fd..4a8f067852d 100644 --- a/hotspot/test/compiler/rangechecks/TestRangeCheckSmearing.java +++ b/hotspot/test/compiler/rangechecks/TestRangeCheckSmearing.java @@ -25,7 +25,7 @@ * @test * @bug 8066103 * @summary C2's range check smearing allows out of bound array accesses - * @library /testlibrary /test/lib /compiler/whitebox + * @library /testlibrary /test/lib /compiler/whitebox / * @modules java.base/sun.misc * java.management * @build TestRangeCheckSmearing @@ -42,6 +42,7 @@ import java.util.*; import sun.hotspot.WhiteBox; import sun.hotspot.code.NMethod; import jdk.test.lib.Platform; +import compiler.whitebox.CompilerWhiteBoxTest; public class TestRangeCheckSmearing { private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); diff --git a/hotspot/test/compiler/rangechecks/TestUncommonTrapMerging.java b/hotspot/test/compiler/rangechecks/TestUncommonTrapMerging.java new file mode 100644 index 00000000000..bbdda630811 --- /dev/null +++ b/hotspot/test/compiler/rangechecks/TestUncommonTrapMerging.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8140574 + * @summary Verify proper re-execution of checks after merging of uncommon traps + * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:CompileCommand=compileonly,TestUncommonTrapMerging::test* TestUncommonTrapMerging Test1 + * @run main/othervm -XX:CompileCommand=compileonly,TestUncommonTrapMerging::test* TestUncommonTrapMerging Test2 + */ +public class TestUncommonTrapMerging { + + public static void main(String[] args) throws Throwable { + if (args.length < 1) { + throw new RuntimeException("Not enough arguments!"); + } + TestUncommonTrapMerging mytest = new TestUncommonTrapMerging(); + String testcase = args[0]; + if (testcase.equals("Test1")) { + try { + // '42' should hit the 'arg > 0' check + mytest.test(42); + + } catch (OutOfMemoryError e) { + // expected + } + } else if (testcase.equals("Test2")) { + // Compile test2 with uncommon traps at path 1 and path 2 + for (int i = 0; i < 100_000; i++) { + mytest.test2(-1, 0); + } + + // Compile test3 which inlines test2 with uncommon traps at + // path 1 and path 2. Because test3 always passes 'value = 1', + // C2 will remove the 'value > 0' check and then merge the two + // uncommon traps. + for (int i = 0; i < 100_000; i++) { + mytest.test3(0); + } + + // This should return through path 2 + if (!mytest.test3(42)) { + throw new RuntimeException("test2 returned through wrong path!"); + } + } + } + + public void test(int arg) throws Throwable { + // The following two checks should not be merged if the + // uncommon trap of the dominating if has 'Reason_unloaded' + // because we need to re-execute both checks after deopt. + if (arg < 0) { + throw new RuntimeException("Should not reach here"); + } else if (arg > 0) { + throw new OutOfMemoryError(); + } + throw new RuntimeException("Should not reach here"); + } + + public boolean test2(int arg, int value) { + if (arg < 0) { + if (value > 0) { + // path 1 + return false; + } + } else if (arg > 0) { + // path 2 + return true; + } + // path 3 + return false; + } + + public boolean test3(int arg) { + int i; + for (i = 0; i < 1; ++i) { } + // i == 1 + return test2(arg, i); + } +} diff --git a/hotspot/test/compiler/runtime/7196199/Test7196199.java b/hotspot/test/compiler/runtime/7196199/Test7196199.java index 6aa35369a31..8f0c520df0e 100644 --- a/hotspot/test/compiler/runtime/7196199/Test7196199.java +++ b/hotspot/test/compiler/runtime/7196199/Test7196199.java @@ -27,7 +27,7 @@ * @bug 7196199 * @summary java/text/Bidi/Bug6665028.java failed: Bidi run count incorrect * - * @run main/othervm/timeout=400 -Xmx32m -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -XX:CompileCommand=exclude,Test7196199.test -XX:+SafepointALot -XX:GuaranteedSafepointInterval=100 Test7196199 + * @run main/othervm/timeout=400 -Xmx32m -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation -XX:CompileCommand=exclude,Test7196199.test -XX:+SafepointALot -XX:GuaranteedSafepointInterval=100 Test7196199 */ diff --git a/hotspot/test/compiler/stable/TestStableMemoryBarrier.java b/hotspot/test/compiler/stable/TestStableMemoryBarrier.java new file mode 100644 index 00000000000..6a1e1f6d149 --- /dev/null +++ b/hotspot/test/compiler/stable/TestStableMemoryBarrier.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test TestStableMemoryBarrier + * @bug 8139758 + * @summary tests memory barrier correctly inserted for stable fields + * @library /testlibrary /../../test/lib + * + * @run main/bootclasspath -Xcomp -XX:CompileOnly=::testCompile + * java.lang.invoke.TestStableMemoryBarrier + * + * @author hui.shi@linaro.org + */ +package java.lang.invoke; + +import java.lang.reflect.InvocationTargetException; + +public class TestStableMemoryBarrier { + + public static void main(String[] args) throws Exception { + run(NotDominate.class); + + } + + /* ==================================================== + * Stable field initialized in method, but its allocation + * doesn't dominate MemBar Release at the end of method. + */ + + static class NotDominate{ + public @Stable int v; + public static int[] array = new int[100]; + public static NotDominate testCompile(int n) { + if ((n % 2) == 0) return null; + // add a loop here, trigger PhaseIdealLoop::verify_dominance + for (int i = 0; i < 100; i++) { + array[i] = n; + } + NotDominate nm = new NotDominate(); + nm.v = n; + return nm; + } + + public static void test() throws Exception { + for (int i = 0; i < 1000000; i++) + testCompile(i); + } + } + + public static void run(Class test) { + Throwable ex = null; + System.out.print(test.getName()+": "); + try { + test.getMethod("test").invoke(null); + } catch (InvocationTargetException e) { + ex = e.getCause(); + } catch (Throwable e) { + ex = e; + } finally { + if (ex == null) { + System.out.println("PASSED"); + } else { + System.out.println("FAILED"); + ex.printStackTrace(System.out); + } + } + } +} diff --git a/hotspot/test/compiler/tiered/CompLevelsTest.java b/hotspot/test/compiler/tiered/CompLevelsTest.java index 5c61ba173d7..3aa5ba1d627 100644 --- a/hotspot/test/compiler/tiered/CompLevelsTest.java +++ b/hotspot/test/compiler/tiered/CompLevelsTest.java @@ -26,6 +26,9 @@ * * @author igor.ignatyev@oracle.com */ + +import compiler.whitebox.CompilerWhiteBoxTest; + public abstract class CompLevelsTest extends CompilerWhiteBoxTest { protected CompLevelsTest(TestCase testCase) { super(testCase); diff --git a/hotspot/test/compiler/tiered/ConstantGettersTransitionsTest.java b/hotspot/test/compiler/tiered/ConstantGettersTransitionsTest.java index 8220f10229f..180848ab275 100644 --- a/hotspot/test/compiler/tiered/ConstantGettersTransitionsTest.java +++ b/hotspot/test/compiler/tiered/ConstantGettersTransitionsTest.java @@ -23,10 +23,11 @@ import java.lang.reflect.Executable; import java.util.concurrent.Callable; +import compiler.whitebox.CompilerWhiteBoxTest; /** * @test ConstantGettersTransitionsTest - * @library /testlibrary /test/lib /compiler/whitebox + * @library /testlibrary /test/lib /compiler/whitebox / * @modules java.base/sun.misc * java.management * @build TransitionsTestExecutor ConstantGettersTransitionsTest diff --git a/hotspot/test/compiler/tiered/LevelTransitionTest.java b/hotspot/test/compiler/tiered/LevelTransitionTest.java index be67394d9e7..c3b80504619 100644 --- a/hotspot/test/compiler/tiered/LevelTransitionTest.java +++ b/hotspot/test/compiler/tiered/LevelTransitionTest.java @@ -25,10 +25,12 @@ import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.util.Objects; import java.util.concurrent.Callable; +import compiler.whitebox.CompilerWhiteBoxTest; +import compiler.whitebox.SimpleTestCase; /** * @test LevelTransitionTest - * @library /testlibrary /test/lib /compiler/whitebox + * @library /testlibrary /test/lib /compiler/whitebox / * @modules java.base/sun.misc * java.management * @ignore 8067651 @@ -36,7 +38,7 @@ import java.util.concurrent.Callable; * @run main ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm/timeout=240 -Xmixed -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI -XX:+TieredCompilation - * -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* + * -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCase$Helper::* * -XX:CompileCommand=compileonly,ExtendedTestCase$CompileMethodHolder::* * TransitionsTestExecutor LevelTransitionTest * @summary Test the correctness of compilation level transitions for different methods diff --git a/hotspot/test/compiler/tiered/NonTieredLevelsTest.java b/hotspot/test/compiler/tiered/NonTieredLevelsTest.java index a3596c3b1f0..4a1a52e7219 100644 --- a/hotspot/test/compiler/tiered/NonTieredLevelsTest.java +++ b/hotspot/test/compiler/tiered/NonTieredLevelsTest.java @@ -22,17 +22,18 @@ */ import java.util.function.IntPredicate; +import compiler.whitebox.CompilerWhiteBoxTest; /** * @test NonTieredLevelsTest - * @library /testlibrary /test/lib /compiler/whitebox + * @library /testlibrary /test/lib /compiler/whitebox / * @modules java.management * @build NonTieredLevelsTest * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:-TieredCompilation * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* + * -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCase$Helper::* * NonTieredLevelsTest * @summary Verify that only one level can be used * @author igor.ignatyev@oracle.com diff --git a/hotspot/test/compiler/tiered/TieredLevelsTest.java b/hotspot/test/compiler/tiered/TieredLevelsTest.java index 74158e3fe86..0790b195134 100644 --- a/hotspot/test/compiler/tiered/TieredLevelsTest.java +++ b/hotspot/test/compiler/tiered/TieredLevelsTest.java @@ -21,16 +21,18 @@ * questions. */ +import compiler.whitebox.CompilerWhiteBoxTest; + /** * @test TieredLevelsTest - * @library /testlibrary /test/lib /compiler/whitebox + * @library /testlibrary /test/lib /compiler/whitebox / * @modules java.management * @build TieredLevelsTest * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+TieredCompilation * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* + * -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCase$Helper::* * TieredLevelsTest * @summary Verify that all levels < 'TieredStopAtLevel' can be used * @author igor.ignatyev@oracle.com diff --git a/hotspot/test/compiler/tiered/TransitionsTestExecutor.java b/hotspot/test/compiler/tiered/TransitionsTestExecutor.java index 432c82a54ed..630bac2b79a 100644 --- a/hotspot/test/compiler/tiered/TransitionsTestExecutor.java +++ b/hotspot/test/compiler/tiered/TransitionsTestExecutor.java @@ -29,6 +29,7 @@ import java.lang.management.RuntimeMXBean; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import compiler.whitebox.CompilerWhiteBoxTest; /** * Executes given test in a separate VM with enabled Tiered Compilation for diff --git a/hotspot/test/compiler/types/TestMeetIncompatibleInterfaceArrays.java b/hotspot/test/compiler/types/TestMeetIncompatibleInterfaceArrays.java new file mode 100644 index 00000000000..f12972c5cd2 --- /dev/null +++ b/hotspot/test/compiler/types/TestMeetIncompatibleInterfaceArrays.java @@ -0,0 +1,353 @@ +/* + * Copyright 2015 SAP AG. 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. + */ + +/* + * @test + * @bug 8141551 + * @summary C2 can not handle returns with inccompatible interface arrays + * @modules java.base/jdk.internal.org.objectweb.asm + * java.base/sun.misc + * @library /testlibrary /../../test/lib + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * -Xbatch + * -XX:CompileThreshold=1 + * -XX:-TieredCompilation + * -XX:CICompilerCount=1 + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:CompileCommand=compileonly,MeetIncompatibleInterfaceArrays*.run + * -XX:CompileCommand=dontinline,TestMeetIncompatibleInterfaceArrays$Helper.createI2* + * -XX:CompileCommand=quiet + * TestMeetIncompatibleInterfaceArrays 0 + * @run main/othervm + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * -Xbatch + * -XX:CompileThreshold=1 + * -XX:-TieredCompilation + * -XX:CICompilerCount=1 + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:CompileCommand=compileonly,MeetIncompatibleInterfaceArrays*.run + * -XX:CompileCommand=inline,TestMeetIncompatibleInterfaceArrays$Helper.createI2* + * -XX:CompileCommand=quiet + * TestMeetIncompatibleInterfaceArrays 1 + * @run main/othervm + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * -Xbatch + * -XX:CompileThreshold=1 + * -XX:Tier0InvokeNotifyFreqLog=0 -XX:Tier2InvokeNotifyFreqLog=0 -XX:Tier3InvokeNotifyFreqLog=0 -XX:Tier23InlineeNotifyFreqLog=0 + * -XX:Tier3InvocationThreshold=2 -XX:Tier3MinInvocationThreshold=2 -XX:Tier3CompileThreshold=2 + * -XX:Tier4InvocationThreshold=1 -XX:Tier4MinInvocationThreshold=1 -XX:Tier4CompileThreshold=1 + * -XX:+TieredCompilation + * -XX:CICompilerCount=2 + * -XX:+PrintCompilation + * -XX:+PrintInlining + * -XX:CompileCommand=compileonly,MeetIncompatibleInterfaceArrays*.run + * -XX:CompileCommand=compileonly,TestMeetIncompatibleInterfaceArrays$Helper.createI2* + * -XX:CompileCommand=inline,TestMeetIncompatibleInterfaceArrays$Helper.createI2* + * -XX:CompileCommand=quiet + * TestMeetIncompatibleInterfaceArrays 2 + * + * @author volker.simonis@gmail.com + */ + +import java.io.FileOutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import static jdk.internal.org.objectweb.asm.Opcodes.*; +import sun.hotspot.WhiteBox; + +public class TestMeetIncompatibleInterfaceArrays extends ClassLoader { + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + public static interface I1 { public String getName(); } + public static interface I2 { public String getName(); } + public static class I2C implements I2 { public String getName() { return "I2";} } + public static class I21C implements I2, I1 { public String getName() { return "I2 and I1";} } + + public static class Helper { + public static I2 createI2Array0() { + return new I2C(); + } + public static I2[] createI2Array1() { + return new I2C[] { new I2C() }; + } + public static I2[][] createI2Array2() { + return new I2C[][] { new I2C[] { new I2C() } }; + } + public static I2[][][] createI2Array3() { + return new I2C[][][] { new I2C[][] { new I2C[] { new I2C() } } }; + } + public static I2[][][][] createI2Array4() { + return new I2C[][][][] { new I2C[][][] { new I2C[][] { new I2C[] { new I2C() } } } }; + } + public static I2[][][][][] createI2Array5() { + return new I2C[][][][][] { new I2C[][][][] { new I2C[][][] { new I2C[][] { new I2C[] { new I2C() } } } } }; + } + public static I2 createI21Array0() { + return new I21C(); + } + public static I2[] createI21Array1() { + return new I21C[] { new I21C() }; + } + public static I2[][] createI21Array2() { + return new I21C[][] { new I21C[] { new I21C() } }; + } + public static I2[][][] createI21Array3() { + return new I21C[][][] { new I21C[][] { new I21C[] { new I21C() } } }; + } + public static I2[][][][] createI21Array4() { + return new I21C[][][][] { new I21C[][][] { new I21C[][] { new I21C[] { new I21C() } } } }; + } + public static I2[][][][][] createI21Array5() { + return new I21C[][][][][] { new I21C[][][][] { new I21C[][][] { new I21C[][] { new I21C[] { new I21C() } } } } }; + } + } + + // Location for the generated class files + public static final String PATH = System.getProperty("test.classes", ".") + java.io.File.separator; + + /* + * With 'good == false' this helper method creates the following classes + * (using the nested 'Helper' class and the nested interfaces 'I1' and 'I2'). + * For brevity I omit the enclosing class 'TestMeetIncompatibleInterfaceArrays' in the + * following examples: + * + * public class MeetIncompatibleInterfaceArrays0ASM { + * public static I1 run() { + * return Helper.createI2Array0(); // returns I2 + * } + * public static void test() { + * I1 i1 = run(); + * System.out.println(i1.getName()); + * } + * } + * public class MeetIncompatibleInterfaceArrays1ASM { + * public static I1[] run() { + * return Helper.createI2Array1(); // returns I2[] + * } + * public static void test() { + * I1[] i1 = run(); + * System.out.println(i1[0].getName()); + * } + * } + * ... + * // MeetIncompatibleInterfaceArrays4ASM is special because it creates + * // an illegal class which will be rejected by the verifier. + * public class MeetIncompatibleInterfaceArrays4ASM { + * public static I1[][][][] run() { + * return Helper.createI2Array3(); // returns I1[][][] which gives a verifier error because return expects I1[][][][] + * } + * public static void test() { + * I1[][][][][] i1 = run(); + * System.out.println(i1[0][0][0][0][0].getName()); + * } + * ... + * public class MeetIncompatibleInterfaceArrays5ASM { + * public static I1[][][][][] run() { + * return Helper.createI2Array5(); // returns I2[][][][][] + * } + * public static void test() { + * I1[][][][][] i1 = run(); + * System.out.println(i1[0][0][0][0][0].getName()); + * } + * } + * + * Notice that this is not legal Java code. We would have to use a cast in "run()" to make it legal: + * + * public static I1[] run() { + * return (I1[])Helper.createI2Array1(); // returns I2[] + * } + * + * But in pure bytecode, the "run()" methods are perfectly legal: + * + * public static I1[] run(); + * Code: + * 0: invokestatic #16 // Method Helper.createI2Array1:()[LI2; + * 3: areturn + * + * The "test()" method calls the "getName()" function from I1 on the objects returned by "run()". + * This will epectedly fail with an "IncompatibleClassChangeError" because the objects returned + * by "run()" (and by createI2Array()) are actually of type "I2C" and only implement "I2" but not "I1". + * + * + * With 'good == true' this helper method will create the following classes: + * + * public class MeetIncompatibleInterfaceArraysGood0ASM { + * public static I1 run() { + * return Helper.createI21Array0(); // returns I2 + * } + * public static void test() { + * I1 i1 = run(); + * System.out.println(i1.getName()); + * } + * } + * + * Calling "test()" on these objects will succeed and output "I2 and I1" because now the "run()" + * method calls "createI21Array()" which actually return an object (or an array of objects) of + * type "I21C" which implements both "I2" and "I1". + * + * Notice that at the bytecode level, the code for the "run()" and "test()" methods in + * "MeetIncompatibleInterfaceArraysASM" and "MeetIncompatibleInterfaceArraysGoodASM" look exactly + * the same. I.e. the verifier has no chance to verify if the I2 object returned by "createI1Array()" + * or "createI21Array()" implements "I1" or not. That's actually the reason why both versions of + * generated classes are legal from a verifier point of view. + * + */ + static void generateTestClass(int dim, boolean good) throws Exception { + String baseClassName = "MeetIncompatibleInterfaceArrays"; + if (good) + baseClassName += "Good"; + String createName = "createI2" + (good ? "1" : "") + "Array"; + String a = ""; + for (int i = 0; i < dim; i++) + a += "["; + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + cw.visit(V1_8, ACC_PUBLIC, baseClassName + dim + "ASM", null, "java/lang/Object", null); + MethodVisitor constr = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + constr.visitCode(); + constr.visitVarInsn(ALOAD, 0); + constr.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + constr.visitInsn(RETURN); + constr.visitMaxs(0, 0); + constr.visitEnd(); + MethodVisitor run = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "run", + "()" + a + "LTestMeetIncompatibleInterfaceArrays$I1;", null, null); + run.visitCode(); + if (dim == 4) { + run.visitMethodInsn(INVOKESTATIC, "TestMeetIncompatibleInterfaceArrays$Helper", createName + 3, + "()" + "[[[" + "LTestMeetIncompatibleInterfaceArrays$I2;", false); + } else { + run.visitMethodInsn(INVOKESTATIC, "TestMeetIncompatibleInterfaceArrays$Helper", createName + dim, + "()" + a + "LTestMeetIncompatibleInterfaceArrays$I2;", false); + } + run.visitInsn(ARETURN); + run.visitMaxs(0, 0); + run.visitEnd(); + MethodVisitor test = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "test", "()V", null, null); + test.visitCode(); + test.visitMethodInsn(INVOKESTATIC, baseClassName + dim + "ASM", "run", + "()" + a + "LTestMeetIncompatibleInterfaceArrays$I1;", false); + test.visitVarInsn(ASTORE, 0); + if (dim > 0) { + test.visitVarInsn(ALOAD, 0); + for (int i = 1; i <= dim; i++) { + test.visitInsn(ICONST_0); + test.visitInsn(AALOAD); + } + test.visitVarInsn(ASTORE, 1); + } + test.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + test.visitVarInsn(ALOAD, dim > 0 ? 1 : 0); + test.visitMethodInsn(INVOKEINTERFACE, "TestMeetIncompatibleInterfaceArrays$I1", "getName", + "()Ljava/lang/String;", true); + test.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false); + test.visitInsn(RETURN); + test.visitMaxs(0, 0); + test.visitEnd(); + + // Get the bytes of the class.. + byte[] b = cw.toByteArray(); + // ..and write them into a class file (for debugging) + FileOutputStream fos = new FileOutputStream(PATH + baseClassName + dim + "ASM.class"); + fos.write(b); + fos.close(); + + } + + public static String[][] tier = { { "interpreted", "C2 (tier 4) without inlining", "C2 (tier4) without inlining" }, + { "interpreted", "C2 (tier 4) with inlining", "C2 (tier4) with inlining" }, + { "interpreted", "C1 (tier 3) with inlining", "C2 (tier4) with inlining" } }; + + public static void main(String[] args) throws Exception { + final int pass = Integer.parseInt(args.length > 0 ? args[0] : "0"); + + // Load and initialize some classes required for compilation + Class.forName("TestMeetIncompatibleInterfaceArrays$I1"); + Class.forName("TestMeetIncompatibleInterfaceArrays$I2"); + Class.forName("TestMeetIncompatibleInterfaceArrays$Helper"); + + for (int g = 0; g < 2; g++) { + String baseClassName = "MeetIncompatibleInterfaceArrays"; + boolean good = (g == 0) ? false : true; + if (good) + baseClassName += "Good"; + for (int i = 0; i < 6; i++) { + System.out.println(); + System.out.println("Creating " + baseClassName + i + "ASM.class"); + System.out.println("========================================" + "=" + "========="); + // Create the "MeetIncompatibleInterfaceArraysASM" class + generateTestClass(i, good); + Class c = null; + try { + c = Class.forName(baseClassName + i + "ASM"); + } catch (VerifyError ve) { + if (i == 4) { + System.out.println("OK - must be (" + ve.getMessage() + ")."); + } else { + throw ve; + } + continue; + } + // Call MeetIncompatibleInterfaceArraysASM.test() + Method m = c.getMethod("test"); + Method r = c.getMethod("run"); + for (int j = 0; j < 3; j++) { + System.out.println((j + 1) + ". invokation of " + baseClassName + i + "ASM.test() [should be " + + tier[pass][j] + "]"); + try { + m.invoke(null); + } catch (InvocationTargetException ite) { + if (good) { + throw ite; + } else { + if (ite.getCause() instanceof IncompatibleClassChangeError) { + System.out.println(" OK - catched InvocationTargetException(" + + ite.getCause().getMessage() + ")."); + } else { + throw ite; + } + } + } + } + System.out.println("Method " + r + (WB.isMethodCompiled(r) ? " has" : " has not") + " been compiled."); + if (!WB.isMethodCompiled(r)) { + throw new Exception("Method " + r + " must be compiled!"); + } + } + } + } +} diff --git a/hotspot/test/compiler/whitebox/ClearMethodStateTest.java b/hotspot/test/compiler/whitebox/ClearMethodStateTest.java index 63a0ec53e8c..2b93fad4bba 100644 --- a/hotspot/test/compiler/whitebox/ClearMethodStateTest.java +++ b/hotspot/test/compiler/whitebox/ClearMethodStateTest.java @@ -22,16 +22,17 @@ */ import java.util.function.Function; +import compiler.whitebox.CompilerWhiteBoxTest; /* * @test ClearMethodStateTest * @bug 8006683 8007288 8022832 - * @library /testlibrary /test/lib + * @library /testlibrary /test/lib / * @modules java.management * @build ClearMethodStateTest * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission - * @run main/othervm -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* ClearMethodStateTest + * @run main/othervm -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCase$Helper::* ClearMethodStateTest * @summary testing of WB::clearMethodState() * @author igor.ignatyev@oracle.com */ diff --git a/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java b/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java index 1972b35eb1d..269113074e4 100644 --- a/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java +++ b/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java @@ -20,13 +20,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +package compiler.whitebox; import sun.hotspot.WhiteBox; import sun.hotspot.code.NMethod; - -import java.lang.reflect.Constructor; import java.lang.reflect.Executable; -import java.lang.reflect.Method; import java.util.Objects; import java.util.concurrent.Callable; import java.util.function.Function; @@ -38,19 +36,19 @@ import java.util.function.Function; */ public abstract class CompilerWhiteBoxTest { /** {@code CompLevel::CompLevel_none} -- Interpreter */ - protected static final int COMP_LEVEL_NONE = 0; + public static final int COMP_LEVEL_NONE = 0; /** {@code CompLevel::CompLevel_any}, {@code CompLevel::CompLevel_all} */ - protected static final int COMP_LEVEL_ANY = -1; + public static final int COMP_LEVEL_ANY = -1; /** {@code CompLevel::CompLevel_simple} -- C1 */ - protected static final int COMP_LEVEL_SIMPLE = 1; + public static final int COMP_LEVEL_SIMPLE = 1; /** {@code CompLevel::CompLevel_limited_profile} -- C1, invocation & backedge counters */ - protected static final int COMP_LEVEL_LIMITED_PROFILE = 2; + public static final int COMP_LEVEL_LIMITED_PROFILE = 2; /** {@code CompLevel::CompLevel_full_profile} -- C1, invocation & backedge counters + mdo */ - protected static final int COMP_LEVEL_FULL_PROFILE = 3; + public static final int COMP_LEVEL_FULL_PROFILE = 3; /** {@code CompLevel::CompLevel_full_optimization} -- C2 or Shark */ - protected static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; + public static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; /** Maximal value for CompLevel */ - protected static final int COMP_LEVEL_MAX = COMP_LEVEL_FULL_OPTIMIZATION; + public static final int COMP_LEVEL_MAX = COMP_LEVEL_FULL_OPTIMIZATION; /** Instance of WhiteBox */ protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); @@ -70,7 +68,7 @@ public abstract class CompilerWhiteBoxTest { protected static final boolean IS_VERBOSE = System.getProperty("verbose") != null; /** invocation count to trigger compilation */ - protected static final int THRESHOLD; + public static final int THRESHOLD; /** invocation count to trigger OSR compilation */ protected static final long BACKEDGE_THRESHOLD; /** Value of {@code java.vm.info} (interpreted|mixed|comp mode) */ @@ -312,7 +310,7 @@ public abstract class CompilerWhiteBoxTest { * * @param executable Executable */ - protected static final void waitBackgroundCompilation(Executable executable) { + public static final void waitBackgroundCompilation(Executable executable) { if (!BACKGROUND_COMPILATION) { return; } @@ -441,7 +439,7 @@ public abstract class CompilerWhiteBoxTest { * @return {@code true} if the test should be skipped, * {@code false} otherwise */ - protected static boolean skipOnTieredCompilation(boolean value) { + public static boolean skipOnTieredCompilation(boolean value) { if (value == CompilerWhiteBoxTest.TIERED_COMPILATION) { System.err.println("Test isn't applicable w/ " + (value ? "enabled" : "disabled") @@ -452,256 +450,3 @@ public abstract class CompilerWhiteBoxTest { } } -enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { - /** constructor test case */ - CONSTRUCTOR_TEST(Helper.CONSTRUCTOR, Helper.CONSTRUCTOR_CALLABLE, false), - /** method test case */ - METHOD_TEST(Helper.METHOD, Helper.METHOD_CALLABLE, false), - /** static method test case */ - STATIC_TEST(Helper.STATIC, Helper.STATIC_CALLABLE, false), - /** OSR constructor test case */ - OSR_CONSTRUCTOR_TEST(Helper.OSR_CONSTRUCTOR, - Helper.OSR_CONSTRUCTOR_CALLABLE, true), - /** OSR method test case */ - OSR_METHOD_TEST(Helper.OSR_METHOD, Helper.OSR_METHOD_CALLABLE, true), - /** OSR static method test case */ - OSR_STATIC_TEST(Helper.OSR_STATIC, Helper.OSR_STATIC_CALLABLE, true); - - private final Executable executable; - private final Callable callable; - private final boolean isOsr; - - private SimpleTestCase(Executable executable, Callable callable, - boolean isOsr) { - this.executable = executable; - this.callable = callable; - this.isOsr = isOsr; - } - - @Override - public Executable getExecutable() { - return executable; - } - - @Override - public Callable getCallable() { - return callable; - } - - @Override - public boolean isOsr() { - return isOsr; - } - - private static class Helper { - - private static final Callable CONSTRUCTOR_CALLABLE - = new Callable() { - @Override - public Integer call() throws Exception { - return new Helper(1337).hashCode(); - } - }; - - private static final Callable METHOD_CALLABLE - = new Callable() { - private final Helper helper = new Helper(); - - @Override - public Integer call() throws Exception { - return helper.method(); - } - }; - - private static final Callable STATIC_CALLABLE - = new Callable() { - @Override - public Integer call() throws Exception { - return staticMethod(); - } - }; - - private static final Callable OSR_CONSTRUCTOR_CALLABLE - = new Callable() { - @Override - public Integer call() throws Exception { - return new Helper(null, CompilerWhiteBoxTest.BACKEDGE_THRESHOLD).hashCode(); - } - }; - - private static final Callable OSR_METHOD_CALLABLE - = new Callable() { - private final Helper helper = new Helper(); - - @Override - public Integer call() throws Exception { - return helper.osrMethod(CompilerWhiteBoxTest.BACKEDGE_THRESHOLD); - } - }; - - private static final Callable OSR_STATIC_CALLABLE - = new Callable() { - @Override - public Integer call() throws Exception { - return osrStaticMethod(CompilerWhiteBoxTest.BACKEDGE_THRESHOLD); - } - }; - - private static final Constructor CONSTRUCTOR; - private static final Constructor OSR_CONSTRUCTOR; - private static final Method METHOD; - private static final Method STATIC; - private static final Method OSR_METHOD; - private static final Method OSR_STATIC; - - static { - try { - CONSTRUCTOR = Helper.class.getDeclaredConstructor(int.class); - } catch (NoSuchMethodException | SecurityException e) { - throw new RuntimeException( - "exception on getting method Helper.(int)", e); - } - try { - OSR_CONSTRUCTOR = Helper.class.getDeclaredConstructor( - Object.class, long.class); - } catch (NoSuchMethodException | SecurityException e) { - throw new RuntimeException( - "exception on getting method Helper.(Object, long)", e); - } - METHOD = getMethod("method"); - STATIC = getMethod("staticMethod"); - OSR_METHOD = getMethod("osrMethod", long.class); - OSR_STATIC = getMethod("osrStaticMethod", long.class); - } - - private static Method getMethod(String name, Class... parameterTypes) { - try { - return Helper.class.getDeclaredMethod(name, parameterTypes); - } catch (NoSuchMethodException | SecurityException e) { - throw new RuntimeException( - "exception on getting method Helper." + name, e); - } - } - - private static int staticMethod() { - return 1138; - } - - private int method() { - return 42; - } - - /** - * Deoptimizes all non-osr versions of the given executable after - * compilation finished. - * - * @param e Executable - * @throws Exception - */ - private static void waitAndDeoptimize(Executable e) { - CompilerWhiteBoxTest.waitBackgroundCompilation(e); - if (WhiteBox.getWhiteBox().isMethodQueuedForCompilation(e)) { - throw new RuntimeException(e + " must not be in queue"); - } - // Deoptimize non-osr versions of executable - WhiteBox.getWhiteBox().deoptimizeMethod(e, false); - } - - /** - * Executes the method multiple times to make sure we have - * enough profiling information before triggering an OSR - * compilation. Otherwise the C2 compiler may add uncommon traps. - * - * @param m Method to be executed - * @return Number of times the method was executed - * @throws Exception - */ - private static int warmup(Method m) throws Exception { - waitAndDeoptimize(m); - Helper helper = new Helper(); - int result = 0; - for (long i = 0; i < CompilerWhiteBoxTest.THRESHOLD; ++i) { - result += (int)m.invoke(helper, 1); - } - // Wait to make sure OSR compilation is not blocked by - // non-OSR compilation in the compile queue - CompilerWhiteBoxTest.waitBackgroundCompilation(m); - return result; - } - - /** - * Executes the constructor multiple times to make sure we - * have enough profiling information before triggering an OSR - * compilation. Otherwise the C2 compiler may add uncommon traps. - * - * @param c Constructor to be executed - * @return Number of times the constructor was executed - * @throws Exception - */ - private static int warmup(Constructor c) throws Exception { - waitAndDeoptimize(c); - int result = 0; - for (long i = 0; i < CompilerWhiteBoxTest.THRESHOLD; ++i) { - result += c.newInstance(null, 1).hashCode(); - } - // Wait to make sure OSR compilation is not blocked by - // non-OSR compilation in the compile queue - CompilerWhiteBoxTest.waitBackgroundCompilation(c); - return result; - } - - private static int osrStaticMethod(long limit) throws Exception { - int result = 0; - if (limit != 1) { - result = warmup(OSR_STATIC); - } - // Trigger osr compilation - for (long i = 0; i < limit; ++i) { - result += staticMethod(); - } - return result; - } - - private int osrMethod(long limit) throws Exception { - int result = 0; - if (limit != 1) { - result = warmup(OSR_METHOD); - } - // Trigger osr compilation - for (long i = 0; i < limit; ++i) { - result += method(); - } - return result; - } - - private final int x; - - // for method and OSR method test case - public Helper() { - x = 0; - } - - // for OSR constructor test case - private Helper(Object o, long limit) throws Exception { - int result = 0; - if (limit != 1) { - result = warmup(OSR_CONSTRUCTOR); - } - // Trigger osr compilation - for (long i = 0; i < limit; ++i) { - result += method(); - } - x = result; - } - - // for constructor test case - private Helper(int x) { - this.x = x; - } - - @Override - public int hashCode() { - return x; - } - } -} diff --git a/hotspot/test/compiler/whitebox/DeoptimizeAllTest.java b/hotspot/test/compiler/whitebox/DeoptimizeAllTest.java index d66e60dd6d2..b5ecf979e54 100644 --- a/hotspot/test/compiler/whitebox/DeoptimizeAllTest.java +++ b/hotspot/test/compiler/whitebox/DeoptimizeAllTest.java @@ -21,15 +21,17 @@ * questions. */ +import compiler.whitebox.CompilerWhiteBoxTest; + /* * @test DeoptimizeAllTest * @bug 8006683 8007288 8022832 - * @library /testlibrary /test/lib + * @library /testlibrary /test/lib / * @modules java.management * @build DeoptimizeAllTest * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* DeoptimizeAllTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCase$Helper::* DeoptimizeAllTest * @summary testing of WB::deoptimizeAll() * @author igor.ignatyev@oracle.com */ diff --git a/hotspot/test/compiler/whitebox/DeoptimizeFramesTest.java b/hotspot/test/compiler/whitebox/DeoptimizeFramesTest.java index c13a3814ac3..75ac8cdc1ea 100644 --- a/hotspot/test/compiler/whitebox/DeoptimizeFramesTest.java +++ b/hotspot/test/compiler/whitebox/DeoptimizeFramesTest.java @@ -21,10 +21,12 @@ * questions. */ +import compiler.whitebox.CompilerWhiteBoxTest; + /* * @test DeoptimizeFramesTest * @bug 8028595 - * @library /testlibrary /test/lib + * @library /testlibrary /test/lib / * @modules java.management * @build DeoptimizeFramesTest * @run main ClassFileInstaller sun.hotspot.WhiteBox diff --git a/hotspot/test/compiler/whitebox/DeoptimizeMethodTest.java b/hotspot/test/compiler/whitebox/DeoptimizeMethodTest.java index bbd34105cd8..473f56ce178 100644 --- a/hotspot/test/compiler/whitebox/DeoptimizeMethodTest.java +++ b/hotspot/test/compiler/whitebox/DeoptimizeMethodTest.java @@ -21,15 +21,17 @@ * questions. */ +import compiler.whitebox.CompilerWhiteBoxTest; + /* * @test DeoptimizeMethodTest * @bug 8006683 8007288 8022832 - * @library /testlibrary /test/lib + * @library /testlibrary /test/lib / * @modules java.management * @build DeoptimizeMethodTest * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* DeoptimizeMethodTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCase$Helper::* DeoptimizeMethodTest * @summary testing of WB::deoptimizeMethod() * @author igor.ignatyev@oracle.com */ diff --git a/hotspot/test/compiler/whitebox/DeoptimizeMultipleOSRTest.java b/hotspot/test/compiler/whitebox/DeoptimizeMultipleOSRTest.java index 54dfe6312ee..dfd5dd2a10a 100644 --- a/hotspot/test/compiler/whitebox/DeoptimizeMultipleOSRTest.java +++ b/hotspot/test/compiler/whitebox/DeoptimizeMultipleOSRTest.java @@ -24,11 +24,12 @@ import sun.hotspot.WhiteBox; import java.lang.reflect.Executable; import java.lang.reflect.Method; +import compiler.whitebox.CompilerWhiteBoxTest; /* * @test DeoptimizeMultipleOSRTest * @bug 8061817 - * @library /testlibrary /test/lib + * @library /testlibrary /test/lib / * @modules java.management * @build DeoptimizeMultipleOSRTest * @run main ClassFileInstaller sun.hotspot.WhiteBox diff --git a/hotspot/test/compiler/whitebox/EnqueueMethodForCompilationTest.java b/hotspot/test/compiler/whitebox/EnqueueMethodForCompilationTest.java index 77323900077..cec51a811bc 100644 --- a/hotspot/test/compiler/whitebox/EnqueueMethodForCompilationTest.java +++ b/hotspot/test/compiler/whitebox/EnqueueMethodForCompilationTest.java @@ -21,15 +21,17 @@ * questions. */ +import compiler.whitebox.CompilerWhiteBoxTest; + /* * @test EnqueueMethodForCompilationTest * @bug 8006683 8007288 8022832 - * @library /testlibrary /test/lib + * @library /testlibrary /test/lib / * @modules java.management * @build EnqueueMethodForCompilationTest * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission - * @run main/othervm/timeout=600 -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* EnqueueMethodForCompilationTest + * @run main/othervm/timeout=600 -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCase$Helper::* EnqueueMethodForCompilationTest * @summary testing of WB::enqueueMethodForCompilation() * @author igor.ignatyev@oracle.com */ diff --git a/hotspot/test/compiler/whitebox/ForceNMethodSweepTest.java b/hotspot/test/compiler/whitebox/ForceNMethodSweepTest.java index 64918100a19..b37838b4922 100644 --- a/hotspot/test/compiler/whitebox/ForceNMethodSweepTest.java +++ b/hotspot/test/compiler/whitebox/ForceNMethodSweepTest.java @@ -30,18 +30,19 @@ import sun.hotspot.code.BlobType; import jdk.test.lib.Asserts; import jdk.test.lib.InfiniteLoop; +import compiler.whitebox.CompilerWhiteBoxTest; /* * @test * @bug 8059624 8064669 - * @library /testlibrary /test/lib + * @library /testlibrary /test/lib / * @modules java.management * @build ForceNMethodSweepTest * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:-TieredCompilation -XX:+WhiteBoxAPI - * -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* + * -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCase$Helper::* * -XX:-BackgroundCompilation ForceNMethodSweepTest * @summary testing of WB::forceNMethodSweep */ diff --git a/hotspot/test/compiler/whitebox/GetNMethodTest.java b/hotspot/test/compiler/whitebox/GetNMethodTest.java index 229f031d32e..285a300c43b 100644 --- a/hotspot/test/compiler/whitebox/GetNMethodTest.java +++ b/hotspot/test/compiler/whitebox/GetNMethodTest.java @@ -25,16 +25,17 @@ import sun.hotspot.code.BlobType; import sun.hotspot.code.NMethod; import jdk.test.lib.Asserts; +import compiler.whitebox.CompilerWhiteBoxTest; /* * @test GetNMethodTest * @bug 8038240 - * @library /testlibrary /test/lib + * @library /testlibrary /test/lib / * @modules java.management * @build GetNMethodTest * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission - * @run main/othervm -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* GetNMethodTest + * @run main/othervm -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCase$Helper::* GetNMethodTest * @summary testing of WB::getNMethod() * @author igor.ignatyev@oracle.com */ diff --git a/hotspot/test/compiler/whitebox/IsMethodCompilableTest.java b/hotspot/test/compiler/whitebox/IsMethodCompilableTest.java index fec015193d2..2178acf4028 100644 --- a/hotspot/test/compiler/whitebox/IsMethodCompilableTest.java +++ b/hotspot/test/compiler/whitebox/IsMethodCompilableTest.java @@ -24,7 +24,7 @@ /* * @test IsMethodCompilableTest * @bug 8007270 8006683 8007288 8022832 - * @library /testlibrary /test/lib + * @library /testlibrary /test/lib / * @modules java.base/sun.misc * java.management * @build jdk.test.lib.* sun.hotspot.WhiteBox @@ -32,12 +32,13 @@ * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main ClassFileInstaller jdk.test.lib.Platform - * @run main/othervm/timeout=2400 -Xbootclasspath/a:. -Xmixed -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:PerMethodRecompilationCutoff=3 -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* IsMethodCompilableTest + * @run main/othervm/timeout=2400 -Xbootclasspath/a:. -Xmixed -XX:-TieredCompilation -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:PerMethodRecompilationCutoff=3 -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCase$Helper::* IsMethodCompilableTest * @summary testing of WB::isMethodCompilable() * @author igor.ignatyev@oracle.com */ import jdk.test.lib.Platform; +import compiler.whitebox.CompilerWhiteBoxTest; public class IsMethodCompilableTest extends CompilerWhiteBoxTest { /** diff --git a/hotspot/test/compiler/whitebox/LockCompilationTest.java b/hotspot/test/compiler/whitebox/LockCompilationTest.java index 93a1837730d..2dd5275dc5e 100644 --- a/hotspot/test/compiler/whitebox/LockCompilationTest.java +++ b/hotspot/test/compiler/whitebox/LockCompilationTest.java @@ -24,12 +24,12 @@ /* * @test LockCompilationTest * @bug 8059624 - * @library /testlibrary /test/lib + * @library /testlibrary /test/lib / * @modules java.management * @build LockCompilationTest * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission - * @run main/othervm/timeout=600 -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* LockCompilationTest + * @run main/othervm/timeout=600 -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCase$Helper::* LockCompilationTest * @summary testing of WB::lock/unlockCompilation() */ @@ -37,7 +37,7 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; - +import compiler.whitebox.CompilerWhiteBoxTest; import jdk.test.lib.Asserts; public class LockCompilationTest extends CompilerWhiteBoxTest { diff --git a/hotspot/test/compiler/whitebox/MakeMethodNotCompilableTest.java b/hotspot/test/compiler/whitebox/MakeMethodNotCompilableTest.java index e9b808d3620..82b568a30c6 100644 --- a/hotspot/test/compiler/whitebox/MakeMethodNotCompilableTest.java +++ b/hotspot/test/compiler/whitebox/MakeMethodNotCompilableTest.java @@ -21,15 +21,17 @@ * questions. */ +import compiler.whitebox.CompilerWhiteBoxTest; + /* * @test MakeMethodNotCompilableTest * @bug 8012322 8006683 8007288 8022832 - * @library /testlibrary /test/lib + * @library /testlibrary /test/lib / * @modules java.management * @build MakeMethodNotCompilableTest * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission - * @run main/othervm/timeout=2400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* MakeMethodNotCompilableTest + * @run main/othervm/timeout=2400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCase$Helper::* MakeMethodNotCompilableTest * @summary testing of WB::makeMethodNotCompilable() * @author igor.ignatyev@oracle.com */ diff --git a/hotspot/test/compiler/whitebox/SetDontInlineMethodTest.java b/hotspot/test/compiler/whitebox/SetDontInlineMethodTest.java index 322074baa3a..94846dec932 100644 --- a/hotspot/test/compiler/whitebox/SetDontInlineMethodTest.java +++ b/hotspot/test/compiler/whitebox/SetDontInlineMethodTest.java @@ -21,15 +21,17 @@ * questions. */ +import compiler.whitebox.CompilerWhiteBoxTest; + /* * @test SetDontInlineMethodTest * @bug 8006683 8007288 8022832 - * @library /testlibrary /test/lib + * @library /testlibrary /test/lib / * @modules java.management * @build SetDontInlineMethodTest * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* SetDontInlineMethodTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCase$Helper::* SetDontInlineMethodTest * @summary testing of WB::testSetDontInlineMethod() * @author igor.ignatyev@oracle.com */ diff --git a/hotspot/test/compiler/whitebox/SetForceInlineMethodTest.java b/hotspot/test/compiler/whitebox/SetForceInlineMethodTest.java index 1720e93e673..497e8948e91 100644 --- a/hotspot/test/compiler/whitebox/SetForceInlineMethodTest.java +++ b/hotspot/test/compiler/whitebox/SetForceInlineMethodTest.java @@ -21,15 +21,17 @@ * questions. */ +import compiler.whitebox.CompilerWhiteBoxTest; + /* * @test SetForceInlineMethodTest * @bug 8006683 8007288 8022832 - * @library /testlibrary /test/lib + * @library /testlibrary /test/lib / * @modules java.management * @build SetForceInlineMethodTest * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* SetForceInlineMethodTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCase$Helper::* SetForceInlineMethodTest * @summary testing of WB::testSetForceInlineMethod() * @author igor.ignatyev@oracle.com */ diff --git a/hotspot/test/compiler/whitebox/SimpleTestCase.java b/hotspot/test/compiler/whitebox/SimpleTestCase.java new file mode 100644 index 00000000000..76954acf20d --- /dev/null +++ b/hotspot/test/compiler/whitebox/SimpleTestCase.java @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2015, 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. + */ + +package compiler.whitebox; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.util.concurrent.Callable; +import sun.hotspot.WhiteBox; + +public enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { + /** constructor test case */ + CONSTRUCTOR_TEST(Helper.CONSTRUCTOR, Helper.CONSTRUCTOR_CALLABLE, false), + /** method test case */ + METHOD_TEST(Helper.METHOD, Helper.METHOD_CALLABLE, false), + /** static method test case */ + STATIC_TEST(Helper.STATIC, Helper.STATIC_CALLABLE, false), + /** OSR constructor test case */ + OSR_CONSTRUCTOR_TEST(Helper.OSR_CONSTRUCTOR, + Helper.OSR_CONSTRUCTOR_CALLABLE, true), + /** OSR method test case */ + OSR_METHOD_TEST(Helper.OSR_METHOD, Helper.OSR_METHOD_CALLABLE, true), + /** OSR static method test case */ + OSR_STATIC_TEST(Helper.OSR_STATIC, Helper.OSR_STATIC_CALLABLE, true); + + private final Executable executable; + private final Callable callable; + private final boolean isOsr; + + private SimpleTestCase(Executable executable, Callable callable, + boolean isOsr) { + this.executable = executable; + this.callable = callable; + this.isOsr = isOsr; + } + + @Override + public Executable getExecutable() { + return executable; + } + + @Override + public Callable getCallable() { + return callable; + } + + @Override + public boolean isOsr() { + return isOsr; + } + + private static class Helper { + + private static final Callable CONSTRUCTOR_CALLABLE + = new Callable() { + @Override + public Integer call() throws Exception { + return new Helper(1337).hashCode(); + } + }; + + private static final Callable METHOD_CALLABLE + = new Callable() { + private final Helper helper = new Helper(); + + @Override + public Integer call() throws Exception { + return helper.method(); + } + }; + + private static final Callable STATIC_CALLABLE + = new Callable() { + @Override + public Integer call() throws Exception { + return staticMethod(); + } + }; + + private static final Callable OSR_CONSTRUCTOR_CALLABLE + = new Callable() { + @Override + public Integer call() throws Exception { + return new Helper(null, CompilerWhiteBoxTest.BACKEDGE_THRESHOLD).hashCode(); + } + }; + + private static final Callable OSR_METHOD_CALLABLE + = new Callable() { + private final Helper helper = new Helper(); + + @Override + public Integer call() throws Exception { + return helper.osrMethod(CompilerWhiteBoxTest.BACKEDGE_THRESHOLD); + } + }; + + private static final Callable OSR_STATIC_CALLABLE + = new Callable() { + @Override + public Integer call() throws Exception { + return osrStaticMethod(CompilerWhiteBoxTest.BACKEDGE_THRESHOLD); + } + }; + + private static final Constructor CONSTRUCTOR; + private static final Constructor OSR_CONSTRUCTOR; + private static final Method METHOD; + private static final Method STATIC; + private static final Method OSR_METHOD; + private static final Method OSR_STATIC; + + static { + try { + CONSTRUCTOR = Helper.class.getDeclaredConstructor(int.class); + } catch (NoSuchMethodException | SecurityException e) { + throw new RuntimeException( + "exception on getting method Helper.(int)", e); + } + try { + OSR_CONSTRUCTOR = Helper.class.getDeclaredConstructor( + Object.class, long.class); + } catch (NoSuchMethodException | SecurityException e) { + throw new RuntimeException( + "exception on getting method Helper.(Object, long)", e); + } + METHOD = getMethod("method"); + STATIC = getMethod("staticMethod"); + OSR_METHOD = getMethod("osrMethod", long.class); + OSR_STATIC = getMethod("osrStaticMethod", long.class); + } + + private static Method getMethod(String name, Class... parameterTypes) { + try { + return Helper.class.getDeclaredMethod(name, parameterTypes); + } catch (NoSuchMethodException | SecurityException e) { + throw new RuntimeException( + "exception on getting method Helper." + name, e); + } + } + + private static int staticMethod() { + return 1138; + } + + private int method() { + return 42; + } + + /** + * Deoptimizes all non-osr versions of the given executable after + * compilation finished. + * + * @param e Executable + * @throws Exception + */ + private static void waitAndDeoptimize(Executable e) { + CompilerWhiteBoxTest.waitBackgroundCompilation(e); + if (WhiteBox.getWhiteBox().isMethodQueuedForCompilation(e)) { + throw new RuntimeException(e + " must not be in queue"); + } + // Deoptimize non-osr versions of executable + WhiteBox.getWhiteBox().deoptimizeMethod(e, false); + } + + /** + * Executes the method multiple times to make sure we have + * enough profiling information before triggering an OSR + * compilation. Otherwise the C2 compiler may add uncommon traps. + * + * @param m Method to be executed + * @return Number of times the method was executed + * @throws Exception + */ + private static int warmup(Method m) throws Exception { + waitAndDeoptimize(m); + Helper helper = new Helper(); + int result = 0; + for (long i = 0; i < CompilerWhiteBoxTest.THRESHOLD; ++i) { + result += (int)m.invoke(helper, 1); + } + // Wait to make sure OSR compilation is not blocked by + // non-OSR compilation in the compile queue + CompilerWhiteBoxTest.waitBackgroundCompilation(m); + return result; + } + + /** + * Executes the constructor multiple times to make sure we + * have enough profiling information before triggering an OSR + * compilation. Otherwise the C2 compiler may add uncommon traps. + * + * @param c Constructor to be executed + * @return Number of times the constructor was executed + * @throws Exception + */ + private static int warmup(Constructor c) throws Exception { + waitAndDeoptimize(c); + int result = 0; + for (long i = 0; i < CompilerWhiteBoxTest.THRESHOLD; ++i) { + result += c.newInstance(null, 1).hashCode(); + } + // Wait to make sure OSR compilation is not blocked by + // non-OSR compilation in the compile queue + CompilerWhiteBoxTest.waitBackgroundCompilation(c); + return result; + } + + private static int osrStaticMethod(long limit) throws Exception { + int result = 0; + if (limit != 1) { + result = warmup(OSR_STATIC); + } + // Trigger osr compilation + for (long i = 0; i < limit; ++i) { + result += staticMethod(); + } + return result; + } + + private int osrMethod(long limit) throws Exception { + int result = 0; + if (limit != 1) { + result = warmup(OSR_METHOD); + } + // Trigger osr compilation + for (long i = 0; i < limit; ++i) { + result += method(); + } + return result; + } + + private final int x; + + // for method and OSR method test case + public Helper() { + x = 0; + } + + // for OSR constructor test case + private Helper(Object o, long limit) throws Exception { + int result = 0; + if (limit != 1) { + result = warmup(OSR_CONSTRUCTOR); + } + // Trigger osr compilation + for (long i = 0; i < limit; ++i) { + result += method(); + } + x = result; + } + + // for constructor test case + private Helper(int x) { + this.x = x; + } + + @Override + public int hashCode() { + return x; + } + } +} diff --git a/hotspot/test/gc/g1/TestHumongousAllocNearlyFullRegion.java b/hotspot/test/gc/g1/TestHumongousAllocNearlyFullRegion.java new file mode 100644 index 00000000000..2e5a470f18d --- /dev/null +++ b/hotspot/test/gc/g1/TestHumongousAllocNearlyFullRegion.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2012, 2015, 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. + */ + +/* + * @test TestHumongousAllocNearlyFullRegion + * @bug 8143587 + * @summary G1: humongous object allocations should work even when there is + * not enough space in the heapRegion to fit a filler object. + * @library /testlibrary + * @run driver TestHumongousAllocNearlyFullRegion + */ + +import jdk.test.lib.*; + +public class TestHumongousAllocNearlyFullRegion { + // Heap sizes < 224 MB are increased to 224 MB if vm_page_size == 64K to + // fulfill alignment constraints. + private static final int heapSize = 224; // MB + private static final int heapRegionSize = 1; // MB + + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UseG1GC", + "-Xms" + heapSize + "m", + "-Xmx" + heapSize + "m", + "-XX:G1HeapRegionSize=" + heapRegionSize + "m", + "-XX:+PrintGC", + HumongousObjectAllocator.class.getName()); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("GC pause (G1 Humongous Allocation) (young) (initial-mark)"); + output.shouldHaveExitValue(0); + } + + static class HumongousObjectAllocator { + public static void main(String [] args) { + for (int i = 0; i < heapSize; i++) { + // 131069 is the number of longs it takes to fill a heapRegion except + // for 8 bytes on 64 bit. + long[] largeObect = new long[131069]; + } + } + } +} + diff --git a/hotspot/test/gc/parallel/TestPrintGCDetailsVerbose.java b/hotspot/test/gc/parallel/TestPrintGCDetailsVerbose.java new file mode 100644 index 00000000000..8e138db694d --- /dev/null +++ b/hotspot/test/gc/parallel/TestPrintGCDetailsVerbose.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test TestPrintGCDetailsVerbose + * @bug 8016740 + * @summary Tests that jvm with PrintGCDetails and Verbose flags do not crash when ParOldGC has no memory + * @key gc + * @requires java.version ~= ".*fastdebug" + * @requires vm.gc=="Parallel" | vm.gc=="null" + * @library /testlibrary + * @run main/othervm -Xmx50m -XX:+UseParallelOldGC -XX:+PrintGCDetails -XX:+Verbose TestPrintGCDetailsVerbose + */ +public class TestPrintGCDetailsVerbose { + + public static void main(String[] args) { + for (int t = 0; t <= 10; t++) { + byte a[][] = new byte[100000][]; + try { + for (int i = 0; i < a.length; i++) { + a[i] = new byte[100000]; + } + } catch (OutOfMemoryError oome) { + a = null; + System.out.println("OOM!"); + continue; + } + } + } +} + diff --git a/hotspot/test/runtime/CommandLine/IgnoreUnrecognizedVMOptions.java b/hotspot/test/runtime/CommandLine/IgnoreUnrecognizedVMOptions.java index c876b136969..10f68c675f9 100644 --- a/hotspot/test/runtime/CommandLine/IgnoreUnrecognizedVMOptions.java +++ b/hotspot/test/runtime/CommandLine/IgnoreUnrecognizedVMOptions.java @@ -131,47 +131,41 @@ public class IgnoreUnrecognizedVMOptions { /* #1.7 locked flag: - diagnostic & locked experimental & locked commercial & locked - -XX:-UnlockDiagnosticVMOptions -XX:-UnlockExperimentalVMOptions -XX:-UnlockCommercialFeatures - -XX:+PrintInlining -XX:+AlwaysSafeConstructors -XX:+FlightRecorder - -IgnoreUnrecognizedVMOptions ERR ERR ERR - +IgnoreUnrecognizedVMOptions ERR ERR ERR + diagnostic & locked experimental & locked + -XX:-UnlockDiagnosticVMOptions -XX:-UnlockExperimentalVMOptions + -XX:+PrintInlining -XX:+AlwaysSafeConstructors + -IgnoreUnrecognizedVMOptions ERR ERR + +IgnoreUnrecognizedVMOptions ERR ERR */ runJavaAndCheckExitValue(false, "-XX:-IgnoreUnrecognizedVMOptions", "-XX:-UnlockDiagnosticVMOptions", "-XX:+PrintInlining", "-version"); runJavaAndCheckExitValue(false, "-XX:-IgnoreUnrecognizedVMOptions", "-XX:-UnlockExperimentalVMOptions", "-XX:+AlwaysSafeConstructors", "-version"); - runJavaAndCheckExitValue(false, "-XX:-IgnoreUnrecognizedVMOptions", "-XX:-UnlockCommercialFeatures", "-XX:+FlightRecorder", "-version"); runJavaAndCheckExitValue(false, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UnlockDiagnosticVMOptions", "-XX:+PrintInlining", "-version"); runJavaAndCheckExitValue(false, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UnlockExperimentalVMOptions", "-XX:+AlwaysSafeConstructors", "-version"); - runJavaAndCheckExitValue(false, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UnlockCommercialFeatures", "-XX:+FlightRecorder", "-version"); /* #1.8 malformed locked flag: - diagnostic & locked experimental & locked commercial & locked - -XX:-UnlockDiagnosticVMOptions -XX:-UnlockExperimentalVMOptions -XX:-UnlockCommercialFeatures - -XX:PrintInlining -XX:AlwaysSafeConstructors -XX:FlightRecorder - -IgnoreUnrecognizedVMOptions ERR ERR ERR - +IgnoreUnrecognizedVMOptions ERR ERR ERR + diagnostic & locked experimental & locked + -XX:-UnlockDiagnosticVMOptions -XX:-UnlockExperimentalVMOptions + -XX:PrintInlining -XX:AlwaysSafeConstructors + -IgnoreUnrecognizedVMOptions ERR ERR + +IgnoreUnrecognizedVMOptions ERR ERR */ runJavaAndCheckExitValue(false, "-XX:-IgnoreUnrecognizedVMOptions", "-XX:-UnlockDiagnosticVMOptions", "-XX:PrintInlining", "-version"); runJavaAndCheckExitValue(false, "-XX:-IgnoreUnrecognizedVMOptions", "-XX:-UnlockExperimentalVMOptions", "-XX:AlwaysSafeConstructors", "-version"); - runJavaAndCheckExitValue(false, "-XX:-IgnoreUnrecognizedVMOptions", "-XX:-UnlockCommercialFeatures", "-XX:FlightRecorder", "-version"); runJavaAndCheckExitValue(false, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UnlockDiagnosticVMOptions", "-XX:PrintInlining", "-version"); runJavaAndCheckExitValue(false, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UnlockExperimentalVMOptions", "-XX:AlwaysSafeConstructors", "-version"); - runJavaAndCheckExitValue(false, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UnlockCommercialFeatures", "-XX:FlightRecorder", "-version"); /* #1.9 malformed unlocked flag: - diagnostic & locked experimental & locked commercial & locked - -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UnlockCommercialFeatures - -XX:PrintInlining -XX:AlwaysSafeConstructors -XX:FlightRecorder - -IgnoreUnrecognizedVMOptions ERR ERR ERR - +IgnoreUnrecognizedVMOptions ERR ERR ERR + diagnostic & locked experimental & locked + -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions + -XX:PrintInlining -XX:AlwaysSafeConstructors + -IgnoreUnrecognizedVMOptions ERR ERR + +IgnoreUnrecognizedVMOptions ERR ERR */ - runJavaAndCheckExitValue(false, "-XX:-IgnoreUnrecognizedVMOptions", "-XX:-UnlockDiagnosticVMOptions", "-XX:PrintInlining", "-version"); - runJavaAndCheckExitValue(false, "-XX:-IgnoreUnrecognizedVMOptions", "-XX:-UnlockExperimentalVMOptions", "-XX:AlwaysSafeConstructors", "-version"); - runJavaAndCheckExitValue(false, "-XX:-IgnoreUnrecognizedVMOptions", "-XX:-UnlockCommercialFeatures", "-XX:FlightRecorder", "-version"); - runJavaAndCheckExitValue(false, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UnlockDiagnosticVMOptions", "-XX:PrintInlining", "-version"); - runJavaAndCheckExitValue(false, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UnlockExperimentalVMOptions", "-XX:AlwaysSafeConstructors", "-version"); - runJavaAndCheckExitValue(false, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UnlockCommercialFeatures", "-XX:FlightRecorder", "-version"); + runJavaAndCheckExitValue(false, "-XX:-IgnoreUnrecognizedVMOptions", "-XX:+UnlockDiagnosticVMOptions", "-XX:PrintInlining", "-version"); + runJavaAndCheckExitValue(false, "-XX:-IgnoreUnrecognizedVMOptions", "-XX:+UnlockExperimentalVMOptions", "-XX:AlwaysSafeConstructors", "-version"); + runJavaAndCheckExitValue(false, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UnlockDiagnosticVMOptions", "-XX:PrintInlining", "-version"); + runJavaAndCheckExitValue(false, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UnlockExperimentalVMOptions", "-XX:AlwaysSafeConstructors", "-version"); } } diff --git a/hotspot/test/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java b/hotspot/test/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java index fa9ad87eb52..eece4005eaf 100644 --- a/hotspot/test/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java +++ b/hotspot/test/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java @@ -41,37 +41,86 @@ import optionsvalidation.JVMOptionsUtils; public class TestOptionsWithRanges { + private static Map allOptionsAsMap; + + private static void excludeTestMaxRange(String optionName) { + JVMOption option = allOptionsAsMap.get(optionName); + + if (option != null) { + option.excludeTestMaxRange(); + } + } + + private static void excludeTestMinRange(String optionName) { + JVMOption option = allOptionsAsMap.get(optionName); + + if (option != null) { + option.excludeTestMinRange(); + } + } + + private static void excludeTestRange(String optionName) { + allOptionsAsMap.remove(optionName); + } + public static void main(String[] args) throws Exception { int failedTests; - Map allOptionsAsMap = JVMOptionsUtils.getOptionsWithRangeAsMap(); List allOptions; + allOptionsAsMap = JVMOptionsUtils.getOptionsWithRangeAsMap(); + /* * Remove CICompilerCount from testing because currently it can hang system */ - allOptionsAsMap.remove("CICompilerCount"); + excludeTestMaxRange("CICompilerCount"); /* * JDK-8136766 * Temporarily remove ThreadStackSize from testing because Windows can set it to 0 * (for default OS size) but other platforms insist it must be greater than 0 */ - allOptionsAsMap.remove("ThreadStackSize"); + excludeTestRange("ThreadStackSize"); + + /* + * JDK-8141650 + * Temporarily exclude SharedMiscDataSize as it will exit the VM with exit code 2 and + * "The shared miscellaneous data space is not large enough to preload requested classes." + * message at min value. + */ + excludeTestRange("SharedMiscDataSize"); + + /* + * JDK-8142874 + * Temporarily exclude Shared* flagse as they will exit the VM with exit code 2 and + * "The shared miscellaneous data space is not large enough to preload requested classes." + * message at max values. + */ + excludeTestRange("SharedReadWriteSize"); + excludeTestRange("SharedReadOnlySize"); + excludeTestRange("SharedMiscDataSize"); + excludeTestRange("SharedMiscCodeSize"); + + /* + * Remove the flag controlling the size of the stack because the + * flag has direct influence on the physical memory usage of + * the VM. + */ + allOptionsAsMap.remove("CompilerThreadStackSize"); /* * Exclude MallocMaxTestWords as it is expected to exit VM at small values (>=0) */ - allOptionsAsMap.remove("MallocMaxTestWords"); + excludeTestMinRange("MallocMaxTestWords"); /* * Exclude below options as their maximum value would consume too much memory * and would affect other tests that run in parallel. */ - allOptionsAsMap.remove("G1ConcRefinementThreads"); - allOptionsAsMap.remove("G1RSetRegionEntries"); - allOptionsAsMap.remove("G1RSetSparseRegionEntries"); - allOptionsAsMap.remove("G1UpdateBufferSize"); - allOptionsAsMap.remove("InitialBootClassLoaderMetaspaceSize"); + excludeTestMaxRange("G1ConcRefinementThreads"); + excludeTestMaxRange("G1RSetRegionEntries"); + excludeTestMaxRange("G1RSetSparseRegionEntries"); + excludeTestMaxRange("G1UpdateBufferSize"); + excludeTestMaxRange("InitialBootClassLoaderMetaspaceSize"); /* * Remove parameters controlling the code cache. As these @@ -83,13 +132,13 @@ public class TestOptionsWithRanges { * hotspot/src/shared/vm/code/codeCache.cpp), therefore * omitting testing for them does not pose a problem. */ - allOptionsAsMap.remove("InitialCodeCacheSize"); - allOptionsAsMap.remove("CodeCacheMinimumUseSpace"); - allOptionsAsMap.remove("ReservedCodeCacheSize"); - allOptionsAsMap.remove("NonProfiledCodeHeapSize"); - allOptionsAsMap.remove("ProfiledCodeHeapSize"); - allOptionsAsMap.remove("NonNMethodCodeHeapSize"); - allOptionsAsMap.remove("CodeCacheExpansionSize"); + excludeTestMaxRange("InitialCodeCacheSize"); + excludeTestMaxRange("CodeCacheMinimumUseSpace"); + excludeTestMaxRange("ReservedCodeCacheSize"); + excludeTestMaxRange("NonProfiledCodeHeapSize"); + excludeTestMaxRange("ProfiledCodeHeapSize"); + excludeTestMaxRange("NonNMethodCodeHeapSize"); + excludeTestMaxRange("CodeCacheExpansionSize"); allOptions = new ArrayList<>(allOptionsAsMap.values()); diff --git a/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/DoubleJVMOption.java b/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/DoubleJVMOption.java index d6c4abc563c..b80fc268515 100644 --- a/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/DoubleJVMOption.java +++ b/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/DoubleJVMOption.java @@ -123,22 +123,28 @@ public class DoubleJVMOption extends JVMOption { protected List getValidValues() { List validValues = new ArrayList<>(); - validValues.add(formatValue(min)); - validValues.add(formatValue(max)); - - if ((Double.compare(min, ADDITIONAL_TEST_DOUBLE_NEGATIVE) < 0) - && (Double.compare(max, ADDITIONAL_TEST_DOUBLE_NEGATIVE) > 0)) { - validValues.add(formatValue(ADDITIONAL_TEST_DOUBLE_NEGATIVE)); + if (testMinRange) { + validValues.add(formatValue(min)); + } + if (testMaxRange) { + validValues.add(formatValue(max)); } - if ((Double.compare(min, ADDITIONAL_TEST_DOUBLE_ZERO) < 0) - && (Double.compare(max, ADDITIONAL_TEST_DOUBLE_ZERO) > 0)) { - validValues.add(formatValue(ADDITIONAL_TEST_DOUBLE_ZERO)); - } + if (testMinRange) { + if ((Double.compare(min, ADDITIONAL_TEST_DOUBLE_NEGATIVE) < 0) + && (Double.compare(max, ADDITIONAL_TEST_DOUBLE_NEGATIVE) > 0)) { + validValues.add(formatValue(ADDITIONAL_TEST_DOUBLE_NEGATIVE)); + } - if ((Double.compare(min, ADDITIONAL_TEST_DOUBLE_POSITIVE) < 0) - && (Double.compare(max, ADDITIONAL_TEST_DOUBLE_POSITIVE) > 0)) { - validValues.add(formatValue(ADDITIONAL_TEST_DOUBLE_POSITIVE)); + if ((Double.compare(min, ADDITIONAL_TEST_DOUBLE_ZERO) < 0) + && (Double.compare(max, ADDITIONAL_TEST_DOUBLE_ZERO) > 0)) { + validValues.add(formatValue(ADDITIONAL_TEST_DOUBLE_ZERO)); + } + + if ((Double.compare(min, ADDITIONAL_TEST_DOUBLE_POSITIVE) < 0) + && (Double.compare(max, ADDITIONAL_TEST_DOUBLE_POSITIVE) > 0)) { + validValues.add(formatValue(ADDITIONAL_TEST_DOUBLE_POSITIVE)); + } } return validValues; diff --git a/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/IntJVMOption.java b/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/IntJVMOption.java index 90270e96613..417178ead8d 100644 --- a/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/IntJVMOption.java +++ b/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/IntJVMOption.java @@ -200,43 +200,51 @@ public class IntJVMOption extends JVMOption { protected List getValidValues() { List validValues = new ArrayList<>(); - validValues.add(min.toString()); - validValues.add(max.toString()); - - if ((min.compareTo(MINUS_ONE) == -1) && (max.compareTo(MINUS_ONE) == 1)) { - /* - * Add -1 as valid value if min is less than -1 and max is greater than -1 - */ - validValues.add("-1"); + if (testMinRange) { + validValues.add(min.toString()); + } + if (testMaxRange) { + validValues.add(max.toString()); } - if ((min.compareTo(BigInteger.ZERO) == -1) && (max.compareTo(BigInteger.ZERO) == 1)) { - /* - * Add 0 as valid value if min is less than 0 and max is greater than 0 - */ - validValues.add("0"); - } - if ((min.compareTo(BigInteger.ONE) == -1) && (max.compareTo(BigInteger.ONE) == 1)) { - /* - * Add 1 as valid value if min is less than 1 and max is greater than 1 - */ - validValues.add("1"); + if (testMinRange) { + if ((min.compareTo(MINUS_ONE) == -1) && (max.compareTo(MINUS_ONE) == 1)) { + /* + * Add -1 as valid value if min is less than -1 and max is greater than -1 + */ + validValues.add("-1"); + } + + if ((min.compareTo(BigInteger.ZERO) == -1) && (max.compareTo(BigInteger.ZERO) == 1)) { + /* + * Add 0 as valid value if min is less than 0 and max is greater than 0 + */ + validValues.add("0"); + } + if ((min.compareTo(BigInteger.ONE) == -1) && (max.compareTo(BigInteger.ONE) == 1)) { + /* + * Add 1 as valid value if min is less than 1 and max is greater than 1 + */ + validValues.add("1"); + } } - if (max.compareTo(MAX_4_BYTE_INT_PLUS_ONE) == 1) { - /* - * Check for overflow when flag is assigned to the - * 4 byte int variable - */ - validValues.add(MAX_4_BYTE_INT_PLUS_ONE.toString()); - } + if (testMaxRange) { + if ((min.compareTo(MAX_4_BYTE_INT_PLUS_ONE) == -1) && (max.compareTo(MAX_4_BYTE_INT_PLUS_ONE) == 1)) { + /* + * Check for overflow when flag is assigned to the + * 4 byte int variable + */ + validValues.add(MAX_4_BYTE_INT_PLUS_ONE.toString()); + } - if (max.compareTo(MAX_4_BYTE_UNSIGNED_INT_PLUS_ONE) == 1) { - /* - * Check for overflow when flag is assigned to the - * 4 byte unsigned int variable - */ - validValues.add(MAX_4_BYTE_UNSIGNED_INT_PLUS_ONE.toString()); + if ((min.compareTo(MAX_4_BYTE_UNSIGNED_INT_PLUS_ONE) == -1) && (max.compareTo(MAX_4_BYTE_UNSIGNED_INT_PLUS_ONE) == 1)) { + /* + * Check for overflow when flag is assigned to the + * 4 byte unsigned int variable + */ + validValues.add(MAX_4_BYTE_UNSIGNED_INT_PLUS_ONE.toString()); + } } return validValues; @@ -252,24 +260,28 @@ public class IntJVMOption extends JVMOption { protected List getInvalidValues() { List invalidValues = new ArrayList<>(); + /* Return invalid values only for options which have defined range in VM */ if (withRange) { - /* Return invalid values only for options which have defined range in VM */ - if ((is32Bit && min.compareTo(MIN_4_BYTE_INT) != 0) - || (!is32Bit && min.compareTo(MIN_LONG) != 0)) { - invalidValues.add(min.subtract(BigInteger.ONE).toString()); - } + if (unsigned) { + /* Only add non-negative out-of-range values for unsigned options */ + if (min.compareTo(BigInteger.ZERO) == 1) { + invalidValues.add(min.subtract(BigInteger.ONE).toString()); + } - if (!unsigned - && ((is32Bit && (max.compareTo(MAX_4_BYTE_INT) != 0)) - || (!is32Bit && (max.compareTo(MAX_LONG) != 0)))) { - invalidValues.add(max.add(BigInteger.ONE).toString()); - } - - if (unsigned - && ((is32Bit && (max.compareTo(MAX_4_BYTE_UNSIGNED_INT) != 0)) - || (!is32Bit && !uint64 && (max.compareTo(MAX_UNSIGNED_LONG) != 0)) - || (uint64 && (max.compareTo(MAX_UNSIGNED_LONG_64) != 0)))) { - invalidValues.add(max.add(BigInteger.ONE).toString()); + if ((is32Bit && (max.compareTo(MAX_4_BYTE_UNSIGNED_INT) != 0)) + || (!is32Bit && !uint64 && (max.compareTo(MAX_UNSIGNED_LONG) != 0)) + || (uint64 && (max.compareTo(MAX_UNSIGNED_LONG_64) != 0))) { + invalidValues.add(max.add(BigInteger.ONE).toString()); + } + } else { + if ((is32Bit && min.compareTo(MIN_4_BYTE_INT) != 0) + || (!is32Bit && min.compareTo(MIN_LONG) != 0)) { + invalidValues.add(min.subtract(BigInteger.ONE).toString()); + } + if ((is32Bit && (max.compareTo(MAX_4_BYTE_INT) != 0)) + || (!is32Bit && (max.compareTo(MAX_LONG) != 0))) { + invalidValues.add(max.add(BigInteger.ONE).toString()); + } } } diff --git a/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java b/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java index 3f4ad09b269..bd964e19ae0 100644 --- a/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java +++ b/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java @@ -54,6 +54,16 @@ public abstract class JVMOption { */ protected boolean withRange; + /** + * Test valid min range value and additional small values + */ + protected boolean testMinRange; + + /** + * Test valid max range value and additional big values + */ + protected boolean testMaxRange; + /** * Prepend string which added before testing option to the command line */ @@ -64,6 +74,8 @@ public abstract class JVMOption { this.prepend = new ArrayList<>(); prependString = new StringBuilder(); withRange = false; + testMinRange = true; + testMaxRange = true; } /** @@ -135,6 +147,20 @@ public abstract class JVMOption { withRange = true; } + /** + * Exclude testing of min range value for this option + */ + public final void excludeTestMinRange() { + testMinRange = false; + } + + /** + * Exclude testing of max range value for this option + */ + public final void excludeTestMaxRange() { + testMaxRange = false; + } + /** * Set new minimum option value * diff --git a/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOptionsUtils.java b/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOptionsUtils.java index 7158b356eed..a5c8ab0d6f8 100644 --- a/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOptionsUtils.java +++ b/hotspot/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOptionsUtils.java @@ -27,6 +27,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -49,6 +50,8 @@ public class JVMOptionsUtils { /* Used to start the JVM with the same type as current */ static String VMType; + private static Map optionsAsMap; + static { if (Platform.isServer()) { VMType = "-server"; @@ -63,6 +66,84 @@ public class JVMOptionsUtils { } } + public static boolean fitsRange(String optionName, BigDecimal number) throws Exception { + JVMOption option; + String minRangeString = null; + String maxRangeString = null; + boolean fits = true; + + if (optionsAsMap == null) { + optionsAsMap = getOptionsWithRangeAsMap(); + } + + option = optionsAsMap.get(optionName); + if (option != null) { + minRangeString = option.getMin(); + if (minRangeString != null) { + fits = (number.compareTo(new BigDecimal(minRangeString)) >= 0); + } + maxRangeString = option.getMax(); + if (maxRangeString != null) { + fits &= (number.compareTo(new BigDecimal(maxRangeString)) <= 0); + } + } + + return fits; + } + + public static boolean fitsRange(String optionName, String number) throws Exception { + String lowerCase = number.toLowerCase(); + String multiplier = "1"; + if (lowerCase.endsWith("k")) { + multiplier = "1024"; + lowerCase = lowerCase.substring(0, lowerCase.length()-1); + } else if (lowerCase.endsWith("m")) { + multiplier = "1048576"; //1024*1024 + lowerCase = lowerCase.substring(0, lowerCase.length()-1); + } else if (lowerCase.endsWith("g")) { + multiplier = "1073741824"; //1024*1024*1024 + lowerCase = lowerCase.substring(0, lowerCase.length()-1); + } else if (lowerCase.endsWith("t")) { + multiplier = "1099511627776"; //1024*1024*1024*1024 + lowerCase = lowerCase.substring(0, lowerCase.length()-1); + } + BigDecimal valueBig = new BigDecimal(lowerCase); + BigDecimal multiplierBig = new BigDecimal(multiplier); + return fitsRange(optionName, valueBig.multiply(multiplierBig)); + } + + public static String getMinOptionRange(String optionName) throws Exception { + JVMOption option; + String minRange = null; + + if (optionsAsMap == null) { + optionsAsMap = getOptionsWithRangeAsMap(); + } + + option = optionsAsMap.get(optionName); + if (option != null) { + minRange = option.getMin(); + } + + return minRange; + } + + public static String getMaxOptionRange(String optionName) throws Exception { + JVMOption option; + String maxRange = null; + + if (optionsAsMap == null) { + optionsAsMap = getOptionsWithRangeAsMap(); + } + + option = optionsAsMap.get(optionName); + if (option != null) { + maxRange = option.getMax(); + } + + return maxRange; + } + /** * Add dependency for option depending on it's name. E.g. enable G1 GC for * G1 options or add prepend options to not hit constraints. @@ -80,6 +161,13 @@ public class JVMOptionsUtils { option.addPrepend("-XX:+UseConcMarkSweepGC"); } + if (name.startsWith("Shared")) { + option.addPrepend("-XX:+UnlockDiagnosticVMOptions"); + String fileName = "Test" + name + ".jsa"; + option.addPrepend("-XX:SharedArchiveFile=" + fileName); + option.addPrepend("-Xshare:dump"); + } + switch (name) { case "MinHeapFreeRatio": option.addPrepend("-XX:MaxHeapFreeRatio=100"); @@ -112,7 +200,6 @@ public class JVMOptionsUtils { /* Do nothing */ break; } - } /** diff --git a/hotspot/test/runtime/CommandLine/VMOptionWarning.java b/hotspot/test/runtime/CommandLine/VMOptionWarning.java index 894b2c5297e..1d81ff18060 100644 --- a/hotspot/test/runtime/CommandLine/VMOptionWarning.java +++ b/hotspot/test/runtime/CommandLine/VMOptionWarning.java @@ -47,12 +47,12 @@ public class VMOptionWarning { output = new OutputAnalyzer(pb.start()); output.shouldContain("Error: VM option 'PrintInlining' is diagnostic and must be enabled via -XX:+UnlockDiagnosticVMOptions."); - pb = ProcessTools.createJavaProcessBuilder("-XX:+TraceJNICalls", "-version"); + pb = ProcessTools.createJavaProcessBuilder("-XX:+VerifyStack", "-version"); output = new OutputAnalyzer(pb.start()); - output.shouldContain("Error: VM option 'TraceJNICalls' is develop and is available only in debug version of VM."); + output.shouldContain("Error: VM option 'VerifyStack' is develop and is available only in debug version of VM."); - pb = ProcessTools.createJavaProcessBuilder("-XX:+TraceJVMCalls", "-version"); + pb = ProcessTools.createJavaProcessBuilder("-XX:+ExecuteInternalVMTests", "-version"); output = new OutputAnalyzer(pb.start()); - output.shouldContain("Error: VM option 'TraceJVMCalls' is notproduct and is available only in debug version of VM."); + output.shouldContain("Error: VM option 'ExecuteInternalVMTests' is notproduct and is available only in debug version of VM."); } } diff --git a/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java b/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java index 1cb136c3b57..824fd94f41f 100644 --- a/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java +++ b/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java @@ -23,50 +23,72 @@ /* @test LimitSharedSizes * @summary Test handling of limits on shared space size - * @library /testlibrary + * @library /testlibrary /runtime/CommandLine/OptionsValidation/common * @modules java.base/sun.misc * java.management * @run main LimitSharedSizes */ import jdk.test.lib.*; +import optionsvalidation.JVMOptionsUtils; public class LimitSharedSizes { + static enum Result { + OUT_OF_RANGE, + TOO_SMALL, + VALID, + VALID_ARCHIVE + } + static enum Region { RO, RW, MD, MC } + private static final boolean fitsRange(String name, String value) throws RuntimeException { + boolean fits = true; + try { + fits = JVMOptionsUtils.fitsRange(name, value); + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); + } + return fits; + } + private static class SharedSizeTestData { public String optionName; public String optionValue; - public String expectedErrorMsg; + public Result optionResult; - public SharedSizeTestData(Region region, String value, String msg) { - optionName = getName(region); + public SharedSizeTestData(Region region, String value) { + optionName = "-XX:"+getName(region); optionValue = value; - expectedErrorMsg = msg; + if (fitsRange(getName(region), value) == false) { + optionResult = Result.OUT_OF_RANGE; + } else { + optionResult = Result.TOO_SMALL; + } } - public SharedSizeTestData(Region region, String msg) { - optionName = getName(region); - optionValue = getValue(region); - expectedErrorMsg = msg; + public SharedSizeTestData(Region region, String value, Result result) { + optionName = "-XX:"+getName(region); + optionValue = value; + optionResult = result; } private String getName(Region region) { String name; switch (region) { case RO: - name = "-XX:SharedReadOnlySize"; + name = "SharedReadOnlySize"; break; case RW: - name = "-XX:SharedReadWriteSize"; + name = "SharedReadWriteSize"; break; case MD: - name = "-XX:SharedMiscDataSize"; + name = "SharedMiscDataSize"; break; case MC: - name = "-XX:SharedMiscCodeSize"; + name = "SharedMiscCodeSize"; break; default: name = "Unknown"; @@ -75,53 +97,37 @@ public class LimitSharedSizes { return name; } - private String getValue(Region region) { - String value; - switch (region) { - case RO: - value = Platform.is64bit() ? "9M" : "8M"; - break; - case RW: - value = Platform.is64bit() ? "12M" : "7M"; - break; - case MD: - value = Platform.is64bit() ? "4M" : "2M"; - break; - case MC: - value = "120k"; - break; - default: - value = "0M"; - break; - } - return value; + public Result getResult() { + return optionResult; } } private static final SharedSizeTestData[] testTable = { // Too small of a region size should not cause a vm crash. - // It should result in an error message like the following: + // It should result in an error message either like the following #1: // The shared miscellaneous code space is not large enough // to preload requested classes. Use -XX:SharedMiscCodeSize= // to increase the initial size of shared miscellaneous code space. - new SharedSizeTestData(Region.RO, "4M", "read only"), - new SharedSizeTestData(Region.RW, "4M", "read write"), - new SharedSizeTestData(Region.MD, "50k", "miscellaneous data"), - new SharedSizeTestData(Region.MC, "20k", "miscellaneous code"), + // or #2: + // The shared miscellaneous code space is outside the allowed range + new SharedSizeTestData(Region.RO, "4M"), + new SharedSizeTestData(Region.RW, "4M"), + new SharedSizeTestData(Region.MD, "50k"), + new SharedSizeTestData(Region.MC, "20k"), - // these values are larger than default ones, but should + // these values are larger than default ones, and should // be acceptable and not cause failure - new SharedSizeTestData(Region.RO, "20M", null), - new SharedSizeTestData(Region.RW, "20M", null), - new SharedSizeTestData(Region.MD, "20M", null), - new SharedSizeTestData(Region.MC, "20M", null), + new SharedSizeTestData(Region.RO, "20M", Result.VALID), + new SharedSizeTestData(Region.RW, "20M", Result.VALID), + new SharedSizeTestData(Region.MD, "20M", Result.VALID), + new SharedSizeTestData(Region.MC, "20M", Result.VALID), // test with sizes which just meet the minimum required sizes // the following tests also attempt to use the shared archive - new SharedSizeTestData(Region.RO, "UseArchive"), - new SharedSizeTestData(Region.RW, "UseArchive"), - new SharedSizeTestData(Region.MD, "UseArchive"), - new SharedSizeTestData(Region.MC, "UseArchive") + new SharedSizeTestData(Region.RO, Platform.is64bit() ? "9M":"8M", Result.VALID_ARCHIVE), + new SharedSizeTestData(Region.RW, Platform.is64bit() ? "12M":"7M", Result.VALID_ARCHIVE), + new SharedSizeTestData(Region.MD, Platform.is64bit() ? "4M":"2M", Result.VALID_ARCHIVE), + new SharedSizeTestData(Region.MC, "120k", Result.VALID_ARCHIVE), }; public static void main(String[] args) throws Exception { @@ -131,6 +137,7 @@ public class LimitSharedSizes { counter++; String option = td.optionName + "=" + td.optionValue; + System.out.println("testing option number <" + counter + ">"); System.out.println("testing option <" + option + ">"); ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( @@ -141,43 +148,52 @@ public class LimitSharedSizes { OutputAnalyzer output = new OutputAnalyzer(pb.start()); - if (td.expectedErrorMsg != null) { - if (!td.expectedErrorMsg.equals("UseArchive")) { - output.shouldContain("The shared " + td.expectedErrorMsg - + " space is not large enough"); + switch (td.getResult()) { + case VALID: + case VALID_ARCHIVE: + { + output.shouldNotContain("space is not large enough"); + output.shouldHaveExitValue(0); - output.shouldHaveExitValue(2); - } else { - output.shouldNotContain("space is not large enough"); - output.shouldHaveExitValue(0); + if (td.getResult() == Result.VALID_ARCHIVE) { + // try to use the archive + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:SharedArchiveFile=./" + fileName, + "-XX:+PrintSharedArchiveAndExit", + "-version"); - // try to use the archive - pb = ProcessTools.createJavaProcessBuilder( - "-XX:+UnlockDiagnosticVMOptions", - "-XX:SharedArchiveFile=./" + fileName, - "-XX:+PrintSharedArchiveAndExit", - "-version"); - - try { - output = new OutputAnalyzer(pb.start()); - output.shouldContain("archive is valid"); - } catch (RuntimeException e) { - // if sharing failed due to ASLR or similar reasons, - // check whether sharing was attempted at all (UseSharedSpaces) - if ((output.getOutput().contains("Unable to use shared archive") || - output.getOutput().contains("Unable to map ReadOnly shared space at required address.") || - output.getOutput().contains("Unable to map ReadWrite shared space at required address.") || - output.getOutput().contains("Unable to reserve shared space at required address")) && - output.getExitValue() == 1) { - System.out.println("Unable to use shared archive: test not executed; assumed passed"); - return; - } - } - output.shouldHaveExitValue(0); + try { + output = new OutputAnalyzer(pb.start()); + output.shouldContain("archive is valid"); + } catch (RuntimeException e) { + // if sharing failed due to ASLR or similar reasons, + // check whether sharing was attempted at all (UseSharedSpaces) + if ((output.getOutput().contains("Unable to use shared archive") || + output.getOutput().contains("Unable to map ReadOnly shared space at required address.") || + output.getOutput().contains("Unable to map ReadWrite shared space at required address.") || + output.getOutput().contains("Unable to reserve shared space at required address")) && + output.getExitValue() == 1) { + System.out.println("Unable to use shared archive: test not executed; assumed passed"); + return; + } + } + output.shouldHaveExitValue(0); + } } - } else { - output.shouldNotContain("space is not large enough"); - output.shouldHaveExitValue(0); + break; + case TOO_SMALL: + { + output.shouldContain("space is not large enough"); + output.shouldHaveExitValue(2); + } + break; + case OUT_OF_RANGE: + { + output.shouldContain("outside the allowed range"); + output.shouldHaveExitValue(1); + } + break; } } } diff --git a/hotspot/test/runtime/ThreadSignalMask/Prog.java b/hotspot/test/runtime/ThreadSignalMask/Prog.java new file mode 100644 index 00000000000..586b64e50e1 --- /dev/null +++ b/hotspot/test/runtime/ThreadSignalMask/Prog.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015, 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. + */ + +public class Prog { + + public static void main(String args[]) { + System.out.println("Java class invoked: " + args[0]); + } +} diff --git a/hotspot/test/runtime/ThreadSignalMask/ThreadSignalMask.java b/hotspot/test/runtime/ThreadSignalMask/ThreadSignalMask.java new file mode 100644 index 00000000000..39af699218d --- /dev/null +++ b/hotspot/test/runtime/ThreadSignalMask/ThreadSignalMask.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.lang.ProcessBuilder.Redirect; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Arrays; +import java.util.List; +import jdk.test.lib.Asserts; + +/* + * @test + * @key cte_test + * @bug 4345157 + * @summary JDK 1.3.0 alters thread signal mask + * @requires (os.simpleArch == "sparcv9") + * @library /testlibrary + * @compile Prog.java + * @run main/native ThreadSignalMask + */ +public class ThreadSignalMask { + + public static void main(String args[]) throws Exception { + + String testClasses = getSystemProperty("test.classes"); + + String testNativePath = getSystemProperty("test.nativepath"); + + String testJdk = getSystemProperty("test.jdk"); + + Path currentDirPath = Paths.get("."); + + Path classFilePath = Paths.get(testClasses, + Prog.class.getSimpleName() + ".class"); + + // copy Prog.class file to be invoked from native + Files.copy(classFilePath, + currentDirPath.resolve(Prog.class.getSimpleName() + ".class"), + StandardCopyOption.REPLACE_EXISTING); + + Path executableFilePath = Paths.get(testNativePath, + ThreadSignalMask.class.getSimpleName()); + + Path executableFileLocalPath = currentDirPath.resolve( + ThreadSignalMask.class.getSimpleName()); + + // copy compiled native executable ThreadSignalMask + Files.copy(executableFilePath, + executableFileLocalPath, + StandardCopyOption.REPLACE_EXISTING); + + executableFileLocalPath.toFile().setExecutable(true); + + long[] intervalsArray = {2000, 5000, 10000, 20000}; + + List processArgs = Arrays.asList( + executableFileLocalPath.toString(), + testJdk); + ProcessBuilder pb = new ProcessBuilder(processArgs); + pb.redirectOutput(Redirect.INHERIT); + pb.redirectError(Redirect.INHERIT); + int result = 0; + for (long interval : intervalsArray) { + Process p = pb.start(); + + // sleep for a specified period of time to let native run + sleep(interval); + p.destroy(); + + // wait for process to finish, get exit value and validate it + result = p.waitFor(); + System.out.println("Result = " + result); + if (result == 0) { + break; + } + } + + Asserts.assertEquals(result, 0); + } + + // Utility method to handle Thread.sleep + private static void sleep(long millis) throws InterruptedException { + System.out.println("Sleep for " + millis); + Thread.sleep(millis); + } + + // Utility method to retrieve and validate system properties + private static String getSystemProperty(String propertyName) throws Error { + String systemProperty = System.getProperty(propertyName, "").trim(); + System.out.println(propertyName + " = " + systemProperty); + if (systemProperty.isEmpty()) { + throw new Error("TESTBUG: property " + propertyName + " is empty"); + } + return systemProperty; + } +} diff --git a/hotspot/test/runtime/ThreadSignalMask/exeThreadSignalMask.c b/hotspot/test/runtime/ThreadSignalMask/exeThreadSignalMask.c new file mode 100644 index 00000000000..7e879e4c09c --- /dev/null +++ b/hotspot/test/runtime/ThreadSignalMask/exeThreadSignalMask.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2015, 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. + */ + +#define _POSIX_PTHREAD_SEMANTICS // to enable POSIX semantics for certain common APIs + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void *handle; +char *error; +char path[PATH_MAX]; + +jint(JNICALL *jni_create_java_vm)(JavaVM **, JNIEnv **, void *) = NULL; + +JavaVM *jvm; + +// method to perform dlclose on an open dynamic library handle +void closeHandle() { + dlclose(handle); + if ((error = dlerror()) != NULL) { + fputs("Error occurred while closing handle\n", stderr); + } +} + +// method to exit with a fail status +void fail() { + if (handle) { + closeHandle(); + } + exit(1); +} + +// method to handle occurred error and fail +void handleError(char *messageTitle, char *messageBody) { + fprintf(stderr, "%s: %s\n", messageTitle, messageBody); + fail(); +} + +// method to load the dynamic library libjvm +void loadJVM() { + char lib[PATH_MAX]; + snprintf(lib, sizeof (lib), "%s/lib/sparcv9/server/libjvm.so", path); + handle = dlopen(lib, RTLD_LAZY); + if (!handle) { + handleError(dlerror(), "2"); + } + fputs("Will load JVM...\n", stdout); + + // find the address of function + *(void **) (&jni_create_java_vm) = dlsym(handle, "JNI_CreateJavaVM"); + if ((error = dlerror()) != NULL) { + handleError(error, "3"); + } + + fputs("JVM loaded okay.\n", stdout); +} + +// method to get created jvm environment +JNIEnv* initJVM() { + JNIEnv *env = NULL; + JavaVMInitArgs vm_args; + JavaVMOption options[1]; + jint res; + + options[0].optionString = "-Xrs"; + + vm_args.version = JNI_VERSION_1_2; + vm_args.nOptions = 1; + vm_args.options = options; + vm_args.ignoreUnrecognized = JNI_FALSE; + + fputs("Will create JVM...\n", stdout); + + res = (*jni_create_java_vm)(&jvm, &env, &vm_args); + if (res < 0) { + handleError("Can't create Java VM", strerror(res)); + } + + fputs("JVM created OK!\n", stdout); + return env; +} + +// method to invoke java method from java class +void callJava(JNIEnv *env) { + jclass cls; + jmethodID mid; + jstring jstr; + jobjectArray args; + + cls = (*env)->FindClass(env, "Prog"); + if (cls == 0) { + handleError("FindClass", "Can't find Prog class"); + } + + mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V"); + if (mid == 0) { + handleError("GetStaticMethodID", "Can't find Prog.main"); + } + + jstr = (*env)->NewStringUTF(env, "from C!"); + if (jstr == 0) { + handleError("NewStringUTF", "Out of memory"); + } + args = (*env)->NewObjectArray(env, 1, + (*env)->FindClass(env, "java/lang/String"), jstr); + if (args == 0) { + handleError("NewObjectArray", "Out of memory"); + } + (*env)->CallStaticVoidMethod(env, cls, mid, args); + +} + +// method to load, init jvm and then invoke java method +void* loadAndCallJava(void* x) { + JNIEnv *env; + + fputs("Some thread will create JVM.\n", stdout); + loadJVM(); + env = initJVM(); + + fputs("Some thread will call Java.\n", stdout); + + callJava(env); + + if ((*jvm)->DetachCurrentThread(jvm) != 0) + fputs("Error: thread not detached!\n", stderr); + fputs("Some thread exiting.\n", stdout); + return env; +} + +int main(int argc, char **argv) { + JNIEnv *env; + sigset_t set; + pthread_t thr1; + pthread_attr_t attr; + size_t ss = 0; + int sig; + int rc; // return code for pthread_* methods + + // verify input + if (argc != 2) { + handleError("usage", "a.out jdk_path"); + } + // copy input jdk path into a char buffer + strncpy(path, argv[1], PATH_MAX); + // add null termination character + path[PATH_MAX - 1] = '\0'; + + fputs("Main thread will set signal mask.\n", stdout); + + // initialize the signal set + sigemptyset(&set); + // add a number of signals to a signal set + sigaddset(&set, SIGPIPE); + sigaddset(&set, SIGTERM); + sigaddset(&set, SIGHUP); + sigaddset(&set, SIGINT); + + // examine and change mask of blocked signal + if ((rc = pthread_sigmask(SIG_BLOCK, &set, NULL))) { + // handle error if occurred + handleError("main: pthread_sigmask() error", strerror(rc)); + } + + // initializes the thread attributes object with default attribute values + if ((rc = pthread_attr_init(&attr))) { + // handle error if occurred + handleError("main: pthread_attr_init() error", strerror(rc)); + } + + ss = 1024 * 1024; + // set the stack size attribute of the thread attributes object + if ((rc = pthread_attr_setstacksize(&attr, ss))) { + // handle error if occurred + handleError("main: pthread_attr_setstacksize() error", strerror(rc)); + } + // get the stack size attribute of the thread attributes object + if ((rc = pthread_attr_getstacksize(&attr, &ss))) { + // handle error if occurred + handleError("main: pthread_attr_getstacksize() error", strerror(rc)); + } + fprintf(stderr, "Stack size: %zu\n", ss); + + // start a new thread in the calling process, + // loadAndCallJava logic is passed as a start_routine argument + if ((rc = pthread_create(&thr1, NULL, loadAndCallJava, NULL))) { + // handle error if occurred + handleError("main: pthread_create() error", strerror(rc)); + } + + // initialize the signal set + sigemptyset(&set); + // add a number of signals to a signal set + sigaddset(&set, SIGTERM); + sigaddset(&set, SIGHUP); + sigaddset(&set, SIGINT); + + fputs("Main thread waiting for signal.\n", stdout); + + do { + int err; + + sig = 0; + err = sigwait(&set, &sig); + if (err != 0) { + // print error message if unexpected signal occurred + fprintf(stderr, "main: sigwait() error: %s\n", strerror(err)); + } else { + // print success message and exit if expected signal occurred + // this branch generally acts when JVM executes destroy() + fprintf(stdout, "main: sigwait() got: %d\nSucceed!\n", sig); + exit(0); + } + } while (sig != SIGTERM && sig != SIGINT); // exit the loop condition + + // join with a terminated thread + if ((rc = pthread_join(thr1, NULL))) { + // handle error if occurred + handleError("main: pthread_join() error", strerror(rc)); + } + + // close an open dynamic library handle + closeHandle(); + fputs("Main thread exiting.\n", stdout); + return 0; +} diff --git a/hotspot/test/runtime/contended/Basic.java b/hotspot/test/runtime/contended/Basic.java index ddc685e4ff2..3ce7eb20e82 100644 --- a/hotspot/test/runtime/contended/Basic.java +++ b/hotspot/test/runtime/contended/Basic.java @@ -36,7 +36,7 @@ import java.util.regex.Pattern; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import sun.misc.Unsafe; -import sun.misc.Contended; +import jdk.internal.vm.annotation.Contended; /* * @test diff --git a/hotspot/test/runtime/contended/DefaultValue.java b/hotspot/test/runtime/contended/DefaultValue.java index 05cc698f3ef..a121c7a63e9 100644 --- a/hotspot/test/runtime/contended/DefaultValue.java +++ b/hotspot/test/runtime/contended/DefaultValue.java @@ -36,7 +36,7 @@ import java.util.regex.Pattern; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import sun.misc.Unsafe; -import sun.misc.Contended; +import jdk.internal.vm.annotation.Contended; /* * @test diff --git a/hotspot/test/runtime/contended/HasNonStatic.java b/hotspot/test/runtime/contended/HasNonStatic.java index 6ff21826428..33878fa6115 100644 --- a/hotspot/test/runtime/contended/HasNonStatic.java +++ b/hotspot/test/runtime/contended/HasNonStatic.java @@ -36,7 +36,7 @@ import java.util.regex.Pattern; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import sun.misc.Unsafe; -import sun.misc.Contended; +import jdk.internal.vm.annotation.Contended; /* * @test diff --git a/hotspot/test/runtime/contended/Inheritance1.java b/hotspot/test/runtime/contended/Inheritance1.java index e6cf2bd1a7f..3ea318c40dc 100644 --- a/hotspot/test/runtime/contended/Inheritance1.java +++ b/hotspot/test/runtime/contended/Inheritance1.java @@ -36,7 +36,7 @@ import java.util.regex.Pattern; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import sun.misc.Unsafe; -import sun.misc.Contended; +import jdk.internal.vm.annotation.Contended; /* * @test diff --git a/hotspot/test/runtime/contended/OopMaps.java b/hotspot/test/runtime/contended/OopMaps.java index 2501ec1f06f..5e1b39c8271 100644 --- a/hotspot/test/runtime/contended/OopMaps.java +++ b/hotspot/test/runtime/contended/OopMaps.java @@ -36,7 +36,7 @@ import java.util.regex.Pattern; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import sun.misc.Unsafe; -import sun.misc.Contended; +import jdk.internal.vm.annotation.Contended; /* * @test diff --git a/hotspot/test/runtime/contended/OopMapsSameGroup.java b/hotspot/test/runtime/contended/OopMapsSameGroup.java index d17ae1ee9f6..af441a576b9 100644 --- a/hotspot/test/runtime/contended/OopMapsSameGroup.java +++ b/hotspot/test/runtime/contended/OopMapsSameGroup.java @@ -36,7 +36,7 @@ import java.util.regex.Pattern; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import sun.misc.Unsafe; -import sun.misc.Contended; +import jdk.internal.vm.annotation.Contended; /* * @test diff --git a/hotspot/test/runtime/lambda-features/TestInterfaceInit.java b/hotspot/test/runtime/lambda-features/TestInterfaceInit.java index 0493a60bb1e..f3d8e8aacd5 100644 --- a/hotspot/test/runtime/lambda-features/TestInterfaceInit.java +++ b/hotspot/test/runtime/lambda-features/TestInterfaceInit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2015, 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 @@ -25,7 +25,8 @@ /* * @test * @bug 8034275 - * @summary [JDK 8u40] Test interface initialization: only for interfaces declaring default methods + * @bug 8098557 + * @summary [JDK 8u40] Test interface init: only for interfaces declaring default methods, when subclass inits * @run main TestInterfaceInit */ import java.util.List; @@ -39,43 +40,59 @@ public class TestInterfaceInit { // Declares a default method and initializes interface I { boolean v = TestInterfaceInit.out(I.class); - default void x() {} + default void ix() {} } // Declares a default method and initializes interface J extends I { boolean v = TestInterfaceInit.out(J.class); - default void x() {} + default void jx() {} } - // No default method, does not initialize + // No default method, has an abstract method, does not initialize interface JN extends J { boolean v = TestInterfaceInit.out(JN.class); + public abstract void jnx(); } // Declares a default method and initializes interface K extends I { boolean v = TestInterfaceInit.out(K.class); - default void x() {} + default void kx() {} } - // No default method, does not initialize + // No default method, has a static method, does not initialize interface KN extends K { boolean v = TestInterfaceInit.out(KN.class); + static void knx() {} } interface L extends JN, KN { boolean v = TestInterfaceInit.out(L.class); - default void x() {} + default void lx() {} + } + + static class ChildClass implements JN, KN { + boolean v = TestInterfaceInit.out(ChildClass.class); + public void jnx() {} } public static void main(String[] args) { // Trigger initialization boolean v = L.v; - List> expectedCInitOrder = Arrays.asList(I.class,J.class,K.class,L.class); + List> expectedCInitOrder = Arrays.asList(L.class); if (!cInitOrder.equals(expectedCInitOrder)) { throw new RuntimeException(String.format("Class initialization array %s not equal to expected array %s", cInitOrder, expectedCInitOrder)); } + + ChildClass myC = new ChildClass(); + boolean w = myC.v; + + expectedCInitOrder = Arrays.asList(L.class,I.class,J.class,K.class,ChildClass.class); + if (!cInitOrder.equals(expectedCInitOrder)) { + throw new RuntimeException(String.format("Class initialization array %s not equal to expected array %s", cInitOrder, expectedCInitOrder)); + } + } static boolean out(Class c) { diff --git a/hotspot/test/runtime/libadimalloc.solaris.sparc/SEGVOverflow.java b/hotspot/test/runtime/libadimalloc.solaris.sparc/SEGVOverflow.java new file mode 100644 index 00000000000..da754da73ac --- /dev/null +++ b/hotspot/test/runtime/libadimalloc.solaris.sparc/SEGVOverflow.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, 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. + */ +public class SEGVOverflow { + + static { + System.loadLibrary("overflow"); + } + + native static String nativesegv(); + + public static void main(String[] args) { + String str = nativesegv(); + if (str == null) { + System.out.println("FAILED: malloc returned null"); + } else { + System.out.println(str); + } + } +} diff --git a/hotspot/test/runtime/libadimalloc.solaris.sparc/Testlibadimalloc.java b/hotspot/test/runtime/libadimalloc.solaris.sparc/Testlibadimalloc.java new file mode 100644 index 00000000000..e9a8406f0f1 --- /dev/null +++ b/hotspot/test/runtime/libadimalloc.solaris.sparc/Testlibadimalloc.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015, 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. + */ + + +/* + * @test Testlibadimalloc.java + * @bug 8141445 + * @summary make sure the Solaris Sparc M7 libadimalloc.so library generates SIGSEGV's on buffer overflow + * @requires (os.family == "solaris" & os.arch == "sparcv9") + * @library /testlibrary + * @build jdk.test.lib.* + * @compile SEGVOverflow.java + * @run driver Testlibadimalloc + */ + +import java.io.*; +import java.nio.file.*; +import java.util.*; +import jdk.test.lib.ProcessTools; + +public class Testlibadimalloc { + + // Expected return value when java program cores + static final int EXPECTED_RET_VAL = 6; + + public static void main(String[] args) throws Throwable { + + // See if the libadimalloc.so library exists + Path path = Paths.get("/usr/lib/64/libadimalloc.so"); + + // If the libadimalloc.so file does not exist, pass the test + if (!(Files.isRegularFile(path) || Files.isSymbolicLink(path))) { + System.out.println("Test skipped; libadimalloc.so does not exist"); + return; + } + + // Get the JDK, library and class path properties + String libpath = System.getProperty("java.library.path"); + + // Create a new java process for the SEGVOverflow Java/JNI test + ProcessBuilder builder = ProcessTools.createJavaProcessBuilder( + "-Djava.library.path=" + libpath + ":.", "SEGVOverflow"); + + // Add the LD_PRELOAD_64 value to the environment + Map env = builder.environment(); + env.put("LD_PRELOAD_64", "libadimalloc.so"); + + // Start the process, get the pid and then wait for the test to finish + Process process = builder.start(); + long pid = process.getPid(); + int retval = process.waitFor(); + + // make sure the SEGVOverflow test crashed + boolean found = false; + if (retval == EXPECTED_RET_VAL) { + String filename = "hs_err_pid" + pid + ".log"; + Path filepath = Paths.get(filename); + // check to see if hs_err_file exists + if (Files.isRegularFile(filepath)) { + // see if the crash was due to a SEGV_ACCPERR signal + File hs_err_file = new File(filename); + Scanner scanner = new Scanner(hs_err_file); + while (!found && scanner.hasNextLine()) { + String nextline = scanner.nextLine(); + if (nextline.contains("SEGV_ACCPERR")) { + found = true; + } + } + } else { + System.out.println("Test failed; hs_err_file does not exist: " + + filepath); + } + } else { + System.out.println("Test failed; java test program did not " + + "return expected error: expected = " + + EXPECTED_RET_VAL + ", retval = " + retval); + } + // If SEGV_ACCPERR was not found in the hs_err file fail the test + if (!found) { + System.out.println("FAIL: SEGV_ACCPERR not found"); + throw new RuntimeException("FAIL: SEGV_ACCPERR not found"); + } + } +} diff --git a/hotspot/test/runtime/libadimalloc.solaris.sparc/liboverflow.c b/hotspot/test/runtime/libadimalloc.solaris.sparc/liboverflow.c new file mode 100644 index 00000000000..36b5dae9fb0 --- /dev/null +++ b/hotspot/test/runtime/libadimalloc.solaris.sparc/liboverflow.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015, 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. + */ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +JNIEXPORT jstring JNICALL Java_SEGVOverflow_nativesegv(JNIEnv *env, jobject obj) { + char *buffer1; + char *buffer2; + char *buffer3; + char ch; + + jstring ret = NULL; + + // sleep for a bit to let the libadimalloc library initialize + sleep(5); + + // allocate three buffers + buffer1 = (char *)malloc(64); + buffer2 = (char *)malloc(64); + buffer3 = (char *)malloc(64); + if ((buffer1 == NULL) || (buffer2 == NULL) || (buffer3 == NULL)) { + // this return will result in a test failure + return ret; + } + + // Read past the end of each buffer multiple times to increase the probability + // that an ADI version mismatch occurs so an ADI fault is triggered. + ch = buffer1[70]; + ch = buffer2[70]; + ch = buffer3[70]; + ch = buffer1[140]; + ch = buffer2[140]; + ch = buffer3[140]; + + // create a failed test return value because this test should have cored + buffer1 = "TEST FAILED, a read past the end of a buffer succeeded."; + ret = (*env)->NewStringUTF(env, buffer1); + + return ret; +} + +#ifdef __cplusplus +} +#endif diff --git a/hotspot/test/runtime/logging/VMOperationTest.java b/hotspot/test/runtime/logging/VMOperationTest.java new file mode 100644 index 00000000000..630dcd14386 --- /dev/null +++ b/hotspot/test/runtime/logging/VMOperationTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8143157 + * @summary vmoperation=debug should have logging output + * @library /testlibrary + * @compile VMOperationTestMain.java + * @modules java.base/sun.misc + * java.management + * @run main VMOperationTest + */ + +import jdk.test.lib.*; + +public class VMOperationTest { + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-Xlog:vmoperation=debug", "-Xmx64m", "-Xms64m", "VMOperationTestMain"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("VM_Operation ("); + output.shouldHaveExitValue(0); + } +} + diff --git a/hotspot/test/runtime/logging/VMOperationTestMain.java b/hotspot/test/runtime/logging/VMOperationTestMain.java new file mode 100644 index 00000000000..53101892dc2 --- /dev/null +++ b/hotspot/test/runtime/logging/VMOperationTestMain.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.lang.ref.WeakReference; + +public class VMOperationTestMain { + public static byte[] garbage; + public static volatile WeakReference weakref; + + public static void createweakref() { + Object o = new Object(); + weakref = new WeakReference<>(o); + } + + // Loop until a GC runs. + public static void main(String[] args) throws Exception { + createweakref(); + while (weakref.get() != null) { + garbage = new byte[8192]; + System.gc(); + } + } +} diff --git a/hotspot/test/serviceability/dcmd/compiler/control2.txt b/hotspot/test/serviceability/dcmd/compiler/control2.txt index 03a32cf91e9..805ce3a5ab0 100644 --- a/hotspot/test/serviceability/dcmd/compiler/control2.txt +++ b/hotspot/test/serviceability/dcmd/compiler/control2.txt @@ -11,7 +11,7 @@ }, inline : [ "+javax/util.*", "-comx/sun.*"], PrintAssembly: false, - IGVPrintLevel: 2 + MaxNodeLimit: 80001 }, { match: ["baz.*","frob.*"], diff --git a/hotspot/test/serviceability/logging/TestMultipleXlogArgs.java b/hotspot/test/serviceability/logging/TestMultipleXlogArgs.java new file mode 100644 index 00000000000..6914927fdb3 --- /dev/null +++ b/hotspot/test/serviceability/logging/TestMultipleXlogArgs.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test TestMultipleXlogArgs + * @summary Ensure multiple -Xlog arguments aggregate the logging options. + * @library /testlibrary + */ + +import jdk.test.lib.ProcessTools; +import jdk.test.lib.OutputAnalyzer; + +public class TestMultipleXlogArgs { + + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-Xlog:logging=debug", + "-Xlog:logging=trace", + "-Xlog:defaultmethods=trace", + "-Xlog:defaultmethods=off", + "-Xlog:safepoint=info", + "-Xlog:safepoint=info", + "-version"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + // -Xlog:logging=trace means that the log configuration will be printed. + String stdoutConfigLine = "\\[logging *\\] #0: stdout .*"; + // Ensure logging=trace has overwritten logging=debug + output.shouldMatch(stdoutConfigLine + "logging=trace").shouldNotMatch(stdoutConfigLine + "logging=debug"); + // Make sure safepoint=info is printed exactly once even though we're setting it twice + output.shouldMatch(stdoutConfigLine + "safepoint=info").shouldNotMatch(stdoutConfigLine + "safepoint=info.*safepoint=info"); + // Shouldn't see defaultmethods at all, because disabled tags are not listed + output.shouldNotMatch(stdoutConfigLine + "defaultmethods"); + output.shouldHaveExitValue(0); + } +} + diff --git a/hotspot/test/testlibrary/jdk/test/lib/Utils.java b/hotspot/test/testlibrary/jdk/test/lib/Utils.java index 6776db006fe..6e9196a7c4e 100644 --- a/hotspot/test/testlibrary/jdk/test/lib/Utils.java +++ b/hotspot/test/testlibrary/jdk/test/lib/Utils.java @@ -44,6 +44,7 @@ import java.util.Iterator; import java.util.Map; import java.util.HashMap; import java.util.List; +import java.util.Objects; import java.util.Random; import java.util.function.BooleanSupplier; import java.util.concurrent.TimeUnit; @@ -82,6 +83,16 @@ public final class Utils { */ public static final String TEST_SRC = System.getProperty("test.src", ".").trim(); + /* + * Returns the value of 'test.jdk' system property + */ + public static final String TEST_JDK = System.getProperty("test.jdk"); + + /** + * Returns the value of 'test.classes' system property + */ + public static final String TEST_CLASSES = System.getProperty("test.classes", "."); + private static Unsafe unsafe = null; /** @@ -616,5 +627,18 @@ public final class Utils { NULL_VALUES.put(float.class, 0.0f); NULL_VALUES.put(double.class, 0.0d); } + + /** + * Returns mandatory property value + * @param propName is a name of property to request + * @return a String with requested property value + */ + public static String getMandatoryProperty(String propName) { + Objects.requireNonNull(propName, "Requested null property"); + String prop = System.getProperty(propName); + Objects.requireNonNull(prop, + String.format("A mandatory property '%s' isn't set", propName)); + return prop; + } } diff --git a/hotspot/test/testlibrary/jittester/Makefile b/hotspot/test/testlibrary/jittester/Makefile new file mode 100644 index 00000000000..53f62ba99c6 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/Makefile @@ -0,0 +1,125 @@ +# +# Copyright (c) 2015, 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. +# + +ifeq "x$(BOOTDIR)" "x" + JDK_HOME := $(shell dirname $(shell which java))/.. +else + JDK_HOME := $(BOOTDIR) +endif + +ifeq "x$(PROPERTY_FILE)" "x" + PROPERTY_FILE := conf/default.properties +endif + +ifeq "x$(TESTBASE_DIR)" "x" + TESTBASE_DIR := ws/hotspot/test +endif + +JAVA = $(JDK_HOME)/bin/java +JAVAC = $(JDK_HOME)/bin/javac +JAR = $(JDK_HOME)/bin/jar + +BUILD_DIR = build +CLASSES_DIR = $(BUILD_DIR)/classes +SRC_DIR = src +TEST_DIR = test +MANIFEST = manifest.mf +APPLICATION_ARGS = \ + --property-file $(PROPERTY_FILE) \ + --testbase-dir $(TESTBASE_DIR) +MAIN_CLASS = JitTestGenerator.Automatic + +TESTGROUP_FILE = $(TESTBASE_DIR)/TEST.groups +TESTROOT_FILE = $(TESTBASE_DIR)/TEST.ROOT + +DIST_DIR = dist +DIST_JAR = $(DIST_DIR)/JITtester.jar + +SRC_FILES = $(shell find $(SRC_DIR) -name '*.java') +TESTLIBRARY_SRC_DIR = ../jdk/test/lib +TESTLIBRARY_SRC_FILES = $(TESTLIBRARY_SRC_DIR)/Asserts.java \ + $(TESTLIBRARY_SRC_DIR)/JDKToolFinder.java \ + $(TESTLIBRARY_SRC_DIR)/JDKToolLauncher.java \ + $(TESTLIBRARY_SRC_DIR)/OutputAnalyzer.java \ + $(TESTLIBRARY_SRC_DIR)/OutputBuffer.java \ + $(TESTLIBRARY_SRC_DIR)/Pair.java \ + $(TESTLIBRARY_SRC_DIR)/Platform.java \ + $(TESTLIBRARY_SRC_DIR)/ProcessTools.java \ + $(TESTLIBRARY_SRC_DIR)/StreamPumper.java \ + $(TESTLIBRARY_SRC_DIR)/Utils.java + +.PHONY: cleantmp + +all: JAR + +JAR: INIT COMPILE manifest + $(JAR) cfm $(DIST_JAR) $(MANIFEST) -C $(CLASSES_DIR) . + +manifest: + @echo 'Manifest-Version: 1.0' > $(MANIFEST) + @echo 'X-COMMENT: Main-Class will be added automatically by build' >> $(MANIFEST) + @echo 'Main-Class: jdk.test.lib.jittester.Automatic' >> $(MANIFEST) + +compile_testlib: INIT + $(JAVAC) -XDignore.symbol.file -Xlint $(TESTLIBRARY_SRC_FILES) -d $(CLASSES_DIR) -source 1.8 + +COMPILE: INIT filelist compile_testlib + $(JAVAC) -cp $(CLASSES_DIR) -XDignore.symbol.file -Xlint -sourcepath $(SRC_DIR) -d $(CLASSES_DIR) -source 1.8 @filelist + +filelist: $(SRC_FILES) + @rm -f $@ + @echo $(SRC_FILES) > $@ + +INIT: $(DIST_DIR) + $(shell if [ ! -d $(CLASSES_DIR) ]; then mkdir -p $(CLASSES_DIR); fi) + +install: clean_testbase testgroup testroot copytestlibrary JAR cleantmp + $(JAVA) -jar $(DIST_JAR) $(APPLICATION_ARGS) + +clean_testbase: + @rm -rf $(TESTBASE_DIR) + +cleantmp: + @rm filelist + @rm -rf $(CLASSES_DIR) + +copytestlibrary: + @cp -r src/jdk/test/lib/jittester/jtreg $(TESTBASE_DIR)/ + @cp -r ../jdk $(TESTBASE_DIR)/ + +testgroup: $(TESTBASE_DIR) + @echo 'jittester_all = \\' > $(TESTGROUP_FILE) + @echo ' /' >> $(TESTGROUP_FILE) + @echo '' >> $(TESTGROUP_FILE) + @echo 'main = \\' >> $(TESTGROUP_FILE) + @echo ' Test_0.java' >> $(TESTGROUP_FILE) + +testroot: $(TESTBASE_DIR) + @echo 'groups=TEST.groups' > $(TESTROOT_FILE) + +$(TESTBASE_DIR): + $(shell if [ ! -d $@ ]; then mkdir -p $@; fi) + +$(DIST_DIR): + $(shell if [ ! -d $@ ]; then mkdir -p $@; fi) + diff --git a/hotspot/test/testlibrary/jittester/conf/classes.lst b/hotspot/test/testlibrary/jittester/conf/classes.lst new file mode 100644 index 00000000000..2fae8dc7824 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/conf/classes.lst @@ -0,0 +1,20 @@ +java.lang.Object +java.lang.String +java.lang.Number +java.lang.Boolean +java.lang.Byte +java.lang.Short +java.lang.Character +java.lang.Integer +java.lang.Long +java.lang.Float +java.lang.Double +java.lang.Math +java.lang.System +java.lang.Runnable +java.util.AbstractSet +java.util.HashSet +java.lang.RuntimeException +java.lang.IllegalArgumentException +java.lang.NumberFormatException +java.lang.IndexOutOfBoundsException diff --git a/hotspot/test/testlibrary/jittester/conf/default.properties b/hotspot/test/testlibrary/jittester/conf/default.properties new file mode 100644 index 00000000000..edc980fd36e --- /dev/null +++ b/hotspot/test/testlibrary/jittester/conf/default.properties @@ -0,0 +1,11 @@ +seed=SEED2 +number-of-tests=1000 +testbase-dir=ws/hotspot/test +fp-precision=7 +min-cfg-depth=5 +max-cfg-depth=5 +classes-file=conf/classes.lst +exclude-methods-file=conf/exclude.methods.lst +print-complexity=true +print-hierarchy=true +disable-static=true diff --git a/hotspot/test/testlibrary/jittester/conf/exclude.methods.lst b/hotspot/test/testlibrary/jittester/conf/exclude.methods.lst new file mode 100644 index 00000000000..c9b20d2c0ee --- /dev/null +++ b/hotspot/test/testlibrary/jittester/conf/exclude.methods.lst @@ -0,0 +1,22 @@ +java/lang/Object::notify() +java/lang/Object::notifyAll() +java/lang/Object::wait() +java/lang/Object::wait(J) +java/lang/Object::wait(JI) +java/lang/Object::toString() +java/lang/String::String([BIILjava/lang/String;) +java/lang/String::String([BLjava/lang/String;) +java/lang/String::getBytes(Ljava/lang/String;) +java/lang/String::join(Ljava/lang/CharSequence;[Ljava/lang/CharSequence;) +java/lang/String::format(Ljava/lang/String;[Ljava/lang/Object;) +java/lang/String::format(Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;) +java/lang/System::exit(I) +java/lang/System::inheritedChannel() +java/lang/System::runFinalizersOnExit(Z) +java/util/AbstractSet::toString() +java/util/HashSet::toString() +java/lang/RuntimeException::setStackTrace([Ljava/lang/StackTraceElement;) +java/lang/RuntimeException::RuntimeException(Ljava/lang/Throwable;) +java/lang/RuntimeException::RuntimeException(Ljava/lang/String;Ljava/lang/Throwable;) +java/lang/annotation/IncompleteAnnotationException::IncompleteAnnotationException(Ljava/lang/Class;Ljava/lang/String;) +java/lang/EnumConstantNotPresentException::EnumConstantNotPresentException(Ljava/lang/Class;Ljava/lang/String;) diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Automatic.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Automatic.java new file mode 100644 index 00000000000..77e7b23c68f --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Automatic.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.factories.IRNodeBuilder; +import jdk.test.lib.jittester.TypesParser; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.visitors.JavaCodeVisitor; +import jdk.test.lib.jittester.utils.OptionResolver; +import jdk.test.lib.jittester.utils.OptionResolver.Option; +import jdk.test.lib.jittester.utils.PseudoRandom; + +public class Automatic { + public static final int minutesToWait = 3; + + private static String makeTestCase(String name) { + SymbolTable.removeAll(); + TypeList.removeAll(); + StringBuilder resultVis = new StringBuilder(); + StringBuilder headerBuilder = new StringBuilder(); + try { + IRNodeBuilder builder = new IRNodeBuilder() + .setPrefix(name) + .setName(name) + .setLevel(0); + + JavaCodeVisitor vis = new JavaCodeVisitor(); + String synopsis = "seed = '" + ProductionParams.seed.value() + "'"; + String pathPrefix = ProductionParams.testbaseDir.value() + .replaceAll("([^/]+)", ".."); + headerBuilder + .append("/*\n") + .append(" * @test\n") + .append(" * @summary ") + .append(synopsis) + .append("\n") + .append(" * @compile ") + .append(name) + .append(".java\n") + .append(" * @run build jdk.test.lib.jittester.jtreg.JitTesterDriver\n") + .append(" * @run driver jdk.test.lib.jittester.jtreg.JitTesterDriver ") + .append(name) + .append("\n") + .append(" */\n\n"); + + + if (!ProductionParams.disableClasses.value()) { + long comlexityLimit = (long) (ProductionParams.complexityLimit.value() + * PseudoRandom.random()); + IRNode privateClasses = builder.setComplexityLimit(comlexityLimit) + .getClassDefinitionBlockFactory() + .produce(); + if (privateClasses != null) { + resultVis.append(privateClasses.accept(vis)); + } + } + long mainComplexityLimit = (long) (ProductionParams.complexityLimit.value() + * PseudoRandom.random()); + IRNode mainClass = builder.setComplexityLimit(mainComplexityLimit) + .getMainKlassFactory() + .produce(); + resultVis.append(mainClass.accept(vis)); + + if (ProductionParams.printHierarchy.value()) { + headerBuilder + .append("/*\n") + .append(Automatic.printHierarchy()) + .append("*/\n"); + } + } catch (Exception e) { + e.printStackTrace(System.out); + } + return headerBuilder.append(resultVis).toString(); + } + + private static void initializeTestGenerator(String[] params) { + OptionResolver parser = new OptionResolver(); + Option propertyFileOpt = parser.addStringOption('p', "property-file", "", + "File to read properties from"); + ProductionParams.register(parser); + parser.parse(params, propertyFileOpt); + jdk.test.lib.jittester.utils.PseudoRandom.reset(ProductionParams.seed.value()); + TypesParser.parseTypesAndMethods(ProductionParams.classesFile.value(), ProductionParams.excludeMethodsFile.value()); + } + + public static void main(String[] args) { + initializeTestGenerator(args); + int counter = 0; + try { + String testbaseDir = ProductionParams.testbaseDir.value(); + do { + double start = System.currentTimeMillis(); + String name = "Test_" + counter; + generateTestFile(name); + double generationTime = System.currentTimeMillis() - start; + String path = getJavaPath(); + ProcessBuilder pb = new ProcessBuilder(path + "javac", testbaseDir + "/" + name + ".java"); + runProcess(pb, testbaseDir + "/" + name); + + start = System.currentTimeMillis(); + pb = new ProcessBuilder(path + "java", "-Xint", "-cp", testbaseDir, name); + name = name + ".gold"; + runProcess(pb, testbaseDir + "/" + name); + double runningTime = System.currentTimeMillis() - start; + System.out.printf("%4d : generation time (ms) : %8.0f running time (ms) : %8.0f\n", + counter, generationTime, runningTime); + if (runningTime < TimeUnit.MINUTES.toMillis(minutesToWait)) + ++counter; + } while (counter < ProductionParams.numberOfTests.value()); + } catch (IOException | InterruptedException ex) { + Logger.getLogger(Automatic.class.getName()).log(Level.SEVERE, null, ex); + } + } + + private static String getJavaPath() { + String[] env = { "JDK_HOME", "JAVA_HOME", "BOOTDIR" }; + for (String name : env) { + String path = System.getenv(name); + if (path != null) { + return path + "/bin/"; + } + } + return ""; + } + + private static void runProcess(ProcessBuilder pb, String name) + throws IOException, InterruptedException { + pb.redirectError(new File(name + ".err")); + pb.redirectOutput(new File(name + ".out")); + Process process = pb.start(); + if (process.waitFor(minutesToWait, TimeUnit.MINUTES)) { + try (FileWriter file = new FileWriter(name + ".exit")) { + file.write(Integer.toString(process.exitValue())); + } + } else { + process.destroyForcibly(); + } + TimeUnit.MILLISECONDS.sleep(300); + } + + private static void generateTestFile(String testName) { + String code = makeTestCase(testName); + String testbaseDir = ProductionParams.testbaseDir.value(); + String fileName = testbaseDir + "/" + testName + ".java"; + try (FileWriter file = new FileWriter(fileName)) { + file.write(code); + //file.close(); + } catch (IOException ex) { + Logger.getLogger(Automatic.class.getName()) + .log(Level.SEVERE, " Cannot write to file " + fileName, ex); + } + } + + private static String printHierarchy() { + String r = "CLASS HIERARCHY:\n"; + for (Type t : TypeList.getAll()) { + if (t instanceof TypeKlass) { + TypeKlass k = (TypeKlass) t; + if (k.isAbstract()) { + r += "abstract "; + } + if (k.isFinal()) { + r += "final "; + } + if (k.isInterface()) { + r += "interface "; + } else { + r += "class "; + } + r += k.getName() + ": "; + HashSet parents = k.getParentsNames(); + if (parents != null) { + Iterator n = parents.iterator(); + int size = parents.size(); + for (int i = 0; n.hasNext() && i < size - 1; i++) { + r += n.next() + ", "; + } + if (n.hasNext()) { + r += n.next(); + } + } + r += "\n"; + } + } + return r; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/BinaryOperator.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/BinaryOperator.java new file mode 100644 index 00000000000..0d06d0ce5da --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/BinaryOperator.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class BinaryOperator extends Operator { + protected String operationCode; + protected Type resultType; + + public BinaryOperator(OperatorKind opKind, IRNode leftOperand, IRNode rightOperand) { + super(opKind.priority); + operationCode = opKind.text; + addChild(leftOperand); + addChild(rightOperand); + } + + @Override + public long complexity() { + IRNode leftOperand = getChild(Order.LEFT.ordinal()); + IRNode rightOperand = getChild(Order.RIGHT.ordinal()); + if (leftOperand != null && rightOperand != null) { + return leftOperand.complexity() + rightOperand.complexity() + 1; + } else { + return 0; + } + } + + public String getOperationCode() { + return operationCode; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Block.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Block.java new file mode 100644 index 00000000000..5b5edb85589 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Block.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import java.util.List; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.visitors.Visitor; + +public class Block extends IRNode { + private final Type returnType; + + public Block(TypeKlass klass, Type returnType, List content, int level) { + setKlass(klass); + addChildren(content); + this.level = level; + this.returnType = returnType; + } + + public Type getReturnType() { + return returnType; + } + + protected int size() { + return getChildren().size(); + } + + @Override + public long complexity() { + return getChildren() + .stream() + .mapToLong(IRNode::complexity) + .sum(); + } + + @Override + public long countDepth() { + return Long.max(level, super.countDepth()); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Break.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Break.java new file mode 100644 index 00000000000..f38adf8b454 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Break.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class Break extends IRNode { + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/BuiltInType.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/BuiltInType.java new file mode 100644 index 00000000000..59b979a0f13 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/BuiltInType.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +public abstract class BuiltInType extends Type { + + private static class BuiltInTypeCapacityHelper { + + static String builtInTypes[] = {"boolean", "byte", "short", "char", "int", "long", "float", "double"}; + + static private int getIndexFor(String typeName) { + for (int i = 0; i < builtInTypes.length; i++) { + if (typeName.compareTo(builtInTypes[i]) == 0) { + return i; + } + } + return -1; + } + + static public int compare(String typeName1, String typeName2) { + int i1 = getIndexFor(typeName1); + int i2 = getIndexFor(typeName2); + + return i1 - i2; + } + } + + protected BuiltInType(String name) { + super(name); + } + + @Override + public boolean canImplicitlyCastTo(Type type) { + if (equals(type)) { + return true; + } + try { + BuiltInType t = (BuiltInType) type; + // You cannot impicitly cast anything to char or boolean + if (t.getName().compareTo("boolean") == 0 || t.getName().compareTo("char") == 0) { + return false; + } + if (t.isMoreCapaciousThan(this)) { + return true; + } + } catch (Exception e) { + } + return false; + } + + @Override + public boolean canExplicitlyCastTo(Type t) { + if (equals(t)) { + return true; + } + try { + BuiltInType _t = (BuiltInType) t; + if (_t.getName().compareTo("boolean") != 0) { + return true; + } + } catch (Exception e) { + } + return false; + } + + public boolean isMoreCapaciousThan(BuiltInType t) { + return BuiltInTypeCapacityHelper.compare(this.getName(), t.getName()) > 0; + } + + @Override + public boolean canCompareTo(Type t) { + return true; + } + + @Override + public boolean canEquateTo(Type t) { + return true; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/CastOperator.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/CastOperator.java new file mode 100644 index 00000000000..27e04c795f2 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/CastOperator.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class CastOperator extends Operator { + private final Type resultType; + + public CastOperator(Type resultType, IRNode casted) { + super(13); + this.resultType = resultType; + addChild(casted); + } + + @Override + public long complexity() { + return getChild(0).complexity() + 1; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public Type getResultType() { + return resultType; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/CatchBlock.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/CatchBlock.java new file mode 100644 index 00000000000..b49a4bfe9b2 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/CatchBlock.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester; + +import java.util.Collections; +import java.util.List; +import jdk.test.lib.jittester.visitors.Visitor; + +public class CatchBlock extends IRNode { + public final List throwables; + + public CatchBlock(IRNode body, List throwables, int level) { + this.level = level; + this.throwables = Collections.unmodifiableList(throwables); + addChild(body); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Continue.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Continue.java new file mode 100644 index 00000000000..2caed6b8c8e --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Continue.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class Continue extends IRNode { + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Declaration.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Declaration.java new file mode 100644 index 00000000000..6633c541256 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Declaration.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class Declaration extends IRNode { + public Declaration(IRNode declarationExpr) { + addChild(declarationExpr); + } + + @Override + public long complexity() { + return getChild(0).complexity(); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/IRNode.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/IRNode.java new file mode 100644 index 00000000000..7221d086681 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/IRNode.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +// Production is base class for all elements of the IR-tree. +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import jdk.test.lib.jittester.loops.For; +import jdk.test.lib.jittester.loops.DoWhile; +import jdk.test.lib.jittester.loops.While; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.visitors.Visitor; + +public abstract class IRNode { + private IRNode parent; + private final List children = new ArrayList<>(); + protected IRNode klass; + protected int level; + //TODO + //private boolean isCFDeviation; + + public abstract T accept(Visitor v); + + public void setKlass(TypeKlass klass) { + this.klass = klass; + if (Objects.isNull(klass)) { + System.out.println(getClass().getName() + " null"); + for (StackTraceElement s : Thread.currentThread().getStackTrace()) { + System.out.println(s.toString()); + } + } + } + + public void addChild(IRNode child) { + children.add(child); + if (!Objects.isNull(child)) { + child.parent = this; + } + } + + public void addChildren(List ch) { + if (!Objects.isNull(ch)) { + children.addAll(ch); + for (IRNode c : ch) { + if (!Objects.isNull(c)) { + c.parent = this; + } + } + } + } + + public List getChildren() { + return children; + } + + public IRNode getChild(int i) { + return i < children.size() ? children.get(i) : null; + } + + public IRNode getKlass() { + return klass; + } + + public IRNode getParent() { + return parent; + } + + public void setChild(int index, IRNode child) { + children.set(index, child); + if (!Objects.isNull(child)) { + child.parent = this; + } + } + + public boolean removeChild(IRNode l) { + return children.remove(l); + } + + public boolean removeSelf() { + return parent.children.remove(this); + } + + public void resizeUpChildren(int size) { + for (int i = children.size(); i < size; ++i) { + children.add(null); + } + } + + public void removeAllChildren() { + children.clear(); + } + + public String getTreeTextView(int indent) { + StringBuilder sb = new StringBuilder(); + if (level > 0) { + for (int i = 0; i < indent; ++i) { + sb.append("\t"); + } + sb.append(getName()) + .append(" [") + .append(level) + .append("]") + .append(System.lineSeparator()); + } + children.stream() + .filter(ch -> !Objects.isNull(ch)) + .forEach(ch -> sb.append(ch.getTreeTextView(indent + 1))); + return sb.toString(); + } + + protected IRNode evolve() { + throw new Error("Not implemented"); + } + + public int getLevel() { + return level; + } + + public long complexity() { + return 0L; + } + + @Override + public final String toString() { + throw new Error("Should be toJavaCode"); + } + + public String getName() { + return this.getClass().getName(); + } + + public static long countDepth(Collection input) { + return input.stream() + .filter(c -> !Objects.isNull(c)) + .mapToLong(IRNode::countDepth) + .max().orElse(0L); + } + + public long countDepth() { + return IRNode.countDepth(children); + } + + public List getStackableLeaves() { + List result = new ArrayList<>(); + children.stream() + .filter(c -> !Objects.isNull(c)) + .forEach(c -> { + if (countDepth() == c.level && (c instanceof Block)) { + result.add(c); + } else { + result.addAll(c.getStackableLeaves()); + } + }); + return result; + } + + public List getDeviantBlocks(long depth) { + List result = new ArrayList<>(); + children.stream() + .filter(c -> !Objects.isNull(c)) + .forEach(c -> { + if (depth == c.level && c.isCFDeviation()) { + result.add(c); + } else { + result.addAll(c.getDeviantBlocks(depth)); + } + }); + return result; + } + + // TODO: add field instead this function + public boolean isCFDeviation() { + return this instanceof If || this instanceof Switch + || this instanceof For || this instanceof While + || this instanceof DoWhile + || (this instanceof Block && this.parent instanceof Block); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/If.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/If.java new file mode 100644 index 00000000000..b7637f6d15d --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/If.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class If extends IRNode { + public enum IfPart { + CONDITION, + THEN, + ELSE, + }; + + public If(IRNode condition, IRNode thenBlock, IRNode elseBlock, int level) { + this.level = level; + resizeUpChildren(IfPart.values().length); + setChild(IfPart.CONDITION.ordinal(), condition); + setChild(IfPart.THEN.ordinal(), thenBlock); + setChild(IfPart.ELSE.ordinal(), elseBlock); + } + + @Override + public long complexity() { + IRNode condition = getChild(IfPart.CONDITION.ordinal()); + IRNode thenBlock= getChild(IfPart.THEN.ordinal()); + IRNode elseBlock = getChild(IfPart.ELSE.ordinal()); + return (condition != null ? condition.complexity() : 0) + + Math.max(thenBlock != null ? thenBlock.complexity() : 0, + elseBlock != null ? elseBlock.complexity() : 0); + + } + + @Override + public long countDepth() { + return Long.max(level, super.countDepth()); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Initialization.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Initialization.java new file mode 100644 index 00000000000..c286641e8fc --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Initialization.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public abstract class Initialization extends IRNode { + protected VariableInfo variableInfo = new VariableInfo(); + + protected Initialization() { + } + + protected Initialization(VariableInfo varInfo, IRNode initExpr) { + variableInfo = varInfo; + addChild(initExpr); + } + + public VariableInfo get() { + return variableInfo; + } + + @Override + public long complexity() { + return getChild(0).complexity() + 1; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public VariableInfo getVariableInfo() { + return variableInfo; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Literal.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Literal.java new file mode 100644 index 00000000000..598338fbb36 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Literal.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class Literal extends IRNode { + public final Object value; + protected final Type resultType; + + public Literal(Object v, Type t) { + value = v; + resultType = t; + } + + @Override + public long complexity() { + return 0; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public Type getResultType() { + return resultType; + } + + public Object getValue() { + return value; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/LiteralInitializer.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/LiteralInitializer.java new file mode 100644 index 00000000000..7c5f2e359b1 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/LiteralInitializer.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +public class LiteralInitializer extends Literal { + public LiteralInitializer(Object v, Type t) { + super(v, t); + addChild(t); //TODO: check if it's needed + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/LocalVariable.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/LocalVariable.java new file mode 100644 index 00000000000..6543f4d7877 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/LocalVariable.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class LocalVariable extends IRNode implements VariableBase { + private VariableInfo value = new VariableInfo(); + + public LocalVariable(VariableInfo value) { + this.value = value; + } + + @Override + public VariableInfo get() { + return value; + } + + @Override + public long complexity() { + return 1; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/LogicOperator.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/LogicOperator.java new file mode 100644 index 00000000000..e4b7499da86 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/LogicOperator.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class LogicOperator extends IRNode { + + protected LogicOperator() { + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/NonStaticMemberVariable.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/NonStaticMemberVariable.java new file mode 100644 index 00000000000..4615b80924d --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/NonStaticMemberVariable.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class NonStaticMemberVariable extends IRNode implements VariableBase { + private final VariableInfo value; + + public NonStaticMemberVariable(IRNode object, VariableInfo value) { + this.value = value; + addChild(object); + } + + @Override + public VariableInfo get() { + return value; + } + + @Override + public long complexity() { + return getChild(0).complexity(); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public VariableInfo getValue() { + return value; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Nothing.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Nothing.java new file mode 100644 index 00000000000..cbd7b1ad1d0 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Nothing.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class Nothing extends IRNode { + + @Override + public long complexity() { + return 0; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Operator.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Operator.java new file mode 100644 index 00000000000..88d38fc0a8e --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Operator.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +public abstract class Operator extends IRNode { + protected int operatorPriority; + + public int getPriority() { + return operatorPriority; + } + + public enum Order { + LEFT, RIGHT + }; + + // This constructor is called to construct an IR-tree node. + protected Operator(int operatorPriority) { + this.operatorPriority = operatorPriority; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/OperatorKind.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/OperatorKind.java new file mode 100644 index 00000000000..d7dbb585c62 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/OperatorKind.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester; + +// all unary and binary operator kinds +public enum OperatorKind { + COMPOUND_ADD("+=", 1), + COMPOUND_SUB("-=", 1), + COMPOUND_MUL("*=", 1), + COMPOUND_DIV("-=", 1), + COMPOUND_MOD("%=", 1), + COMPOUND_AND("&=", 1), + COMPOUND_OR ("|=", 1), + COMPOUND_XOR("^=", 1), + COMPOUND_SHR(">>=", 1), + COMPOUND_SHL("<<=", 1), + COMPOUND_SAR(">>>=", 1), + ASSIGN ("=", 1), + OR ("||", 3), + BIT_OR ("|", 5), + BIT_XOR ("^", 6), + AND ("&&", 7), + BIT_AND ("&", 7), + EQ ("==", 8), + NE ("!=", 8), + GT (">", 9), + LT ("<", 9), + GE (">=", 9), + LE ("<=", 9), + SHR (">>", 10), + SHL ("<<", 10), + SAR (">>>", 10), + ADD ("+", 11), + STRADD ("+", 11), + SUB ("-", 11), + MUL ("*", 12), + DIV ("/", 12), + MOD ("%", 12), + NOT ("!", 14), + BIT_NOT ("~", 14), + UNARY_PLUS ("+", 14), + UNARY_MINUS ("-", 14), + PRE_DEC ("--", 15, true), + POST_DEC ("--", 15, false), + PRE_INC ("++", 16, true), + POST_INC ("++", 16, false), + ; + + public final String text; + public final int priority; + public final boolean isPrefix; // used for unary operators + + private OperatorKind(String text, int priority) { + this(text, priority, true); + } + + private OperatorKind(String text, int priority, boolean isPrefix) { + this.text = text; + this.priority = priority; + this.isPrefix = isPrefix; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/PrintVariables.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/PrintVariables.java new file mode 100644 index 00000000000..a593f740eb6 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/PrintVariables.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import java.util.ArrayList; +import java.util.List; +import jdk.test.lib.jittester.visitors.Visitor; + +public class PrintVariables extends IRNode { + private final String printerName; + private final ArrayList vars; + + public PrintVariables(String printerName, ArrayList vars, int level) { + this.printerName = printerName; + this.vars = vars; + this.level = level; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public List getVars() { + return vars; + } + + public String getPrinterName() { + return printerName; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/ProductionFailedException.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/ProductionFailedException.java new file mode 100644 index 00000000000..d06ecb00242 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/ProductionFailedException.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +public class ProductionFailedException extends Exception { + static final long serialVersionUID = -2325617203741536725L; + + public ProductionFailedException(String msg) { + super(msg); + } + + public ProductionFailedException() { + super(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/ProductionLimiter.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/ProductionLimiter.java new file mode 100644 index 00000000000..2d4fdbbeff7 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/ProductionLimiter.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; +// an utility class to limit steps in the production of an expression +public class ProductionLimiter { + + private static Integer limit = -1; + + public static void setUnlimited() { + limit = -1; + } + + // initialize limit state + public static void setLimit() { + limit = ProductionParams.productionLimit.value(); + } + + // iterate a limit, throwing exception in case it hit + public static void limitProduction() throws ProductionFailedException { + if (limit > 0) { + limit--; + } + if (limit != -1 && limit <= 0) { + throw new ProductionFailedException(); + } + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/ProductionParams.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/ProductionParams.java new file mode 100644 index 00000000000..0fbfbe7f8ff --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/ProductionParams.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.utils.OptionResolver; +import jdk.test.lib.jittester.utils.OptionResolver.Option; + +public class ProductionParams { + + public static Option productionLimit = null; + public static Option dataMemberLimit = null; + public static Option statementLimit = null; + public static Option testStatementLimit = null; + public static Option operatorLimit = null; + public static Option complexityLimit = null; + public static Option memberFunctionsLimit = null; + public static Option memberFunctionsArgLimit = null; + public static Option stringLiteralSizeLimit = null; + public static Option classesLimit = null; + public static Option implementationLimit = null; + public static Option dimensionsLimit = null; + public static Option floatingPointPrecision = null; + public static Option minCfgDepth = null; + public static Option maxCfgDepth = null; + public static Option enableStrictFP = null; + public static Option printComplexity = null; + public static Option printHierarchy = null; + //public static BooleanOption disableFinals = optionResolver.addBooleanOption("disable-finals", "Don\'t use finals"); + public static Option disableFinalClasses = null; + public static Option disableFinalMethods = null; + public static Option disableFinalVariables = null; + public static Option disableIf = null; + public static Option disableSwitch = null; + public static Option disableWhile = null; + public static Option disableDoWhile = null; + public static Option disableFor = null; + public static Option disableFunctions = null; + public static Option disableVarsInBlock = null; + public static Option disableExprInInit = null; + public static Option disableExternalSymbols = null; + public static Option addExternalSymbols = null; + public static Option disableInheritance = null; + public static Option disableDowncasts = null; + public static Option disableStatic = null; + public static Option disableInterfaces = null; + public static Option disableClasses = null; + public static Option disableNestedBlocks = null; + public static Option disableArrays = null; + public static Option enableFinalizers = null; + // workaraound: to reduce chance throwing ArrayIndexOutOfBoundsException + public static Option chanceExpressionIndex = null; + public static Option testbaseDir = null; + public static Option numberOfTests = null; + public static Option seed = null; + public static Option classesFile = null; + public static Option excludeMethodsFile = null; + + public static void register(OptionResolver optionResolver) { + productionLimit = optionResolver.addIntegerOption('l', "production-limit", 100, "Limit on steps in the production of an expression"); + dataMemberLimit = optionResolver.addIntegerOption('v', "data-member-limit", 10, "Upper limit on data members"); + statementLimit = optionResolver.addIntegerOption('s', "statement-limit", 30, "Upper limit on statements in function"); + testStatementLimit = optionResolver.addIntegerOption('e', "test-statement-limit", 300, "Upper limit on statements in test() function"); + operatorLimit = optionResolver.addIntegerOption('o', "operator-limit", 50, "Upper limit on operators in a statement"); + complexityLimit = optionResolver.addLongOption('x', "complexity-limit", 10000000, "Upper limit on complexity"); + memberFunctionsLimit = optionResolver.addIntegerOption('m', "member-functions-limit", 15, "Upper limit on member functions"); + memberFunctionsArgLimit = optionResolver.addIntegerOption('a', "member-functions-arg-limit", 5, "Upper limit on the number of member function args"); + stringLiteralSizeLimit = optionResolver.addIntegerOption("string-literal-size-limit", 10, "Upper limit on the number of chars in string literal"); + classesLimit = optionResolver.addIntegerOption('c', "classes-limit", 12, "Upper limit on the number of classes"); + implementationLimit = optionResolver.addIntegerOption('i', "implementation-limit", 3, "Upper limit on a number of interfaces a class can implement"); + dimensionsLimit = optionResolver.addIntegerOption('d', "dimensions-limit", 3, "Upper limit on array dimensions"); + floatingPointPrecision = optionResolver.addIntegerOption("fp-precision", 8, "A non-negative decimal integer used to restrict the number of digits after the decimal separator"); + minCfgDepth = optionResolver.addIntegerOption("min-cfg-depth", 2, "A non-negative decimal integer used to restrict the lower bound of depth of control flow graph"); + maxCfgDepth = optionResolver.addIntegerOption("max-cfg-depth", 3, "A non-negative decimal integer used to restrict the upper bound of depth of control flow graph"); + enableStrictFP = optionResolver.addBooleanOption("enable-strict-fp", "Add strictfp attribute to test class"); + printComplexity = optionResolver.addBooleanOption("print-complexity", "Print complexity of each statement"); + printHierarchy = optionResolver.addBooleanOption("print-hierarchy", "Print resulting class hierarchy"); + //disableFinals = optionResolver.addBooleanOption("disable-finals", "Don\'t use finals"); + disableFinalClasses = optionResolver.addBooleanOption("disable-final-classes", "Don\'t use final classes"); + disableFinalMethods = optionResolver.addBooleanOption("disable-final-methods", "Don\'t use final methods"); + disableFinalVariables = optionResolver.addBooleanOption("disable-final-variabless", "Don\'t use final variables"); + disableIf = optionResolver.addBooleanOption("disable-if", "Don\'t use conditionals"); + disableSwitch = optionResolver.addBooleanOption("disable-switch", "Don\'t use switch"); + disableWhile = optionResolver.addBooleanOption("disable-while", "Don\'t use while"); + disableDoWhile = optionResolver.addBooleanOption("disable-do-while", "Don\'t use do-while"); + disableFor = optionResolver.addBooleanOption("disable-for", "Don\'t use for"); + disableFunctions = optionResolver.addBooleanOption("disable-functions", "Don\'t use functions"); + disableVarsInBlock = optionResolver.addBooleanOption("disable-vars-in-block", "Don\'t generate variables in blocks"); + disableExprInInit = optionResolver.addBooleanOption("disable-expr-in-init", "Don\'t use complex expressions in variable initialization"); + disableExternalSymbols = optionResolver.addBooleanOption("disable-external-symbols", "Don\'t use external symbols"); + addExternalSymbols = optionResolver.addStringOption("add-external-symbols", "all", "Add symbols for listed classes (comma-separated list)"); + disableInheritance = optionResolver.addBooleanOption("disable-inheritance", "Disable inheritance"); + disableDowncasts = optionResolver.addBooleanOption("disable-downcasts", "Disable downcasting of objects"); + disableStatic = optionResolver.addBooleanOption("disable-static", "Disable generation of static objects and functions"); + disableInterfaces = optionResolver.addBooleanOption("disable-interfaces", "Disable generation of interfaces"); + disableClasses = optionResolver.addBooleanOption("disable-classes", "Disable generation of classes"); + disableNestedBlocks = optionResolver.addBooleanOption("disable-nested-blocks", "Disable generation of nested blocks"); + disableArrays = optionResolver.addBooleanOption("disable-arrays", "Disable generation of arrays"); + enableFinalizers = optionResolver.addBooleanOption("enable-finalizers", "Enable finalizers (for stress testing)"); + chanceExpressionIndex = optionResolver.addIntegerOption("chance-expression-index", 0, "A non negative decimal integer used to restrict chane of generating expression in array index while creating or accessing by index"); + testbaseDir = optionResolver.addStringOption("testbase-dir", ".", "Testbase dir"); + numberOfTests = optionResolver.addIntegerOption('n', "number-of-tests", 0, "Number of test classes to generate"); + seed = optionResolver.addStringOption("seed", "", "Random seed"); + classesFile = optionResolver.addStringOption('f', "classes-file", "", "File to read classes from"); + excludeMethodsFile = optionResolver.addStringOption('r', "exclude-methods-file", "", "File to read excluded methods from"); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Rule.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Rule.java new file mode 100644 index 00000000000..b27279c3957 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Rule.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.TreeSet; +import jdk.test.lib.jittester.factories.Factory; +import jdk.test.lib.jittester.utils.PseudoRandom; + +/** + * The Rule. A helper to perform production. + */ +public class Rule extends Factory implements Comparable { + private String name; + private Integer limit = -1; + + @Override + public int compareTo(Rule rule) { + return name.compareTo(rule.name); + } + + private TreeSet variants; + + public Rule(String name) { + this.name = name; + variants = new TreeSet<>(); + } + + public void add(String ruleName, Factory factory) { + add(ruleName, factory, 1.0); + } + + public void add(String ruleName, Factory factory, double weight) { + variants.add(new RuleEntry(ruleName, factory, weight)); + } + + public int size() { + return variants.size(); + } + + @Override + public IRNode produce() throws ProductionFailedException { + if (!variants.isEmpty()) { + // Begin production. + LinkedList rulesList = new LinkedList<>(variants); + PseudoRandom.shuffle(rulesList); + + while (!rulesList.isEmpty() && (limit == -1 || limit > 0)) { + double sum = rulesList.stream() + .mapToDouble(r -> r.weight) + .sum(); + double rnd = PseudoRandom.random() * sum; + Iterator iterator = rulesList.iterator(); + RuleEntry ruleEntry; + double weightAccumulator = 0; + do { + ruleEntry = iterator.next(); + weightAccumulator += ruleEntry.weight; + if (weightAccumulator >= rnd) { + break; + } + } while (iterator.hasNext()); + try { + return ruleEntry.produce(); + } catch (ProductionFailedException e) { + } + iterator.remove(); + if (limit != -1) { + limit--; + } + } + //throw new ProductionFailedException(); + } + // should probably throw exception here.. + //return getChildren().size() > 0 ? getChild(0).produce() : null; + throw new ProductionFailedException(); + } + + private class RuleEntry extends Factory implements Comparable { + private final double weight; + private final Factory factory; + private final String name; + + private RuleEntry(String name, Factory factory, double weight) { + this.name = name; + this.weight = weight; + this.factory = factory; + } + + @Override + public IRNode produce() throws ProductionFailedException { + return factory.produce(); + } + + @Override + public int compareTo(RuleEntry entry) { + return name.compareTo(entry.name); + } + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Statement.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Statement.java new file mode 100644 index 00000000000..6a324219544 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Statement.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class Statement extends IRNode { + private final boolean needSemicolon; + + public Statement(IRNode statementBody, boolean needSemicolon) { + this.needSemicolon = needSemicolon; + addChild(statementBody); + } + + @Override + public long complexity() { + return getChild(0).complexity(); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public boolean isSemicolonNeeded() { + return needSemicolon; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/StaticMemberVariable.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/StaticMemberVariable.java new file mode 100644 index 00000000000..e1a9059db33 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/StaticMemberVariable.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.visitors.Visitor; + +public class StaticMemberVariable extends IRNode implements VariableBase { + private final VariableInfo varInfo; + + public StaticMemberVariable(TypeKlass owner, VariableInfo varInfo) { + setKlass(owner); + this.varInfo = varInfo; + } + + @Override + public VariableInfo get() { + return varInfo; + } + + @Override + public long complexity() { + return 1; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Switch.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Switch.java new file mode 100644 index 00000000000..cc908b91234 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Switch.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import java.util.List; +import jdk.test.lib.jittester.visitors.Visitor; + +public class Switch extends IRNode { + private final int caseBlockIdx; + + public Switch(int level, List chldrn, int caseBlockIdx) { + this.level = level; + addChildren(chldrn); + this.caseBlockIdx = caseBlockIdx; + } + + @Override + public long complexity() { + IRNode switchExp = getChild(0); + long complexity = switchExp != null ? switchExp.complexity() : 0; + for (int i = caseBlockIdx; i < getChildren().size(); ++i) { + complexity += getChild(i).complexity(); + } + return complexity; + } + + @Override + public long countDepth() { + return Long.max(level, super.countDepth()); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public int getCaseBlockIndex() { + return caseBlockIdx; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Symbol.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Symbol.java new file mode 100644 index 00000000000..15f0068045b --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Symbol.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.types.TypeKlass; + +public class Symbol { + + public String name; + public Type type; + public TypeKlass klass; + public static final int NONE = 0x00; + public static final int PRIVATE = 0x01; + public static final int DEFAULT = 0x02; + public static final int PROTECTED = 0x04; + public static final int PUBLIC = 0x08; + public static final int ACCESS_ATTRS_MASK = PRIVATE + PROTECTED + DEFAULT + PUBLIC; + public static final int STATIC = 0x10; + public static final int FINAL = 0x20; + public int flags = NONE; + + protected Symbol() { + } + + protected Symbol(String name) { + this.name = name; + } + + public Symbol(String name, TypeKlass klass, Type type, int flags) { + this.name = name; + this.klass = klass; + this.type = type; + this.flags = flags; + } + + protected Symbol(Symbol value) { + this.name = value.name; + this.klass = value.klass; + this.type = value.type; + this.flags = value.flags; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || !(o instanceof Symbol)) { + return false; + } + try { + Symbol s = (Symbol) o; + return klass.equals(s.klass) && name.equals(s.name); + } catch (Exception e) { + return false; + } + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + + public boolean isStatic() { + return (flags & STATIC) > 0; + } + + public boolean isFinal() { + return (flags & FINAL) > 0; + } + + public boolean isPublic() { + return (flags & PUBLIC) > 0; + } + + public boolean isProtected() { + return (flags & PROTECTED) > 0; + } + + public boolean isPrivate() { + return (flags & PRIVATE) > 0; + } + + protected Symbol copy() { + return new Symbol(this); + } + + public Symbol deepCopy() { + return new Symbol(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/SymbolTable.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/SymbolTable.java new file mode 100644 index 00000000000..9547bdbf41f --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/SymbolTable.java @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Stack; +import jdk.test.lib.jittester.types.TypeKlass; + + +public class SymbolTable { + + private static final Stack>> SYMBOL_STACK + = new Stack<>(); + private static int VARIABLE_NUMBER = 0; + private static int FUNCTION_NUMBER = 0; + + static private void initExternalSymbols() { + + String classList = ProductionParams.addExternalSymbols.value(); + if (classList.equals("all")) { + for (Type type : TypeList.getReferenceTypes()) { + type.exportSymbols(); + } + } else { + String[] splittedList = classList.split(","); + for (Type type : TypeList.getReferenceTypes()) { + for (String str : splittedList) { + if (type.getName().equals(str)) { + type.exportSymbols(); + break; + } + } + } + } + + } + + static { + SYMBOL_STACK.push(new HashMap<>()); + if (!ProductionParams.disableExternalSymbols.value()) { + initExternalSymbols(); + } + } + + public static void add(Symbol symbol) { + HashMap> vars = SYMBOL_STACK.peek(); + if (!vars.containsKey(symbol.type)) { + vars.put(symbol.type, new ArrayList<>()); + } + vars.get(symbol.type).add(symbol); + } + + public static void remove(Symbol symbol) { + HashMap> vars = SYMBOL_STACK.peek(); + if (vars.containsKey(symbol.type)) { + ArrayList symbolsOfType = vars.get(symbol.type); + symbolsOfType.remove(symbol); + if (symbolsOfType.isEmpty()) { + vars.remove(symbol.type); + } + } + } + + protected static Collection get(Type type) { + HashMap> vars = SYMBOL_STACK.peek(); + if (vars.containsKey(type)) { + return vars.get(type); + } + return new ArrayList<>(); + } + + public static Collection get(Type type, Class classToCheck) { + HashMap> vars = SYMBOL_STACK.peek(); + if (vars.containsKey(type)) { + ArrayList result = new ArrayList<>(); + for (Symbol symbol : vars.get(type)) { + if (classToCheck.isInstance(symbol)) { + result.add(symbol); + } + } + return result; + } + return new ArrayList<>(); + } + + protected static Collection get(TypeKlass typeKlass, Type type, + Class classToCheck) { + HashMap> vars = SYMBOL_STACK.peek(); + if (vars.containsKey(type)) { + ArrayList result = new ArrayList<>(); + for (Symbol symbol : vars.get(type)) { + if (classToCheck.isInstance(symbol) && typeKlass.equals(symbol.klass)) { + result.add(symbol); + } + } + return result; + } + return new ArrayList<>(); + } + + protected static HashMap> getAll() { + return SYMBOL_STACK.peek(); + } + + protected static HashMap> getAll(Class classToCheck) { + HashMap> result = new HashMap<>(); + + for (Type type : SYMBOL_STACK.peek().keySet()) { + ArrayList symbolsOfType = SYMBOL_STACK.peek().get(type); + for (Symbol symbol : symbolsOfType) { + if (classToCheck.isInstance(symbol)) { + if (!result.containsKey(type)) { + result.put(type, new ArrayList<>()); + } + result.get(type).add(symbol); + } + } + } + + return result; + } + + protected static HashMap> getAll(TypeKlass typeKlass, Class classToCheck) { + HashMap> result = new HashMap<>(); + + for (Type type : SYMBOL_STACK.peek().keySet()) { + ArrayList symbolsOfType = SYMBOL_STACK.peek().get(type); + for (Symbol symbol : symbolsOfType) { + if (classToCheck.isInstance(symbol) && typeKlass.equals(symbol.klass)) { + if (!result.containsKey(type)) { + result.put(type, new ArrayList<>()); + } + result.get(type).add(symbol); + } + } + } + + return result; + } + + protected static ArrayList getAllCombined() { + ArrayList result = new ArrayList<>(); + for (Type type : SYMBOL_STACK.peek().keySet()) { + ArrayList symbolsOfType = SYMBOL_STACK.peek().get(type); + for (Symbol symbol : symbolsOfType) { + result.add(symbol); + } + } + + return result; + } + + public static ArrayList getAllCombined(Class classToCheck) { + ArrayList result = new ArrayList<>(); + for (Type type : SYMBOL_STACK.peek().keySet()) { + ArrayList symbolsOfType = SYMBOL_STACK.peek().get(type); + for (Symbol symbol : symbolsOfType) { + if (classToCheck.isInstance(symbol)) { + result.add(symbol); + } + } + } + + return result; + } + + public static ArrayList getAllCombined(TypeKlass typeKlass, Class classToCheck) { + ArrayList result = new ArrayList<>(); + for (Type type : SYMBOL_STACK.peek().keySet()) { + ArrayList symbolsOfType = SYMBOL_STACK.peek().get(type); + for (Symbol symbol : symbolsOfType) { + if (classToCheck.isInstance(symbol) && typeKlass.equals(symbol.klass)) { + result.add(symbol); + } + } + } + + return result; + } + + public static ArrayList getAllCombined(TypeKlass typeKlass) { + ArrayList result = new ArrayList<>(); + for (Type t : SYMBOL_STACK.peek().keySet()) { + ArrayList symbolsOfType = SYMBOL_STACK.peek().get(t); + for (Symbol symbol : symbolsOfType) { + if (typeKlass.equals(symbol.klass)) { + result.add(symbol); + } + } + } + + return result; + } + + protected static ArrayList getAllCombined(String name, Class classToCheck) { + ArrayList result = new ArrayList<>(); + for (Type type : SYMBOL_STACK.peek().keySet()) { + ArrayList symbolsOfType = SYMBOL_STACK.peek().get(type); + for (Symbol symbol : symbolsOfType) { + if (classToCheck.isInstance(symbol) && name.equals(symbol.name)) { + result.add(symbol); + } + } + } + + return result; + } + + public static Symbol get(String name, Class classToCheck) { + for (Type type : SYMBOL_STACK.peek().keySet()) { + ArrayList symbolsOfType = SYMBOL_STACK.peek().get(type); + for (Symbol symbol : symbolsOfType) { + if (classToCheck.isInstance(symbol) && name.equals(symbol.name)) { + return symbol; + } + } + } + return null; + } + + public static void removeAll() { + SYMBOL_STACK.clear(); + SYMBOL_STACK.push(new HashMap<>()); + VARIABLE_NUMBER = 0; + FUNCTION_NUMBER = 0; + if (!ProductionParams.disableExternalSymbols.value()) { + initExternalSymbols(); + } + } + + public static void push() { + // Do deep cloning.. + HashMap> prev = SYMBOL_STACK.peek(); + SYMBOL_STACK.push(new HashMap<>()); + HashMap> top = SYMBOL_STACK.peek(); + for (Type type : prev.keySet()) { + ArrayList prevArray = prev.get(type); + top.put(type, new ArrayList<>(prevArray.size())); + ArrayList topArray = top.get(type); + for (Symbol symbol : prevArray) { + topArray.add(symbol.copy()); + } + } + } + + public static void merge() { + // Merging means moving element at the top of stack one step down, while removing the + // previous element. + HashMap> top = SYMBOL_STACK.pop(); + SYMBOL_STACK.pop(); + SYMBOL_STACK.push(top); + } + + public static void pop() { + SYMBOL_STACK.pop(); + } + + public static int getNextVariableNumber() { + return ++VARIABLE_NUMBER; + } + + public static int getNextFunctionNumber() { + return ++FUNCTION_NUMBER; + } + + @Override + public String toString() { + return SYMBOL_STACK.toString(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/TernaryOperator.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/TernaryOperator.java new file mode 100644 index 00000000000..e07d195c57b --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/TernaryOperator.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class TernaryOperator extends Operator { + public enum TernaryPart { + CONDITION, + TRUE, + FALSE, + }; + //protected Production conditionalExpression, leftExpression, rightExpression; + protected Type resultType; + + public TernaryOperator(IRNode condition, IRNode trueBranch, IRNode falseBranch) { + super(2); + resizeUpChildren(TernaryPart.values().length); + setChild(TernaryPart.CONDITION.ordinal(), condition); + setChild(TernaryPart.TRUE.ordinal(), trueBranch); + setChild(TernaryPart.FALSE.ordinal(), falseBranch); + } + + @Override + public long complexity() { + IRNode conditionalExp = getChild(TernaryPart.CONDITION.ordinal()); + IRNode trueBranch = getChild(TernaryPart.TRUE.ordinal()); + IRNode falseBranch = getChild(TernaryPart.FALSE.ordinal()); + if (conditionalExp != null && trueBranch != null && falseBranch != null) { + return conditionalExp.complexity() + trueBranch.complexity() + falseBranch.complexity() + 1; + } else { + return 0; + } + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Throw.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Throw.java new file mode 100644 index 00000000000..efa58ee42e0 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Throw.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class Throw extends IRNode { + public Throw(IRNode throwable) { + addChild(throwable); + } + + @Override + public long complexity() { + return getThowable().complexity(); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public IRNode getThowable() { + return getChild(0); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/TryCatchBlock.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/TryCatchBlock.java new file mode 100644 index 00000000000..075774c80b4 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/TryCatchBlock.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester; + +import java.util.List; +import java.util.stream.Collectors; +import jdk.test.lib.jittester.visitors.Visitor; + +public class TryCatchBlock extends IRNode { + public TryCatchBlock(IRNode body, IRNode finallyBlock, List catchBlocks, int level) { + this.level = level; + addChild(body); + addChild(finallyBlock); + addChildren(catchBlocks); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + @Override + public long complexity() { + return getChildren().stream() + .filter(elem -> elem != null) + .collect(Collectors.summingLong(IRNode::complexity)); + } +} + diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Type.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Type.java new file mode 100644 index 00000000000..a355e10e32e --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/Type.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +/** + * Type system's core.. + */ +public abstract class Type extends IRNode implements Comparable { + + private final String typeName; + + protected Type(String typeName) { + this.typeName = typeName; + } + + @Override + public boolean equals(Object t) { + if (this == t) { + return true; + } + if (t == null || !(t instanceof Type)) { + return false; + } + return typeName.equals(((Type) t).typeName); + } + + @Override + public int compareTo(Type t) { + return typeName.compareTo(t.typeName); + } + + @Override + public int hashCode() { + return typeName.hashCode(); + } + + public abstract boolean canImplicitlyCastTo(Type t); + + public abstract boolean canExplicitlyCastTo(Type t); + + public abstract boolean canCompareTo(Type t); + + public abstract boolean canEquateTo(Type t); + + protected void exportSymbols() { + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + @Override + public String getName() { + return typeName; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/TypeList.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/TypeList.java new file mode 100644 index 00000000000..00eaa7b8d1f --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/TypeList.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Predicate; +import jdk.test.lib.jittester.types.TypeArray; +import jdk.test.lib.jittester.types.TypeBoolean; +import jdk.test.lib.jittester.types.TypeByte; +import jdk.test.lib.jittester.types.TypeChar; +import jdk.test.lib.jittester.types.TypeDouble; +import jdk.test.lib.jittester.types.TypeFloat; +import jdk.test.lib.jittester.types.TypeInt; +import jdk.test.lib.jittester.types.TypeLong; +import jdk.test.lib.jittester.types.TypeShort; +import jdk.test.lib.jittester.types.TypeVoid; + +public class TypeList { + private static final TypeVoid TYPE_VOID = new TypeVoid(); + private static final List TYPES = new ArrayList<>(); + private static final List BUILTIN_TYPES = new ArrayList<>(); + private static final List BUILTIN_INT_TYPES = new ArrayList<>(); + private static final List BUILTIN_FP_TYPES = new ArrayList<>(); + private static final List REFERENCE_TYPES = new ArrayList<>(); + + static { + BUILTIN_INT_TYPES.add(new TypeBoolean()); + BUILTIN_INT_TYPES.add(new TypeByte()); + BUILTIN_INT_TYPES.add(new TypeChar()); + BUILTIN_INT_TYPES.add(new TypeShort()); + BUILTIN_INT_TYPES.add(new TypeInt()); + BUILTIN_INT_TYPES.add(new TypeLong()); + BUILTIN_FP_TYPES.add(new TypeFloat()); + BUILTIN_FP_TYPES.add(new TypeDouble()); + + BUILTIN_TYPES.addAll(BUILTIN_INT_TYPES); + BUILTIN_TYPES.addAll(BUILTIN_FP_TYPES); + + TYPES.addAll(BUILTIN_TYPES); + + if (!ProductionParams.disableArrays.value()) { + REFERENCE_TYPES.add(new TypeArray().produce()); + TYPES.addAll(REFERENCE_TYPES); + } + } + + public static TypeVoid getVoid() { + return TYPE_VOID; + } + + public static Collection getAll() { + return TYPES; + } + + public static Collection getBuiltIn() { + return BUILTIN_TYPES; + } + + public static Collection getBuiltInInt() { + return BUILTIN_INT_TYPES; + } + + protected static Collection getBuiltInFP() { + return BUILTIN_FP_TYPES; + } + + protected static Collection getReferenceTypes() { + return REFERENCE_TYPES; + } + + protected static boolean isBuiltInFP(Type t) { + return BUILTIN_FP_TYPES.contains(t); + } + + public static boolean isBuiltInInt(Type t) { + return BUILTIN_INT_TYPES.contains(t); + } + + public static boolean isBuiltIn(Type t) { + return isBuiltInInt(t) || isBuiltInFP(t); + } + + protected static boolean isIn(Type t) { + return TYPES.contains(t); + } + + protected static boolean isReferenceType(Type t) { + return REFERENCE_TYPES.contains(t); + } + + public static Type find(Type t) { + int i = TYPES.indexOf(t); + if (i != -1) { + return TYPES.get(i); + } + return null; + } + + protected static Type findReferenceType(Type t) { + int i = REFERENCE_TYPES.indexOf(t); + if (i != -1) { + return REFERENCE_TYPES.get(i); + } + return null; + } + + public static Type find(String name) { + for (Type t : TYPES) { + if (t.getName().equals(name)) { + return t; + } + } + return null; + } + + public static void add(Type t) { + REFERENCE_TYPES.add(t); + TYPES.add(t); + } + + protected static void remove(Type t) { + REFERENCE_TYPES.remove(t); + TYPES.remove(t); + } + + public static void removeAll() { + Predicate isNotBasic = t -> t.getName().startsWith("Test_"); + TYPES.removeIf(isNotBasic); + REFERENCE_TYPES.removeIf(isNotBasic); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/TypeUtil.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/TypeUtil.java new file mode 100644 index 00000000000..181d0a52b9b --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/TypeUtil.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + + +public class TypeUtil { + + public static Collection getImplicitlyCastable(Collection types, Type type) { + ArrayList result = new ArrayList<>(types); + Iterator iterator = result.iterator(); + while (iterator.hasNext()) { + if (!iterator.next().canImplicitlyCastTo(type)) { + iterator.remove(); + } + } + return result; + } + + public static Collection getExplicitlyCastable(Collection types, Type type) { + ArrayList result = new ArrayList<>(types); + Iterator iterator = result.iterator(); + while (iterator.hasNext()) { + if (!iterator.next().canExplicitlyCastTo(type)) { + iterator.remove(); + } + } + return result; + } + + public static List getMoreCapatiousThan(Collection types, BuiltInType type) { + ArrayList result = new ArrayList<>(); + Iterator iterator = types.iterator(); + while (iterator.hasNext()) { + try { + BuiltInType builtInType = (BuiltInType) iterator.next(); + if (builtInType.isMoreCapaciousThan(type)) { + result.add(builtInType); + } + } catch (Exception e) { + } + } + return result; + } + + public static List getLessCapatiousOrEqualThan(Collection types, BuiltInType type) { + ArrayList result = new ArrayList<>(); + Iterator iterator = types.iterator(); + while (iterator.hasNext()) { + try { + BuiltInType builtInType = (BuiltInType) iterator.next(); + if (!builtInType.isMoreCapaciousThan(type) || builtInType.equals(type)) { + result.add(builtInType); + } + } catch (Exception e) { + } + } + return result; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/TypesParser.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/TypesParser.java new file mode 100644 index 00000000000..79ea14b4293 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/TypesParser.java @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester; + +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import jdk.test.lib.Asserts; +import jdk.test.lib.jittester.functions.FunctionInfo; +import jdk.test.lib.jittester.types.TypeArray; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeVoid; + +/** + * Class used for parsing included classes file and excluded methods file + */ +public class TypesParser { + + private static final HashMap, Type> TYPE_CACHE = new HashMap<>(); + + /** + * Parses included classes file and excluded methods file to TypeList and SymbolTable. + * This routine takes all classes named in the classes file and puts them to the TypeList, + * it also puts all the methods of the classes to the SymbolTable. + * Excluded methods file is used to remove methods from the SymbolTable. + * @param klassesFileName - name of the included classes file + * @param exMethodsFileName - name of the excluded method file + */ + public static void parseTypesAndMethods(String klassesFileName, String exMethodsFileName) { + Asserts.assertNotNull(klassesFileName, "Classes input file name is null"); + Asserts.assertFalse(klassesFileName.isEmpty(), "Classes input file name is empty"); + Set> klasses = parseKlasses(klassesFileName); + Set methodsToExclude; + if (exMethodsFileName != null && !exMethodsFileName.isEmpty()) { + methodsToExclude = parseMethods(exMethodsFileName); + } else { + methodsToExclude = new HashSet<>(); + } + klasses.stream().forEach(klass -> { + TypeKlass typeKlass = (TypeKlass) getType(klass); + if (TypeList.isReferenceType(typeKlass)) { + return; + } + TypeList.add(typeKlass); + Set methods = new HashSet<>(); + methods.addAll(Arrays.asList(klass.getMethods())); + methods.addAll(Arrays.asList(klass.getConstructors())); + methods.removeAll(methodsToExclude); + methods.stream().forEach(method -> { + if (method.isSynthetic()) { + return; + } + String name = method.getName(); + boolean isConstructor = false; + Type returnType; + if (name.equals(klass.getName())) { + isConstructor = true; + returnType = typeKlass; + } else { + returnType = getType(((Method) method).getReturnType()); + } + ArrayList paramList = new ArrayList<>(); + int flags = getMethodFlags(method); + if (!isConstructor && ((flags & FunctionInfo.STATIC) == 0)) { + paramList.add(new VariableInfo("this", typeKlass, typeKlass, + VariableInfo.LOCAL | VariableInfo.INITIALIZED)); + } + Class[] paramKlasses = method.getParameterTypes(); + int argNum = 0; + for (Class paramKlass : paramKlasses) { + argNum++; + Type paramType = getType(paramKlass); + paramList.add(new VariableInfo("arg" + argNum, typeKlass, paramType, + VariableInfo.LOCAL | VariableInfo.INITIALIZED)); + } + typeKlass.addSymbol(new FunctionInfo(name, typeKlass, returnType, 1, flags, + paramList)); + }); + }); + } + + private static Type getType(Class klass) { + Type type = TYPE_CACHE.get(klass); + if (type != null) { + return type; + } + if (klass.isPrimitive()) { + if (klass.equals(void.class)) { + type = new TypeVoid(); + } else { + type = TypeList.find(klass.getName()); + } + } else { + int flags = getKlassFlags(klass); + if (klass.isArray()) { + TypeKlass elementType + = new TypeKlass(klass.getCanonicalName().replaceAll("\\[\\]", ""), flags); + int dim = getArrayClassDimension(klass); + type = new TypeArray(elementType, dim); + } else { + String canonicalName = klass.getCanonicalName(); + if (!"java.lang.Object".equals(canonicalName)) { + flags |= TypeKlass.FINAL; + } + type = new TypeKlass(canonicalName, flags); + } + Class parentKlass = klass.getSuperclass(); + if (parentKlass != null) { + TypeKlass parentTypeKlass = (TypeKlass) getType(parentKlass); + ((TypeKlass) type).addParent(parentTypeKlass.getName()); + ((TypeKlass) type).setParent(parentTypeKlass); + } + } + TYPE_CACHE.put(klass, type); + return type; + } + + private static int getArrayClassDimension(Class klass) { + if (!klass.isArray()) { + return 0; + } + String name = klass.getName(); + int begin = name.indexOf('['); + name = name.substring(begin, name.length()); + return name.length() / 2; + } + + private static int getKlassFlags(Class klass) { + int flags = TypeKlass.NONE; + if (klass.isInterface()) { + flags = flags | TypeKlass.INTERFACE; + } else if ((klass.getModifiers() & Modifier.ABSTRACT) != 0) { + flags = flags | TypeKlass.ABSTRACT; + } else if ((klass.getModifiers() & Modifier.FINAL) != 0) { + flags = flags | TypeKlass.FINAL; + } + return flags; + } + + private static int getMethodFlags(Executable method) { + int flags = FunctionInfo.NONE; + int modifiers = method.getModifiers(); + if (Modifier.isAbstract(modifiers)) { + flags |= FunctionInfo.ABSTRACT; + } + if (Modifier.isFinal(modifiers)) { + flags |= FunctionInfo.FINAL; + } + if (Modifier.isPublic(modifiers)) { + flags |= FunctionInfo.PUBLIC; + } else if (Modifier.isProtected(modifiers)) { + flags |= FunctionInfo.PROTECTED; + } else if (Modifier.isPrivate(modifiers)) { + flags |= FunctionInfo.PRIVATE; + } else { + flags |= FunctionInfo.DEFAULT; + } + if (Modifier.isStatic(modifiers)) { + flags |= FunctionInfo.STATIC; + } + if (Modifier.isSynchronized(modifiers)) { + flags |= FunctionInfo.SYNCHRONIZED; + } + return flags; + } + + private static Set> parseKlasses(String klassesFileName) { + Asserts.assertNotNull(klassesFileName, "Classes input file name is null"); + Asserts.assertFalse(klassesFileName.isEmpty(), "Classes input file name is empty"); + Set klassNamesSet = new HashSet<>(); + Path klassesFilePath = (new File(klassesFileName)).toPath(); + try { + Files.lines(klassesFilePath).forEach(line -> { + line = line.trim(); + if (line.isEmpty()) { + return; + } + String msg = String.format("Format of the classes input file \"%s\" is incorrect," + + " line \"%s\" has wrong format", klassesFileName, line); + Asserts.assertTrue(line.matches("\\w[\\w\\.$]*"), msg); + klassNamesSet.add(line.replaceAll(";", "")); + }); + } catch (IOException ex) { + throw new Error("Error reading klasses file", ex); + } + Set> klassesSet = new HashSet<>(); + klassNamesSet.stream().forEach(klassName -> { + try { + klassesSet.add(Class.forName(klassName)); + } catch (ClassNotFoundException ex) { + throw new Error("Unexpected exception while parsing klasses file", ex); + } + }); + return klassesSet; + } + + private static Set parseMethods(String methodsFileName) { + Asserts.assertNotNull(methodsFileName, "Methods exclude input file name is null"); + Asserts.assertFalse(methodsFileName.isEmpty(), "Methods exclude input file name is empty"); + LinkedList methodNamesList = new LinkedList<>(); + Path klassesFilePath = (new File(methodsFileName)).toPath(); + try { + Files.lines(klassesFilePath).forEach(line -> { + line = line.trim(); + if (line.isEmpty()) { + return; + } + String msg = String.format("Format of the methods exclude input file \"%s\" is incorrect," + + " line \"%s\" has wrong format", methodsFileName, line); + Asserts.assertTrue(line.matches("\\w[\\w/$]*::[\\w$]+\\((\\[?[ZBSCIJFD]|\\[?L[\\w/$]+;)*\\)"), msg); + methodNamesList.add(line.substring(0, line.length() - 1)); + }); + } catch (IOException ex) { + throw new Error("Error reading exclude method file", ex); + } + Set methodsList = new HashSet<>(); + methodNamesList.stream().forEach(methodName -> { + String[] klassAndNameAndSig = methodName.split("::"); + String klassName = klassAndNameAndSig[0].replaceAll("/", "\\."); + String[] nameAndSig = klassAndNameAndSig[1].split("[\\(\\)]"); + String name = nameAndSig[0]; + String signature = ""; + if (nameAndSig.length > 1) { + signature = nameAndSig[1]; + } + Class klass = null; + List> signatureTypes = null; + try { + klass = Class.forName(klassName); + signatureTypes = parseSignature(signature); + } catch (ClassNotFoundException ex) { + throw new Error("Unexpected exception while parsing exclude methods file", ex); + } + try { + Executable method; + if (name.equals(klass.getSimpleName())) { + method = klass.getConstructor(signatureTypes.toArray(new Class[0])); + } else { + method = klass.getMethod(name, signatureTypes.toArray(new Class[0])); + } + methodsList.add(method); + } catch (NoSuchMethodException | SecurityException ex) { + throw new Error("Unexpected exception while parsing exclude methods file", ex); + } + }); + return methodsList; + } + + private static List> parseSignature(String signature) throws ClassNotFoundException { + LinkedList> sigClasses = new LinkedList<>(); + char typeChar; + boolean isArray; + String klassName; + StringBuilder sb; + StringBuilder arrayDim; + try (StringReader str = new StringReader(signature)) { + int symbol = str.read(); + while (symbol != -1){ + typeChar = (char) symbol; + arrayDim = new StringBuilder(); + Class primArrayClass = null; + if (typeChar == '[') { + isArray = true; + arrayDim.append('['); + symbol = str.read(); + while (symbol == '['){ + arrayDim.append('['); + symbol = str.read(); + } + typeChar = (char) symbol; + if (typeChar != 'L') { + primArrayClass = Class.forName(arrayDim.toString() + typeChar); + } + } else { + isArray = false; + } + switch (typeChar) { + case 'Z': + sigClasses.add(isArray ? primArrayClass : boolean.class); + break; + case 'I': + sigClasses.add(isArray ? primArrayClass : int.class); + break; + case 'J': + sigClasses.add(isArray ? primArrayClass : long.class); + break; + case 'F': + sigClasses.add(isArray ? primArrayClass : float.class); + break; + case 'D': + sigClasses.add(isArray ? primArrayClass : double.class); + break; + case 'B': + sigClasses.add(isArray ? primArrayClass : byte.class); + break; + case 'S': + sigClasses.add(isArray ? primArrayClass : short.class); + break; + case 'C': + sigClasses.add(isArray ? primArrayClass : char.class); + break; + case 'L': + sb = new StringBuilder(); + symbol = str.read(); + while (symbol != ';') { + sb.append((char) symbol); + symbol = str.read(); + } + klassName = sb.toString().replaceAll("/", "\\."); + if (isArray) { + klassName = arrayDim.toString() + "L" + klassName + ";"; + } + Class klass = Class.forName(klassName); + sigClasses.add(klass); + break; + default: + throw new Error("Unknown type " + typeChar); + } + symbol = str.read(); + } + } catch (IOException ex) { + throw new Error("Unexpected exception while parsing exclude methods file", ex); + } + return sigClasses; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/UnaryOperator.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/UnaryOperator.java new file mode 100644 index 00000000000..d5b24340f55 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/UnaryOperator.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class UnaryOperator extends Operator { + protected OperatorKind opKind; + protected Type resultType; + + public UnaryOperator(OperatorKind opKind, IRNode expression) { + super(opKind.priority); + this.opKind = opKind; + addChild(expression); + } + + @Override + public long complexity() { + IRNode expression = getChild(0); + return expression != null ? expression.complexity() + 1 : 0; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public boolean isPrefix() { + return opKind.isPrefix; + } + + public String getOperatorText() { + return opKind.text; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/VariableBase.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/VariableBase.java new file mode 100644 index 00000000000..1b93c272c25 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/VariableBase.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +public interface VariableBase { + + VariableInfo get(); +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/VariableDeclaration.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/VariableDeclaration.java new file mode 100644 index 00000000000..0d74f2a4985 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/VariableDeclaration.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.visitors.Visitor; + +public class VariableDeclaration extends IRNode { + protected final VariableInfo variableInfo; + + public VariableDeclaration(VariableInfo value) { + variableInfo = value; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public VariableInfo getVariableInfo() { + return variableInfo; + } + + @Override + public String getName() { + return variableInfo.name; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/VariableDeclarationBlock.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/VariableDeclarationBlock.java new file mode 100644 index 00000000000..56dbab64003 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/VariableDeclarationBlock.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import java.util.ArrayList; +import jdk.test.lib.jittester.visitors.Visitor; + +public class VariableDeclarationBlock extends IRNode { + public VariableDeclarationBlock(ArrayList content, int level) { + addChildren(content); + this.level = level; + } + + @Override + public long complexity() { + return getChildren() + .stream() + .mapToLong(IRNode::complexity) + .sum(); + } + + protected int size() { + return getChildren() != null ? getChildren().size() : 0; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/VariableInfo.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/VariableInfo.java new file mode 100644 index 00000000000..610e71761ad --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/VariableInfo.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +import jdk.test.lib.jittester.types.TypeKlass; + + +public class VariableInfo extends Symbol { + + public static final int LOCAL = 0x40; + public static final int INITIALIZED = 0x80; + + protected VariableInfo() { + } + + public VariableInfo(VariableInfo value) { + super(value); + } + + public VariableInfo(String name, TypeKlass owner, Type type, int flags) { + super(name, owner, type, flags); + } + + public VariableInfo(TypeKlass owner, Type type) { + super("", owner, type, Symbol.NONE); + } + + @Override + protected Symbol copy() { + return new VariableInfo(this); + } + + @Override + public Symbol deepCopy() { + return new VariableInfo(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/VariableInitialization.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/VariableInitialization.java new file mode 100644 index 00000000000..005b43d3370 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/VariableInitialization.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester; + +public class VariableInitialization extends Initialization { + public VariableInitialization(VariableInfo info, IRNode initExpression) { + super(info, initExpression); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/arrays/ArrayCreation.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/arrays/ArrayCreation.java new file mode 100644 index 00000000000..821b4a389c0 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/arrays/ArrayCreation.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.arrays; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.Literal; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.VariableDeclaration; +import jdk.test.lib.jittester.types.TypeArray; +import jdk.test.lib.jittester.visitors.Visitor; + +public class ArrayCreation extends IRNode { + private final VariableDeclaration variable; + private final TypeArray array; + private final List dims; + + public ArrayCreation(VariableDeclaration var, TypeArray array, ArrayList dimensionSizeExpressions) { + this.variable = var; + this.array = array; + addChildren(dimensionSizeExpressions); + this.dims = dimensionSizeExpressions.stream() + .map(d -> { + if (d instanceof Literal) { + Literal n = (Literal) d; + return (Byte)n.getValue(); + } + return (byte)0; + }) + .collect(Collectors.toList()); + TypeArray type = (TypeArray) variable.getVariableInfo().type; + type.setDimentions(dims); + } + + public Type getArrayType() { + return array.type; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public byte getDimensionSize(int dimensionIndex) { + return dims.get(dimensionIndex); + } + + public int getDimensionsCount() { + return dims.size(); + } + + public VariableDeclaration getVariable() { + return variable; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/arrays/ArrayElement.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/arrays/ArrayElement.java new file mode 100644 index 00000000000..3c34e16d8f7 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/arrays/ArrayElement.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.arrays; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.visitors.Visitor; + +public class ArrayElement extends IRNode { + public ArrayElement(IRNode array, ArrayList dimensionExpressions) { + addChild(array); + addChildren(dimensionExpressions); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/arrays/ArrayExtraction.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/arrays/ArrayExtraction.java new file mode 100644 index 00000000000..30401780b14 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/arrays/ArrayExtraction.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.arrays; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.Literal; +import jdk.test.lib.jittester.LocalVariable; +import jdk.test.lib.jittester.types.TypeArray; +import jdk.test.lib.jittester.visitors.Visitor; + + +/* +Array extraction produces and array with N dimentions from an array with M +dimentions, where N < M. + */ +public class ArrayExtraction extends IRNode { + private final List dims; + public ArrayExtraction(IRNode array, ArrayList dimensionExpressions) { + addChild(array); + addChildren(dimensionExpressions); + if (array instanceof ArrayCreation) { + dims = new ArrayList<>(); + ArrayCreation ac = (ArrayCreation) array; + for (int i = dimensionExpressions.size(); i < ac.getDimensionsCount(); ++i) { + dims.add(ac.getDimensionSize(i)); + } + } else if (array instanceof ArrayExtraction) { + dims = new ArrayList<>(); + ArrayExtraction ae = (ArrayExtraction) array; + for (int i = dimensionExpressions.size(); i < ae.getDimsNumber(); ++i) { + dims.add(ae.getDim(i)); + } + } else if (array instanceof LocalVariable) { + LocalVariable loc = (LocalVariable) array; + TypeArray type = (TypeArray) loc.get().type; + dims = type.getDims(); + for (int i = dimensionExpressions.size(); i < type.dimensions; ++i) { + dims.add(type.getDims().get(i)); + } + } else { + dims = dimensionExpressions.stream() + .map(d -> { + if (d instanceof Literal) { + Literal n = (Literal) d; + return (Byte)n.getValue(); + } + return (byte)0; + }) + .collect(Collectors.toList()); + } + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public byte getDim(int dim) { + return dims.get(dim); + } + + public int getDimsNumber() { + return dims.size(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/classes/ClassDefinitionBlock.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/classes/ClassDefinitionBlock.java new file mode 100644 index 00000000000..653e765d0f4 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/classes/ClassDefinitionBlock.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.classes; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.visitors.Visitor; + +public class ClassDefinitionBlock extends IRNode { + public ClassDefinitionBlock(ArrayList content, int level) { + this.level = level; + addChildren(content); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/classes/Interface.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/classes/Interface.java new file mode 100644 index 00000000000..cabe704d69c --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/classes/Interface.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.classes; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.visitors.Visitor; + +public class Interface extends IRNode { + private final String name; + private TypeKlass parent = null; + + public Interface(TypeKlass parent, String name, int level, IRNode functionDeclaraionBlock) { + this.parent = parent; + this.name = name; + this.level = level; + addChild(functionDeclaraionBlock); + } + + @Override + public long complexity() { + return 0; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + @Override + public String getName() { + return name; + } + + public TypeKlass getParentKlass() { + return parent; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/classes/Klass.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/classes/Klass.java new file mode 100644 index 00000000000..a72dd854f03 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/classes/Klass.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.classes; + +import java.util.ArrayList; +import java.util.List; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.visitors.Visitor; + + +public class Klass extends IRNode { + + public TypeKlass getThisKlass() { + return thisKlass; + } + + public List getInterfaces() { + return interfaces; + } + + public TypeKlass getParentKlass() { + return parentKlass; + } + + @Override + public String getName() { + return name; + } + + public enum KlassPart { + DATA_MEMBERS, + CONSTRUCTORS, + REDEFINED_FUNCTIONS, + OVERRIDEN_FUNCTIONS, + MEMBER_FUNCTIONS, + MEMBER_FUNCTIONS_DECLARATIONS, + PRINT_VARIABLES, + } + + protected final String name; + protected final TypeKlass thisKlass; + private final TypeKlass parentKlass; + private final ArrayList interfaces; + + public Klass(TypeKlass thisKlass, TypeKlass parent, + ArrayList interfaces, String name, int level, + IRNode variableDeclarations, IRNode constructorDefinitions, + IRNode functionDefinitions, IRNode abstractFunctionRedefinitions, + IRNode overridenFunctionRedefitions, IRNode functionDeclarations, + IRNode printVariablesBlock) { + this.thisKlass = thisKlass; + klass = thisKlass; + this.parentKlass = parent; + this.interfaces = interfaces; + this.name = name; + this.level = level; + addChild(variableDeclarations); + addChild(constructorDefinitions); + addChild(abstractFunctionRedefinitions); + addChild(overridenFunctionRedefitions); + addChild(functionDefinitions); + addChild(functionDeclarations); + addChild(printVariablesBlock); + } + + @Override + public long complexity() { + return 0; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/classes/MainKlass.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/classes/MainKlass.java new file mode 100644 index 00000000000..015264d61ef --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/classes/MainKlass.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.classes; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.visitors.Visitor; + +public class MainKlass extends IRNode { + public enum MainKlassPart { + DATA_MEMBERS, + MEMBER_FUNCTIONS, + TEST_FUNCTION, + PRINT_VARIABLES, + } + + private final String name; + private final TypeKlass thisKlass; + + public MainKlass(String name, TypeKlass thisKlass, IRNode variableDeclarations, + IRNode functionDefinitions, IRNode testFunction, IRNode printVariables) { + addChild(variableDeclarations); + addChild(functionDefinitions); + addChild(testFunction); + addChild(printVariables); + this.name = name; + this.thisKlass = thisKlass; + } + + @Override + public long complexity() { + IRNode dataMembers = getChild(MainKlassPart.DATA_MEMBERS.ordinal()); + IRNode testFunction = getChild(MainKlassPart.TEST_FUNCTION.ordinal()); + return dataMembers.complexity() + testFunction.complexity(); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + @Override + public String getName() { + return name; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ArgumentDeclarationFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ArgumentDeclarationFactory.java new file mode 100644 index 00000000000..2a4e8993b44 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ArgumentDeclarationFactory.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.functions.ArgumentDeclaration; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class ArgumentDeclarationFactory extends Factory { + private final int argumentNumber; + private final TypeKlass ownerClass; + + ArgumentDeclarationFactory(TypeKlass ownerClass, int argumentNumber) { + this.ownerClass = ownerClass; + this.argumentNumber = argumentNumber; + } + + @Override + public ArgumentDeclaration produce() throws ProductionFailedException { + Type resultType = PseudoRandom.randomElement(TypeList.getAll()); + String resultName = "arg_" + argumentNumber; + int flags = ((!ProductionParams.disableFinalVariables.value() + && PseudoRandom.randomBoolean()) ? VariableInfo.FINAL : VariableInfo.NONE) + | VariableInfo.LOCAL | VariableInfo.INITIALIZED; + VariableInfo v = new VariableInfo(resultName, ownerClass, resultType, flags); + SymbolTable.add(v); + return new ArgumentDeclaration(v); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ArithmeticOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ArithmeticOperatorFactory.java new file mode 100644 index 00000000000..38175cbd9c3 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ArithmeticOperatorFactory.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Rule; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.types.TypeKlass; + +class ArithmeticOperatorFactory extends Factory { + private final Rule rule; + + ArithmeticOperatorFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, + Type resultType, boolean exceptionSafe, boolean noconsts) throws ProductionFailedException { + IRNodeBuilder builder = new IRNodeBuilder() + .setComplexityLimit(complexityLimit) + .setOperatorLimit(operatorLimit) + .setOwnerKlass(ownerClass) + .setResultType(resultType) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts); + rule = new Rule("arithmetic"); + rule.add("add", builder.setOperatorKind(OperatorKind.ADD).getBinaryOperatorFactory()); + rule.add("sub", builder.setOperatorKind(OperatorKind.SUB).getBinaryOperatorFactory()); + rule.add("mul", builder.setOperatorKind(OperatorKind.MUL).getBinaryOperatorFactory()); + if (!exceptionSafe) { + rule.add("div", builder.setOperatorKind(OperatorKind.DIV).getBinaryOperatorFactory()); + rule.add("mod", builder.setOperatorKind(OperatorKind.MOD).getBinaryOperatorFactory()); + } + rule.add("unary_plus", builder.setOperatorKind(OperatorKind.UNARY_PLUS).getUnaryOperatorFactory()); + rule.add("unary_minus", builder.setOperatorKind(OperatorKind.UNARY_MINUS).getUnaryOperatorFactory()); + } + + @Override + public IRNode produce() throws ProductionFailedException { + return rule.produce(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ArrayCreationFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ArrayCreationFactory.java new file mode 100644 index 00000000000..7933ba470f2 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ArrayCreationFactory.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.Literal; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.VariableDeclaration; +import jdk.test.lib.jittester.arrays.ArrayCreation; +import jdk.test.lib.jittester.types.TypeArray; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeByte; +import jdk.test.lib.jittester.types.TypeVoid; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class ArrayCreationFactory extends SafeFactory { + private final long complexityLimit; + private final int operatorLimit; + private final Type resultType; + private final boolean exceptionSafe; + private final boolean noconsts; + private final TypeKlass ownerClass; + + ArrayCreationFactory(long complexityLimit, int operatorLimit, + TypeKlass ownerClass, Type resultType, boolean exceptionSafe, boolean noconsts) { + this.complexityLimit = complexityLimit; + this.operatorLimit = operatorLimit; + this.ownerClass = ownerClass; + this.resultType = resultType; + this.exceptionSafe = exceptionSafe; + this.noconsts = noconsts; + } + + @Override + protected IRNode sproduce() throws ProductionFailedException { + if (resultType instanceof TypeArray) { + TypeArray arrayResultType = (TypeArray) resultType; + if (arrayResultType.type.equals(new TypeVoid())) { + arrayResultType = arrayResultType.produce(); + } + IRNodeBuilder builder = new IRNodeBuilder() + .setComplexityLimit(complexityLimit) + .setOwnerKlass(ownerClass) + .setResultType(new TypeByte()) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts); + double chanceExpression = ProductionParams.chanceExpressionIndex.value() / 100; + ArrayList dims = new ArrayList<>(arrayResultType.dimensions); + for (int i = 0; i < arrayResultType.dimensions; i++) { + if (PseudoRandom.randomBoolean(chanceExpression)) { + dims.add(builder.setOperatorLimit((int) (PseudoRandom.random() + * operatorLimit / arrayResultType.dimensions)) + .getExpressionFactory() + .produce()); + } else { + Literal dimension = (Literal)builder.getLiteralFactory().produce(); + while (Integer.valueOf(dimension.getValue().toString()) < 1) { + dimension = (Literal)builder.getLiteralFactory().produce(); + } + dims.add(dimension); + } + } + VariableDeclaration var = (VariableDeclaration) builder + .setOwnerKlass(ownerClass) + .setResultType(arrayResultType) + .setIsLocal(true) + .setIsStatic(false) + .getVariableDeclarationFactory() + .produce(); + return new ArrayCreation(var, arrayResultType, dims); + } + throw new ProductionFailedException(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ArrayElementFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ArrayElementFactory.java new file mode 100644 index 00000000000..ab3639972cd --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ArrayElementFactory.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.Literal; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.arrays.ArrayCreation; +import jdk.test.lib.jittester.arrays.ArrayElement; +import jdk.test.lib.jittester.arrays.ArrayExtraction; +import jdk.test.lib.jittester.types.TypeArray; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeByte; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class ArrayElementFactory extends SafeFactory { + private final long complexityLimit; + private final int operatorLimit; + private final Type resultType; + private final TypeKlass ownerClass; + private final boolean exceptionSafe; + private final boolean noconsts; + + ArrayElementFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, + Type resultType, boolean exceptionSafe, boolean noconsts) { + this.complexityLimit = complexityLimit; + this.operatorLimit = operatorLimit; + this.ownerClass = ownerClass; + this.resultType = resultType; + this.exceptionSafe = exceptionSafe; + this.noconsts = noconsts; + } + + @Override + protected IRNode sproduce() throws ProductionFailedException { + if (resultType instanceof TypeArray) { + throw new ProductionFailedException(); + } + long arrayComplexityLimit = (long) (complexityLimit * 0.5 * PseudoRandom.random()); + int arrayOperatorLimit = (int) (operatorLimit * 0.5 * PseudoRandom.random()); + int dimensionsCount = PseudoRandom.randomNotZero(ProductionParams.dimensionsLimit.value()); + long complexityPerDimension = (long) ((complexityLimit - arrayComplexityLimit) + * PseudoRandom.random()) / dimensionsCount; + int operatorLimitPerDimension = (int) ((operatorLimit - arrayOperatorLimit - dimensionsCount) + * PseudoRandom.random()) / dimensionsCount; + IRNodeBuilder builder = new IRNodeBuilder().setOwnerKlass(ownerClass) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts); + IRNode arrayReturningExpression = builder + .setComplexityLimit(arrayComplexityLimit) + .setOperatorLimit(arrayOperatorLimit) + .setResultType(new TypeArray(resultType, dimensionsCount)) + .getExpressionFactory() + .produce(); + ExpressionFactory expressionFactory = builder + .setComplexityLimit(complexityPerDimension) + .setOperatorLimit(operatorLimitPerDimension) + .setResultType(new TypeByte()) + .getExpressionFactory(); + double chanceExpression = ProductionParams.chanceExpressionIndex.value() / 100.; + ArrayList perDimensionExpressions = new ArrayList<>(dimensionsCount); + for (int i = 0; i < dimensionsCount; i++) { + if (PseudoRandom.randomBoolean(chanceExpression)) { + perDimensionExpressions.add(expressionFactory.produce()); + } else { + byte dimLimit = 0; + if (arrayReturningExpression instanceof ArrayCreation) { + ArrayCreation arrayCreation = (ArrayCreation) arrayReturningExpression; + dimLimit = arrayCreation.getDimensionSize(i); + } else if (arrayReturningExpression instanceof ArrayExtraction) { + ArrayExtraction arrayExtraction = (ArrayExtraction) arrayReturningExpression; + if (i < arrayExtraction.getDimsNumber()) + dimLimit = arrayExtraction.getDim(i); + } + perDimensionExpressions.add(new Literal(PseudoRandom.randomNotNegative(dimLimit), new TypeByte())); + } + } + return new ArrayElement(arrayReturningExpression, perDimensionExpressions); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ArrayExtractionFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ArrayExtractionFactory.java new file mode 100644 index 00000000000..de444de76bc --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ArrayExtractionFactory.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.Literal; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.arrays.ArrayCreation; +import jdk.test.lib.jittester.arrays.ArrayExtraction; +import jdk.test.lib.jittester.types.TypeArray; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeByte; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class ArrayExtractionFactory extends SafeFactory { + private final long complexityLimit; + private final int operatorLimit; + private final Type resultType; + private final TypeKlass ownerClass; + private final boolean exceptionSafe; + private final boolean noconsts; + + ArrayExtractionFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, + Type resultType, boolean exceptionSafe, boolean noconsts) { + this.complexityLimit = complexityLimit; + this.operatorLimit = operatorLimit; + this.ownerClass = ownerClass; + this.resultType = resultType; + this.exceptionSafe = exceptionSafe; + this.noconsts = noconsts; + } + + @Override + public IRNode sproduce() throws ProductionFailedException { + if (resultType instanceof TypeArray) { + TypeArray arrayType = (TypeArray) resultType; + int delta = PseudoRandom.randomNotZero(ProductionParams.dimensionsLimit.value() + - arrayType.dimensions); + if (arrayType.dimensions + delta <= ProductionParams.dimensionsLimit.value()) { + long arrayComplLimit = (long) (complexityLimit * 0.5 * PseudoRandom.random()); + int arrayOpLimit = (int) (operatorLimit * 0.5 * PseudoRandom.random()); + IRNodeBuilder builder = new IRNodeBuilder().setOwnerKlass(ownerClass) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts); + IRNode arrayReturningExpression = builder + .setComplexityLimit(arrayComplLimit) + .setOperatorLimit(arrayOpLimit) + .setResultType(new TypeArray(arrayType.type, arrayType.dimensions + delta)) + .getExpressionFactory().produce(); + ArrayList perDimensionExpression = new ArrayList<>(delta); + long dimComplLimit = (long) ((complexityLimit - arrayComplLimit) + * PseudoRandom.random()) / delta; + int dimOpLimit = (int) ((operatorLimit - arrayOpLimit - delta) + * PseudoRandom.random()) / delta; + double chanceExpression = ProductionParams.chanceExpressionIndex.value() / 100.; + for (int i = 0; i < delta; i++) { + if (PseudoRandom.randomBoolean(chanceExpression)) { + perDimensionExpression.add(builder.setResultType(new TypeByte()) + .setComplexityLimit(dimComplLimit) + .setOperatorLimit(dimOpLimit) + .getExpressionFactory() + .produce()); + } else { + byte dimLimit = 0; + if (arrayReturningExpression instanceof ArrayCreation) { + ArrayCreation arratCreation = (ArrayCreation) arrayReturningExpression; + dimLimit = arratCreation.getDimensionSize(i); + } else if (arrayReturningExpression instanceof ArrayExtraction) { + ArrayExtraction arrayExtraction = (ArrayExtraction) arrayReturningExpression; + if (i < arrayExtraction.getDimsNumber()) + dimLimit = arrayExtraction.getDim(i); + } + perDimensionExpression.add(new Literal(PseudoRandom.randomNotNegative(dimLimit), new TypeByte())); + } + } + return new ArrayExtraction(arrayReturningExpression, perDimensionExpression); + } + } + throw new ProductionFailedException(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/AssignmentOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/AssignmentOperatorFactory.java new file mode 100644 index 00000000000..6563ed857c4 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/AssignmentOperatorFactory.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Rule; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class AssignmentOperatorFactory extends Factory { + private final int operatorLimit; + private final long complexityLimit; + private final Type resultType; + private final boolean exceptionSafe; + private final boolean noconsts; + private final TypeKlass ownerClass; + + private Rule fillRule(Type resultType) throws ProductionFailedException { + Rule rule = new Rule("assignment"); + IRNodeBuilder builder = new IRNodeBuilder() + .setComplexityLimit(complexityLimit) + .setOperatorLimit(operatorLimit) + .setOwnerKlass(ownerClass) + .setResultType(resultType) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts); + rule.add("simple_assign", builder.setOperatorKind(OperatorKind.ASSIGN).getBinaryOperatorFactory()); + rule.add("compound_add", builder.setOperatorKind(OperatorKind.COMPOUND_ADD).getBinaryOperatorFactory()); + rule.add("compound_sub", builder.setOperatorKind(OperatorKind.COMPOUND_SUB).getBinaryOperatorFactory()); + rule.add("compound_mul", builder.setOperatorKind(OperatorKind.COMPOUND_MUL).getBinaryOperatorFactory()); + if (!exceptionSafe) { + rule.add("compound_div", builder.setOperatorKind(OperatorKind.COMPOUND_DIV).getBinaryOperatorFactory()); + rule.add("compound_mod", builder.setOperatorKind(OperatorKind.COMPOUND_MOD).getBinaryOperatorFactory()); + } + rule.add("compound_and", builder.setOperatorKind(OperatorKind.COMPOUND_AND).getBinaryOperatorFactory()); + rule.add("compound_or", builder.setOperatorKind(OperatorKind.COMPOUND_OR).getBinaryOperatorFactory()); + rule.add("compound_xor", builder.setOperatorKind(OperatorKind.COMPOUND_XOR).getBinaryOperatorFactory()); + rule.add("compound_shr", builder.setOperatorKind(OperatorKind.COMPOUND_SHR).getBinaryOperatorFactory()); + rule.add("compound_sar", builder.setOperatorKind(OperatorKind.COMPOUND_SAR).getBinaryOperatorFactory()); + rule.add("compound_shl", builder.setOperatorKind(OperatorKind.COMPOUND_SHL).getBinaryOperatorFactory()); + + rule.add("prefix_inc", builder.setOperatorKind(OperatorKind.PRE_INC).getUnaryOperatorFactory()); + rule.add("prefix_dec", builder.setOperatorKind(OperatorKind.PRE_DEC).getUnaryOperatorFactory()); + rule.add("postfix_inc", builder.setOperatorKind(OperatorKind.POST_INC).getUnaryOperatorFactory()); + rule.add("postfix_dec", builder.setOperatorKind(OperatorKind.POST_DEC).getUnaryOperatorFactory()); + return rule; + } + + AssignmentOperatorFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, + Type resultType, boolean exceptionSafe, boolean noconsts) { + this.ownerClass = ownerClass; + this.complexityLimit = complexityLimit; + this.operatorLimit = operatorLimit; + this.resultType = resultType; + this.exceptionSafe = exceptionSafe; + this.noconsts = noconsts; + } + + @Override + public IRNode produce() throws ProductionFailedException { + if (resultType == null) { // if no result type is given - choose any. + ArrayList allTypes = new ArrayList<>(TypeList.getAll()); + PseudoRandom.shuffle(allTypes); + for (Type type : allTypes) { + SymbolTable.push(); + try { + IRNode result = fillRule(type).produce(); + SymbolTable.merge(); + return result; + } catch (ProductionFailedException e) { + SymbolTable.pop(); + } + } + } else { + return fillRule(resultType).produce(); + } + throw new ProductionFailedException(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/AssignmentOperatorImplFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/AssignmentOperatorImplFactory.java new file mode 100644 index 00000000000..2658be21303 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/AssignmentOperatorImplFactory.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.Pair; +import jdk.test.lib.jittester.BinaryOperator; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Rule; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.TypeUtil; +import jdk.test.lib.jittester.VariableBase; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class AssignmentOperatorImplFactory extends BinaryOperatorFactory { + AssignmentOperatorImplFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, + Type resultType, boolean exceptionSafe, boolean noconsts) { + super(OperatorKind.ASSIGN, complexityLimit, operatorLimit, ownerClass, resultType, exceptionSafe, noconsts); + } + + @Override + protected boolean isApplicable(Type resultType) { + return true; + } + + @Override + protected Pair generateTypes() throws ProductionFailedException { + return new Pair<>(resultType, PseudoRandom.randomElement( + TypeUtil.getImplicitlyCastable(TypeList.getAll(), resultType))); + } + + @Override + protected BinaryOperator generateProduction(Type leftOperandType, Type rightOperandType) + throws ProductionFailedException { + long leftComplexityLimit = (long) (PseudoRandom.random() * complexityLimit); + long rightComplexityLimit = complexityLimit - leftComplexityLimit; + int leftOperatorLimit = (int) (PseudoRandom.random() * operatorLimit); + int rightOperatorLimit = operatorLimit = leftOperatorLimit; + IRNodeBuilder builder = new IRNodeBuilder().setOwnerKlass((TypeKlass) ownerClass) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts) + .setComplexityLimit(leftComplexityLimit) + .setOperatorLimit(leftOperatorLimit) + .setResultType(leftOperandType) + .setIsConstant(false); + Rule rule = new Rule("assignment"); + rule.add("initialized_nonconst_var", builder.setIsInitialized(true).getVariableFactory()); + rule.add("uninitialized_nonconst_var", builder.setIsInitialized(false).getVariableFactory()); + IRNode leftOperandValue = rule.produce(); + IRNode rightOperandValue = builder.setComplexityLimit(rightComplexityLimit) + .setOperatorLimit(rightOperatorLimit) + .setResultType(rightOperandType) + .getExpressionFactory() + .produce(); + try { + VariableBase v = (VariableBase) leftOperandValue; + if ((v.get().flags & VariableInfo.INITIALIZED) == 0) { + v.get().flags |= VariableInfo.INITIALIZED; + } + } catch (Exception e) { + throw new ProductionFailedException(e.getMessage()); + } + return new BinaryOperator(opKind, leftOperandValue, rightOperandValue); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryArithmeticOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryArithmeticOperatorFactory.java new file mode 100644 index 00000000000..96f9e1842a6 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryArithmeticOperatorFactory.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.Pair; +import jdk.test.lib.jittester.BuiltInType; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.TypeUtil; +import jdk.test.lib.jittester.types.TypeInt; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +import java.util.Collection; + +class BinaryArithmeticOperatorFactory extends BinaryOperatorFactory { + BinaryArithmeticOperatorFactory(OperatorKind opKind, long complexityLimit, int operatorLimit, + TypeKlass ownerClass, Type resultType, boolean exceptionSafe, boolean noconsts) { + super(opKind, complexityLimit, operatorLimit, ownerClass, resultType, exceptionSafe, noconsts); + } + + @Override + protected boolean isApplicable(Type resultType) { + // arithmetic for built-in types less capacious than "int" is not supported. + if (TypeList.isBuiltIn(resultType)) { + BuiltInType builtInType = (BuiltInType) resultType; + return builtInType.equals(new TypeInt()) || builtInType.isMoreCapaciousThan(new TypeInt()); + } else { + return false; + } + } + + @Override + protected Pair generateTypes() throws ProductionFailedException { + Collection castableFromResultType = TypeUtil.getImplicitlyCastable(TypeList.getBuiltIn(), resultType); + // built-in types less capacious than int are automatically casted to int in arithmetic. + final Type leftType = PseudoRandom.randomElement(castableFromResultType); + final Type rightType = resultType.equals(new TypeInt()) ? + PseudoRandom.randomElement(castableFromResultType) : resultType; + //TODO: is there sense to swap them randomly as it was done in original code? + return PseudoRandom.randomBoolean() ? new Pair<>(leftType, rightType) : new Pair<>(rightType, leftType); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryBitwiseOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryBitwiseOperatorFactory.java new file mode 100644 index 00000000000..288eac1814f --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryBitwiseOperatorFactory.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.Pair; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.TypeUtil; +import jdk.test.lib.jittester.types.TypeBoolean; +import jdk.test.lib.jittester.types.TypeInt; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeLong; +import jdk.test.lib.jittester.utils.PseudoRandom; + +import java.util.Collection; + +class BinaryBitwiseOperatorFactory extends BinaryOperatorFactory { + BinaryBitwiseOperatorFactory(OperatorKind opKind, long complexityLimit, int operatorLimit, + TypeKlass ownerClass, Type resultType, boolean exceptionSafe, boolean noconsts) { + super(opKind, complexityLimit, operatorLimit, ownerClass, resultType, exceptionSafe, noconsts); + } + + @Override + protected boolean isApplicable(Type resultType) { + return resultType.equals(new TypeInt()) || resultType.equals(new TypeLong()) || resultType.equals(new TypeBoolean()); + } + + @Override + protected Pair generateTypes() throws ProductionFailedException { + Collection castableFromResult = TypeUtil.getImplicitlyCastable(TypeList.getBuiltIn(), resultType); + // built-in types less capacious than int are automatically casted to int in arithmetic. + final Type leftType = PseudoRandom.randomElement(castableFromResult); + final Type rightType = resultType.equals(new TypeInt()) ? PseudoRandom.randomElement(castableFromResult) : resultType; + //TODO: is there sense to swap them randomly as it was done in original code? + return PseudoRandom.randomBoolean() ? new Pair<>(leftType, rightType) : new Pair<>(rightType, leftType); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryComparisonOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryComparisonOperatorFactory.java new file mode 100644 index 00000000000..4f5150e86f4 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryComparisonOperatorFactory.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.Pair; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.types.TypeBoolean; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +import java.util.ArrayList; +import java.util.List; + +class BinaryComparisonOperatorFactory extends BinaryOperatorFactory { + BinaryComparisonOperatorFactory(OperatorKind opKind, long complexityLimit, int operatorLimit, + TypeKlass ownerClass, Type resultType, boolean exceptionSafe, boolean noconsts) { + super(opKind, complexityLimit, operatorLimit, ownerClass, resultType, exceptionSafe, noconsts); + } + + @Override + protected boolean isApplicable(Type resultType) { + return resultType.equals(new TypeBoolean()); + } + + @Override + protected Pair generateTypes() throws ProductionFailedException { + final List builtInExceptBoolean = new ArrayList<>(TypeList.getBuiltIn()); + builtInExceptBoolean.remove(new TypeBoolean()); + return new Pair<>(PseudoRandom.randomElement(builtInExceptBoolean), + PseudoRandom.randomElement(builtInExceptBoolean)); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryEqualityOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryEqualityOperatorFactory.java new file mode 100644 index 00000000000..e3458c3463e --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryEqualityOperatorFactory.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.Pair; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.types.TypeBoolean; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +import java.util.ArrayList; +import java.util.List; + +class BinaryEqualityOperatorFactory extends BinaryOperatorFactory { + BinaryEqualityOperatorFactory(OperatorKind opKind, long complexityLimit, int operatorLimit, + TypeKlass ownerClass, Type resultType, boolean exceptionSafe, boolean noconsts) { + super(opKind, complexityLimit, operatorLimit, ownerClass, resultType, exceptionSafe, noconsts); + } + + @Override + protected boolean isApplicable(Type resultType) { + return resultType.equals(new TypeBoolean()); + } + + @Override + protected Pair generateTypes() throws ProductionFailedException { + final List builtInExceptBoolean = new ArrayList<>(TypeList.getBuiltIn()); + builtInExceptBoolean.remove(new TypeBoolean()); + return new Pair<>(PseudoRandom.randomElement(builtInExceptBoolean), PseudoRandom.randomElement(builtInExceptBoolean)); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryLogicOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryLogicOperatorFactory.java new file mode 100644 index 00000000000..2ccf715edff --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryLogicOperatorFactory.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.Pair; +import jdk.test.lib.jittester.BinaryOperator; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.types.TypeBoolean; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +public class BinaryLogicOperatorFactory extends BinaryOperatorFactory { + BinaryLogicOperatorFactory(OperatorKind opKind, long complexityLimit, int operatorLimit, + TypeKlass ownerClass, Type resultType, boolean exceptionSafe, boolean noconsts) { + super(opKind, complexityLimit, operatorLimit, ownerClass, resultType, exceptionSafe, noconsts); + } + + @Override + protected boolean isApplicable(Type resultType) { + return resultType.equals(new TypeBoolean()); + } + + @Override + protected Pair generateTypes() throws ProductionFailedException { + return new Pair<>(resultType, resultType); + } + + @Override + protected BinaryOperator generateProduction(Type leftType, Type rightType) throws ProductionFailedException { + int leftOpLimit = (int) (PseudoRandom.random() * (operatorLimit - 1)); + int rightOpLimit = operatorLimit - 1 - leftOpLimit; + long leftComplLimit = (long) (PseudoRandom.random() * (complexityLimit - 1)); + long rightComplLimit = complexityLimit - 1 - leftComplLimit; + if (leftOpLimit == 0 || rightOpLimit == 0 || leftComplLimit == 0 || rightComplLimit == 0) { + throw new ProductionFailedException(); + } + boolean swap = PseudoRandom.randomBoolean(); + IRNodeBuilder builder = new IRNodeBuilder().setOwnerKlass((TypeKlass) ownerClass) + .setExceptionSafe(exceptionSafe); + IRNode leftOperand = builder.setComplexityLimit(leftComplLimit) + .setOperatorLimit(leftOpLimit) + .setResultType(leftType) + .setNoConsts(swap && noconsts) + .getExpressionFactory() + .produce(); + // Right branch won't necessarily execute. Ignore initalization performed in it. + SymbolTable.push(); + IRNode rightOperand; + try { + rightOperand = builder.setComplexityLimit(rightComplLimit) + .setOperatorLimit(rightOpLimit) + .setResultType(rightType) + .setNoConsts(!swap && noconsts) + .getExpressionFactory() + .produce(); + } finally { + SymbolTable.pop(); + } + return new BinaryOperator(opKind, leftOperand, rightOperand); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryOperatorFactory.java new file mode 100644 index 00000000000..1f519d920e8 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryOperatorFactory.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.Pair; +import jdk.test.lib.jittester.BinaryOperator; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +abstract class BinaryOperatorFactory extends OperatorFactory { + protected final OperatorKind opKind; + protected final Type resultType; + protected final Type ownerClass; + + protected BinaryOperatorFactory(OperatorKind opKind, long complexityLimit, int operatorLimit, + Type ownerClass, Type resultType, boolean exceptionSafe, boolean noconsts) { + super(opKind.priority, complexityLimit, operatorLimit, exceptionSafe, noconsts); + this.opKind = opKind; + this.resultType = resultType; + this.ownerClass = ownerClass; + } + + protected abstract boolean isApplicable(Type resultType); + + protected abstract Pair generateTypes() throws ProductionFailedException; + + protected BinaryOperator generateProduction(Type leftType, Type rightType) throws ProductionFailedException { + int leftOpLimit = (int) (PseudoRandom.random() * (operatorLimit - 1)); + int rightOpLimit = operatorLimit - 1 - leftOpLimit; + long leftComplLimit = (long) (PseudoRandom.random() * (complexityLimit - 1)); + long rightComplLimit = complexityLimit - 1 - leftComplLimit; + if (leftOpLimit == 0 || rightOpLimit == 0 || leftComplLimit == 0 || rightComplLimit == 0) { + throw new ProductionFailedException(); + } + boolean swap = PseudoRandom.randomBoolean(); + IRNodeBuilder builder = new IRNodeBuilder().setExceptionSafe(exceptionSafe) + .setOwnerKlass((TypeKlass) ownerClass) + .setNoConsts(!swap && noconsts); + IRNode leftExpr = builder.setComplexityLimit(leftComplLimit) + .setOperatorLimit(leftOpLimit) + .setResultType(leftType) + .getExpressionFactory() + .produce(); + IRNode rightExpr = builder.setComplexityLimit(rightComplLimit) + .setOperatorLimit(rightOpLimit) + .setResultType(rightType) + .getExpressionFactory() + .produce(); + return new BinaryOperator(opKind, leftExpr, rightExpr); + } + + @Override + public final IRNode produce() throws ProductionFailedException { + if (!isApplicable(resultType)) { + //avoid implicit use of resultType.toString() + throw new ProductionFailedException("Type " + resultType.getName() + " is not applicable by " + getClass().getName()); + } + + Pair types; + try { + types = generateTypes(); + } catch (RuntimeException ex) { + throw new ProductionFailedException(ex.getMessage()); + } + + try { + SymbolTable.push(); + IRNode p = generateProduction(types.first, types.second); + SymbolTable.merge(); + return p; + } catch (ProductionFailedException e) { + SymbolTable.pop(); + throw e; + } + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryShiftOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryShiftOperatorFactory.java new file mode 100644 index 00000000000..81ffbd9cece --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryShiftOperatorFactory.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.Pair; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.TypeUtil; +import jdk.test.lib.jittester.types.TypeInt; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeLong; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class BinaryShiftOperatorFactory extends BinaryOperatorFactory { + BinaryShiftOperatorFactory(OperatorKind opKind, long complexityLimit, int operatorLimit, + TypeKlass ownerClass, Type resultType, boolean exceptionSafe, boolean noconsts) { + super(opKind, complexityLimit, operatorLimit, ownerClass, resultType, exceptionSafe, noconsts); + } + + @Override + protected boolean isApplicable(Type resultType) { + return resultType.equals(new TypeInt()) || resultType.equals(new TypeLong()); + } + + @Override + protected Pair generateTypes() throws ProductionFailedException { + Type leftType = resultType.equals(new TypeInt()) ? PseudoRandom.randomElement(TypeUtil.getImplicitlyCastable(TypeList.getBuiltInInt(), resultType)) : resultType; + Type rightType = PseudoRandom.randomElement(TypeUtil.getImplicitlyCastable(TypeList.getBuiltInInt(), new TypeLong())); + return new Pair<>(leftType, rightType); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryStringPlusFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryStringPlusFactory.java new file mode 100644 index 00000000000..99de01cde91 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BinaryStringPlusFactory.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.Pair; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.types.TypeKlass; + +class BinaryStringPlusFactory extends BinaryOperatorFactory { + BinaryStringPlusFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, + Type resultType, boolean exceptionSafe, boolean noconsts) { + super(OperatorKind.STRADD, complexityLimit, operatorLimit, ownerClass, resultType, exceptionSafe, noconsts); + } + + @Override + protected boolean isApplicable(Type resultType) { + return resultType.equals(TypeList.find("java.lang.String")); + } + + @Override + protected Pair generateTypes() throws ProductionFailedException { + return new Pair<>(resultType, resultType); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BitwiseInversionOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BitwiseInversionOperatorFactory.java new file mode 100644 index 00000000000..6ed509f6728 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BitwiseInversionOperatorFactory.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.TypeUtil; +import jdk.test.lib.jittester.UnaryOperator; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeInt; +import jdk.test.lib.jittester.types.TypeLong; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class BitwiseInversionOperatorFactory extends UnaryOperatorFactory { + BitwiseInversionOperatorFactory(long complexityLimit, int operatorLimit, Type ownerClass, + Type resultType, boolean exceptionSafe, boolean noconsts) { + super(OperatorKind.BIT_NOT, complexityLimit, operatorLimit, ownerClass, resultType, exceptionSafe, noconsts); + } + + @Override + protected boolean isApplicable(Type resultType) { + return resultType.equals(new TypeInt()) || resultType.equals(new TypeLong()); + } + + @Override + protected Type generateType() throws ProductionFailedException { + if (resultType.equals(new TypeInt())) { + return PseudoRandom.randomElement(TypeUtil.getImplicitlyCastable(TypeList.getBuiltIn(), resultType)); + } else { + return resultType; + } + } + + @Override + protected IRNode generateProduction(Type resultType) throws ProductionFailedException { + return new UnaryOperator(opKind, new IRNodeBuilder().setComplexityLimit(complexityLimit - 1) + .setOperatorLimit(operatorLimit - 1) + .setOwnerKlass((TypeKlass) ownerClass) + .setResultType(resultType) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts) + .getExpressionFactory() + .produce()); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BitwiseOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BitwiseOperatorFactory.java new file mode 100644 index 00000000000..bc9acdc1dc5 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BitwiseOperatorFactory.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Rule; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.types.TypeKlass; + +class BitwiseOperatorFactory extends Factory { + private final Rule rule; + + BitwiseOperatorFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, + Type resultType, boolean exceptionSafe, boolean noconsts) throws ProductionFailedException { + IRNodeBuilder builder = new IRNodeBuilder() + .setComplexityLimit(complexityLimit) + .setOperatorLimit(operatorLimit) + .setOwnerKlass(ownerClass) + .setResultType(resultType) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts); + rule = new Rule("bitwise"); + rule.add("and", builder.setOperatorKind(OperatorKind.BIT_AND).getBinaryOperatorFactory()); + rule.add("or", builder.setOperatorKind(OperatorKind.BIT_OR).getBinaryOperatorFactory()); + rule.add("xor", builder.setOperatorKind(OperatorKind.BIT_XOR).getBinaryOperatorFactory()); + rule.add("not", builder.setOperatorKind(OperatorKind.BIT_NOT).getUnaryOperatorFactory()); + rule.add("shl", builder.setOperatorKind(OperatorKind.SHL).getBinaryOperatorFactory()); + rule.add("shr", builder.setOperatorKind(OperatorKind.SHR).getBinaryOperatorFactory()); + rule.add("sar", builder.setOperatorKind(OperatorKind.SAR).getBinaryOperatorFactory()); + } + + @Override + public IRNode produce() throws ProductionFailedException { + return rule.produce(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BlockFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BlockFactory.java new file mode 100644 index 00000000000..a47f4ae36cb --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BlockFactory.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.Block; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.If; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Rule; +import jdk.test.lib.jittester.Switch; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.TypeUtil; +import jdk.test.lib.jittester.loops.DoWhile; +import jdk.test.lib.jittester.loops.For; +import jdk.test.lib.jittester.loops.While; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeVoid; +import jdk.test.lib.jittester.utils.PseudoRandom; + +import java.util.ArrayList; +import java.util.List; + +class BlockFactory extends Factory { + private final Type returnType; + private final long complexityLimit; + private final int statementLimit; + private final int operatorLimit; + private final boolean subBlock; + private final boolean canHaveBreaks; + private final boolean canHaveContinues; + private final boolean canHaveReturn; + private final boolean canHaveThrow; + private final int level; + private final TypeKlass ownerClass; + + BlockFactory(TypeKlass klass, Type returnType, long complexityLimit, int statementLimit, + int operatorLimit, int level, boolean subBlock, boolean canHaveBreaks, + boolean canHaveContinues, boolean canHaveReturn, boolean canHaveThrows) { + this.ownerClass = klass; + this.returnType = returnType; + this.complexityLimit = complexityLimit; + this.statementLimit = statementLimit; + this.operatorLimit = operatorLimit; + this.level = level; + this.subBlock = subBlock; + this.canHaveBreaks = canHaveBreaks; + this.canHaveContinues = canHaveContinues; + this.canHaveReturn = canHaveReturn; + this.canHaveThrow = canHaveThrows; + } + + @Override + public IRNode produce() throws ProductionFailedException { + if (statementLimit > 0 && complexityLimit > 0) { + List content = new ArrayList<>(); + int slimit = PseudoRandom.randomNotZero(statementLimit); + long climit = complexityLimit; + IRNodeBuilder builder = new IRNodeBuilder() + .setOperatorLimit(operatorLimit) + .setOwnerKlass(ownerClass) + .setResultType(returnType) + .setCanHaveReturn(canHaveReturn) + .setCanHaveThrow(canHaveThrow) + .setCanHaveBreaks(canHaveBreaks) + .setCanHaveContinues(canHaveContinues) + .setExceptionSafe(false) + .setNoConsts(false); + Rule rule; + SymbolTable.push(); + for (int i = 0; i < slimit && climit > 0; ) { + int subLimit = (int) (PseudoRandom.random() * (slimit - i - 1)); + builder.setComplexityLimit((long) (PseudoRandom.random() * climit)); + rule = new Rule("block"); + rule.add("statement", builder.getStatementFactory(), 5); + if (!ProductionParams.disableVarsInBlock.value()) { + rule.add("decl", builder.setIsLocal(true).getDeclarationFactory()); + } + if (subLimit > 0) { + builder.setStatementLimit(subLimit).setLevel(level + 1); + if (!ProductionParams.disableNestedBlocks.value()) { + rule.add("block", builder.setCanHaveReturn(false) + .setCanHaveThrow(false) + .setCanHaveBreaks(false) + .setCanHaveContinues(false) + .getBlockFactory()); + rule.add("try-catch", builder.getTryCatchBlockFactory(), 0.3); + builder.setCanHaveReturn(canHaveReturn) + .setCanHaveThrow(canHaveThrow) + .setCanHaveBreaks(canHaveBreaks) + .setCanHaveContinues(canHaveContinues); + } + addControlFlowDeviation(rule, builder); + } + try { + IRNode choiceResult = rule.produce(); + if (choiceResult instanceof If || choiceResult instanceof While || choiceResult instanceof DoWhile + || choiceResult instanceof For || choiceResult instanceof Switch) { + i += subLimit; + } else { + i++; + } + //climit -= subBlockComplLimit; // very approximate. to obnain a precise value, change to p.complexity() + climit -= choiceResult.complexity(); + content.add(choiceResult); + } catch (ProductionFailedException e) { + i++; + } + } + // Ok, if the block can end with break and continue. Generate the appropriate productions. + rule = new Rule("block_ending"); + if (canHaveBreaks && !subBlock) { + rule.add("break", builder.getBreakFactory()); + } + if (canHaveContinues && !subBlock) { + rule.add("continue", builder.getContinueFactory()); + } + if (canHaveReturn && !subBlock && !returnType.equals(new TypeVoid())) { + rule.add("return", builder.setComplexityLimit(climit).getReturnFactory()); + } + if (canHaveThrow && !subBlock) { + Type rtException = TypeList.find("java.lang.RuntimeException"); + rtException = PseudoRandom.randomElement(TypeUtil.getImplicitlyCastable(TypeList.getAll(), rtException)); + rule.add("throw", builder.setResultType(rtException) + .setComplexityLimit(Math.max(climit, 5)) + .setOperatorLimit(Math.max(operatorLimit, 5)) + .getThrowFactory()); + } + + try { + if (rule.size() > 0) { + content.add(rule.produce()); + } + } catch (ProductionFailedException e) { + } + if (!subBlock) { + SymbolTable.pop(); + } else { + SymbolTable.merge(); + } + return new Block(ownerClass, returnType, content, level); + } + throw new ProductionFailedException(); + } + + private void addControlFlowDeviation(Rule rule, IRNodeBuilder builder) { + if (!ProductionParams.disableIf.value()) { + rule.add("if", builder.getIfFactory()); + } + if (!ProductionParams.disableWhile.value()) { + rule.add("while", builder.getWhileFactory()); + } + if (!ProductionParams.disableDoWhile.value()) { + rule.add("do_while", builder.getDoWhileFactory()); + } + if (!ProductionParams.disableFor.value()) { + rule.add("for", builder.getForFactory()); + } + if (!ProductionParams.disableSwitch.value()) { + rule.add("switch", builder.getSwitchFactory(), 0.1); + } + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BreakFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BreakFactory.java new file mode 100644 index 00000000000..a03874e3612 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/BreakFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.Break; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; + +class BreakFactory extends Factory { + @Override + public IRNode produce() throws ProductionFailedException { + return new Break(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CastOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CastOperatorFactory.java new file mode 100644 index 00000000000..7935eb697aa --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CastOperatorFactory.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import jdk.test.lib.jittester.CastOperator; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class CastOperatorFactory extends OperatorFactory { + private final Type resultType; + private final Type ownerClass; + + CastOperatorFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, + Type resultType, boolean exceptionSafe, boolean noconsts) { + super(13, complexityLimit, operatorLimit, exceptionSafe, noconsts); + this.resultType = resultType; + this.ownerClass = ownerClass; + } + + @Override + public IRNode produce() throws ProductionFailedException { + ArrayList argType = new ArrayList<>(TypeList.getAll()); + PseudoRandom.shuffle(argType); + for (Type type : argType) { + try { + ExpressionFactory expressionFactory = new IRNodeBuilder() + .setComplexityLimit(complexityLimit - 1) + .setOperatorLimit(operatorLimit - 1) + .setOwnerKlass((TypeKlass) ownerClass) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts) + .setResultType(type) + .getExpressionFactory(); + SymbolTable.push(); + if (type.equals(resultType)) { + IRNode expr = expressionFactory.produce(); + SymbolTable.merge(); + return expr; + } else if ((!exceptionSafe || exceptionSafe && !(type instanceof TypeKlass)) + && type.canExplicitlyCastTo(resultType)) { + // In safe mode we cannot explicitly cast an object, because it may throw. + IRNode castOperator = new CastOperator(resultType, expressionFactory.produce()); + SymbolTable.merge(); + return castOperator; + } + throw new ProductionFailedException(); + } catch (ProductionFailedException e) { + SymbolTable.pop(); + } + } + throw new ProductionFailedException(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ClassDefinitionBlockFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ClassDefinitionBlockFactory.java new file mode 100644 index 00000000000..28586eb67cd --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ClassDefinitionBlockFactory.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; +import jdk.test.lib.jittester.Block; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Rule; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.classes.ClassDefinitionBlock; +import jdk.test.lib.jittester.classes.Klass; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class ClassDefinitionBlockFactory extends Factory { + private final String prefix; + private final long complexityLimit; + private final int classesLimit; + private final int statementLimit; + private final int operatorLimit; + private final int memberFunctionsLimit; + private final int memberFunctionsArgLimit; + private final int level; + + ClassDefinitionBlockFactory(String prefix, int classesLimit, int memberFunctionsLimit, + int memberFunctionsArgLimit, long complexityLimit, int statementLimit, + int operatorLimit, int level) { + this.prefix = prefix; + this.classesLimit = classesLimit; + this.memberFunctionsLimit = memberFunctionsLimit; + this.memberFunctionsArgLimit = memberFunctionsArgLimit; + this.complexityLimit = complexityLimit; + this.statementLimit = statementLimit; + this.operatorLimit = operatorLimit; + this.level = level; + } + + @Override + public IRNode produce() throws ProductionFailedException { + ArrayList content = new ArrayList<>(); + int limit = (int) Math.ceil(PseudoRandom.random() * classesLimit); + if (limit > 0) { + long classCompl = complexityLimit / limit; + IRNodeBuilder builder = new IRNodeBuilder().setLevel(level) + .setMemberFunctionsArgLimit(memberFunctionsArgLimit) + .setStatementLimit(statementLimit) + .setOperatorLimit(operatorLimit) + .setComplexityLimit(classCompl); + for (int i = 0; i < limit; i++) { + try { + Rule rule = new Rule("class"); + rule.add("basic_class", builder.setName(prefix + "_Class_" + i) + .setPrinterName(prefix + ".Printer") + .setMemberFunctionsLimit(memberFunctionsLimit) + .getKlassFactory()); + if (!ProductionParams.disableInterfaces.value()) { + rule.add("interface", builder.setName(prefix + "_Interface_" + i) + .setMemberFunctionsLimit((int) (memberFunctionsLimit * 0.2)) + .getInterfaceFactory(), 0.1); + } + // TODO: Add enums + content.add(rule.produce()); + } catch (ProductionFailedException e) { + } + } + } + ensureMinDepth(content); + ensureMaxDepth(content); + return new ClassDefinitionBlock(content, level); + } + + private void ensureMinDepth(Collection content) throws ProductionFailedException { + int minDepth = ProductionParams.minCfgDepth.value(); + List childs = content.stream() + .filter(c -> c instanceof Klass) + .collect(Collectors.toList()); + addMoreChildren(childs, content, minDepth); + } + + private void addMoreChildren(List childs, Collection content, int minDepth) + throws ProductionFailedException { + while (!childs.isEmpty() && IRNode.countDepth(content) < minDepth) { + PseudoRandom.shuffle(childs); + IRNode randomChild = childs.get(0); + List leaves = randomChild.getStackableLeaves(); + if (!leaves.isEmpty()) { + PseudoRandom.shuffle(leaves); + Block randomLeaf = (Block) leaves.get(0); + TypeKlass klass = (TypeKlass) randomChild.getKlass(); + int newLevel = randomLeaf.getLevel() + 1; + Type retType = randomLeaf.getReturnType(); + IRNodeBuilder b = new IRNodeBuilder() + .setOwnerKlass(klass) + .setResultType(retType) + .setComplexityLimit(complexityLimit) + .setStatementLimit(statementLimit) + .setOperatorLimit(operatorLimit) + .setLevel(newLevel); + IRNode newBlock = b.getBlockFactory().produce(); + List siblings = randomLeaf.getChildren(); + // to avoid break; + int index = PseudoRandom.randomNotZero(siblings.size() - 1); + siblings.add(index, newBlock); + } + } + } + + private void ensureMaxDepth(Collection content) { + int maxDepth = ProductionParams.maxCfgDepth.value(); + List childs = content.stream() + .filter(c -> c instanceof Klass && c.countDepth() > maxDepth) + .collect(Collectors.toList()); + for (IRNode ch : childs) { + List leaves = null; + do { + long depth = Math.max(ch.countDepth(), maxDepth + 1); + leaves = ch.getDeviantBlocks(depth); + if(leaves.size() > 0) { + leaves.get(0).removeSelf(); + } + } while (!leaves.isEmpty() && ch.countDepth() > maxDepth); + } + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CompoundArithmeticAssignmentOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CompoundArithmeticAssignmentOperatorFactory.java new file mode 100644 index 00000000000..a8d934c254a --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CompoundArithmeticAssignmentOperatorFactory.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.Pair; +import jdk.test.lib.jittester.BinaryOperator; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.TypeUtil; +import jdk.test.lib.jittester.types.TypeBoolean; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class CompoundArithmeticAssignmentOperatorFactory extends BinaryOperatorFactory { + CompoundArithmeticAssignmentOperatorFactory(OperatorKind opKind, long complexityLimit, + int operatorLimit, TypeKlass ownerClass, Type resultType, boolean exceptionSafe, boolean noconsts) { + super(opKind, complexityLimit, operatorLimit, ownerClass, resultType, exceptionSafe, noconsts); + } + + @Override + protected boolean isApplicable(Type resultType) { + return TypeList.isBuiltIn(resultType) && !resultType.equals(new TypeBoolean()); + } + + @Override + protected Pair generateTypes() throws ProductionFailedException { + return new Pair<>(resultType, PseudoRandom.randomElement( + TypeUtil.getExplicitlyCastable(TypeList.getBuiltIn(), resultType))); + } + + @Override + protected BinaryOperator generateProduction(Type leftType, Type rightType) throws ProductionFailedException { + long leftComplexityLimit = (long) (PseudoRandom.random() * complexityLimit); + long rightComplexityLimit = complexityLimit - leftComplexityLimit; + int leftOperatorLimit = (int) (PseudoRandom.random() * operatorLimit); + int rightOperatorLimit = operatorLimit = leftOperatorLimit; + IRNodeBuilder builder = new IRNodeBuilder().setOwnerKlass((TypeKlass) ownerClass) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts); + IRNode rightExpr = builder.setComplexityLimit(rightComplexityLimit) + .setOperatorLimit(rightOperatorLimit) + .setResultType(rightType) + .getExpressionFactory() + .produce(); + IRNode leftExpr = builder.setComplexityLimit(leftComplexityLimit) + .setOperatorLimit(leftOperatorLimit) + .setResultType(leftType) + .setIsConstant(false) + .setIsInitialized(true) + .getVariableFactory() + .produce(); + return new BinaryOperator(opKind, leftExpr, rightExpr); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CompoundBitwiseAssignmentOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CompoundBitwiseAssignmentOperatorFactory.java new file mode 100644 index 00000000000..1752b0c5c77 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CompoundBitwiseAssignmentOperatorFactory.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.Pair; +import jdk.test.lib.jittester.BinaryOperator; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.TypeUtil; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class CompoundBitwiseAssignmentOperatorFactory extends BinaryOperatorFactory { + CompoundBitwiseAssignmentOperatorFactory(OperatorKind opKind, long complexityLimit, + int operatorLimit, TypeKlass ownerClass, Type resultType, boolean exceptionSafe, boolean noconsts) { + super(opKind, complexityLimit, operatorLimit, ownerClass, resultType, exceptionSafe, noconsts); + } + + @Override + protected boolean isApplicable(Type resultType) { + return TypeList.isBuiltInInt(resultType); + } + + @Override + protected Pair generateTypes() throws ProductionFailedException { + return new Pair<>(resultType, PseudoRandom.randomElement(TypeUtil.getExplicitlyCastable(TypeList.getBuiltInInt(), resultType))); + } + + @Override + protected BinaryOperator generateProduction(Type leftType, Type rightType) throws ProductionFailedException { + long leftComplexityLimit = (long) (PseudoRandom.random() * complexityLimit); + long rightComplexityLimit = complexityLimit - leftComplexityLimit; + int leftOperatorLimit = (int) (PseudoRandom.random() * operatorLimit); + int rightOperatorLimit = operatorLimit = leftOperatorLimit; + IRNodeBuilder builder = new IRNodeBuilder().setOwnerKlass((TypeKlass) ownerClass) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts); + IRNode leftExpr = builder.setComplexityLimit(leftComplexityLimit) + .setOperatorLimit(leftOperatorLimit) + .setResultType(leftType) + .setIsConstant(false) + .setIsInitialized(true) + .getVariableFactory() + .produce(); + IRNode rightExpr = builder.setComplexityLimit(rightComplexityLimit) + .setOperatorLimit(rightOperatorLimit) + .setResultType(rightType) + .getExpressionFactory() + .produce(); + return new BinaryOperator(opKind, leftExpr, rightExpr); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CompoundShiftAssignmentOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CompoundShiftAssignmentOperatorFactory.java new file mode 100644 index 00000000000..286b6d719ff --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CompoundShiftAssignmentOperatorFactory.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.Pair; +import jdk.test.lib.jittester.BinaryOperator; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.TypeUtil; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeBoolean; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class CompoundShiftAssignmentOperatorFactory extends BinaryOperatorFactory { + CompoundShiftAssignmentOperatorFactory(OperatorKind opKind, long complexityLimit, + int operatorLimit, TypeKlass ownerClass, Type resultType, boolean exceptionSafe, boolean noconsts) { + super(opKind, complexityLimit, operatorLimit, ownerClass, resultType, exceptionSafe, noconsts); + } + + @Override + protected boolean isApplicable(Type resultType) { + return TypeList.isBuiltInInt(resultType) && !resultType.equals(new TypeBoolean()); + } + + @Override + protected Pair generateTypes() throws ProductionFailedException { + return new Pair<>(resultType, PseudoRandom.randomElement( + TypeUtil.getExplicitlyCastable(TypeList.getBuiltInInt(), resultType))); + } + + @Override + protected BinaryOperator generateProduction(Type leftType, Type rightType) throws ProductionFailedException { + long leftComplexityLimit = (long) (PseudoRandom.random() * complexityLimit); + long rightComplexityLimit = complexityLimit - leftComplexityLimit; + int leftOperatorLimit = (int) (PseudoRandom.random() * operatorLimit); + int rightOperatorLimit = operatorLimit = leftOperatorLimit; + IRNodeBuilder builder = new IRNodeBuilder().setOwnerKlass((TypeKlass) ownerClass) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts); + IRNode leftExpr = builder.setComplexityLimit(leftComplexityLimit) + .setOperatorLimit(leftOperatorLimit) + .setResultType(leftType) + .setIsConstant(false) + .setIsInitialized(true) + .getVariableFactory() + .produce(); + IRNode rightExpr = builder.setComplexityLimit(rightComplexityLimit) + .setOperatorLimit(rightOperatorLimit) + .setResultType(rightType) + .getExpressionFactory() + .produce(); + return new BinaryOperator(opKind, leftExpr, rightExpr); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ConstructorDefinitionBlockFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ConstructorDefinitionBlockFactory.java new file mode 100644 index 00000000000..5da833a74e2 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ConstructorDefinitionBlockFactory.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.functions.ConstructorDefinitionBlock; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class ConstructorDefinitionBlockFactory extends Factory { + private final long complexityLimit; + private final int statementLimit; + private final int operatorLimit; + private final int memberFunctionsLimit; + private final int memberFunctionsArgLimit; + private final TypeKlass ownerClass; + private final int level; + + ConstructorDefinitionBlockFactory(TypeKlass ownerClass, int memberFunctionsLimit, + int memberFunctionsArgLimit, long complexityLimit, int statementLimit, + int operatorLimit, int level) { + this.ownerClass = ownerClass; + this.memberFunctionsLimit = memberFunctionsLimit; + this.memberFunctionsArgLimit = memberFunctionsArgLimit; + this.complexityLimit = complexityLimit; + this.statementLimit = statementLimit; + this.operatorLimit = operatorLimit; + this.level = level; + } + + @Override + public IRNode produce() throws ProductionFailedException { + IRNodeBuilder builder = new IRNodeBuilder() + .setOwnerKlass(ownerClass) + .setStatementLimit(statementLimit) + .setOperatorLimit(operatorLimit) + .setLevel(level); + ArrayList content = new ArrayList<>(); + int memFunLimit = PseudoRandom.randomNotZero(memberFunctionsLimit); + builder.setComplexityLimit(complexityLimit / memFunLimit); + if (!ProductionParams.disableStatic.value() && PseudoRandom.randomBoolean()) { + // Generate static constructor + content.add(builder.getStaticConstructorDefinitionFactory().produce()); + // take static constructor into account + --memFunLimit; + } + // No matter what, generate default constructor first. + // This would guarantee a way to initialize a data member in case, + // when arguments to a non-default constructor cannot be generated. + content.add(builder.setMemberFunctionsArgLimit(0) + .getConstructorDefinitionFactory() + .produce()); + if (--memFunLimit > 0) { + for (int i = 0; i < memFunLimit; i++) { + try { + content.add(builder.setMemberFunctionsArgLimit(memberFunctionsArgLimit) + .getConstructorDefinitionFactory() + .produce()); + } catch (ProductionFailedException e) { + } + } + } + return new ConstructorDefinitionBlock(content, level); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ConstructorDefinitionFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ConstructorDefinitionFactory.java new file mode 100644 index 00000000000..1a7852871fa --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ConstructorDefinitionFactory.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.functions.ArgumentDeclaration; +import jdk.test.lib.jittester.functions.ConstructorDefinition; +import jdk.test.lib.jittester.functions.FunctionInfo; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeVoid; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class ConstructorDefinitionFactory extends Factory { + private final long complexityLimit; + private final int statementLimit; + private final int operatorLimit; + private final int memberFunctionsArgLimit; + private final int level; + private final TypeKlass ownerClass; + + ConstructorDefinitionFactory(TypeKlass ownerClass, long complexityLimit, int statementLimit, + int operatorLimit, int memberFunctionsArgLimit, int level) { + this.ownerClass = ownerClass; + this.complexityLimit = complexityLimit; + this.statementLimit = statementLimit; + this.operatorLimit = operatorLimit; + this.memberFunctionsArgLimit = memberFunctionsArgLimit; + this.level = level; + } + + @Override + public IRNode produce() throws ProductionFailedException { + int argNumber = (int) (PseudoRandom.random() * memberFunctionsArgLimit); + ArrayList argumentsInfo = new ArrayList<>(argNumber); + ArrayList argumentsDeclaration = new ArrayList<>(argNumber); + SymbolTable.push(); + IRNode body; + FunctionInfo functionInfo; + try { + int i = 0; + IRNodeBuilder builder = new IRNodeBuilder().setArgumentType(ownerClass).setOwnerKlass(ownerClass); + for (; i < argNumber; i++) { + ArgumentDeclaration d = builder.setVariableNumber(i).getArgumentDeclarationFactory() + .produce(); + argumentsDeclaration.add(d); + argumentsInfo.add(d.variableInfo); + } + for (boolean dup = true; dup; i++) { + /* Check if these is a function with a same signature + (includes original class name) defined. */ + functionInfo = new FunctionInfo(ownerClass.getName(), ownerClass, + ownerClass, 0, FunctionInfo.PUBLIC, argumentsInfo); + dup = false; + for (Symbol symbol : SymbolTable.get(ownerClass, FunctionInfo.class)) { + if (functionInfo.equals(symbol)) { + ArgumentDeclaration argDecl = builder.setVariableNumber(i) + .getArgumentDeclarationFactory().produce(); + argumentsDeclaration.add(argDecl); + argumentsInfo.add(argDecl.variableInfo); + dup = true; + break; + } + } + } + long blockComplLimit = (long) (PseudoRandom.random() * complexityLimit); + try { + body = builder.setResultType(new TypeVoid()) + .setComplexityLimit(blockComplLimit) + .setStatementLimit(statementLimit) + .setOperatorLimit(operatorLimit) + .setLevel(level) + .setSubBlock(true) + .getBlockFactory() + .produce(); + } catch (ProductionFailedException e) { + body = null; + } + } finally { + SymbolTable.pop(); + } + functionInfo = new FunctionInfo(ownerClass.getName(), ownerClass, ownerClass, + body != null ? body.complexity() : 0, FunctionInfo.PUBLIC, argumentsInfo); + // If it's all ok, add the function to the symbol table. + SymbolTable.add(functionInfo); + return new ConstructorDefinition(functionInfo, argumentsDeclaration, body); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ContinueFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ContinueFactory.java new file mode 100644 index 00000000000..a969567b1d2 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ContinueFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.Continue; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; + +class ContinueFactory extends Factory { + @Override + public IRNode produce() throws ProductionFailedException { + return new Continue(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CounterInitializerFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CounterInitializerFactory.java new file mode 100644 index 00000000000..8892901b98a --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CounterInitializerFactory.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.List; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.LiteralInitializer; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.TypeUtil; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.loops.CounterInitializer; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeInt; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class CounterInitializerFactory extends SafeFactory { + private final int counterValue; + private final TypeKlass ownerClass; + + CounterInitializerFactory(TypeKlass ownerClass, int counterValue) { + this.ownerClass = ownerClass; + this.counterValue = counterValue; + } + + @Override + protected IRNode sproduce() throws ProductionFailedException { + List types = TypeUtil.getMoreCapatiousThan(TypeList.getBuiltIn(), new TypeInt()); + types.add(new TypeInt()); + final Type selectedType = PseudoRandom.randomElement(types); + IRNode init = new LiteralInitializer(counterValue, selectedType); + String resultName = "var_" + SymbolTable.getNextVariableNumber(); + VariableInfo varInfo = new VariableInfo(resultName, ownerClass, selectedType, + VariableInfo.FINAL | VariableInfo.LOCAL | VariableInfo.INITIALIZED); + SymbolTable.add(varInfo); + return new CounterInitializer(varInfo, init); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CounterManipulatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CounterManipulatorFactory.java new file mode 100644 index 00000000000..a4fc059e8df --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/CounterManipulatorFactory.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.LocalVariable; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.UnaryOperator; +import jdk.test.lib.jittester.loops.CounterManipulator; + +class CounterManipulatorFactory extends Factory { + private final LocalVariable counter; + + CounterManipulatorFactory(LocalVariable counter) { + this.counter = counter; + } + + @Override + public IRNode produce() throws ProductionFailedException { + // We'll keep it simple for the time being.. + IRNode manipulator = new UnaryOperator(OperatorKind.POST_DEC, counter); + return new CounterManipulator(manipulator); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/DeclarationFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/DeclarationFactory.java new file mode 100644 index 00000000000..66aa9ea0d51 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/DeclarationFactory.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.Declaration; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Rule; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.types.TypeKlass; + +class DeclarationFactory extends Factory { + private final int operatorLimit; + private final long complexityLimit; + private final boolean isLocal; + private final boolean exceptionSafe; + private final TypeKlass ownerClass; + + DeclarationFactory(TypeKlass ownerClass, long complexityLimit, + int operatorLimit, boolean isLocal, boolean safe) { + this.ownerClass = ownerClass; + this.isLocal = isLocal; + this.exceptionSafe = safe; + this.complexityLimit = complexityLimit; + this.operatorLimit = operatorLimit; + } + + @Override + public IRNode produce() throws ProductionFailedException { + Rule rule = new Rule("declaration"); + IRNodeBuilder builder = new IRNodeBuilder().setOwnerKlass(ownerClass) + .setResultType(TypeList.getVoid()) + .setIsLocal(isLocal) + .setComplexityLimit(complexityLimit) + .setOperatorLimit(operatorLimit) + .setIsLocal(isLocal) + .setExceptionSafe(exceptionSafe); + rule.add("decl", builder.setIsStatic(false).getVariableDeclarationFactory()); + rule.add("decl_and_init", builder.setIsConstant(false) + .setIsStatic(false).getVariableInitializationFactory()); + if (!ProductionParams.disableFinalVariables.value()) { + rule.add("const_decl_and_init", builder.setIsConstant(true) + .setIsStatic(false).getVariableInitializationFactory()); + } + if (!isLocal && !ProductionParams.disableStatic.value()) { + rule.add("static_decl", builder.setIsStatic(true).getVariableDeclarationFactory()); + rule.add("static_decl_and_init", builder.setIsConstant(false) + .setIsStatic(true).getVariableInitializationFactory()); + if (!ProductionParams.disableFinalVariables.value()) { + rule.add("static_const_decl_and_init", builder.setIsConstant(true) + .setIsStatic(true).getVariableInitializationFactory()); + } + } + return new Declaration(rule.produce()); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/DoWhileFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/DoWhileFactory.java new file mode 100644 index 00000000000..71fda5b08b6 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/DoWhileFactory.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.Initialization; +import jdk.test.lib.jittester.Literal; +import jdk.test.lib.jittester.LocalVariable; +import jdk.test.lib.jittester.Nothing; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.loops.DoWhile; +import jdk.test.lib.jittester.loops.Loop; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeInt; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class DoWhileFactory extends SafeFactory { + private final Loop loop; + private final long complexityLimit; + private final int statementLimit; + private final int operatorLimit; + private boolean canHaveReturn = false; + private final TypeKlass ownerClass; + private final int level; + private final Type returnType; + private long thisLoopIterLimit; + + DoWhileFactory(TypeKlass ownerClass, Type returnType, long complexityLimit, int statementLimit, + int operatorLimit, int level, boolean canHaveReturn) { + loop = new Loop(); + this.ownerClass = ownerClass; + this.returnType = returnType; + this.complexityLimit = complexityLimit; + this.statementLimit = statementLimit; + this.operatorLimit = operatorLimit; + this.level = level; + this.canHaveReturn = canHaveReturn; + thisLoopIterLimit = 0; + } + + @Override + protected IRNode sproduce() throws ProductionFailedException { + if (statementLimit > 0 && complexityLimit > 0) { + long complexity = complexityLimit; + // Loop header parameters + long headerComplLimit = (long) (0.005 * complexity * PseudoRandom.random()); + complexity -= headerComplLimit; + int headerStatementLimit = PseudoRandom.randomNotZero((int) (statementLimit / 3.0)); + // Loop body parameters + thisLoopIterLimit = (long) (0.0001 * complexity * PseudoRandom.random()); + if (thisLoopIterLimit > Integer.MAX_VALUE || thisLoopIterLimit == 0) { + throw new ProductionFailedException(); + } + complexity = thisLoopIterLimit > 0 ? complexity / thisLoopIterLimit : 0; + long condComplLimit = (long) (complexity * PseudoRandom.random()); + complexity -= condComplLimit; + long body1ComplLimit = (long) (complexity * PseudoRandom.random()); + complexity -= body1ComplLimit; + int body1StatementLimit = PseudoRandom.randomNotZero((int) (statementLimit / 3.0)); + long body2ComplLimit = (long) (complexity * PseudoRandom.random()); + complexity -= body2ComplLimit; + int body2StatementLimit = PseudoRandom.randomNotZero((int) (statementLimit / 3.0)); + // Production + IRNodeBuilder builder = new IRNodeBuilder() + .setOwnerKlass(ownerClass) + .setResultType(returnType) + .setOperatorLimit(operatorLimit); + loop.initialization = builder.getCounterInitializerFactory(0).produce(); + IRNode header; + try { + header = builder.setComplexityLimit(headerComplLimit) + .setStatementLimit(headerStatementLimit) + .setLevel(level - 1) + .setSubBlock(true) + .setCanHaveBreaks(false) + .setCanHaveContinues(false) + .setCanHaveReturn(false) + .getBlockFactory() + .produce(); + } catch (ProductionFailedException e) { + header = new Nothing(); + } + // getChildren().set(DoWhile.DoWhilePart.HEADER.ordinal(), header); + LocalVariable counter = new LocalVariable(((Initialization) loop.initialization).get()); + Literal limiter = new Literal(Integer.valueOf((int) thisLoopIterLimit), new TypeInt()); + loop.condition = builder.setComplexityLimit(condComplLimit) + .setLocalVariable(counter) + .getLoopingConditionFactory(limiter) + .produce(); + SymbolTable.push(); + IRNode body1; + try { + body1 = builder.setComplexityLimit(body1ComplLimit) + .setStatementLimit(body1StatementLimit) + .setLevel(level) + .setSubBlock(true) + .setCanHaveBreaks(true) + .setCanHaveContinues(false) + .setCanHaveReturn(false) + .getBlockFactory() + .produce(); + } catch (ProductionFailedException e) { + body1 = new Nothing(); + } + // getChildren().set(DoWhile.DoWhilePart.BODY1.ordinal(), body1); + loop.manipulator = builder.setLocalVariable(counter).getCounterManipulatorFactory().produce(); + IRNode body2; + try { + body2 = builder.setComplexityLimit(body2ComplLimit) + .setStatementLimit(body2StatementLimit) + .setLevel(level) + .setSubBlock(true) + .setCanHaveBreaks(true) + .setCanHaveContinues(false) + .setCanHaveReturn(canHaveReturn) + .getBlockFactory() + .produce(); + } catch (ProductionFailedException e) { + body2 = new Nothing(); + } + // getChildren().set(DoWhile.DoWhilePart.BODY2.ordinal(), body2); + SymbolTable.pop(); + return new DoWhile(level, loop, thisLoopIterLimit, header, body1, body2); + } + throw new ProductionFailedException(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ExpressionFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ExpressionFactory.java new file mode 100644 index 00000000000..06b1071464a --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ExpressionFactory.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionLimiter; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Rule; +import jdk.test.lib.jittester.Type;; +import jdk.test.lib.jittester.types.TypeKlass; + +class ExpressionFactory extends SafeFactory { + private final Rule rule; + + ExpressionFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, Type resultType, + boolean exceptionSafe, boolean noconsts) throws ProductionFailedException { + IRNodeBuilder builder = new IRNodeBuilder() + .setComplexityLimit(complexityLimit) + .setOperatorLimit(operatorLimit) + .setOwnerKlass(ownerClass) + .setResultType(resultType) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts); + rule = new Rule("expression"); + if (!noconsts) { + rule.add("literal", builder.getLiteralFactory()); + rule.add("constant", builder.setIsConstant(true) + .setIsInitialized(true) + //.setVariableType(resultType) + .getVariableFactory()); + } + rule.add("variable", builder.setIsConstant(false).setIsInitialized(true).getVariableFactory()); + if (operatorLimit > 0 && complexityLimit > 0) { + rule.add("cast", builder.getCastOperatorFactory(), 0.1); + rule.add("arithmetic", builder.getArithmeticOperatorFactory()); + rule.add("logic", builder.getLogicOperatorFactory()); + rule.add("bitwise", new BitwiseOperatorFactory(complexityLimit, operatorLimit, ownerClass, + resultType, exceptionSafe, noconsts)); + rule.add("assignment", builder.getAssignmentOperatorFactory()); + rule.add("ternary", builder.getTernaryOperatorFactory()); + rule.add("function", builder.getFunctionFactory(), 0.1); + rule.add("str_plus", builder.setOperatorKind(OperatorKind.STRADD).getBinaryOperatorFactory()); + if (!ProductionParams.disableArrays.value() && !exceptionSafe) { + //rule.add("array_creation", builder.getArrayCreationFactory()); + rule.add("array_element", builder.getArrayElementFactory()); + rule.add("array_extraction", builder.getArrayExtractionFactory()); + } + } + } + + @Override + protected IRNode sproduce() throws ProductionFailedException { + ProductionLimiter.limitProduction(); + return rule.produce(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/Factory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/Factory.java new file mode 100644 index 00000000000..1929ee9c82d --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/Factory.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; + +public abstract class Factory { + public abstract IRNode produce() throws ProductionFailedException; +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ForFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ForFactory.java new file mode 100644 index 00000000000..ffb881b8670 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ForFactory.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.Initialization; +import jdk.test.lib.jittester.Literal; +import jdk.test.lib.jittester.LocalVariable; +import jdk.test.lib.jittester.Nothing; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Rule; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.loops.For; +import jdk.test.lib.jittester.loops.Loop; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeInt; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class ForFactory extends SafeFactory { + private final Loop loop; + private final long complexityLimit; + private final int statementLimit; + private final int operatorLimit; + private final TypeKlass ownerClass; + private final Type returnType; + private final int level; + private long thisLoopIterLimit = 0; + private final boolean canHaveReturn; + + ForFactory(TypeKlass ownerClass, Type returnType, long complexityLimit, int statementLimit, + int operatorLimit, int level, boolean canHaveReturn) { + this.ownerClass = ownerClass; + this.returnType = returnType; + this.complexityLimit = complexityLimit; + this.statementLimit = statementLimit; + this.operatorLimit = operatorLimit; + this.level = level; + loop = new Loop(); + this.canHaveReturn = canHaveReturn; + } + + @Override + protected IRNode sproduce() throws ProductionFailedException { + if (statementLimit <= 0 || complexityLimit <= 0) { + throw new ProductionFailedException(); + } + IRNodeBuilder builder = new IRNodeBuilder() + .setOwnerKlass(ownerClass) + .setResultType(returnType) + .setOperatorLimit(operatorLimit) + .setSemicolon(false) + .setExceptionSafe(false) + .setNoConsts(false); + long complexity = complexityLimit; + // Loop header parameters + long headerComplLimit = (long) (0.005 * complexity * PseudoRandom.random()); + complexity -= headerComplLimit; + int headerStatementLimit = PseudoRandom.randomNotZero((int) (statementLimit / 4.0)); + long statement1ComplLimit = (long) (0.005 * complexity * PseudoRandom.random()); + complexity -= statement1ComplLimit; + // Loop body parameters + thisLoopIterLimit = (long) (0.0001 * complexity * PseudoRandom.random()); + if (thisLoopIterLimit > Integer.MAX_VALUE || thisLoopIterLimit == 0) { + throw new ProductionFailedException(); + } + complexity = thisLoopIterLimit > 0 ? complexity / thisLoopIterLimit : 0; + long condComplLimit = (long) (complexity * PseudoRandom.random()); + complexity -= condComplLimit; + long statement2ComplLimit = (long) (complexity * PseudoRandom.random()); + complexity -= statement2ComplLimit; + long body1ComplLimit = (long) (complexity * PseudoRandom.random()); + complexity -= body1ComplLimit; + int body1StatementLimit = PseudoRandom.randomNotZero((int) (statementLimit / 4.0)); + long body2ComplLimit = (long) (complexity * PseudoRandom.random()); + complexity -= body2ComplLimit; + int body2StatementLimit = PseudoRandom.randomNotZero((int) (statementLimit / 4.0)); + long body3ComplLimit = complexity; + int body3StatementLimit = PseudoRandom.randomNotZero((int) (statementLimit / 4.0)); + // Production + loop.initialization = builder.getCounterInitializerFactory(0).produce(); + IRNode header; + try { + header = builder.setComplexityLimit(headerComplLimit) + .setStatementLimit(headerStatementLimit) + .setLevel(level - 1) + .setSubBlock(true) + .setCanHaveBreaks(false) + .setCanHaveContinues(false) + .setCanHaveReturn(false) + .getBlockFactory() + .produce(); + } catch (ProductionFailedException e) { + header = new Nothing(); + } + SymbolTable.push(); + IRNode statement1; + try { + Rule rule = new Rule("statement1"); + builder.setComplexityLimit(statement1ComplLimit); + rule.add("assignment", builder.getAssignmentOperatorFactory()); + rule.add("function", builder.getFunctionFactory(), 0.1); + rule.add("initialization", builder.setIsConstant(false) + .setIsStatic(false) + .setIsLocal(true) + .getVariableInitializationFactory()); + statement1 = rule.produce(); + } catch (ProductionFailedException e) { + statement1 = new Nothing(); + } + LocalVariable counter = new LocalVariable(((Initialization) loop.initialization).get()); + Literal limiter = new Literal(Integer.valueOf((int) thisLoopIterLimit), new TypeInt()); + loop.condition = builder.setComplexityLimit(condComplLimit) + .setLocalVariable(counter) + .getLoopingConditionFactory(limiter) + .produce(); + IRNode statement2; + try { + statement2 = builder.setComplexityLimit(statement2ComplLimit) + .getAssignmentOperatorFactory().produce(); + } catch (ProductionFailedException e) { + statement2 = new Nothing(); + } + IRNode body1; + try { + body1 = builder.setComplexityLimit(body1ComplLimit) + .setStatementLimit(body1StatementLimit) + .setLevel(level) + .setSubBlock(true) + .setCanHaveBreaks(true) + .setCanHaveContinues(false) + .setCanHaveReturn(false) + .getBlockFactory() + .produce(); + } catch (ProductionFailedException e) { + body1 = new Nothing(); + } + loop.manipulator = builder.setLocalVariable(counter).getCounterManipulatorFactory().produce(); + IRNode body2; + try { + body2 = builder.setComplexityLimit(body2ComplLimit) + .setStatementLimit(body2StatementLimit) + .setLevel(level) + .setSubBlock(true) + .setCanHaveBreaks(true) + .setCanHaveContinues(true) + .setCanHaveReturn(false) + .getBlockFactory() + .produce(); + } catch (ProductionFailedException e) { + body2 = new Nothing(); + } + IRNode body3; + try { + body3 = builder.setComplexityLimit(body3ComplLimit) + .setStatementLimit(body3StatementLimit) + .setLevel(level) + .setSubBlock(true) + .setCanHaveBreaks(true) + .setCanHaveContinues(false) + .setCanHaveReturn(canHaveReturn) + .getBlockFactory() + .produce(); + } catch (ProductionFailedException e) { + body3 = new Nothing(); + } + SymbolTable.pop(); + return new For(level, loop, thisLoopIterLimit, header, statement1, statement2, body1, + body2, body3); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionDeclarationBlockFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionDeclarationBlockFactory.java new file mode 100644 index 00000000000..269b6d634c1 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionDeclarationBlockFactory.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.functions.FunctionDeclarationBlock; +import jdk.test.lib.jittester.functions.FunctionInfo; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class FunctionDeclarationBlockFactory extends Factory { + private final int memberFunctionsLimit; + private final int memberFunctionsArgLimit; + private final int level; + private final TypeKlass ownerClass; + + FunctionDeclarationBlockFactory(TypeKlass ownerClass, int memberFunctionsLimit, + int memberFunctionsArgLimit, int level) { + this.ownerClass = ownerClass; + this.memberFunctionsLimit = memberFunctionsLimit; + this.memberFunctionsArgLimit = memberFunctionsArgLimit; + this.level = level; + } + + @Override + public IRNode produce() throws ProductionFailedException { + ArrayList content = new ArrayList<>(); + int memFunLimit = (int) (PseudoRandom.random() * memberFunctionsLimit); + if (memFunLimit > 0) { + IRNodeBuilder builder = new IRNodeBuilder() + .setOwnerKlass(ownerClass) + .setMemberFunctionsArgLimit(memberFunctionsArgLimit) + .setFlags(FunctionInfo.ABSTRACT | FunctionInfo.PUBLIC); + for (int i = 0; i < memFunLimit; i++) { + try { + content.add(builder.setName("func_" + i).getFunctionDeclarationFactory().produce()); + } catch (ProductionFailedException e) { + // TODO: do we have to react here? + } + } + } + return new FunctionDeclarationBlock(ownerClass, content, level); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionDeclarationFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionDeclarationFactory.java new file mode 100644 index 00000000000..99226f3d034 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionDeclarationFactory.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.functions.ArgumentDeclaration; +import jdk.test.lib.jittester.functions.FunctionDeclaration; +import jdk.test.lib.jittester.functions.FunctionDefinition; +import jdk.test.lib.jittester.functions.FunctionInfo; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeVoid; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class FunctionDeclarationFactory extends Factory { + private final Type resultType; + private final TypeKlass ownerClass; + private final String name; + private final int memberFunctionsArgLimit; + private final int flags; + + FunctionDeclarationFactory(String name, TypeKlass ownerClass, Type resultType, + int memberFunctionsArgLimit, int flags) { + this.name = name; + this.ownerClass = ownerClass; + this.resultType = resultType; + this.memberFunctionsArgLimit = memberFunctionsArgLimit; + this.flags = flags; + } + + @Override + public IRNode produce() throws ProductionFailedException { + Type resType = resultType; + if (resType == null) { + List types = new ArrayList<>(TypeList.getAll()); + types.add(new TypeVoid()); + resType = PseudoRandom.randomElement(types); + } + int argNumber = (int) (PseudoRandom.random() * memberFunctionsArgLimit); + ArrayList argumentsInfo = new ArrayList<>(argNumber + 1); + argumentsInfo.add(new VariableInfo("this", ownerClass, ownerClass, + VariableInfo.FINAL | VariableInfo.LOCAL | VariableInfo.INITIALIZED)); + ArrayList argumentsDeclaration = new ArrayList<>(argNumber); + SymbolTable.push(); + FunctionInfo functionInfo; + IRNodeBuilder builder = new IRNodeBuilder().setArgumentType(ownerClass); + try { + int i = 0; + for (; i < argNumber; i++) { + ArgumentDeclaration d = builder.setVariableNumber(i) + .getArgumentDeclarationFactory().produce(); + argumentsDeclaration.add(d); + argumentsInfo.add(d.variableInfo); + } + Collection thisKlassFuncs = SymbolTable + .getAllCombined(ownerClass, FunctionInfo.class); + Collection parentFuncs = FunctionDefinition.getFuncsFromParents(ownerClass); + while (true) { + functionInfo = new FunctionInfo(name, ownerClass, resType, 0, flags, argumentsInfo); + if (thisKlassFuncs.contains(functionInfo) + || FunctionDefinition.isInvalidOverride(functionInfo, parentFuncs)) { + // try changing the signature, and go checking again. + ArgumentDeclaration d = builder.setVariableNumber(i++) + .getArgumentDeclarationFactory().produce(); + argumentsDeclaration.add(d); + argumentsInfo.add(d.variableInfo); + } else { + break; + } + } + } finally { + SymbolTable.pop(); + } + //addChildren(argumentsDeclaration); // not neccessary while complexity is 0 + functionInfo = new FunctionInfo(name, ownerClass, resType, 0, flags, argumentsInfo); + // If it's all ok, add the function to the symbol table. + SymbolTable.add(functionInfo); + return new FunctionDeclaration(functionInfo, argumentsDeclaration); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionDefinitionBlockFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionDefinitionBlockFactory.java new file mode 100644 index 00000000000..da3481ddb8d --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionDefinitionBlockFactory.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.functions.FunctionDefinitionBlock; +import jdk.test.lib.jittester.functions.FunctionInfo; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class FunctionDefinitionBlockFactory extends Factory { + private final long complexityLimit; + private final int statementLimit; + private final int operatorLimit; + private final int memberFunctionsLimit; + private final int memberFunctionsArgLimit; + private final int initialFlags; + private final int level; + private final TypeKlass ownerClass; + + FunctionDefinitionBlockFactory(TypeKlass ownerClass, int memberFunctionsLimit, + int memberFunctionsArgLimit, long complexityLimit, int statementLimit, + int operatorLimit, int level, int initialFlags) { + this.ownerClass = ownerClass; + this.memberFunctionsLimit = memberFunctionsLimit; + this.memberFunctionsArgLimit = memberFunctionsArgLimit; + this.complexityLimit = complexityLimit; + this.statementLimit = statementLimit; + this.operatorLimit = operatorLimit; + this.level = level; + this.initialFlags = initialFlags; + } + + @Override + public IRNode produce() throws ProductionFailedException { + ArrayList content = new ArrayList<>(); + int memFunLimit = (int) (PseudoRandom.random() * memberFunctionsLimit); + if (memFunLimit > 0) { + long memFunCompl = complexityLimit / memFunLimit; + IRNodeBuilder builder = new IRNodeBuilder().setOwnerKlass(ownerClass) + .setComplexityLimit(memFunCompl) + .setStatementLimit(statementLimit) + .setOperatorLimit(operatorLimit) + .setMemberFunctionsArgLimit(memberFunctionsArgLimit) + .setLevel(level); + for (int i = 0; i < memFunLimit; i++) { + int flags = initialFlags; + if (PseudoRandom.randomBoolean()) { + flags |= FunctionInfo.STATIC; + } + if (!ProductionParams.disableFinalMethods.value() && PseudoRandom.randomBoolean()) { + flags |= FunctionInfo.FINAL; + } + if (PseudoRandom.randomBoolean()) { + flags |= FunctionInfo.NONRECURSIVE; + } + if (PseudoRandom.randomBoolean()) { + flags |= FunctionInfo.SYNCHRONIZED; + } + switch ((int) (PseudoRandom.random() * 4)) { + case 0: + flags |= FunctionInfo.PRIVATE; + break; + case 1: + flags |= FunctionInfo.PROTECTED; + break; + case 2: + flags |= FunctionInfo.DEFAULT; + break; + case 3: + flags |= FunctionInfo.PUBLIC; + break; + } + Symbol thisSymbol = null; + if ((flags & FunctionInfo.STATIC) > 0) { + thisSymbol = SymbolTable.get("this", VariableInfo.class); + SymbolTable.remove(thisSymbol); + } + try { + content.add(builder.setName("func_" + i) + .setFlags(flags) + .getFunctionDefinitionFactory() + .produce()); + } catch (ProductionFailedException e) { + } + if ((flags & FunctionInfo.STATIC) > 0) { + SymbolTable.add(thisSymbol); + } + } + } + return new FunctionDefinitionBlock(content, level, ownerClass); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionDefinitionFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionDefinitionFactory.java new file mode 100644 index 00000000000..76ea7374749 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionDefinitionFactory.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.functions.ArgumentDeclaration; +import jdk.test.lib.jittester.functions.FunctionDefinition; +import jdk.test.lib.jittester.functions.FunctionInfo; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeVoid; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class FunctionDefinitionFactory extends Factory { + private final Type resultType; + private final String name; + private final long complexityLimit; + private final int statementLimit; + private final int operatorLimit; + private final int memberFunctionsArgLimit; + private final int flags; + private final int level; + private final TypeKlass ownerClass; + + FunctionDefinitionFactory(String name, TypeKlass ownerClass, Type resultType, + long complexityLimit, int statementLimit, int operatorLimit, + int memberFunctionsArgLimit, int level, int flags) { + this.name = name; + this.ownerClass = ownerClass; + this.resultType = resultType; + this.complexityLimit = complexityLimit; + this.statementLimit = statementLimit; + this.operatorLimit = operatorLimit; + this.memberFunctionsArgLimit = memberFunctionsArgLimit; + this.level = level; + this.flags = flags; + } + + @Override + public IRNode produce() throws ProductionFailedException { + Type resType = resultType; + if (resType == null) { + List types = new ArrayList<>(TypeList.getAll()); + types.add(new TypeVoid()); + resType = PseudoRandom.randomElement(types); + } + int argNumber = (int) (PseudoRandom.random() * memberFunctionsArgLimit); + ArrayList argumentsInfo; + if ((flags & FunctionInfo.STATIC) > 0) { + argumentsInfo = new ArrayList<>(argNumber); + } else { + argumentsInfo = new ArrayList<>(argNumber + 1); + argumentsInfo.add(new VariableInfo("this", ownerClass, ownerClass, + VariableInfo.FINAL | VariableInfo.LOCAL | VariableInfo.INITIALIZED)); + } + ArrayList argumentsDeclaration = new ArrayList<>(argNumber); + SymbolTable.push(); + IRNode body; + IRNode returnNode; + FunctionInfo functionInfo; + try { + IRNodeBuilder builder = new IRNodeBuilder().setArgumentType(ownerClass); + int i = 0; + for (; i < argNumber; i++) { + ArgumentDeclaration d = builder.setVariableNumber(i).getArgumentDeclarationFactory() + .produce(); + argumentsDeclaration.add(d); + argumentsInfo.add(d.variableInfo); + } + Collection thisKlassFuncs = SymbolTable.getAllCombined(ownerClass, + FunctionInfo.class); + Collection parentFuncs = FunctionDefinition.getFuncsFromParents(ownerClass); + while (true) { + functionInfo = new FunctionInfo(name, ownerClass, resType, 0, flags, + argumentsInfo); + if (thisKlassFuncs.contains(functionInfo) + || FunctionDefinition.isInvalidOverride(functionInfo, parentFuncs)) { + // try changing the signature, and go checking again. + ArgumentDeclaration argDecl = builder.setVariableNumber(i++) + .getArgumentDeclarationFactory().produce(); + argumentsDeclaration.add(argDecl); + argumentsInfo.add(argDecl.variableInfo); + } else { + break; + } + } + long blockComplLimit = (long) (PseudoRandom.random() * complexityLimit); + body = builder.setOwnerKlass(ownerClass) + .setResultType(resType) + .setComplexityLimit(blockComplLimit) + .setStatementLimit(statementLimit) + .setOperatorLimit(operatorLimit) + .setLevel(level) + .setSubBlock(true) + .setCanHaveBreaks(false) + .setCanHaveContinues(false) + .setCanHaveReturn(true) + .getBlockFactory() + .produce(); + if (!resType.equals(new TypeVoid())) { + returnNode = builder.setComplexityLimit(complexityLimit - blockComplLimit) + .setExceptionSafe(false) + .getReturnFactory() + .produce(); + } else { + returnNode = null; + } + } finally { + SymbolTable.pop(); + } + // addChildren(argumentsDeclaration); // not neccessary while complexity() doesn't use it + functionInfo = new FunctionInfo(name, ownerClass, resType, body == null ? 0 : body.complexity(), + flags, argumentsInfo); + // If it's all ok, add the function to the symbol table. + SymbolTable.add(functionInfo); + return new FunctionDefinition(functionInfo, argumentsDeclaration, body, returnNode); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionFactory.java new file mode 100644 index 00000000000..419388fc0f7 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionFactory.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.functions.Function; +import jdk.test.lib.jittester.functions.FunctionInfo; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class FunctionFactory extends SafeFactory { + private final FunctionInfo functionInfo; + private final int operatorLimit; + private final long complexityLimit; + private final boolean exceptionSafe; + private final TypeKlass ownerClass; + + FunctionFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, + Type resultType, boolean exceptionSafe) { + functionInfo = new FunctionInfo(); + this.complexityLimit = complexityLimit; + this.operatorLimit = operatorLimit; + this.ownerClass = ownerClass; + this.functionInfo.type = resultType; + this.exceptionSafe = exceptionSafe; + } + + @Override + protected IRNode sproduce() throws ProductionFailedException { + // Currently no function is exception-safe + if (exceptionSafe) { + throw new ProductionFailedException(); + } + ArrayList allFunctions; + if (functionInfo.type == null) { + allFunctions = new ArrayList<>(SymbolTable.getAllCombined(FunctionInfo.class)); + } else { + allFunctions = new ArrayList<>(SymbolTable.get(functionInfo.type, FunctionInfo.class)); + } + if (!allFunctions.isEmpty()) { + PseudoRandom.shuffle(allFunctions); + Collection klassHierarchy = ownerClass.getAllParents(); + for (Symbol function : allFunctions) { + FunctionInfo functionInfo = (FunctionInfo) function; + // Don't try to construct abstract classes. + if (functionInfo.isConstructor() && functionInfo.klass.isAbstract()) { + continue; + } + // We don't call methods from the same class which are not final, because if we + // do this may produce an infinite recursion. Simple example: + // class A + // { + // f1() { } + // f2() { f1(); } + // } + // + // class B : A + // { + // f1() { f2(); } + // } + // + // However the same example is obviously safe for static and final functions + // Also we introduce a special flag NONRECURSIVE to mark functions that + // are not overrided. We may also call such functions. + + // If it's a local call.. or it's a call using some variable to some object of some type in our hierarchy + boolean inHierarchy = false; + if (ownerClass.equals(functionInfo.klass) || (inHierarchy = klassHierarchy.contains(functionInfo.klass))) { + if ((functionInfo.flags & FunctionInfo.FINAL) == 0 && (functionInfo.flags & FunctionInfo.STATIC) == 0 + && (functionInfo.flags & FunctionInfo.NONRECURSIVE) == 0) { + continue; + } + if (inHierarchy && (functionInfo.flags & FunctionInfo.PRIVATE) > 0) { + continue; + } + } else { + if ((functionInfo.flags & FunctionInfo.PUBLIC) == 0 + && (functionInfo.flags & FunctionInfo.DEFAULT) == 0) { + continue; + } + } + if (functionInfo.complexity < complexityLimit - 1) { + try { + List accum = new ArrayList<>(); + if (!functionInfo.argTypes.isEmpty()) { + // Here we should do some analysis here to determine if + // there are any conflicting functions due to possible + // constant folding. + + // For example the following can be done: + // Scan all the hieirachy where the class is declared. + // If there are function with a same name and same number of args, + // then disable usage of foldable expressions in the args. + boolean noconsts = false; + Collection allFuncsInKlass = SymbolTable.getAllCombined(functionInfo.klass, + FunctionInfo.class); + for (Symbol s2 : allFuncsInKlass) { + FunctionInfo i2 = (FunctionInfo) function; + if (!i2.equals(functionInfo) && i2.name.equals(functionInfo.name) + && i2.argTypes.size() == functionInfo.argTypes.size()) { + noconsts = true; + break; + } + } + long argComp = (complexityLimit - 1 - functionInfo.complexity) / functionInfo.argTypes.size(); + int argumentOperatorLimit = (operatorLimit - 1) / functionInfo.argTypes.size(); + IRNodeBuilder b = new IRNodeBuilder().setOwnerKlass(ownerClass) + .setComplexityLimit(argComp) + .setOperatorLimit(argumentOperatorLimit) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts); + for (VariableInfo argType : functionInfo.argTypes) { + accum.add(b.setResultType(argType.type) + .getExpressionFactory() + .produce()); + } + } + return new Function(ownerClass, functionInfo, accum); + } catch (ProductionFailedException e) { + // removeAllChildren(); + } + } + } + } + throw new ProductionFailedException(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionRedefinitionBlockFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionRedefinitionBlockFactory.java new file mode 100644 index 00000000000..1bf8bde68e6 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionRedefinitionBlockFactory.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import java.util.Collection; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.functions.FunctionInfo; +import jdk.test.lib.jittester.functions.FunctionRedefinitionBlock; +import jdk.test.lib.jittester.types.TypeKlass; + +class FunctionRedefinitionBlockFactory extends Factory { + private final int statementLimit; + private final int operatorLimit; + private final long complexityLimit; + private final int level; + private final TypeKlass ownerClass; + private final Collection functionSet; + + FunctionRedefinitionBlockFactory(Collection functionSet, TypeKlass ownerClass, + long complexityLimit, int statementLimit, int operatorLimit, int level) { + this.functionSet = functionSet; + this.ownerClass = ownerClass; + this.complexityLimit = complexityLimit; + this.statementLimit = statementLimit; + this.operatorLimit = operatorLimit; + this.level = level; + } + + @Override + public IRNode produce() throws ProductionFailedException { + ArrayList content = new ArrayList<>(); + if (functionSet.size() > 0) { + long funcComplexity = complexityLimit / functionSet.size(); + IRNodeBuilder builder = new IRNodeBuilder().setOwnerKlass(ownerClass) + .setComplexityLimit(funcComplexity) + .setStatementLimit(statementLimit) + .setOperatorLimit(operatorLimit) + .setLevel(level); + for (Symbol symbol : functionSet) { + FunctionInfo functionInfo = (FunctionInfo) symbol; + Symbol thisSymbol = null; + if ((functionInfo.flags & FunctionInfo.STATIC) > 0) { + thisSymbol = SymbolTable.get("this", VariableInfo.class); + SymbolTable.remove(thisSymbol); + } + try { + content.add(builder.setFunctionInfo(functionInfo) + .setFlags(functionInfo.flags) + .getFunctionRedefinitionFactory() + .produce()); + } catch (ProductionFailedException e) { + if ((functionInfo.flags & FunctionInfo.STATIC) == 0) { + ownerClass.setAbstract(); + } + } + if ((functionInfo.flags & FunctionInfo.STATIC) > 0) { + SymbolTable.add(thisSymbol); + } + } + } + return new FunctionRedefinitionBlock(content, level); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionRedefinitionFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionRedefinitionFactory.java new file mode 100644 index 00000000000..3a74da66c33 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/FunctionRedefinitionFactory.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.functions.ArgumentDeclaration; +import jdk.test.lib.jittester.functions.FunctionDefinition; +import jdk.test.lib.jittester.functions.FunctionInfo; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeVoid; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class FunctionRedefinitionFactory extends Factory { + private final long complexityLimit; + private final int statementLimit; + private final int operatorLimit; + private final int level; + private final TypeKlass ownerClass; + private final FunctionInfo functionInfo; + + FunctionRedefinitionFactory(FunctionInfo functionInfo, TypeKlass ownerClass, + long complexityLimit, int statementLimit, int operatorLimit, int level, int flags) { + this.ownerClass = ownerClass; + this.functionInfo = new FunctionInfo(functionInfo); // do deep coping + functionInfo.klass = ownerClass; // important! fix klass! + if ((functionInfo.flags & FunctionInfo.STATIC) == 0) { + functionInfo.argTypes.get(0).type = ownerClass; // redefine type of this + } + functionInfo.flags = flags; // apply new flags. + // fix the type of class where the args would be declared + for (VariableInfo varInfo : functionInfo.argTypes) { + varInfo.klass = ownerClass; + } + this.complexityLimit = complexityLimit; + this.statementLimit = statementLimit; + this.operatorLimit = operatorLimit; + this.level = level; + } + + @Override + public IRNode produce() throws ProductionFailedException { + ArrayList argumentsInfo = functionInfo.argTypes; + SymbolTable.push(); + IRNode body; + IRNode returnNode; + ArrayList argumentsDeclaration; + try { + if ((functionInfo.flags & FunctionInfo.STATIC) > 0) { + argumentsDeclaration = new ArrayList<>(argumentsInfo.size()); + for (VariableInfo varInfo : argumentsInfo) { + argumentsDeclaration.add(new ArgumentDeclaration(varInfo)); + SymbolTable.add(varInfo); + } + } else { + argumentsDeclaration = new ArrayList<>(argumentsInfo.size() - 1); + SymbolTable.add(argumentsInfo.get(0)); + for (int i = 1; i < argumentsInfo.size(); i++) { + argumentsDeclaration.add(new ArgumentDeclaration(argumentsInfo.get(i))); + SymbolTable.add(argumentsInfo.get(i)); + } + } + long blockComplLimit = (long) (PseudoRandom.random() * complexityLimit); + IRNodeBuilder builder = new IRNodeBuilder() + .setOwnerKlass(ownerClass) + .setResultType(functionInfo.type) + .setStatementLimit(statementLimit) + .setOperatorLimit(operatorLimit); + body = builder.setComplexityLimit(blockComplLimit) + .setLevel(level) + .setSubBlock(true) + .setCanHaveBreaks(false) + .setCanHaveContinues(false) + .setCanHaveReturn(true) + .getBlockFactory() + .produce(); + if (!functionInfo.type.equals(new TypeVoid())) { + returnNode = builder.setComplexityLimit(complexityLimit - blockComplLimit) + .setExceptionSafe(false) + .getReturnFactory() + .produce(); + } else { + returnNode = null; + } + } catch (ProductionFailedException e) { + SymbolTable.pop(); + SymbolTable.add(functionInfo); + throw e; + } + SymbolTable.pop(); + if ((functionInfo.flags & FunctionInfo.STATIC) == 0) { + functionInfo.flags &= ~FunctionInfo.ABSTRACT; + } + // If it's all ok, add the function to the symbol table. + SymbolTable.add(functionInfo); + return new FunctionDefinition(functionInfo, argumentsDeclaration, body, returnNode); + } + +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/IRNodeBuilder.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/IRNodeBuilder.java new file mode 100644 index 00000000000..a57a840a41b --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/IRNodeBuilder.java @@ -0,0 +1,706 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.Collection; +import java.util.Optional; +import jdk.test.lib.jittester.Literal; +import jdk.test.lib.jittester.LocalVariable; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.functions.FunctionInfo; +import jdk.test.lib.jittester.types.TypeKlass; + +public class IRNodeBuilder { + //private Optional variableType = Optional.empty(); + private Optional argumentType = Optional.empty(); + private Optional variableNumber = Optional.empty(); + private Optional complexityLimit = Optional.empty(); + private Optional operatorLimit = Optional.empty(); + private Optional ownerClass = Optional.empty(); + private Optional resultType = Optional.empty(); + private Optional safe = Optional.empty(); + private Optional noConsts = Optional.empty(); + private Optional opKind = Optional.empty(); + private Optional statementLimit = Optional.empty(); + private Optional subBlock = Optional.empty(); + private Optional canHaveBreaks = Optional.empty(); + private Optional canHaveContinues = Optional.empty(); + private Optional canHaveReturn = Optional.empty(); + //not in use yet because 'throw' is only placed to the locations where 'return' is allowed + private Optional canHaveThrow = Optional.empty(); + private Optional level = Optional.empty(); + private Optional prefix = Optional.empty(); + private Optional memberFunctionsLimit = Optional.empty(); + private Optional memberFunctionsArgLimit = Optional.empty(); + private Optional localVariable = Optional.empty(); + private Optional isLocal = Optional.empty(); + private Optional isStatic = Optional.empty(); + private Optional isConstant = Optional.empty(); + private Optional isInitialized = Optional.empty(); + private Optional name = Optional.empty(); + private Optional printerName = Optional.empty(); + private Optional flags = Optional.empty(); + private Optional functionInfo = Optional.empty(); + private Optional semicolon = Optional.empty(); + + public ArgumentDeclarationFactory getArgumentDeclarationFactory() { + return new ArgumentDeclarationFactory(getArgumentType(), getVariableNumber()); + } + + public Factory getArithmeticOperatorFactory() throws ProductionFailedException { + return new ArithmeticOperatorFactory(getComplexityLimit(), getOperatorLimit(), + getOwnerClass(), getResultType(), getExceptionSafe(), getNoConsts()); + } + + public ArrayCreationFactory getArrayCreationFactory() { + return new ArrayCreationFactory(getComplexityLimit(), getOperatorLimit(), getOwnerClass(), + getResultType(), getExceptionSafe(), getNoConsts()); + } + + public ArrayElementFactory getArrayElementFactory() { + return new ArrayElementFactory(getComplexityLimit(), getOperatorLimit(), getOwnerClass(), + getResultType(), getExceptionSafe(), getNoConsts()); + } + + public ArrayExtractionFactory getArrayExtractionFactory() { + return new ArrayExtractionFactory(getComplexityLimit(), getOperatorLimit(), getOwnerClass(), + getResultType(), getExceptionSafe(), getNoConsts()); + } + + public AssignmentOperatorFactory getAssignmentOperatorFactory() { + return new AssignmentOperatorFactory(getComplexityLimit(), getOperatorLimit(), + getOwnerClass(), resultType.orElse(null), getExceptionSafe(), getNoConsts()); + } + + public BinaryOperatorFactory getBinaryOperatorFactory() throws ProductionFailedException { + OperatorKind o = getOperatorKind(); + switch (o) { + case ASSIGN: + return new AssignmentOperatorImplFactory(getComplexityLimit(), getOperatorLimit(), + getOwnerClass(), resultType.orElse(null), getExceptionSafe(), getNoConsts()); + case AND: + case OR: + return new BinaryLogicOperatorFactory(o, getComplexityLimit(), getOperatorLimit(), + getOwnerClass(), resultType.orElse(null), getExceptionSafe(), getNoConsts()); + case BIT_OR: + case BIT_XOR: + case BIT_AND: + return new BinaryBitwiseOperatorFactory(o, getComplexityLimit(), getOperatorLimit(), + getOwnerClass(), resultType.orElse(null), getExceptionSafe(), getNoConsts()); + + case EQ: + case NE: + return new BinaryEqualityOperatorFactory(o, getComplexityLimit(), + getOperatorLimit(), getOwnerClass(), resultType.orElse(null), getExceptionSafe(), + getNoConsts()); + case GT: + case LT: + case GE: + case LE: + return new BinaryComparisonOperatorFactory(o, getComplexityLimit(), + getOperatorLimit(), getOwnerClass(), resultType.orElse(null), getExceptionSafe(), + getNoConsts()); + case SHR: + case SHL: + case SAR: + return new BinaryShiftOperatorFactory(o, getComplexityLimit(), getOperatorLimit(), + getOwnerClass(), resultType.orElse(null), getExceptionSafe(), getNoConsts()); + case ADD: + case SUB: + case MUL: + case DIV: + case MOD: + return new BinaryArithmeticOperatorFactory(o, getComplexityLimit(), + getOperatorLimit(), getOwnerClass(), resultType.orElse(null), getExceptionSafe(), + getNoConsts()); + case STRADD: + return new BinaryStringPlusFactory(getComplexityLimit(), getOperatorLimit(), + getOwnerClass(), resultType.orElse(null), getExceptionSafe(), getNoConsts()); + case COMPOUND_ADD: + case COMPOUND_SUB: + case COMPOUND_MUL: + case COMPOUND_DIV: + case COMPOUND_MOD: + return new CompoundArithmeticAssignmentOperatorFactory(o, getComplexityLimit(), + getOperatorLimit(), getOwnerClass(), resultType.orElse(null), getExceptionSafe(), + getNoConsts()); + case COMPOUND_AND: + case COMPOUND_OR: + case COMPOUND_XOR: + return new CompoundBitwiseAssignmentOperatorFactory(o, getComplexityLimit(), + getOperatorLimit(), getOwnerClass(), resultType.orElse(null), getExceptionSafe(), + getNoConsts()); + case COMPOUND_SHR: + case COMPOUND_SHL: + case COMPOUND_SAR: + return new CompoundShiftAssignmentOperatorFactory(o, getComplexityLimit(), + getOperatorLimit(), getOwnerClass(), resultType.orElse(null), getExceptionSafe(), + getNoConsts()); + default: + throw new ProductionFailedException(); + } + } + + public UnaryOperatorFactory getUnaryOperatorFactory() throws ProductionFailedException { + OperatorKind o = getOperatorKind(); + switch (o) { + case NOT: + return new LogicalInversionOperatorFactory(getComplexityLimit(), + getOperatorLimit(), getOwnerClass(), resultType.orElse(null), getExceptionSafe(), + getNoConsts()); + case BIT_NOT: + return new BitwiseInversionOperatorFactory(getComplexityLimit(), + getOperatorLimit(), getOwnerClass(), resultType.orElse(null), getExceptionSafe(), + getNoConsts()); + case UNARY_PLUS: + case UNARY_MINUS: + return new UnaryPlusMinusOperatorFactory(o, getComplexityLimit(), + getOperatorLimit(), getOwnerClass(), resultType.orElse(null), getExceptionSafe(), + getNoConsts()); + case PRE_DEC: + case POST_DEC: + case PRE_INC: + case POST_INC: + return new IncDecOperatorFactory(o, getComplexityLimit(), getOperatorLimit(), + getOwnerClass(), resultType.orElse(null), getExceptionSafe(), getNoConsts()); + default: + throw new ProductionFailedException(); + } + } + + public BlockFactory getBlockFactory() throws ProductionFailedException { + return new BlockFactory(getOwnerClass(), getResultType(), getComplexityLimit(), + getStatementLimit(), getOperatorLimit(), getLevel(), subBlock.orElse(false), + canHaveBreaks.orElse(false), canHaveContinues.orElse(false), + canHaveReturn.orElse(false), canHaveReturn.orElse(false)); + //now 'throw' can be placed only in the same positions as 'return' + } + + public BreakFactory getBreakFactory() { + return new BreakFactory(); + } + + public CastOperatorFactory getCastOperatorFactory() { + return new CastOperatorFactory(getComplexityLimit(), getOperatorLimit(), getOwnerClass(), + getResultType(), getExceptionSafe(), getNoConsts()); + } + + public Factory getClassDefinitionBlockFactory() { + return new ClassDefinitionBlockFactory(getPrefix(), + ProductionParams.classesLimit.value(), + ProductionParams.memberFunctionsLimit.value(), + ProductionParams.memberFunctionsArgLimit.value(), + getComplexityLimit(), + ProductionParams.statementLimit.value(), + ProductionParams.operatorLimit.value(), + getLevel()); + } + + public Factory getMainKlassFactory() { + return new MainKlassFactory(getName(), getComplexityLimit(), + ProductionParams.memberFunctionsLimit.value(), + ProductionParams.memberFunctionsArgLimit.value(), + ProductionParams.statementLimit.value(), + ProductionParams.testStatementLimit.value(), + ProductionParams.operatorLimit.value()); + } + + public ConstructorDefinitionBlockFactory getConstructorDefinitionBlockFactory() { + return new ConstructorDefinitionBlockFactory(getOwnerClass(), getMemberFunctionsLimit(), + ProductionParams.memberFunctionsArgLimit.value(), getComplexityLimit(), + getStatementLimit(), getOperatorLimit(), getLevel()); + } + + public ConstructorDefinitionFactory getConstructorDefinitionFactory() { + return new ConstructorDefinitionFactory(getOwnerClass(), getComplexityLimit(), + getStatementLimit(), getOperatorLimit(), + getMemberFunctionsArgLimit(), getLevel()); + } + + public ContinueFactory getContinueFactory() { + return new ContinueFactory(); + } + + public CounterInitializerFactory getCounterInitializerFactory(int counterValue) { + return new CounterInitializerFactory(getOwnerClass(), counterValue); + } + + public CounterManipulatorFactory getCounterManipulatorFactory() { + return new CounterManipulatorFactory(getLocalVariable()); + } + + public DeclarationFactory getDeclarationFactory() { + return new DeclarationFactory(getOwnerClass(), getComplexityLimit(), getOperatorLimit(), + getIsLocal(), getExceptionSafe()); + } + + public DoWhileFactory getDoWhileFactory() { + return new DoWhileFactory(getOwnerClass(), getResultType(), getComplexityLimit(), + getStatementLimit(), getOperatorLimit(), getLevel(), getCanHaveReturn()); + } + + public WhileFactory getWhileFactory() { + return new WhileFactory(getOwnerClass(), getResultType(), getComplexityLimit(), + getStatementLimit(), getOperatorLimit(), getLevel(), getCanHaveReturn()); + } + + public IfFactory getIfFactory() { + return new IfFactory(getOwnerClass(), getResultType(), getComplexityLimit(), + getStatementLimit(), getOperatorLimit(), getLevel(), getCanHaveBreaks(), + getCanHaveContinues(), getCanHaveReturn()); + } + + public ForFactory getForFactory() { + return new ForFactory(getOwnerClass(), getResultType(), getComplexityLimit(), + getStatementLimit(), getOperatorLimit(), getLevel(), getCanHaveReturn()); + } + + public SwitchFactory getSwitchFactory() { // TODO: switch is not used now + return new SwitchFactory(getOwnerClass(), getComplexityLimit(), getStatementLimit(), + getOperatorLimit(), getLevel(), getCanHaveReturn()); + } + + public ExpressionFactory getExpressionFactory() throws ProductionFailedException { + return new ExpressionFactory(getComplexityLimit(), getOperatorLimit(), getOwnerClass(), + getResultType(), getExceptionSafe(), getNoConsts()); + } + + public FunctionDeclarationBlockFactory getFunctionDeclarationBlockFactory() { + return new FunctionDeclarationBlockFactory(getOwnerClass(), getMemberFunctionsLimit(), + getMemberFunctionsArgLimit(), getLevel()); + } + + public FunctionDeclarationFactory getFunctionDeclarationFactory() { + return new FunctionDeclarationFactory(getName(), getOwnerClass(),resultType.orElse(null), + getMemberFunctionsArgLimit(), getFlags()); + } + + public FunctionDefinitionBlockFactory getFunctionDefinitionBlockFactory() { + return new FunctionDefinitionBlockFactory(getOwnerClass(), getMemberFunctionsLimit(), + getMemberFunctionsArgLimit(), getComplexityLimit(), getStatementLimit(), + getOperatorLimit(), getLevel(), getFlags()); + } + + public FunctionDefinitionFactory getFunctionDefinitionFactory() { + return new FunctionDefinitionFactory(getName(), getOwnerClass(), resultType.orElse(null), + getComplexityLimit(), getStatementLimit(), getOperatorLimit(), + getMemberFunctionsArgLimit(), getLevel(), getFlags()); + } + + public FunctionFactory getFunctionFactory() { + return new FunctionFactory(getComplexityLimit(), getOperatorLimit(), getOwnerClass(), + resultType.orElse(null), getExceptionSafe()); + } + + public FunctionRedefinitionBlockFactory getFunctionRedefinitionBlockFactory(Collection + functionSet) { + return new FunctionRedefinitionBlockFactory(functionSet, getOwnerClass(), + getComplexityLimit(), getStatementLimit(), getOperatorLimit(), getLevel()); + } + + public FunctionRedefinitionFactory getFunctionRedefinitionFactory() { + return new FunctionRedefinitionFactory(getFunctionInfo(), getOwnerClass(), + getComplexityLimit(), getStatementLimit(), getOperatorLimit(), getLevel(), + getFlags()); + } + + public InterfaceFactory getInterfaceFactory() { + return new InterfaceFactory(getName(), getMemberFunctionsLimit(), + getMemberFunctionsArgLimit(), getLevel()); + } + + public KlassFactory getKlassFactory() { + return new KlassFactory(getName(), getPrinterName(), getComplexityLimit(), + getMemberFunctionsLimit(), getMemberFunctionsArgLimit(), getStatementLimit(), + getOperatorLimit(), getLevel()); + } + + public LimitedExpressionFactory getLimitedExpressionFactory() throws ProductionFailedException { + return new LimitedExpressionFactory(getComplexityLimit(), getOperatorLimit(), + getOwnerClass(), getResultType(), getExceptionSafe(), getNoConsts()); + } + + public LiteralFactory getLiteralFactory() { + return new LiteralFactory(getResultType()); + } + + public LocalVariableFactory getLocalVariableFactory() { + return new LocalVariableFactory(/*getVariableType()*/getResultType(), getFlags()); + } + + public LogicOperatorFactory getLogicOperatorFactory() throws ProductionFailedException { + return new LogicOperatorFactory(getComplexityLimit(), getOperatorLimit(), getOwnerClass(), + getResultType(), getExceptionSafe(), getNoConsts()); + } + + public LoopingConditionFactory getLoopingConditionFactory(Literal _limiter) { + return new LoopingConditionFactory(getComplexityLimit(), getOperatorLimit(), getOwnerClass(), + getLocalVariable(), _limiter); + } + + public NonStaticMemberVariableFactory getNonStaticMemberVariableFactory() { + return new NonStaticMemberVariableFactory(getComplexityLimit(), getOperatorLimit(), + getOwnerClass(), /*getVariableType()*/getResultType(), getFlags(), getExceptionSafe()); + } + + public NothingFactory getNothingFactory() { + return new NothingFactory(); + } + + public PrintVariablesFactory getPrintVariablesFactory() { + return new PrintVariablesFactory(getPrinterName(), getOwnerClass(), getLevel()); + } + + public ReturnFactory getReturnFactory() { + return new ReturnFactory(getComplexityLimit(), getOperatorLimit(), getOwnerClass(), + getResultType(), getExceptionSafe()); + } + + public ThrowFactory getThrowFactory() { + return new ThrowFactory(getComplexityLimit(), getOperatorLimit(), getOwnerClass(), getResultType(), getExceptionSafe()); + } + + public StatementFactory getStatementFactory() { + return new StatementFactory(getComplexityLimit(), getOperatorLimit(), getOwnerClass(), + getExceptionSafe(), getNoConsts(), semicolon.orElse(true)); + } + + public StaticConstructorDefinitionFactory getStaticConstructorDefinitionFactory() { + return new StaticConstructorDefinitionFactory(getOwnerClass(), getComplexityLimit(), + getStatementLimit(), getOperatorLimit(), getLevel()); + } + + public StaticMemberVariableFactory getStaticMemberVariableFactory() { + return new StaticMemberVariableFactory(getOwnerClass(), /*getVariableType()*/getResultType(), getFlags()); + } + + public TernaryOperatorFactory getTernaryOperatorFactory() { + return new TernaryOperatorFactory(getComplexityLimit(), getOperatorLimit(), getOwnerClass(), + getResultType(), getExceptionSafe(), getNoConsts()); + } + + public VariableDeclarationBlockFactory getVariableDeclarationBlockFactory() { + return new VariableDeclarationBlockFactory(getOwnerClass(), getComplexityLimit(), + getOperatorLimit(), getLevel(), getExceptionSafe()); + } + + public VariableDeclarationFactory getVariableDeclarationFactory() { + return new VariableDeclarationFactory(getOwnerClass(), getIsStatic(), getIsLocal(), getResultType()); + } + + public VariableFactory getVariableFactory() { + return new VariableFactory(getComplexityLimit(), getOperatorLimit(), getOwnerClass(), + /*getVariableType()*/getResultType(), getIsConstant(), getIsInitialized(), getExceptionSafe(), getNoConsts()); + } + + public VariableInitializationFactory getVariableInitializationFactory() { + return new VariableInitializationFactory(getOwnerClass(), getIsConstant(), getIsStatic(), + getIsLocal(), getComplexityLimit(), getOperatorLimit(), getExceptionSafe()); + } + + public TryCatchBlockFactory getTryCatchBlockFactory() { + return new TryCatchBlockFactory(getOwnerClass(), getResultType(), + getComplexityLimit(), getStatementLimit(), getOperatorLimit(), + getLevel(), subBlock.orElse(false), getCanHaveBreaks(), + getCanHaveContinues(), getCanHaveReturn()); + } + +/* public IRNodeBuilder setVariableType(Type value) { + variableType = Optional.of(value); + return this; + }*/ + + public IRNodeBuilder setArgumentType(TypeKlass value) { + argumentType = Optional.of(value); + return this; + } + + public IRNodeBuilder setVariableNumber(int value) { + variableNumber = Optional.of(value); + return this; + } + + public IRNodeBuilder setComplexityLimit(long value) { + complexityLimit = Optional.of(value); + return this; + } + + public IRNodeBuilder setOperatorLimit(int value) { + operatorLimit = Optional.of(value); + return this; + } + + public IRNodeBuilder setStatementLimit(int value) { + statementLimit = Optional.of(value); + return this; + } + + public IRNodeBuilder setOwnerKlass(TypeKlass value) { + ownerClass = Optional.of(value); + return this; + } + + public IRNodeBuilder setResultType(Type value) { + resultType = Optional.of(value); + return this; + } + // TODO: check if safe is always true in current implementation + public IRNodeBuilder setExceptionSafe(boolean value) { + safe = Optional.of(value); + return this; + } + // TODO: check is noconsts is always false in current implementation + public IRNodeBuilder setNoConsts(boolean value) { + noConsts = Optional.of(value); + return this; + } + + public IRNodeBuilder setOperatorKind(OperatorKind value) { + opKind = Optional.of(value); + return this; + } + + public IRNodeBuilder setLevel(int value) { + level = Optional.of(value); + return this; + } + + public IRNodeBuilder setSubBlock(boolean value) { + subBlock = Optional.of(value); + return this; + } + + public IRNodeBuilder setCanHaveBreaks(boolean value) { + canHaveBreaks = Optional.of(value); + return this; + } + + public IRNodeBuilder setCanHaveContinues(boolean value) { + canHaveContinues = Optional.of(value); + return this; + } + + public IRNodeBuilder setCanHaveReturn(boolean value) { + canHaveReturn = Optional.of(value); + return this; + } + + public IRNodeBuilder setCanHaveThrow(boolean value) { + canHaveThrow = Optional.of(value); + return this; + } + + public IRNodeBuilder setPrefix(String value) { + prefix = Optional.of(value); + return this; + } + + public IRNodeBuilder setMemberFunctionsLimit(int value) { + memberFunctionsLimit = Optional.of(value); + return this; + } + + public IRNodeBuilder setMemberFunctionsArgLimit(int value) { + memberFunctionsArgLimit = Optional.of(value); + return this; + } + + public IRNodeBuilder setLocalVariable(LocalVariable value) { + localVariable = Optional.of(value); + return this; + } + + public IRNodeBuilder setIsLocal(boolean value) { + isLocal = Optional.of(value); + return this; + } + + public IRNodeBuilder setIsStatic(boolean value) { + isStatic = Optional.of(value); + return this; + } + + public IRNodeBuilder setIsInitialized(boolean value) { + isInitialized = Optional.of(value); + return this; + } + + public IRNodeBuilder setIsConstant(boolean value) { + isConstant = Optional.of(value); + return this; + } + + public IRNodeBuilder setName(String value) { + name = Optional.of(value); + return this; + } + + public IRNodeBuilder setFlags(int value) { + flags = Optional.of(value); + return this; + } + + public IRNodeBuilder setFunctionInfo(FunctionInfo value) { + functionInfo = Optional.of(value); + return this; + } + + public IRNodeBuilder setPrinterName(String value) { + printerName = Optional.of(value); + return this; + } + + public IRNodeBuilder setSemicolon(boolean value) { + semicolon = Optional.of(value); + return this; + } + + // getters +/* private Type getVariableType() { + return variableType.orElseThrow(() -> new IllegalArgumentException( + "Variable type wasn't set")); + }*/ + + private TypeKlass getArgumentType() { + return argumentType.orElseThrow(() -> new IllegalArgumentException( + "Argument type wasn't set")); + } + + private int getVariableNumber() { + return variableNumber.orElseThrow(() -> new IllegalArgumentException( + "Variable number wasn't set")); + } + + private long getComplexityLimit() { + return complexityLimit.orElseThrow(() -> new IllegalArgumentException( + "Complexity limit wasn't set")); + } + + private int getOperatorLimit() { + return operatorLimit.orElseThrow(() -> new IllegalArgumentException( + "Operator limit wasn't set")); + } + + private int getStatementLimit() { + return statementLimit.orElseThrow(() -> new IllegalArgumentException( + "Statement limit wasn't set")); + } + + private TypeKlass getOwnerClass() { + return ownerClass.orElseThrow(() -> new IllegalArgumentException("Type_Klass wasn't set")); + } + + private Type getResultType() { + return resultType.orElseThrow(() -> new IllegalArgumentException("Return type wasn't set")); + } + + private boolean getExceptionSafe() { + return safe.orElseThrow(() -> new IllegalArgumentException("Safe wasn't set")); + } + + private boolean getNoConsts() { + return noConsts.orElseThrow(() -> new IllegalArgumentException("NoConsts wasn't set")); + } + + private OperatorKind getOperatorKind() { + return opKind.orElseThrow(() -> new IllegalArgumentException("Operator kind wasn't set")); + } + + private int getLevel() { + return level.orElseThrow(() -> new IllegalArgumentException("Level wasn't set")); + } + + private String getPrefix() { + return prefix.orElseThrow(() -> new IllegalArgumentException("Prefix wasn't set")); + } + + private int getMemberFunctionsLimit() { + return memberFunctionsLimit.orElseThrow(() -> new IllegalArgumentException( + "memberFunctions limit wasn't set")); + } + + private int getMemberFunctionsArgLimit() { + return memberFunctionsArgLimit.orElseThrow(() -> new IllegalArgumentException( + "memberFunctionsArg limit wasn't set")); + } + + private LocalVariable getLocalVariable() { + return localVariable.orElseThrow(() -> new IllegalArgumentException( + "local variable wasn't set")); + } + + private boolean getIsLocal() { + return isLocal.orElseThrow(() -> new IllegalArgumentException("isLocal wasn't set")); + } + + private boolean getIsStatic() { + return isStatic.orElseThrow(() -> new IllegalArgumentException("isStatic wasn't set")); + } + + private boolean getIsInitialized() { + return isInitialized.orElseThrow(() -> new IllegalArgumentException( + "isInitialized wasn't set")); + } + + private boolean getIsConstant() { + return isConstant.orElseThrow(() -> new IllegalArgumentException("isConstant wasn't set")); + } + + private boolean getCanHaveReturn() { + return canHaveReturn.orElseThrow(() -> new IllegalArgumentException( + "canHaveReturn wasn't set")); + } + + private boolean getCanHaveBreaks() { + return canHaveBreaks.orElseThrow(() -> new IllegalArgumentException( + "canHaveBreaks wasn't set")); + } + + private boolean getCanHaveContinues() { + return canHaveContinues.orElseThrow(() -> new IllegalArgumentException( + "canHaveContinues wasn't set")); + } + + private String getName() { + return name.orElseThrow(() -> new IllegalArgumentException("Name wasn't set")); + } + + private int getFlags() { + return flags.orElseThrow(() -> new IllegalArgumentException("Flags wasn't set")); + } + + private FunctionInfo getFunctionInfo() { + return functionInfo.orElseThrow(() -> new IllegalArgumentException( + "FunctionInfo wasn't set")); + } + + private String getPrinterName() { + return printerName.orElseThrow(() -> new IllegalArgumentException( + "printerName wasn't set")); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/IfFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/IfFactory.java new file mode 100644 index 00000000000..b069767facc --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/IfFactory.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.If; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeBoolean; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class IfFactory extends SafeFactory { + protected long complexityLimit; + protected int statementLimit; + protected int operatorLimit; + protected boolean canHaveBreaks; + protected boolean canHaveContinues; + protected boolean canHaveReturn; + protected final TypeKlass ownerClass; + protected final Type returnType; + protected final int level; + + IfFactory(TypeKlass ownerClass, Type returnType, long complexityLimit, int statementLimit, + int operatorLimit, int level, boolean canHaveBreaks, boolean canHaveContinues, + boolean canHaveReturn) { + this.ownerClass = ownerClass; + this.returnType = returnType; + this.complexityLimit = complexityLimit; + this.statementLimit = statementLimit; + this.operatorLimit = operatorLimit; + this.level = level; + this.canHaveBreaks = canHaveBreaks; + this.canHaveContinues = canHaveContinues; + this.canHaveReturn = canHaveReturn; + } + + @Override + public IRNode sproduce() throws ProductionFailedException { + // resizeUpChildren(If.IfPart.values().length); + if (statementLimit > 0 && complexityLimit > 0) { + long conditionComplLimit = (long) (0.01 * PseudoRandom.random() * (complexityLimit - 1)); + IRNodeBuilder builder = new IRNodeBuilder() + .setOwnerKlass(ownerClass) + .setOperatorLimit(operatorLimit); + IRNode condition = builder.setComplexityLimit(conditionComplLimit) + .setResultType(new TypeBoolean()) + .setExceptionSafe(false) + .setNoConsts(false) + .getLimitedExpressionFactory() + .produce(); + // setChild(If.IfPart.CONDITION.ordinal(), condition); + long remainder = complexityLimit - 1 - condition.complexity(); + long ifBlockComplLimit = (long) (PseudoRandom.random() * remainder); + long elseBlockComplLimit = remainder - ifBlockComplLimit; + int ifBlockLimit = (int) (PseudoRandom.random() * statementLimit); + int elseBlockLimit = statementLimit - ifBlockLimit; + If.IfPart controlDeviation; + if (ifBlockLimit > 0 && elseBlockLimit <= 0) { + controlDeviation = If.IfPart.THEN; + } else { + controlDeviation = PseudoRandom.randomBoolean() ? If.IfPart.THEN : If.IfPart.ELSE; + } + if (ifBlockLimit > 0 && ifBlockComplLimit > 0) { + IRNode thenBlock = null; + builder.setResultType(returnType) + .setLevel(level) + .setComplexityLimit(ifBlockComplLimit) + .setStatementLimit(ifBlockLimit); + if (controlDeviation == If.IfPart.THEN) { + thenBlock = builder.setSubBlock(false) + .setCanHaveBreaks(canHaveBreaks) + .setCanHaveContinues(canHaveContinues) + .setCanHaveReturn(canHaveReturn) + .getBlockFactory() + .produce(); + } else { + thenBlock = builder.setSubBlock(false) + .setCanHaveBreaks(false) + .setCanHaveContinues(false) + .setCanHaveReturn(false) + .getBlockFactory() + .produce(); + } + // setChild(If.IfPart.THEN.ordinal(), thenBlock); + IRNode elseBlock = null; + if (elseBlockLimit > 0 && elseBlockComplLimit > 0) { + builder.setComplexityLimit(elseBlockComplLimit) + .setStatementLimit(elseBlockLimit); + if (controlDeviation == If.IfPart.ELSE) { + elseBlock = builder.setSubBlock(false) + .setCanHaveBreaks(canHaveBreaks) + .setCanHaveContinues(canHaveContinues) + .setCanHaveReturn(canHaveReturn) + .getBlockFactory() + .produce(); + } else { + elseBlock = builder.setSubBlock(false) + .setCanHaveBreaks(false) + .setCanHaveContinues(false) + .setCanHaveReturn(false) + .getBlockFactory() + .produce(); + } + } + // setChild(If.IfPart.ELSE.ordinal(), elseBlock); + return new If(condition, thenBlock, elseBlock, level); + } + } + throw new ProductionFailedException(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/IncDecOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/IncDecOperatorFactory.java new file mode 100644 index 00000000000..08e7c031613 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/IncDecOperatorFactory.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.UnaryOperator; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeBoolean; + +class IncDecOperatorFactory extends UnaryOperatorFactory { + IncDecOperatorFactory(OperatorKind opKind, long complexityLimit, int operatorLimit, + Type klass, Type resultType, boolean safe, boolean noconsts) { + super(opKind, complexityLimit, operatorLimit, klass, resultType, safe, noconsts); + } + + @Override + protected boolean isApplicable(Type resultType) { + return TypeList.isBuiltInInt(resultType) && !resultType.equals(new TypeBoolean()); + } + + @Override + protected IRNode generateProduction(Type l) throws ProductionFailedException { + return new UnaryOperator(opKind, new IRNodeBuilder().setComplexityLimit(complexityLimit - 1) + .setOperatorLimit(operatorLimit - 1) + .setOwnerKlass((TypeKlass) ownerClass) + .setResultType(l) + .setIsConstant(false) + .setIsInitialized(true) + .setExceptionSafe(exceptionSafe) + .setNoConsts(exceptionSafe) + .getVariableFactory() + .produce()); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/InterfaceFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/InterfaceFactory.java new file mode 100644 index 00000000000..db2c53ed8f9 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/InterfaceFactory.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.Iterator; +import java.util.LinkedList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.classes.Interface; +import jdk.test.lib.jittester.functions.FunctionInfo; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class InterfaceFactory extends Factory { + private final String name; + private final int memberFunctionsLimit; + private final int memberFunctionsArgLimit; + private final int level; + private TypeKlass parent = null; + + InterfaceFactory(String name, int memberFunctionsLimit, int memberFuncArgLimit, int lvl) { + this.name = name; + this.memberFunctionsLimit = memberFunctionsLimit; + this.memberFunctionsArgLimit = memberFuncArgLimit; + this.level = lvl; + } + + @Override + public IRNode produce() throws ProductionFailedException { + TypeKlass thisKlass; + // Do we want to inherit something? + if (!ProductionParams.disableInheritance.value()) { + // Grab all Klasses from the TypeList and select one to be a parent + LinkedList types = new LinkedList<>(TypeList.getAll()); + for (Iterator i = types.iterator(); i.hasNext();) { + Type klass = i.next(); + if (!(klass instanceof TypeKlass) || !((TypeKlass) klass).isInterface()) { + i.remove(); + } + } + PseudoRandom.shuffle(types); + if (!types.isEmpty()) { + parent = (TypeKlass) types.getFirst(); + } + } + thisKlass = new TypeKlass(name, TypeKlass.INTERFACE); + if (parent != null) { + thisKlass.addParent(parent.getName()); + thisKlass.setParent(parent); + parent.addChild(name); + for (Symbol symbol : SymbolTable.getAllCombined(parent, FunctionInfo.class)) { + FunctionInfo functionInfo = (FunctionInfo) symbol.deepCopy(); + functionInfo.klass = thisKlass; + functionInfo.argTypes.get(0).type = thisKlass; + SymbolTable.add(functionInfo); + } + } + IRNode functionDeclarations = null; + try { + functionDeclarations = new IRNodeBuilder().setOwnerKlass(thisKlass) + .setMemberFunctionsLimit(memberFunctionsLimit) + .setMemberFunctionsArgLimit(memberFunctionsArgLimit) + .setLevel(level + 1) + .getFunctionDeclarationBlockFactory() + .produce(); + } finally { + TypeList.add(thisKlass); + } + return new Interface(parent, name, level, functionDeclarations); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/KlassFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/KlassFactory.java new file mode 100644 index 00000000000..964fa236bbc --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/KlassFactory.java @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.classes.Klass; +import jdk.test.lib.jittester.functions.FunctionDeclarationBlock; +import jdk.test.lib.jittester.functions.FunctionDefinition; +import jdk.test.lib.jittester.functions.FunctionInfo; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class KlassFactory extends Factory { + private final String name; + private final String printerName; + private final long complexityLimit; + private final int statementsInFunctionLimit; + private final int operatorLimit; + private final int memberFunctionsArgLimit; + private final int level; + private final ArrayList interfaces; + private TypeKlass thisKlass; + private TypeKlass parent; + private int memberFunctionsLimit; + + KlassFactory(String name, String printerName, long complexityLimit, + int memberFunctionsLimit, int memberFunctionsArgLimit, int statementsInFunctionLimit, + int operatorLimit, int level) { + this.name = name; + this.printerName = printerName; + this.complexityLimit = complexityLimit; + this.memberFunctionsLimit = memberFunctionsLimit; + this.memberFunctionsArgLimit = memberFunctionsArgLimit; + this.statementsInFunctionLimit = statementsInFunctionLimit; + this.operatorLimit = operatorLimit; + this.level = level; + interfaces = new ArrayList<>(); + } + + @Override + public IRNode produce() throws ProductionFailedException { + HashSet abstractSet = new HashSet<>(); + HashSet overrideSet = new HashSet<>(); + thisKlass = new TypeKlass(name); + // Do we want to inherit something? + if (!ProductionParams.disableInheritance.value()) { + inheritClass(); + inheritInterfaces(); + // Now, we should carefully construct a set of all methods with are still abstract. + // In order to do that, we will make two sets of methods: abstract and non-abstract. + // Then by substracting non-abstract from abstract we'll get what we want. + HashSet nonAbstractSet = new HashSet<>(); + for (Symbol symbol : SymbolTable.getAllCombined(thisKlass, FunctionInfo.class)) { + FunctionInfo functionInfo = (FunctionInfo) symbol; + // There could be multiple definitions or declarations encountered, + // but all we interested in are signatures. + if ((functionInfo.flags & FunctionInfo.ABSTRACT) > 0) { + abstractSet.add(functionInfo); + } else { + nonAbstractSet.add(functionInfo); + } + } + abstractSet.removeAll(nonAbstractSet); + // We may randomly remove some elements from the abstract set in order to force generation + // of an abstract class. + if (PseudoRandom.randomBoolean(0.2)) { + // so, we want to be abstract.. + for (Iterator i = abstractSet.iterator(); i.hasNext();) { + i.next(); + if (PseudoRandom.randomBoolean(0.2)) { + thisKlass.setAbstract(); + i.remove(); + } + } + } + if (PseudoRandom.randomBoolean(0.2)) { + int redefineLimit = (int) (memberFunctionsLimit * PseudoRandom.random()); + if (redefineLimit > 0) { + // We may also select some functions from the hierarchy that we want + // to redefine.. + int i = 0; + ArrayList shuffledNonAbstractSet = new ArrayList<>(nonAbstractSet); + PseudoRandom.shuffle(shuffledNonAbstractSet); + for (Symbol symbol : shuffledNonAbstractSet) { + if (++i > redefineLimit) { + break; + } + FunctionInfo functionInfo = (FunctionInfo) symbol; + if ((functionInfo.flags & FunctionInfo.FINAL) > 0) { + continue; + } + overrideSet.add(functionInfo); + } + } + } + memberFunctionsLimit -= abstractSet.size() + overrideSet.size(); + // Ok, remove the symbols from the table which are going to be overrided. + // Because the redefiner would probably modify them and put them back into table. + for (Symbol symbol : abstractSet) { + SymbolTable.remove(symbol); + } + for (Symbol symbol : overrideSet) { + SymbolTable.remove(symbol); + } + } else { + parent = (TypeKlass) TypeList.find("java.lang.Object"); + thisKlass.addParent(parent.getName()); + thisKlass.setParent(parent); + parent.addChild(name); + } + // Just don't print it. It's assumed that we at least are inherited from Object. + if (parent.getName().equals("java.lang.Object")) { + parent = null; + } + SymbolTable.add(new VariableInfo("this", thisKlass, thisKlass, + VariableInfo.FINAL | VariableInfo.LOCAL | VariableInfo.INITIALIZED)); + IRNode variableDeclarations = null; + IRNode constructorDefinitions = null; + IRNode functionDefinitions = null; + IRNode functionDeclarations = null; + IRNode abstractFunctionsRedefinitions = null; + IRNode overridenFunctionsRedefinitions = null; + IRNodeBuilder builder = new IRNodeBuilder().setPrinterName(printerName) + .setOwnerKlass(thisKlass) + .setExceptionSafe(true); + try { + builder.setLevel(level + 1) + .setOperatorLimit(operatorLimit) + .setStatementLimit(statementsInFunctionLimit) + .setMemberFunctionsArgLimit(memberFunctionsArgLimit); + variableDeclarations = builder.setComplexityLimit((long) (complexityLimit * 0.001 * PseudoRandom.random())) + .getVariableDeclarationBlockFactory().produce(); + if (!ProductionParams.disableFunctions.value()) { + // Try to implement all methods. + abstractFunctionsRedefinitions = builder.setComplexityLimit((long) (complexityLimit * 0.3 * PseudoRandom.random())) + .setLevel(level + 1) + .getFunctionRedefinitionBlockFactory(abstractSet) + .produce(); + overridenFunctionsRedefinitions = builder.setComplexityLimit((long) (complexityLimit * 0.3 * PseudoRandom.random())) + .getFunctionRedefinitionBlockFactory(overrideSet) + .produce(); + if (PseudoRandom.randomBoolean(0.2)) { // wanna be abstract ? + functionDeclarations = builder.setMemberFunctionsLimit((int) (memberFunctionsLimit * 0.2 + * PseudoRandom.random())) + .getFunctionDeclarationBlockFactory() + .produce(); + if (((FunctionDeclarationBlock) functionDeclarations).size() > 0) { + thisKlass.setAbstract(); + } + } + functionDefinitions = builder.setComplexityLimit((long) (complexityLimit * 0.5 * PseudoRandom.random())) + .setMemberFunctionsLimit((int) (memberFunctionsLimit * 0.6 + * PseudoRandom.random())) + .setFlags(FunctionInfo.NONE) + .getFunctionDefinitionBlockFactory() + .produce(); + constructorDefinitions = builder.setComplexityLimit((long) (complexityLimit * 0.2 * PseudoRandom.random())) + .setMemberFunctionsLimit((int) (memberFunctionsLimit * 0.2 + * PseudoRandom.random())) + .setStatementLimit(statementsInFunctionLimit) + .setOperatorLimit(operatorLimit) + .setLevel(level + 1) + .getConstructorDefinitionBlockFactory() + .produce(); + } + } catch (ProductionFailedException e) { + System.out.println("Exception during klass production process:"); + e.printStackTrace(System.out); + throw e; + } finally { + SymbolTable.remove(new Symbol("this", thisKlass, thisKlass, VariableInfo.NONE)); + } + // a non-abstract class can be final, so we should allow this to happen. + if (!ProductionParams.disableFinalClasses.value() && !thisKlass.isAbstract() + && PseudoRandom.randomBoolean()) { + thisKlass.setFinal(); + } + TypeList.add(thisKlass); + IRNode printVariables = builder.setLevel(2).getPrintVariablesFactory().produce(); + return new Klass(thisKlass, parent, interfaces, name, level, + variableDeclarations, constructorDefinitions, functionDefinitions, + abstractFunctionsRedefinitions, overridenFunctionsRedefinitions, + functionDeclarations, printVariables); + } + + private void inheritClass() { + // Grab all Klasses from the TypeList and select one to be a parent + LinkedList probableParents = new LinkedList<>(TypeList.getAll()); + for (Iterator i = probableParents.iterator(); i.hasNext();) { + Type klass = i.next(); + if (!(klass instanceof TypeKlass) || ((TypeKlass) klass).isFinal() + || ((TypeKlass) klass).isInterface()) { + // we can not derive from finals and interfaces + i.remove(); + } + } + if (probableParents.isEmpty()) { + parent = (TypeKlass) TypeList.find("java.lang.Object"); + } else { + parent = (TypeKlass) PseudoRandom.randomElement(probableParents); + } + thisKlass.addParent(parent.getName()); + thisKlass.setParent(parent); + parent.addChild(name); + for (Symbol symbol : SymbolTable.getAllCombined(parent)) { + if ((symbol.flags & Symbol.PRIVATE) == 0) { + Symbol symbolCopy = symbol.deepCopy(); + if (symbolCopy instanceof FunctionInfo) { + FunctionInfo functionInfo = (FunctionInfo) symbolCopy; + if (functionInfo.isConstructor()) { + continue; + } + if ((functionInfo.flags & FunctionInfo.STATIC) == 0) { + functionInfo.argTypes.get(0).type = thisKlass; + } + } + symbolCopy.klass = thisKlass; + SymbolTable.add(symbolCopy); + } + } + } + + private void inheritInterfaces() { + // Select interfaces that we'll implement. + LinkedList probableInterfaces = new LinkedList<>(TypeList.getAll()); + for (Iterator i = probableInterfaces.iterator(); i.hasNext();) { + Type klass = i.next(); + if (!(klass instanceof TypeKlass) || !((TypeKlass) klass).isInterface()) { + i.remove(); + } + } + PseudoRandom.shuffle(probableInterfaces); + int implLimit = (int) (ProductionParams.implementationLimit.value() * PseudoRandom.random()); + // Mulitiple inheritance compatibility check + compatibility_check: + for (Iterator i = probableInterfaces.iterator(); i.hasNext() && implLimit > 0; implLimit--) { + TypeKlass iface = (TypeKlass) i.next(); + ArrayList ifaceFuncSet = SymbolTable.getAllCombined(iface, FunctionInfo.class); + for (Symbol symbol : SymbolTable.getAllCombined(thisKlass, FunctionInfo.class)) { + if (FunctionDefinition.isInvalidOverride((FunctionInfo) symbol, ifaceFuncSet)) { + continue compatibility_check; + } + } + interfaces.add(iface); + iface.addChild(name); + thisKlass.addParent(iface.getName()); + thisKlass.setParent(iface); + for (Symbol symbol : SymbolTable.getAllCombined(iface, FunctionInfo.class)) { + FunctionInfo functionInfo = (FunctionInfo) symbol.deepCopy(); + functionInfo.klass = thisKlass; + functionInfo.argTypes.get(0).type = thisKlass; + SymbolTable.add(functionInfo); + } + } + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LimitedExpressionFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LimitedExpressionFactory.java new file mode 100644 index 00000000000..9600a520a96 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LimitedExpressionFactory.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionLimiter; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.types.TypeKlass; + +class LimitedExpressionFactory extends ExpressionFactory { + LimitedExpressionFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, + Type resultType, boolean exceptionSafe, boolean noconsts) throws ProductionFailedException { + super(complexityLimit, operatorLimit, ownerClass, resultType, exceptionSafe, noconsts); + } + + @Override + public IRNode sproduce() throws ProductionFailedException { + ProductionLimiter.setLimit(); + try { + return super.sproduce(); + } finally { + ProductionLimiter.setUnlimited(); + } + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LiteralFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LiteralFactory.java new file mode 100644 index 00000000000..6fb8fc91714 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LiteralFactory.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.Literal; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.types.TypeBoolean; +import jdk.test.lib.jittester.types.TypeByte; +import jdk.test.lib.jittester.types.TypeChar; +import jdk.test.lib.jittester.types.TypeDouble; +import jdk.test.lib.jittester.types.TypeFloat; +import jdk.test.lib.jittester.types.TypeInt; +import jdk.test.lib.jittester.types.TypeLong; +import jdk.test.lib.jittester.types.TypeShort; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class LiteralFactory extends Factory { + protected final Type resultType; + + LiteralFactory(Type resultType) { + this.resultType = resultType; + } + + @Override + public IRNode produce() throws ProductionFailedException { + Literal literal; + if (resultType.equals(new TypeBoolean())) { + literal = new Literal(PseudoRandom.randomBoolean(), new TypeBoolean()); + } else if (resultType.equals(new TypeChar())) { + literal = new Literal((char) ((char) (PseudoRandom.random() * ('z' - 'A')) + 'A'), new TypeChar()); + } else if (resultType.equals(new TypeInt())) { + literal = new Literal((int) (PseudoRandom.random() * Integer.MAX_VALUE), new TypeInt()); + } else if (resultType.equals(new TypeLong())) { + literal = new Literal((long) (PseudoRandom.random() * Long.MAX_VALUE), new TypeLong()); + } else if (resultType.equals(new TypeFloat())) { + literal = new Literal((float) (PseudoRandom.random() * Float.MAX_VALUE), new TypeFloat()); + } else if (resultType.equals(new TypeDouble())) { + literal = new Literal(PseudoRandom.random() * Double.MAX_VALUE, new TypeDouble()); + } else if (resultType.equals(new TypeByte())) { + literal = new Literal((byte)(PseudoRandom.random() * Byte.MAX_VALUE),new TypeByte()); + } else if (resultType.equals(new TypeShort())) { + literal = new Literal((short)(PseudoRandom.random() * Short.MAX_VALUE), new TypeShort()); + } else if (resultType.equals(TypeList.find("java.lang.String"))) { + int size = (int) (PseudoRandom.random() * ProductionParams.stringLiteralSizeLimit.value()); + byte[] str = new byte[size]; + for (int i = 0; i < size; i++) { + str[i] = (byte) ((int) (('z' - 'a') * PseudoRandom.random()) + 'a'); + } + literal = new Literal("\"" + new String(str) + "\"", TypeList.find("java.lang.String")); + } else { + throw new ProductionFailedException(); + } + return literal; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LocalVariableFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LocalVariableFactory.java new file mode 100644 index 00000000000..ea99b180257 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LocalVariableFactory.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.LocalVariable; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class LocalVariableFactory extends Factory { + private final Type type; + private final int flags; + + LocalVariableFactory(Type type, int flags) { + this.type = type; + this.flags = flags; + } + + @Override + public IRNode produce() throws ProductionFailedException { + // Get the variables of the requested type from SymbolTable + ArrayList allVariables = new ArrayList<>(SymbolTable.get(type, VariableInfo.class)); + if (!allVariables.isEmpty()) { + PseudoRandom.shuffle(allVariables); + for (Symbol symbol : allVariables) { + VariableInfo varInfo = (VariableInfo) symbol; + if ((varInfo.flags & VariableInfo.FINAL) == (flags & VariableInfo.FINAL) + && (varInfo.flags & VariableInfo.INITIALIZED) == (flags & VariableInfo.INITIALIZED) + && (varInfo.flags & VariableInfo.LOCAL) > 0) { + return new LocalVariable(varInfo); + } + } + } + throw new ProductionFailedException(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LogicOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LogicOperatorFactory.java new file mode 100644 index 00000000000..180ad34f43d --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LogicOperatorFactory.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Rule; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.types.TypeKlass; + +class LogicOperatorFactory extends Factory { + private final Rule rule; + + LogicOperatorFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, Type resultType, + boolean exceptionSafe, boolean noconsts) throws ProductionFailedException { + IRNodeBuilder builder = new IRNodeBuilder() + .setComplexityLimit(complexityLimit) + .setOperatorLimit(operatorLimit) + .setOwnerKlass(ownerClass) + .setResultType(resultType) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts); + rule = new Rule("arithmetic"); + rule.add("land", builder.setOperatorKind(OperatorKind.AND).getBinaryOperatorFactory()); + rule.add("lor", builder.setOperatorKind(OperatorKind.OR).getBinaryOperatorFactory()); + rule.add("greater", builder.setOperatorKind(OperatorKind.GT).getBinaryOperatorFactory()); + rule.add("less", builder.setOperatorKind(OperatorKind.LT).getBinaryOperatorFactory()); + rule.add("ge", builder.setOperatorKind(OperatorKind.GE).getBinaryOperatorFactory()); + rule.add("le", builder.setOperatorKind(OperatorKind.LE).getBinaryOperatorFactory()); + rule.add("eq", builder.setOperatorKind(OperatorKind.EQ).getBinaryOperatorFactory()); + rule.add("neq", builder.setOperatorKind(OperatorKind.NE).getBinaryOperatorFactory()); + rule.add("lnot", builder.setOperatorKind(OperatorKind.NOT).getUnaryOperatorFactory()); + } + + @Override + public IRNode produce() throws ProductionFailedException { + return rule.produce(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LogicalInversionOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LogicalInversionOperatorFactory.java new file mode 100644 index 00000000000..567a0aa73b3 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LogicalInversionOperatorFactory.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.UnaryOperator; +import jdk.test.lib.jittester.types.TypeBoolean; +import jdk.test.lib.jittester.types.TypeKlass; + +class LogicalInversionOperatorFactory extends UnaryOperatorFactory { + LogicalInversionOperatorFactory(long complexityLimit, int operatorLimit, + Type ownerType, Type resultType, boolean exceptionSafe, boolean noconsts) { + super(OperatorKind.NOT, complexityLimit, operatorLimit, ownerType, resultType, exceptionSafe, noconsts); + } + + @Override + protected boolean isApplicable(Type resultType) { + return resultType.equals(new TypeBoolean()); + } + + @Override + protected IRNode generateProduction(Type resultType) throws ProductionFailedException { + return new UnaryOperator(opKind, new ExpressionFactory(complexityLimit - 1, + operatorLimit - 1, (TypeKlass) ownerClass, resultType, exceptionSafe, noconsts).produce()); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LoopingConditionFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LoopingConditionFactory.java new file mode 100644 index 00000000000..b9e3ac7969b --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/LoopingConditionFactory.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.BinaryOperator; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.Literal; +import jdk.test.lib.jittester.LocalVariable; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.loops.LoopingCondition; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeBoolean; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class LoopingConditionFactory extends Factory { + private final LocalVariable counter; + private final Literal limiter; + private final int operatorLimit; + private final long complexityLimit; + private final TypeKlass ownerClass; + + LoopingConditionFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, + LocalVariable counter, Literal limiter) { + this.complexityLimit = complexityLimit; + this.operatorLimit = operatorLimit; + this.counter = counter; + this.limiter = limiter; + this.ownerClass = ownerClass; + } + + @Override + public IRNode produce() throws ProductionFailedException { + IRNode leftExpression = null; + IRNode rightExpression = null; + LimitedExpressionFactory exprFactory = new IRNodeBuilder() + .setResultType(new TypeBoolean()) + .setComplexityLimit((complexityLimit - 1) / 2) + .setOperatorLimit((operatorLimit - 1) / 2) + .setOwnerKlass(ownerClass) + .setExceptionSafe(false) + .setNoConsts(false) + .getLimitedExpressionFactory(); + if (PseudoRandom.randomBoolean()) { + leftExpression = exprFactory.produce(); + } + if (PseudoRandom.randomBoolean()) { + rightExpression = exprFactory.produce(); + } + // Depending on loop counter direction, we should synthesize limiting condition. + // Example: If the counter is counting forward. Then the looping condition can be: + // counter < n, counter <= n, n > counter, n >= counter, n - counter > 0, etc.. + + // Just as a temporary solution we'll assume that the counter is monotonically increasing. + // And use counter < n condition to limit the loop. + // In future we may introduce other equivalent relations as well. + IRNode condition = new BinaryOperator(OperatorKind.LT, counter, limiter); + condition = (rightExpression != null) ? new BinaryOperator(OperatorKind.AND, condition, + rightExpression) : condition; + condition = (leftExpression != null) ? new BinaryOperator(OperatorKind.AND, leftExpression, + condition) : condition; + return new LoopingCondition(condition); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/MainKlassFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/MainKlassFactory.java new file mode 100644 index 00000000000..1deee728177 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/MainKlassFactory.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import jdk.test.lib.jittester.Block; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.classes.MainKlass; +import jdk.test.lib.jittester.functions.FunctionInfo; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeVoid; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class MainKlassFactory extends Factory { + private final String name; + private final long complexityLimit; + private final int statementsInTestFunctionLimit; + private final int statementsInFunctionLimit; + private final int operatorLimit; + private final int memberFunctionsLimit; + private final int memberFunctionsArgLimit; + private TypeKlass thisKlass; + + MainKlassFactory(String name, long complexityLimit, int memberFunctionsLimit, + int memberFunctionsArgLimit, int statementsInFunctionLimit, + int statementsInTestFunctionLimit, int operatorLimit) { + this.name = name; + this.complexityLimit = complexityLimit; + this.memberFunctionsLimit = memberFunctionsLimit; + this.memberFunctionsArgLimit = memberFunctionsArgLimit; + this.statementsInFunctionLimit = statementsInFunctionLimit; + this.statementsInTestFunctionLimit = statementsInTestFunctionLimit; + this.operatorLimit = operatorLimit; + } + + @Override + public IRNode produce() throws ProductionFailedException { + TypeKlass parent = (TypeKlass) TypeList.find("java.lang.Object"); + thisKlass = new TypeKlass(name); + thisKlass.addParent(parent.getName()); + thisKlass.setParent(parent); + parent.addChild(name); + parent.addChild(thisKlass); + SymbolTable.add(new VariableInfo("this", thisKlass, thisKlass, + VariableInfo.FINAL | VariableInfo.LOCAL | VariableInfo.INITIALIZED)); + IRNodeBuilder builder = new IRNodeBuilder() + .setOwnerKlass(thisKlass) + .setOperatorLimit(operatorLimit) + .setMemberFunctionsLimit(memberFunctionsLimit) + .setMemberFunctionsArgLimit(memberFunctionsArgLimit) + .setStatementLimit(statementsInFunctionLimit) + .setLevel(1) + .setExceptionSafe(true) + .setPrinterName("Printer"); + IRNode variableDeclarations = builder + .setComplexityLimit((long) (complexityLimit * 0.05)) + .getVariableDeclarationBlockFactory().produce(); + IRNode functionDefinitions = null; + if (!ProductionParams.disableFunctions.value()) { + functionDefinitions = builder + .setComplexityLimit((long) (complexityLimit * 0.01 * PseudoRandom.random())) + .setFlags(FunctionInfo.NONRECURSIVE) + .getFunctionDefinitionBlockFactory() + .produce(); + } + IRNode testFunction = builder.setResultType(new TypeVoid()) + .setComplexityLimit(complexityLimit) + .setStatementLimit(statementsInTestFunctionLimit) + .getBlockFactory() + .produce(); + SymbolTable.remove(new Symbol("this", thisKlass, thisKlass, VariableInfo.NONE)); + IRNode printVariables = builder.setLevel(2) + .getPrintVariablesFactory() + .produce(); + List childs = new ArrayList<>(); + childs.add(variableDeclarations); + childs.add(functionDefinitions); + childs.add(testFunction); + childs.add(printVariables); + ensureMinDepth(childs, builder); + ensureMaxDepth(childs); + return new MainKlass(name, thisKlass, variableDeclarations, + functionDefinitions, testFunction, printVariables); + } + + private void ensureMaxDepth(List childs) { + int maxDepth = ProductionParams.maxCfgDepth.value(); + List filtered = childs.stream() + .filter(c -> c.isCFDeviation() && c.countDepth() > maxDepth) + .collect(Collectors.toList()); + for (IRNode child : filtered) { + List leaves = null; + do { + long depth = Math.max(child.countDepth(), maxDepth + 1); + leaves = child.getDeviantBlocks(depth); + leaves.get(0).removeSelf(); + } while (!leaves.isEmpty() && child.countDepth() > maxDepth); + } + } + + private void ensureMinDepth(List childs, IRNodeBuilder builder) + throws ProductionFailedException { + int minDepth = ProductionParams.minCfgDepth.value(); + List filtered = new ArrayList<>(childs); + addMoreChildren(filtered, minDepth, builder); + } + + private void addMoreChildren(List childs, int minDepth, IRNodeBuilder builder) + throws ProductionFailedException { + while (!childs.isEmpty() && IRNode.countDepth(childs) < minDepth) { + PseudoRandom.shuffle(childs); + IRNode randomChild = childs.get(0); + List leaves = randomChild.getStackableLeaves(); + if (!leaves.isEmpty()) { + PseudoRandom.shuffle(leaves); + Block randomLeaf = (Block) leaves.get(0); + TypeKlass klass = (TypeKlass) randomChild.getKlass(); + int newLevel = randomLeaf.getLevel() + 1; + Type retType = randomLeaf.getReturnType(); + IRNode newBlock = builder.setOwnerKlass(klass) + .setResultType(retType) + .setComplexityLimit(complexityLimit) + .setStatementLimit(statementsInFunctionLimit) + .setLevel(newLevel) + .getBlockFactory() + .produce(); + List siblings = randomLeaf.getChildren(); + // to avoid break; + int index = PseudoRandom.randomNotZero(siblings.size() - 1); + siblings.add(index, newBlock); + } + } + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/NonStaticMemberVariableFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/NonStaticMemberVariableFactory.java new file mode 100644 index 00000000000..102c3c77ac4 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/NonStaticMemberVariableFactory.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.NonStaticMemberVariable; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class NonStaticMemberVariableFactory extends Factory { + private final Type type; + private final int flags; + private final long complexityLimit; + private final int operatorLimit; + private final boolean exceptionSafe; + private final Type ownerClass; + + NonStaticMemberVariableFactory(long complexityLimit, int operatorLimit, + TypeKlass ownerClass, Type type, int flags, boolean exceptionSafe) { + this.ownerClass = ownerClass; + this.type = type; + this.flags = flags; + this.complexityLimit = complexityLimit; + this.operatorLimit = operatorLimit; + this.exceptionSafe = exceptionSafe; + } + + @Override + public IRNode produce() throws ProductionFailedException { + // Get the variables of the requested type from SymbolTable + ArrayList variables = new ArrayList<>(SymbolTable.get(type, VariableInfo.class)); + if (!variables.isEmpty()) { + PseudoRandom.shuffle(variables); + IRNodeBuilder builder = new IRNodeBuilder().setComplexityLimit(complexityLimit) + .setOperatorLimit(operatorLimit) + .setOwnerKlass((TypeKlass) ownerClass) + .setExceptionSafe(exceptionSafe) + .setNoConsts(false); + for (Symbol symbol : variables) { + VariableInfo varInfo = (VariableInfo) symbol; + if ((varInfo.flags & VariableInfo.FINAL) == (flags & VariableInfo.FINAL) + && (varInfo.flags & VariableInfo.INITIALIZED) == (flags & VariableInfo.INITIALIZED) + && (varInfo.flags & VariableInfo.STATIC) == 0 + && (varInfo.flags & VariableInfo.LOCAL) == 0) { + try { + IRNode object = builder.setResultType(varInfo.klass) + .getExpressionFactory().produce(); + return new NonStaticMemberVariable(object, varInfo); + } catch (ProductionFailedException e) { + } + } + } + } + throw new ProductionFailedException(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/NothingFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/NothingFactory.java new file mode 100644 index 00000000000..a05d21700f2 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/NothingFactory.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.Nothing; +import jdk.test.lib.jittester.ProductionFailedException; + +public class NothingFactory extends Factory { + @Override + public Nothing produce() throws ProductionFailedException { + return new Nothing(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/OperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/OperatorFactory.java new file mode 100644 index 00000000000..2c2eecf4054 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/OperatorFactory.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +public abstract class OperatorFactory extends Factory { + protected long complexityLimit; + protected boolean exceptionSafe; + protected boolean noconsts; + protected int operatorLimit; + protected int operatorPriority; + + protected OperatorFactory(int operatorPriority, long complexityLimit, int operatorLimit, + boolean exceptionSafe, boolean noconsts) { + this.operatorLimit = operatorLimit; + this.complexityLimit = complexityLimit; + this.operatorPriority = operatorPriority; + this.exceptionSafe = exceptionSafe; + this.noconsts = noconsts; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/PrintVariablesFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/PrintVariablesFactory.java new file mode 100644 index 00000000000..481e088d5f3 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/PrintVariablesFactory.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.PrintVariables; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.types.TypeKlass; + +class PrintVariablesFactory extends Factory { + private final String printerName; + private final TypeKlass ownerClass; + private final int level; + + PrintVariablesFactory(String printerName, TypeKlass ownerClass, int level) { + this.printerName = printerName; + this.ownerClass = ownerClass; + this.level = level; + } + + @Override + public IRNode produce() throws ProductionFailedException { + return new PrintVariables(printerName, SymbolTable.getAllCombined(ownerClass, + VariableInfo.class), level); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ReturnFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ReturnFactory.java new file mode 100644 index 00000000000..9a86a339f42 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ReturnFactory.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.functions.Return; +import jdk.test.lib.jittester.types.TypeKlass; + +class ReturnFactory extends SafeFactory { + private final long complexityLimit; + private final int operatorLimit; + private final Type resultType; + private final boolean exceptionSafe; + private final TypeKlass ownerClass; + + ReturnFactory(long compLimit, int opLimit, TypeKlass ownerClass, + Type resultType, boolean exceptionSafe) { + this.complexityLimit = compLimit; + this.operatorLimit = opLimit; + this.resultType = resultType; + this.ownerClass = ownerClass; + this.exceptionSafe = exceptionSafe; + } + + @Override + protected IRNode sproduce() throws ProductionFailedException { + return new Return(new IRNodeBuilder().setComplexityLimit(complexityLimit - 1) + .setOperatorLimit(operatorLimit - 1) + .setOwnerKlass(ownerClass) + .setResultType(resultType) + .setExceptionSafe(exceptionSafe) + .setNoConsts(false) + .getLimitedExpressionFactory() + .produce()); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/SafeFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/SafeFactory.java new file mode 100644 index 00000000000..4cada2a906f --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/SafeFactory.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.SymbolTable; + +public abstract class SafeFactory extends Factory { + protected abstract IRNode sproduce() throws ProductionFailedException; + + @Override + public IRNode produce() throws ProductionFailedException { + try { + SymbolTable.push(); + IRNode p = sproduce(); + SymbolTable.merge(); + return p; + } catch (ProductionFailedException e) { + SymbolTable.pop(); + throw e; + } + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/StatementFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/StatementFactory.java new file mode 100644 index 00000000000..d3f45f74546 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/StatementFactory.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionLimiter; +import jdk.test.lib.jittester.Rule; +import jdk.test.lib.jittester.Statement; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class StatementFactory extends Factory { + private final Rule rule; + private final boolean needSemicolon; + + StatementFactory(long complexityLimit, int operatorLimit, + TypeKlass ownerClass, boolean exceptionSafe, + boolean noconsts, boolean needSemicolon ){ + this.needSemicolon = needSemicolon; + rule = new Rule("statement"); + IRNodeBuilder builder = new IRNodeBuilder() + .setComplexityLimit(complexityLimit) + .setOperatorLimit(operatorLimit) + .setOwnerKlass(ownerClass) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts) + .setResultType(PseudoRandom.randomElement(TypeList.getAll())); + rule.add("array_creation", builder.getArrayCreationFactory()); + rule.add("assignment", builder.getAssignmentOperatorFactory()); + rule.add("function", builder.getFunctionFactory(), 0.1); + } + + @Override + public IRNode produce() throws ProductionFailedException { + ProductionLimiter.setLimit(); + try { + return new Statement(rule.produce(), needSemicolon); + } finally { + ProductionLimiter.setUnlimited(); + } + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/StaticConstructorDefinitionFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/StaticConstructorDefinitionFactory.java new file mode 100644 index 00000000000..b0eccda93ea --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/StaticConstructorDefinitionFactory.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.functions.StaticConstructorDefinition; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeVoid; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class StaticConstructorDefinitionFactory extends Factory { + private final long complexityLimit; + private final int statementLimit; + private final int operatorLimit; + private final int level; + private final TypeKlass ownerClass; + + StaticConstructorDefinitionFactory(TypeKlass ownerClass, long complexityLimit, + int statementLimit, int operatorLimit, int level) { + this.ownerClass = ownerClass; + this.complexityLimit = complexityLimit; + this.statementLimit = statementLimit; + this.operatorLimit = operatorLimit; + this.level = level; + } + + @Override + public IRNode produce() throws ProductionFailedException { + SymbolTable.push(); + IRNode body; + try { + SymbolTable.remove(SymbolTable.get("this", VariableInfo.class)); + long complLimit = (long) (PseudoRandom.random() * complexityLimit); + body = new IRNodeBuilder() + .setOwnerKlass(ownerClass) + .setResultType(new TypeVoid()) + .setComplexityLimit(complLimit) + .setStatementLimit(statementLimit) + .setOperatorLimit(operatorLimit) + .setLevel(level) + .setSubBlock(true) + .setCanHaveBreaks(true) + .setCanHaveContinues(false) + .setCanHaveReturn(false) + .getBlockFactory() + .produce(); + } finally { + SymbolTable.pop(); + } + return new StaticConstructorDefinition(body); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/StaticMemberVariableFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/StaticMemberVariableFactory.java new file mode 100644 index 00000000000..83e1a3b29d9 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/StaticMemberVariableFactory.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.StaticMemberVariable; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class StaticMemberVariableFactory extends Factory { + private final Type type; + private final int flags; + private final Type ownerClass; + + StaticMemberVariableFactory(TypeKlass ownerClass, Type type, int flags) { + this.ownerClass = ownerClass; + this.type = type; + this.flags = flags; + } + + @Override + public IRNode produce() throws ProductionFailedException { + // Get the variables of the requested type from SymbolTable + ArrayList variables = new ArrayList<>(SymbolTable.get(type, VariableInfo.class)); + if (!variables.isEmpty()) { + PseudoRandom.shuffle(variables); + for (Symbol symbol : variables) { + VariableInfo varInfo = (VariableInfo) symbol; + if ((varInfo.flags & VariableInfo.FINAL) == (flags & VariableInfo.FINAL) + && (varInfo.flags & VariableInfo.INITIALIZED) == (flags & VariableInfo.INITIALIZED) + && (varInfo.flags & VariableInfo.STATIC) > 0) { + return new StaticMemberVariable((TypeKlass) ownerClass, varInfo); + } + } + } + throw new ProductionFailedException(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/SwitchFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/SwitchFactory.java new file mode 100644 index 00000000000..8ee2ecf67ca --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/SwitchFactory.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import jdk.test.lib.jittester.BuiltInType; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.Literal; +import jdk.test.lib.jittester.Nothing; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Rule; +import jdk.test.lib.jittester.Switch; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeUtil; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeByte; +import jdk.test.lib.jittester.types.TypeChar; +import jdk.test.lib.jittester.types.TypeInt; +import jdk.test.lib.jittester.types.TypeShort; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class SwitchFactory extends SafeFactory { + private int caseBlockIdx; + protected long complexityLimit; + protected int statementLimit, operatorLimit; + private boolean canHaveReturn = false; + private final TypeKlass ownerClass; + private final int level; + + SwitchFactory(TypeKlass ownerClass, long complexityLimit, int statementLimit, + int operatorLimit, int level, boolean canHaveReturn) { + this.ownerClass = ownerClass; + this.complexityLimit = complexityLimit; + this.statementLimit = statementLimit; + this.operatorLimit = operatorLimit; + this.level = level; + this.canHaveReturn = canHaveReturn; + } + + @Override + protected IRNode sproduce() throws ProductionFailedException { + if (statementLimit > 0 && complexityLimit > 0) { + ArrayList switchTypes = new ArrayList<>(); + switchTypes.add(new TypeChar()); + switchTypes.add(new TypeByte()); + switchTypes.add(new TypeShort()); + switchTypes.add(new TypeInt()); + PseudoRandom.shuffle(switchTypes); + IRNodeBuilder builder = new IRNodeBuilder() + .setOwnerKlass(ownerClass) + .setOperatorLimit(operatorLimit) + .setSubBlock(false) + .setCanHaveBreaks(true) + .setCanHaveContinues(false) + .setCanHaveReturn(canHaveReturn); + MAIN_LOOP: + for (Type type : switchTypes) { + ArrayList caseConsts = new ArrayList<>(); + ArrayList caseBlocks = new ArrayList<>(); + try { + int accumulatedStatements = 0; + int currentStatementsLimit = 0; + long accumulatedComplexity = 0L; + long currentComplexityLimit = 0L; + currentComplexityLimit = (long) (PseudoRandom.random() + * (complexityLimit - accumulatedComplexity)); + IRNode switchExp = builder.setComplexityLimit(currentComplexityLimit) + .setResultType(type) + .setExceptionSafe(false) + .setNoConsts(true) + .getLimitedExpressionFactory() + .produce(); + accumulatedComplexity += currentComplexityLimit; + ArrayList caseTypes = new ArrayList<>(); + caseTypes.add(new TypeByte()); + caseTypes.add(new TypeChar()); + caseTypes = new ArrayList<>(TypeUtil.getLessCapatiousOrEqualThan(caseTypes, + (BuiltInType) type)); + if (PseudoRandom.randomBoolean()) { // "default" + currentStatementsLimit = (int) (PseudoRandom.random() + * (statementLimit - accumulatedStatements)); + currentComplexityLimit = (long) (PseudoRandom.random() + * (complexityLimit - accumulatedComplexity)); + caseConsts.add(null); + caseBlocks.add(builder.setComplexityLimit(currentComplexityLimit) + .setStatementLimit(currentStatementsLimit) + .setLevel(level + 1) + .setCanHaveReturn(false) + .setCanHaveBreaks(false) + .getBlockFactory() + .produce()); + builder.setCanHaveBreaks(true) + .setCanHaveReturn(canHaveReturn); + accumulatedStatements += currentStatementsLimit; + accumulatedComplexity += currentComplexityLimit; + } + HashSet cases = new HashSet<>(); + while (accumulatedStatements < statementLimit) { // "case"s + currentStatementsLimit = (int) (PseudoRandom.random() + * (statementLimit - accumulatedStatements)); + currentComplexityLimit = (long) (PseudoRandom.random() + * (complexityLimit - accumulatedComplexity)); + PseudoRandom.shuffle(caseTypes); + for (int tryCount = 0; true; tryCount++) { + if (tryCount >= 10) { + continue MAIN_LOOP; + } + Literal literal = (Literal) builder.setResultType(caseTypes.get(0)) + .getLiteralFactory().produce(); + int value = 0; + if (literal.value instanceof Integer) { + value = (Integer) literal.value; + } + if (literal.value instanceof Short) { + value = (Short) literal.value; + } + if (literal.value instanceof Byte) { + value = (Byte) literal.value; + } + if (literal.value instanceof Character) { + value = (Character) literal.value; + } + if (!cases.contains(value)) { + cases.add(value); + caseConsts.add(literal); + break; + } + } + Rule rule = new Rule("case_block"); + rule.add("block", builder.setComplexityLimit(currentComplexityLimit) + .setStatementLimit(currentStatementsLimit) + .setLevel(level) + .setCanHaveReturn(false) + .setCanHaveBreaks(false) + .getBlockFactory()); + builder.setCanHaveBreaks(true) + .setCanHaveReturn(canHaveReturn); + rule.add("nothing", builder.getNothingFactory()); + IRNode choiceResult = rule.produce(); + caseBlocks.add(choiceResult); + if (choiceResult instanceof Nothing) { + accumulatedStatements++; + } else { + accumulatedStatements += currentStatementsLimit; + accumulatedComplexity += currentComplexityLimit; + } + } + PseudoRandom.shuffle(caseConsts); + List accum = new ArrayList<>(); + caseBlockIdx = 1 + caseConsts.size(); + accum.add(switchExp); + for (int i = 1; i < caseBlockIdx; ++i) { + accum.add(caseConsts.get(i - 1)); + } + for (int i = caseBlockIdx; i < 1 + caseConsts.size() + caseBlocks.size(); ++i) { + accum.add(caseBlocks.get(i - caseBlockIdx)); + } + return new Switch(level, accum, caseBlockIdx); + } catch (ProductionFailedException e) { + } + } + } + throw new ProductionFailedException(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/TernaryOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/TernaryOperatorFactory.java new file mode 100644 index 00000000000..5060736bd21 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/TernaryOperatorFactory.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.Pair; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.TernaryOperator; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.TypeUtil; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeBoolean; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class TernaryOperatorFactory extends OperatorFactory { + protected final Type resultType; + protected final TypeKlass ownerClass; + + TernaryOperatorFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, + Type resultType, boolean exceptionSafe, boolean noconsts) { + super(2, complexityLimit, operatorLimit, exceptionSafe, noconsts); + this.resultType = resultType; + this.ownerClass = ownerClass; + } + + private Pair generateTypes() { + Pair types = new Pair<>(resultType, PseudoRandom.randomElement( + TypeUtil.getImplicitlyCastable(TypeList.getAll(), resultType))); + if (PseudoRandom.randomBoolean()) + types = new Pair<>(types.second, types.first); + return types; + } + + private IRNode generateProduction(Type conditionType, Type leftType, Type rightType) throws ProductionFailedException { + int leftOpLimit = (int) (PseudoRandom.random() * 0.3 * (operatorLimit - 1)); + int rightOpLimit = (int) (PseudoRandom.random() * 0.3 * (operatorLimit - 1)); + int condOpLimit = operatorLimit - 1 - leftOpLimit - rightOpLimit; + long leftComplLimit = (long) (PseudoRandom.random() * 0.3 * (complexityLimit - 1)); + long rightComplLimit = (long) (PseudoRandom.random() * 0.3 * (complexityLimit - 1)); + long condComplLimit = complexityLimit - 1 - leftComplLimit - rightComplLimit; + if (leftComplLimit == 0 || rightComplLimit == 0 || condComplLimit == 0 + || leftOpLimit == 0 || rightOpLimit == 0 || condOpLimit == 0) { + throw new ProductionFailedException(); + } + IRNodeBuilder builder = new IRNodeBuilder().setOwnerKlass(ownerClass) + .setExceptionSafe(exceptionSafe); + IRNode conditionalExp = builder.setComplexityLimit(condComplLimit) + .setOperatorLimit(condOpLimit) + .setResultType(conditionType) + .setNoConsts(noconsts) + .getExpressionFactory() + .produce(); + // Ignore initializations performed in left and right branches: + IRNode leftExp; + SymbolTable.push(); + try { + leftExp = builder.setComplexityLimit(leftComplLimit) + .setOperatorLimit(leftOpLimit) + .setResultType(leftType) + .setNoConsts(false) + .getExpressionFactory() + .produce(); + } finally { + SymbolTable.pop(); + } + IRNode rightExp; + SymbolTable.push(); + try { + rightExp = builder.setComplexityLimit(rightComplLimit) + .setOperatorLimit(rightOpLimit) + .setResultType(rightType) + .setNoConsts(false) + .getExpressionFactory() + .produce(); + } finally { + SymbolTable.pop(); + } + return new TernaryOperator(conditionalExp, leftExp, rightExp); + } + + @Override + public IRNode produce() throws ProductionFailedException { + Pair types; + try { + types = generateTypes(); + } catch (RuntimeException ex) { + throw new ProductionFailedException(ex.getMessage()); + } + try { + SymbolTable.push(); + IRNode result = generateProduction(new TypeBoolean(), types.first, types.second); + SymbolTable.merge(); + return result; + } catch (ProductionFailedException e) { + SymbolTable.pop(); + throw e; + } + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ThrowFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ThrowFactory.java new file mode 100644 index 00000000000..00e30f06b83 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/ThrowFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Rule; +import jdk.test.lib.jittester.Throw; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.types.TypeKlass; + +class ThrowFactory extends SafeFactory { + private final Rule rule; + + ThrowFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, + Type resultType, boolean exceptionSafe) { + IRNodeBuilder b = new IRNodeBuilder() + .setComplexityLimit(complexityLimit) + .setOperatorLimit(operatorLimit) + .setOwnerKlass(ownerClass) + .setResultType(resultType) + .setExceptionSafe(exceptionSafe) + .setNoConsts(false); + rule = new Rule("throw"); + rule.add("constant", b.setIsConstant(true).setIsInitialized(true).getVariableFactory()); + rule.add("variable", b.setIsConstant(false).setIsInitialized(true).getVariableFactory()); + + rule.add("assignment", b.getAssignmentOperatorFactory()); + rule.add("function", b.getFunctionFactory(), 2); + } + + @Override + protected IRNode sproduce() throws ProductionFailedException { + return new Throw(rule.produce()); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/TryCatchBlockFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/TryCatchBlockFactory.java new file mode 100644 index 00000000000..dea0c9325f6 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/TryCatchBlockFactory.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import java.util.List; +import jdk.test.lib.jittester.CatchBlock; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.TryCatchBlock; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.TypeUtil; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class TryCatchBlockFactory extends Factory { + private final static double CATCH_SELECTION_COEF = 0.1d; + private final Type returnType; + private final long complexityLimit; + private final int statementLimit, operatorLimit; + private final boolean subBlock; + private final boolean canHaveBreaks; + private final boolean canHaveContinues; + private final boolean canHaveReturn; + private final int level; + private final TypeKlass ownerClass; + + TryCatchBlockFactory(TypeKlass ownerClass, Type returnType, + long complexityLimit, int statementLimit, int operatorLimit, + int level, boolean subBlock, boolean canHaveBreaks, + boolean canHaveContinues, boolean canHaveReturn) { + this.ownerClass = ownerClass; + this.returnType = returnType; + this.complexityLimit = complexityLimit; + this.statementLimit = statementLimit; + this.operatorLimit = operatorLimit; + this.level = level; + this.subBlock = subBlock; + this.canHaveBreaks = canHaveBreaks; + this.canHaveContinues = canHaveContinues; + this.canHaveReturn = canHaveReturn; + } + + @Override + public IRNode produce() throws ProductionFailedException { + if (complexityLimit < 1 || statementLimit < 1) { + throw new ProductionFailedException(); + } + List uncheckedThrowables = getUncheckedThrowables(); + IRNodeBuilder builder = new IRNodeBuilder().setOwnerKlass(ownerClass) + .setResultType(returnType) + .setOperatorLimit(operatorLimit) + .setLevel(level) + .setSubBlock(subBlock) + .setCanHaveReturn(canHaveReturn) + .setCanHaveContinues(canHaveContinues) + .setCanHaveBreaks(canHaveBreaks); + IRNode body = getBlock(builder, 0.6); + int catchBlocksCount = (int) (CATCH_SELECTION_COEF + * PseudoRandom.random() * uncheckedThrowables.size()); + List catchBlocks = new ArrayList<>(); + List caught = new ArrayList<>(); + for (int i = 0; i < catchBlocksCount; i++) { + List whatToCatch = new ArrayList<>(); + int throwableLimit = 1 + (int) ((1/(2*CATCH_SELECTION_COEF)) + * PseudoRandom.random()); + for (int j = 0; j < throwableLimit; j++) { + whatToCatch.add(selectUniqueThrowable(uncheckedThrowables, caught)); + } + catchBlocks.add(new CatchBlock(getBlock(builder, 0.3/catchBlocksCount), + whatToCatch, level)); + } + IRNode finallyBody = PseudoRandom.randomBoolean() || catchBlocksCount == 0 ? getBlock(builder, 0.1) : null; + return new TryCatchBlock(body, finallyBody, catchBlocks, level); + } + + private Type selectUniqueThrowable(List variants, List caught) { + Type selected; + do { + int randomIndex = PseudoRandom.randomNotZero(variants.size()) - 1; + selected = variants.get(randomIndex); + } while (caught.contains(selected)); + caught.add(selected); + return selected; + } + + private IRNode getBlock(IRNodeBuilder builder, double weight) + throws ProductionFailedException { + long actualComplexityLim = (long) (weight * PseudoRandom.random() + * complexityLimit); + int actualStatementLim = (int) (weight * PseudoRandom.random() + * statementLimit); + return builder.setStatementLimit(actualStatementLim) + .setComplexityLimit(actualComplexityLim) + .getBlockFactory() + .produce(); + } + + private List getUncheckedThrowables() { + List result = new ArrayList<>(); + result.addAll(TypeUtil.getImplicitlyCastable(TypeList.getAll(), + new TypeKlass("java.lang.Error"))); + result.addAll(TypeUtil.getImplicitlyCastable(TypeList.getAll(), + new TypeKlass("java.lang.RuntimeException"))); + return result; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/UnaryOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/UnaryOperatorFactory.java new file mode 100644 index 00000000000..7e4149f2f9a --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/UnaryOperatorFactory.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; + +public abstract class UnaryOperatorFactory extends OperatorFactory { + protected final OperatorKind opKind; + protected final Type resultType; + protected final Type ownerClass; + + protected UnaryOperatorFactory(OperatorKind opKind, long complexityLimit, int operatorLimit, + Type ownerClass, Type resultType, boolean exceptionSafe, boolean noconsts) { + super(opKind.priority, complexityLimit, operatorLimit, exceptionSafe, noconsts); + this.opKind = opKind; + this.resultType = resultType; + this.ownerClass = ownerClass; + } + + protected Type generateType() throws ProductionFailedException { + return resultType; + } + + protected abstract IRNode generateProduction(Type type) throws ProductionFailedException; + + protected abstract boolean isApplicable(Type resultType); + + @Override + public IRNode produce() throws ProductionFailedException { + if (!isApplicable(resultType)) { + //avoid implicit use of resultType.toString() + throw new ProductionFailedException("Type " + resultType.getName() + + " is not applicable by " + getClass().getName()); + } + Type type; + try { + type = generateType(); + } catch (Exception ex) { + throw new ProductionFailedException(ex.getMessage()); + } + try { + SymbolTable.push(); + IRNode result = generateProduction(type); + SymbolTable.merge(); + return result; + } catch (ProductionFailedException e) { + SymbolTable.pop(); + throw e; + } + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/UnaryPlusMinusOperatorFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/UnaryPlusMinusOperatorFactory.java new file mode 100644 index 00000000000..79b0e23a7dd --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/UnaryPlusMinusOperatorFactory.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.BuiltInType; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.OperatorKind; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.TypeUtil; +import jdk.test.lib.jittester.UnaryOperator; +import jdk.test.lib.jittester.types.TypeBoolean; +import jdk.test.lib.jittester.types.TypeInt; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class UnaryPlusMinusOperatorFactory extends UnaryOperatorFactory { + UnaryPlusMinusOperatorFactory(OperatorKind opKind, long complexityLimit, int operatorLimit, + Type ownerClass, Type resultType, boolean exceptionSafe, boolean noconsts) { + super(opKind, complexityLimit, operatorLimit, ownerClass, resultType, exceptionSafe, noconsts); + } + + @Override + protected boolean isApplicable(Type resultType) { + if (!TypeList.isBuiltIn(resultType) || resultType.equals(new TypeBoolean())) { + return false; + } + BuiltInType resType = (BuiltInType) resultType; + return resType.equals(new TypeInt()) || resType.isMoreCapaciousThan(new TypeInt()); + } + + @Override + protected Type generateType() throws ProductionFailedException { + if (resultType.equals(new TypeInt())) { + return PseudoRandom.randomElement(TypeUtil.getImplicitlyCastable(TypeList.getBuiltIn(), resultType)); + } else { + return resultType; + } + } + + @Override + protected IRNode generateProduction(Type type) throws ProductionFailedException { + return new UnaryOperator(opKind, new IRNodeBuilder() + .setComplexityLimit(complexityLimit) + .setOperatorLimit(operatorLimit) + .setOwnerKlass((TypeKlass) ownerClass) + .setResultType(type) + .setExceptionSafe(exceptionSafe) + .setNoConsts(noconsts) + .getExpressionFactory() + .produce()); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/VariableDeclarationBlockFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/VariableDeclarationBlockFactory.java new file mode 100644 index 00000000000..3fe9d644d96 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/VariableDeclarationBlockFactory.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.VariableDeclarationBlock; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class VariableDeclarationBlockFactory extends Factory { + private final long complexityLimit; + private final int operatorLimit; + private final boolean exceptionSafe; + private final int level; + private final TypeKlass ownerClass; + + VariableDeclarationBlockFactory(TypeKlass ownerClass, long complexityLimit, + int operatorLimit, int level, boolean exceptionSafe) { + this.ownerClass = ownerClass; + this.complexityLimit = complexityLimit; + this.operatorLimit = operatorLimit; + this.level = level; + this.exceptionSafe = exceptionSafe; + } + + @Override + public IRNode produce() throws ProductionFailedException { + ArrayList content = new ArrayList<>(); + int limit = (int) Math.ceil(PseudoRandom.random() * ProductionParams.dataMemberLimit.value()); + DeclarationFactory declFactory = new IRNodeBuilder() + .setOwnerKlass(ownerClass) + .setComplexityLimit(complexityLimit) + .setOperatorLimit(operatorLimit) + .setIsLocal(false) + .setExceptionSafe(exceptionSafe) + .getDeclarationFactory(); + for (int i = 0; i < limit; i++) { + try { + content.add(declFactory.produce()); + } catch (ProductionFailedException e) { + } + } + return new VariableDeclarationBlock(content, level); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/VariableDeclarationFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/VariableDeclarationFactory.java new file mode 100644 index 00000000000..284a9afbc89 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/VariableDeclarationFactory.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.LinkedList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.VariableDeclaration; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class VariableDeclarationFactory extends Factory { + protected final boolean isStatic; + protected final boolean isLocal; + protected final TypeKlass ownerClass; + private Type resultType; + + VariableDeclarationFactory(TypeKlass ownerClass, boolean isStatic, boolean isLocal, Type resultType) { + this.ownerClass = ownerClass; + this.isStatic = isStatic; + this.isLocal = isLocal; + this.resultType = resultType; + } + + @Override + public IRNode produce() throws ProductionFailedException { + if (resultType == TypeList.getVoid()) { + LinkedList types = new LinkedList<>(TypeList.getAll()); + PseudoRandom.shuffle(types); + if (types.isEmpty()) { + throw new ProductionFailedException(); + } + resultType = types.getFirst(); + } + String resultName = "var_" + SymbolTable.getNextVariableNumber(); + int flags = VariableInfo.NONE; + if (isStatic) { + flags |= VariableInfo.STATIC; + } + if (isLocal) { + flags |= VariableInfo.LOCAL; + } + VariableInfo varInfo = new VariableInfo(resultName, ownerClass, resultType, flags); + SymbolTable.add(varInfo); + return new VariableDeclaration(varInfo); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/VariableFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/VariableFactory.java new file mode 100644 index 00000000000..7868953e9af --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/VariableFactory.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.Rule; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.types.TypeKlass; + +class VariableFactory extends Factory { + private final Rule rule; + + VariableFactory(long complexityLimit, int operatorLimit, TypeKlass ownerClass, Type resultType, + boolean constant, boolean initialized, boolean exceptionSafe, boolean noconsts) { + int flags = VariableInfo.NONE; + if (constant) { + flags |= VariableInfo.FINAL; + } + if (initialized) { + flags |= VariableInfo.INITIALIZED; + } + rule = new Rule("variable"); + IRNodeBuilder b = new IRNodeBuilder().setResultType(resultType) + .setFlags(flags) + .setComplexityLimit(complexityLimit) + .setOperatorLimit(operatorLimit) + .setOwnerKlass(ownerClass) + .setExceptionSafe(exceptionSafe); + rule.add("non_static_member_variable", b.getNonStaticMemberVariableFactory()); + rule.add("static_member_variable", b.getStaticMemberVariableFactory()); + rule.add("local_variable", b.getLocalVariableFactory()); + } + + @Override + public IRNode produce() throws ProductionFailedException { + return rule.produce(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/VariableInitializationFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/VariableInitializationFactory.java new file mode 100644 index 00000000000..f2d45d3bc6d --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/VariableInitializationFactory.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import java.util.LinkedList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Rule; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.VariableInitialization; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class VariableInitializationFactory extends SafeFactory { + private final int operatorLimit; + private final long complexityLimit; + private final boolean constant; + private final boolean isStatic; + private final boolean isLocal; + private final boolean exceptionSafe; + private final TypeKlass ownerClass; + + VariableInitializationFactory(TypeKlass ownerClass, boolean constant, boolean isStatic, + boolean isLocal, long complexityLimit, int operatorLimit, boolean exceptionSafe) { + this.ownerClass = ownerClass; + this.constant = constant; + this.isStatic = isStatic; + this.isLocal = isLocal; + this.complexityLimit = complexityLimit; + this.operatorLimit = operatorLimit; + this.exceptionSafe = exceptionSafe; + } + + @Override + protected IRNode sproduce() throws ProductionFailedException { + LinkedList types = new LinkedList<>(TypeList.getAll()); + PseudoRandom.shuffle(types); + if (types.isEmpty()) { + throw new ProductionFailedException(); + } + Type resultType = types.getFirst(); + IRNodeBuilder b = new IRNodeBuilder().setComplexityLimit(complexityLimit - 1) + .setOperatorLimit(operatorLimit - 1) + .setOwnerKlass(ownerClass) + .setResultType(resultType) + .setExceptionSafe(exceptionSafe) + .setNoConsts(false); + Rule rule = new Rule("initializer"); + rule.add("literal_initializer", b.getLiteralFactory()); + if (!ProductionParams.disableExprInInit.value()) { + rule.add("expression", b.getLimitedExpressionFactory()); + } + Symbol thisSymbol = null; + if (isStatic) { + thisSymbol = SymbolTable.get("this", VariableInfo.class); + SymbolTable.remove(thisSymbol); + } + IRNode init; + try { + init = rule.produce(); + } finally { + if (isStatic) { + SymbolTable.add(thisSymbol); + } + } + String resultName = "var_" + SymbolTable.getNextVariableNumber(); + int flags = VariableInfo.INITIALIZED; + if (constant) { + flags |= VariableInfo.FINAL; + } + if (isStatic) { + flags |= VariableInfo.STATIC; + } + if (isLocal) { + flags |= VariableInfo.LOCAL; + } + VariableInfo varInfo = new VariableInfo(resultName, ownerClass, resultType, flags); + SymbolTable.add(varInfo); + return new VariableInitialization(varInfo, init); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/WhileFactory.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/WhileFactory.java new file mode 100644 index 00000000000..598a32d801d --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/factories/WhileFactory.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.factories; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.Initialization; +import jdk.test.lib.jittester.Literal; +import jdk.test.lib.jittester.LocalVariable; +import jdk.test.lib.jittester.Nothing; +import jdk.test.lib.jittester.ProductionFailedException; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.loops.Loop; +import jdk.test.lib.jittester.loops.While; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeInt; +import jdk.test.lib.jittester.utils.PseudoRandom; + +class WhileFactory extends SafeFactory { + private final Loop loop; + private final long complexityLimit; + private final int statementLimit; + private final int operatorLimit; + private final TypeKlass ownerClass; + private final int level; + private final Type returnType; + private long thisLoopIterLimit = 0; + private final boolean canHaveReturn; + + WhileFactory(TypeKlass ownerClass, Type returnType, long complexityLimit, int statementLimit, + int operatorLimit, int level, boolean canHaveReturn) { + this.ownerClass = ownerClass; + this.returnType = returnType; + loop = new Loop(); + this.complexityLimit = complexityLimit; + this.statementLimit = statementLimit; + this.operatorLimit = operatorLimit; + this.level = level; + this.canHaveReturn = canHaveReturn; + } + + @Override + protected IRNode sproduce() throws ProductionFailedException { + if (statementLimit <= 0 || complexityLimit <= 0) { + throw new ProductionFailedException(); + } + long complexity = complexityLimit; + // Loop header parameters + long headerComplLimit = (long) (0.005 * complexity * PseudoRandom.random()); + complexity -= headerComplLimit; + int headerStatementLimit = PseudoRandom.randomNotZero((int) (statementLimit / 3.0)); + // Loop body parameters + thisLoopIterLimit = (long) (0.0001 * complexity * PseudoRandom.random()); + if (thisLoopIterLimit > Integer.MAX_VALUE || thisLoopIterLimit == 0) { + throw new ProductionFailedException(); + } + complexity = thisLoopIterLimit > 0 ? complexity / thisLoopIterLimit : 0; + long condComplLimit = (long) (complexity * PseudoRandom.random()); + complexity -= condComplLimit; + long body1ComplLimit = (long) (complexity * PseudoRandom.random()); + complexity -= body1ComplLimit; + int body1StatementLimit = PseudoRandom.randomNotZero((int) (statementLimit / 4.0)); + long body2ComplLimit = (long) (complexity * PseudoRandom.random()); + complexity -= body2ComplLimit; + int body2StatementLimit = PseudoRandom.randomNotZero((int) (statementLimit / 4.0)); + long body3ComplLimit = complexity; + int body3StatementLimit = PseudoRandom.randomNotZero((int) (statementLimit / 4.0)); + // Production + IRNodeBuilder builder = new IRNodeBuilder().setOwnerKlass(ownerClass) + .setResultType(returnType) + .setOperatorLimit(operatorLimit); + loop.initialization = builder.getCounterInitializerFactory(0).produce(); + IRNode header; + try { + header = builder.setComplexityLimit(headerComplLimit) + .setStatementLimit(headerStatementLimit) + .setLevel(level - 1) + .setSubBlock(true) + .setCanHaveBreaks(false) + .setCanHaveContinues(false) + .setCanHaveReturn(false) + .getBlockFactory() + .produce(); + } catch (ProductionFailedException e) { + header = new Nothing(); + } + LocalVariable counter = new LocalVariable(((Initialization) loop.initialization).get()); + Literal limiter = new Literal(Integer.valueOf((int) thisLoopIterLimit), new TypeInt()); + loop.condition = builder.setComplexityLimit(condComplLimit) + .setLocalVariable(counter) + .getLoopingConditionFactory(limiter) + .produce(); + IRNode body1; + SymbolTable.push(); + try { + body1 = builder.setComplexityLimit(body1ComplLimit) + .setStatementLimit(body1StatementLimit) + .setLevel(level) + .setSubBlock(true) + .setCanHaveBreaks(true) + .setCanHaveContinues(false) + .setCanHaveReturn(canHaveReturn) + .getBlockFactory() + .produce(); + } catch (ProductionFailedException e) { + body1 = new Nothing(); + } + loop.manipulator = builder.setLocalVariable(counter).getCounterManipulatorFactory().produce(); + IRNode body2; + try { + body2 = builder.setComplexityLimit(body2ComplLimit) + .setStatementLimit(body2StatementLimit) + .setLevel(level) + .setSubBlock(true) + .setCanHaveBreaks(true) + .setCanHaveContinues(true) + .setCanHaveReturn(canHaveReturn) + .getBlockFactory() + .produce(); + } catch (ProductionFailedException e) { + body2 = new Nothing(); + } + IRNode body3; + try { + body3 = builder.setComplexityLimit(body3ComplLimit) + .setStatementLimit(body3StatementLimit) + .setLevel(level) + .setSubBlock(true) + .setCanHaveBreaks(true) + .setCanHaveContinues(false) + .setCanHaveReturn(canHaveReturn) + .getBlockFactory() + .produce(); + } catch (ProductionFailedException e) { + body3 = new Nothing(); + } + SymbolTable.pop(); + return new While(level, loop, thisLoopIterLimit, header, body1, body2, body3); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/ArgumentDeclaration.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/ArgumentDeclaration.java new file mode 100644 index 00000000000..8173f14c58f --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/ArgumentDeclaration.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.functions; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.visitors.Visitor; + +public class ArgumentDeclaration extends IRNode { + public VariableInfo variableInfo; + + public ArgumentDeclaration(VariableInfo variableInfo) { + this.variableInfo = variableInfo; + } + + @Override + public long complexity() { + return 0; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/ConstructorDefinition.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/ConstructorDefinition.java new file mode 100644 index 00000000000..f6c2c210f31 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/ConstructorDefinition.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.functions; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.visitors.Visitor; + +public class ConstructorDefinition extends IRNode { + private final FunctionInfo functionInfo; + + public ConstructorDefinition(FunctionInfo functionInfo, + ArrayList argumentsDeclaration, IRNode body) { + this.functionInfo = functionInfo; + addChild(body); + addChildren(argumentsDeclaration); + } + + @Override + public long complexity() { + IRNode body = getChild(0); + return body != null ? body.complexity() : 0; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public FunctionInfo getFunctionInfo() { + return functionInfo; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/ConstructorDefinitionBlock.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/ConstructorDefinitionBlock.java new file mode 100644 index 00000000000..a30bf7acc72 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/ConstructorDefinitionBlock.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.functions; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.visitors.Visitor; + +public class ConstructorDefinitionBlock extends IRNode { + public ConstructorDefinitionBlock(ArrayList content, int level) { + this.level = level; + addChildren(content); + } + + @Override + public long complexity() { + int complexity = 0; + for (IRNode child : getChildren()) { + complexity += child.complexity(); + } + return complexity; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/Function.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/Function.java new file mode 100644 index 00000000000..0f3872360bf --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/Function.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.functions; + +import java.util.Collection; +import java.util.List; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.visitors.Visitor; + + +public class Function extends IRNode { + private FunctionInfo functionInfo = new FunctionInfo(); + + public Function(TypeKlass ownerClass, FunctionInfo functionInfo, List args) { + setKlass(ownerClass); + this.functionInfo = functionInfo; + addChildren(args); + } + + @Override + public long complexity() { + int argsComplexity = 0; + for (IRNode child : getChildren()) { + argsComplexity += child.complexity(); + } + long funcComplexity = functionInfo.complexity; + TypeKlass typeKlass = (TypeKlass) this.klass; + if (functionInfo.isConstructor()) { + // Sum complexities of all default constructors of parent classes + for (TypeKlass parent : typeKlass.getAllParents()) { + Collection parentFuncs = SymbolTable.getAllCombined(parent, FunctionInfo.class); + for (Symbol f : parentFuncs) { + FunctionInfo c = (FunctionInfo) f; + if (c.name.equals(c.klass.getName()) && c.argTypes.isEmpty()) { + funcComplexity += c.complexity; + } + } + } + // TODO: Complexities of all non-static initializers should be also added.. + } else { + // Perform the CHA and find the highest complexity + for (TypeKlass child : typeKlass.getAllChildren()) { + Collection childFuncs = SymbolTable.getAllCombined(child, FunctionInfo.class); + for (Symbol childFunc : childFuncs) { + if (((FunctionInfo) childFunc).equals(functionInfo)) { + funcComplexity = Math.max(funcComplexity, ((FunctionInfo) childFunc).complexity); + } + } + } + } + return argsComplexity + funcComplexity; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public FunctionInfo getValue() { + return functionInfo; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionDeclaration.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionDeclaration.java new file mode 100644 index 00000000000..025dc46ec6f --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionDeclaration.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.functions; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.visitors.Visitor; + +public class FunctionDeclaration extends IRNode { + private final FunctionInfo functionInfo; + + public FunctionDeclaration(FunctionInfo functionInfo, + ArrayList argumentsDeclaration) { + this.functionInfo = functionInfo; + addChildren(argumentsDeclaration); + } + + @Override + public long complexity() { + return 0; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public FunctionInfo getFunctionInfo() { + return functionInfo; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionDeclarationBlock.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionDeclarationBlock.java new file mode 100644 index 00000000000..bc7070b7afc --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionDeclarationBlock.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.functions; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.visitors.Visitor; + +public class FunctionDeclarationBlock extends IRNode { + public FunctionDeclarationBlock(TypeKlass ownerClass, ArrayList content, int level) { + setKlass(ownerClass); + this.level = level; + addChildren(content); + } + + @Override + public long complexity() { + int complexity = 0; + for (IRNode child : getChildren()) { + complexity += child.complexity(); + } + return complexity; + } + + public int size() { + return getChildren() != null ? getChildren().size() : 0; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionDefinition.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionDefinition.java new file mode 100644 index 00000000000..d68ffde58db --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionDefinition.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.functions; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.visitors.Visitor; + +public class FunctionDefinition extends IRNode { + private final FunctionInfo functionInfo; + + public FunctionDefinition(FunctionInfo functionInfo, + ArrayList argumentsDeclaration, IRNode body, IRNode ret) { + this.functionInfo = functionInfo; + addChild(body); + addChild(ret); + addChildren(argumentsDeclaration); + } + + // get the list of all functions from all parents of the given class. + public static Collection getFuncsFromParents(TypeKlass typeKlass) { + LinkedList result = new LinkedList<>(); + for (TypeKlass parent : typeKlass.getAllParents()) { + result.addAll(SymbolTable.getAllCombined(parent, FunctionInfo.class)); + } + return result; + } + + // Check if the given function prototype f1 is a valid overload of + // prototypes in collection S. + // The override is invalid if function f1 has the same signature as + // function f2 in S, but has different return type. + public static boolean isInvalidOverride(FunctionInfo f1, Collection symbols) { + for (Symbol symbol : symbols) { + FunctionInfo f2 = (FunctionInfo) symbol; + if (f1.hasEqualSignature(f2)) { + if (!f1.type.equals(f2.type)) { + return true; + } + if ((f2.flags & FunctionInfo.NONRECURSIVE) > 0 + || ((f1.flags & FunctionInfo.ABSTRACT) > 0 && (f2.flags & FunctionInfo.ABSTRACT) == 0) + || (f1.flags & FunctionInfo.STATIC) != (f2.flags & FunctionInfo.STATIC) + || (f2.flags & FunctionInfo.FINAL) > 0 + || (f1.flags & FunctionInfo.ACCESS_ATTRS_MASK) < (f2.flags & FunctionInfo.ACCESS_ATTRS_MASK)) { + return true; + } + } + } + return false; + } + + @Override + public long complexity() { + IRNode body = getChild(0); + IRNode ret = getChild(1); + return body.complexity() + (ret != null ? ret.complexity() : 0); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public FunctionInfo getFunctionInfo() { + return functionInfo; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionDefinitionBlock.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionDefinitionBlock.java new file mode 100644 index 00000000000..c9954524e77 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionDefinitionBlock.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.functions; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.visitors.Visitor; + +public class FunctionDefinitionBlock extends IRNode { + public FunctionDefinitionBlock(ArrayList content, int level, TypeKlass ownerClass) { + setKlass(ownerClass); + addChildren(content); + this.level = level; + } + + @Override + public long complexity() { + int complexity = 0; + for (IRNode child : getChildren()) { + complexity += child.complexity(); + } + return complexity; + } + + protected int size() { + return getChildren() != null ? getChildren().size() : 0; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionInfo.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionInfo.java new file mode 100644 index 00000000000..55da565ead7 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionInfo.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.functions; + +import java.util.ArrayList; +import java.util.Arrays; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.types.TypeKlass; + +public class FunctionInfo extends Symbol { + public ArrayList argTypes; + public long complexity = 0; + public static final int ABSTRACT = 0x40; + public static final int NONRECURSIVE = 0x80; + public static final int SYNCHRONIZED = 0x100; + + public FunctionInfo() { + } + + public FunctionInfo(String name, TypeKlass ownerClass, Type returnType, + long complexity, int flags, VariableInfo... args) { + super(name, ownerClass, returnType, flags); + argTypes = new ArrayList<>(); + argTypes.addAll(Arrays.asList(args)); + this.complexity = complexity; + } + + public FunctionInfo(String name, TypeKlass ownerClass, Type returnType, + long complexity, int flags, ArrayList args) { + super(name, ownerClass, returnType, flags); + argTypes = args; + this.complexity = complexity; + } + + public FunctionInfo(FunctionInfo value) { + super(value); + argTypes = new ArrayList<>(); + for (VariableInfo i : value.argTypes) { + argTypes.add(new VariableInfo(i)); + } + complexity = value.complexity; + } + + public boolean isSynchronized() { + return (flags & SYNCHRONIZED) > 0; + } + + @Override + protected Symbol copy() { + return this; + } + + @Override + public Symbol deepCopy() { + return new FunctionInfo(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || !(o instanceof FunctionInfo)) { + return false; + } + + try { + FunctionInfo f = (FunctionInfo) o; + return klass.equals(f.klass) && hasEqualSignature(o); + } catch (Exception e) { + } + return false; + } + + protected boolean hasEqualSignature(Object o) { + try { + FunctionInfo f = (FunctionInfo) o; + if (name.equals(f.name)) { + int i = (flags & STATIC) > 0 ? 0 : 1; + int j = (f.flags & STATIC) > 0 ? 0 : 1; + + if (argTypes.size() - i == f.argTypes.size() - j) { + while (i < argTypes.size() && j < f.argTypes.size()) { + if (!argTypes.get(i++).type.equals(f.argTypes.get(j++).type)) { + return false; + } + } + return true; + } + } + } catch (Exception e) { + } + return false; + } + + public boolean isConstructor() { + return name.equals(klass.getName()); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean isStatic() { + return (flags & STATIC) > 0; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionRedefinition.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionRedefinition.java new file mode 100644 index 00000000000..5ed6d15216d --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionRedefinition.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.functions; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.visitors.Visitor; + +public class FunctionRedefinition extends IRNode { + private final FunctionInfo functionInfo; + + protected FunctionRedefinition(FunctionInfo functionInfo, + ArrayList argumentsDeclaration, IRNode body, IRNode ret) { + this.functionInfo = functionInfo; + addChild(body); + addChild(ret); + addChildren(argumentsDeclaration); + } + + @Override + public long complexity() { + IRNode body = getChild(0); + IRNode ret = getChild(1); + return body.complexity() + (ret != null ? ret.complexity() : 0); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public FunctionInfo getFunctionInfo() { + return functionInfo; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionRedefinitionBlock.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionRedefinitionBlock.java new file mode 100644 index 00000000000..79a8e4edd23 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/FunctionRedefinitionBlock.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.functions; + +import java.util.ArrayList; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.visitors.Visitor; + +public class FunctionRedefinitionBlock extends IRNode { + public FunctionRedefinitionBlock(ArrayList content, int level) { + this.level = level; + addChildren(content); + } + + @Override + public long complexity() { + int complexity = 0; + for (IRNode child : getChildren()) { + complexity += child.complexity(); + } + return complexity; + } + + protected int size() { + return getChildren() != null ? getChildren().size() : 0; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/Return.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/Return.java new file mode 100644 index 00000000000..c974b702d4e --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/Return.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.functions; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.visitors.Visitor; + +public class Return extends IRNode { + private final IRNode returnExpression; + + public Return(IRNode returnExpression) { + this.returnExpression = returnExpression; + addChild(returnExpression); + } + + @Override + public long complexity() { + return returnExpression.complexity(); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public IRNode getExpression() { + return returnExpression; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/StaticConstructorDefinition.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/StaticConstructorDefinition.java new file mode 100644 index 00000000000..095068ffc9c --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/functions/StaticConstructorDefinition.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.functions; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.visitors.Visitor; + +public class StaticConstructorDefinition extends IRNode { + public StaticConstructorDefinition(IRNode body) { + addChild(body); + } + + @Override + public long complexity() { + IRNode body = getChild(0); + return body != null ? body.complexity() : 0; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/jtreg/JitTesterDriver.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/jtreg/JitTesterDriver.java new file mode 100644 index 00000000000..0494f82d515 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/jtreg/JitTesterDriver.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.jtreg; + +import jdk.test.lib.Asserts; +import jdk.test.lib.OutputAnalyzer; +import jdk.test.lib.ProcessTools; +import jdk.test.lib.Utils; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class JitTesterDriver { + + public static void main(String[] args) { + if (args.length != 1) { + throw new IllegalArgumentException( + "[TESTBUG]: wrong number of argument : " + args.length + + ". Expected 1 argument -- jit-tester test name."); + } + OutputAnalyzer oa; + try { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, args[0]); + oa = new OutputAnalyzer(pb.start()); + } catch (Exception e) { + throw new Error("Unexpected exception on test jvm start :" + e, e); + } + + Path testDir = Paths.get(Utils.TEST_SRC); + String goldOut = formatOutput(streamGoldFile(testDir, args[0], "out"), s -> true); + Asserts.assertEQ(oa.getStdout(), goldOut, "Actual stdout isn't equal to golden one"); + + // TODO: add a comment why we skip such lines + Predicate notStartWhitespaces = s -> !(s.startsWith("\t") || s.startsWith(" ")); + String goldErr = formatOutput(streamGoldFile(testDir, args[0], "err"), notStartWhitespaces); + String anlzErr = formatOutput(Arrays.stream(oa.getStderr().split(Utils.NEW_LINE)), + notStartWhitespaces); + Asserts.assertEQ(anlzErr, goldErr, "Actual stderr isn't equal to golden one"); + + int exitValue = Integer.parseInt(streamGoldFile(testDir, args[0], "exit").findFirst().get()); + oa.shouldHaveExitValue(exitValue); + } + + private static String formatOutput(Stream stream, Predicate predicate) { + String result = stream + .filter(predicate) + .collect(Collectors.joining(Utils.NEW_LINE)); + if (result.length() > 0) { + result += Utils.NEW_LINE; + } + return result; + } + + private static Stream streamGoldFile(Path dir, String name, String suffix) { + try { + return Files.lines(dir.resolve(name + ".gold." + suffix)); + } catch (IOException e) { + throw new Error(String.format("Can't read golden %s for %s : %s", suffix, name, e), e); + } + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/CounterInitializer.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/CounterInitializer.java new file mode 100644 index 00000000000..55bd88dd7d2 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/CounterInitializer.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.loops; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.Initialization; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.visitors.Visitor; + +public class CounterInitializer extends Initialization { + public CounterInitializer(VariableInfo varInfo, IRNode expression) { + super(varInfo, expression); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/CounterManipulator.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/CounterManipulator.java new file mode 100644 index 00000000000..f27feeea88d --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/CounterManipulator.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.loops; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.LocalVariable; +import jdk.test.lib.jittester.visitors.Visitor; + +/* + * Note: Can be theoretically subclassed from Operator and have an + * operatorPriority field. Therefore, it can used later as a part + * of some expression. + */ + +public class CounterManipulator extends IRNode { + LocalVariable counter; + + public CounterManipulator(IRNode manipulator) { + addChild(manipulator); + } + + @Override + public long complexity() { + IRNode manipulator = getChild(0); + return manipulator != null ? manipulator.complexity() : 0; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/DoWhile.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/DoWhile.java new file mode 100644 index 00000000000..b8830e120db --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/DoWhile.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.loops; + +import java.util.List; +import jdk.test.lib.jittester.Block; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.visitors.Visitor; + +public class DoWhile extends IRNode { + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public Loop getLoop() { + return loop; + } + public enum DoWhilePart { + HEADER, + BODY1, + BODY2, + }; + private final Loop loop; + // header; [subblock] + // do { + // body1; [subblock with breaks] + // mutate(counter); + // body2; [subblock with breaks] + // } while(condition); + private long thisLoopIterLimit = 0; + + public DoWhile(int level, Loop loop, long thisLoopIterLimit, IRNode header, + IRNode body1, IRNode body2) { + this.level = level; + this.loop = loop; + this.thisLoopIterLimit = thisLoopIterLimit; + addChild(header); + addChild(body1); + addChild(body2); + } + + @Override + public long complexity() { + IRNode header = getChild(DoWhilePart.HEADER.ordinal()); + IRNode body1 = getChild(DoWhilePart.BODY1.ordinal()); + IRNode body2 = getChild(DoWhilePart.BODY2.ordinal()); + return loop.initialization.complexity() + + header.complexity() + + thisLoopIterLimit * (body1.complexity() + + loop.manipulator.complexity() + + body2.complexity() + + loop.condition.complexity()); + } + + @Override + public long countDepth() { + return Long.max(level, super.countDepth()); + } + + @Override + public boolean removeSelf() { + IRNode header = getChildren().get(DoWhilePart.HEADER.ordinal()); + List siblings = getParent().getChildren(); + int index = siblings.indexOf(this); + if (header instanceof Block) { + siblings.remove(this); + siblings.addAll(index, header.getChildren()); + } else { + siblings.set(index, header); + } + siblings.add(index + header.getChildren().size(), loop.initialization); + return true; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/For.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/For.java new file mode 100644 index 00000000000..3c1ab06e103 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/For.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.loops; + +import java.util.List; +import jdk.test.lib.jittester.Block; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.visitors.Visitor; + +public class For extends IRNode { + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public Loop getLoop() { + return loop; + } + public enum ForPart { + HEADER, + STATEMENT1, + STATEMENT2, + BODY1, + BODY2, + BODY3, + }; + + private final Loop loop; + // header; // [subblock] + // statement1, statement2; // for (statement; condition; statement) { + // body1; // [subblock with breaks] + // mutate(x); + // body2; // [subblock with breaks and continues] + // body3; // [subblock with breaks] + // } + private long thisLoopIterLimit = 0; + public For(int level, Loop loop, long thisLoopIterLimit, + IRNode header, IRNode statement1, + IRNode statement2, IRNode body1, IRNode body2, IRNode body3) { + this.level = level; + this.loop = loop; + this.thisLoopIterLimit = thisLoopIterLimit; + resizeUpChildren(ForPart.values().length); + getChildren().set(ForPart.HEADER.ordinal(), header); + getChildren().set(ForPart.STATEMENT1.ordinal(), statement1); + getChildren().set(ForPart.STATEMENT2.ordinal(), statement2); + getChildren().set(ForPart.BODY1.ordinal(), body1); + getChildren().set(ForPart.BODY2.ordinal(), body2); + getChildren().set(ForPart.BODY3.ordinal(), body3); + } + + @Override + public long complexity() { + IRNode header = getChild(ForPart.HEADER.ordinal()); + IRNode statement1 = getChild(ForPart.STATEMENT1.ordinal()); + IRNode statement2 = getChild(ForPart.STATEMENT2.ordinal()); + IRNode body1 = getChild(ForPart.BODY1.ordinal()); + IRNode body2 = getChild(ForPart.BODY2.ordinal()); + IRNode body3 = getChild(ForPart.BODY3.ordinal()); + return loop.initialization.complexity() + + header.complexity() + + statement1.complexity() + + thisLoopIterLimit * (loop.condition.complexity() + + statement2.complexity() + + body1.complexity() + + loop.manipulator.complexity() + + body2.complexity() + + body3.complexity()); + } + + @Override + public long countDepth() { + return Long.max(level, super.countDepth()); + } + + @Override + public boolean removeSelf() { + IRNode header = getChildren().get(ForPart.HEADER.ordinal()); + List siblings = getParent().getChildren(); + int index = siblings.indexOf(this); + if (header instanceof Block) { + siblings.remove(this); + siblings.addAll(index, header.getChildren()); + } else { + siblings.set(index, header); + } + siblings.add(index + header.getChildren().size(), loop.initialization); + return true; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/Loop.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/Loop.java new file mode 100644 index 00000000000..dc81f8756d7 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/Loop.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.loops; + +import jdk.test.lib.jittester.IRNode; + +// Just a structure to hold the values needed to handle basic loop production +public class Loop { + public IRNode initialization; + public IRNode condition; + public IRNode manipulator; +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/LoopingCondition.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/LoopingCondition.java new file mode 100644 index 00000000000..48bc0228f6c --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/LoopingCondition.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.loops; + +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.visitors.Visitor; + +public class LoopingCondition extends IRNode { + private final IRNode condition; + + public LoopingCondition(IRNode condition) { + this.condition = condition; + addChild(condition); + } + + @Override + public long complexity() { + return condition != null ? condition.complexity() : 0; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public IRNode getCondition() { + return condition; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/While.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/While.java new file mode 100644 index 00000000000..9b8c780611d --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/loops/While.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.loops; + +import java.util.List; +import jdk.test.lib.jittester.Block; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.visitors.Visitor; + +public class While extends IRNode { + + public Loop getLoop() { + return loop; + } + public enum WhilePart { + HEADER, + BODY1, + BODY2, + BODY3, + }; + + private final Loop loop; + // int counter = x; + // header; // [subblock] + // while (condition) { + // body1; // [subblock with breaks] + // mutate(counter); + // body2; // [subblock with breaks and continues] + // body3; // [subblock with breaks] + // } + private final long thisLoopIterLimit; + + public While(int level, Loop loop, long thisLoopIterLimit, IRNode header, + IRNode body1, IRNode body2, IRNode body3) { + this.loop = loop; + this.level = level; + this.thisLoopIterLimit = thisLoopIterLimit; + resizeUpChildren(WhilePart.values().length); + getChildren().set(WhilePart.HEADER.ordinal(), header); + getChildren().set(WhilePart.BODY1.ordinal(), body1); + getChildren().set(WhilePart.BODY2.ordinal(), body2); + getChildren().set(WhilePart.BODY3.ordinal(), body3); + } + + @Override + public long complexity() { + IRNode header = getChildren().get(WhilePart.HEADER.ordinal()); + IRNode body1 = getChildren().get(WhilePart.BODY1.ordinal()); + IRNode body2 = getChildren().get(WhilePart.BODY2.ordinal()); + IRNode body3 = getChildren().get(WhilePart.BODY3.ordinal()); + return loop.initialization.complexity() + + header.complexity() + + thisLoopIterLimit * (loop.condition.complexity() + + body1.complexity() + + loop.manipulator.complexity() + + body2.complexity() + + body3.complexity()); + } + + @Override + public long countDepth() { + return Long.max(level, super.countDepth()); + } + + @Override + public boolean removeSelf() { + IRNode header = getChildren().get(WhilePart.HEADER.ordinal()); + List siblings = getParent().getChildren(); + int index = siblings.indexOf(this); + if (header instanceof Block) { + siblings.remove(this); + siblings.addAll(index, header.getChildren()); + } else { + siblings.set(index, header); + } + siblings.add(index + header.getChildren().size(), loop.initialization); + return true; + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeArray.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeArray.java new file mode 100644 index 00000000000..c39f470e37f --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeArray.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.types; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.visitors.Visitor; +import jdk.test.lib.jittester.utils.PseudoRandom; + +public class TypeArray extends TypeKlass { + + public List getDims() { + return dims; + } + + public void setDimentions(List dims) { + this.dims = dims; + } + public final Type type; + public final int dimensions; + private List dims = new ArrayList<>(); + + public TypeArray() { + this(new TypeVoid(), 0); + } + + public TypeArray(Type type, int dimensions) { + super("Array", TypeKlass.FINAL); + addParent("java.lang.Object"); + setParent((TypeKlass) TypeList.find("java.lang.Object")); + this.type = type; + this.dimensions = dimensions; + } + + @Override + protected void exportSymbols() { + SymbolTable.add(new VariableInfo("length", this, new TypeInt(), VariableInfo.PUBLIC | VariableInfo.FINAL)); + } + + @Override + public boolean equals(Object t) { + if (this == t) { + return true; + } + if (t == null || !(t instanceof TypeArray)) { + return false; + } + + if (super.equals(t)) { // make sure we're compating to an array + try { + TypeArray a = (TypeArray) t; + return a.type.equals(type) && (a.dimensions == dimensions + || a.dimensions == -1 + || dimensions == -1); + } catch (Exception e) { + } + } + return false; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 53 * hash + Objects.hashCode(this.type); + hash = 313 * hash + this.dimensions; + return hash; + } + + @Override + public int compareTo(Type t) { + int r = 0; + r = super.compareTo(t); + if (r == 0) { + try { + TypeArray a = (TypeArray) t; + r = type.compareTo(t); + if (r == 0) { + r = dimensions - a.dimensions; + } + } catch (Exception e) { + } + } + + return r; + } + + public TypeArray produce() { + ArrayList all = new ArrayList<>(TypeList.getAll()); + PseudoRandom.shuffle(all); + for (Type t : all) { + if (t instanceof TypeArray) { + continue; + } + int dims = PseudoRandom.randomNotZero(ProductionParams.dimensionsLimit.value()); + return new TypeArray(t, dims); + } + throw new Error("Shouldn't happen"); + } + + @Override + public T accept(Visitor v) { + return v.visit(this); + } + + public Type getType() { + return type; + } + + public int getDimensions() { + return dimensions; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeBoolean.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeBoolean.java new file mode 100644 index 00000000000..a040387a6fc --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeBoolean.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.types; + +import jdk.test.lib.jittester.BuiltInType; +import jdk.test.lib.jittester.Type; + + +public class TypeBoolean extends BuiltInType { + + @Override + public boolean canImplicitlyCastTo(Type t) { + return equals(t); + } + + @Override + public boolean canExplicitlyCastTo(Type t) { + return equals(t); + } + + @Override + public boolean canCompareTo(Type t) { + return false; + } + + @Override + public boolean canEquateTo(Type t) { + return equals(t); + } + + public TypeBoolean() { + super("boolean"); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeByte.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeByte.java new file mode 100644 index 00000000000..524ce8d190d --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeByte.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.types; + +import jdk.test.lib.jittester.BuiltInType; + +public class TypeByte extends BuiltInType { + + public TypeByte() { + super("byte"); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeChar.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeChar.java new file mode 100644 index 00000000000..c24cd7dc9f9 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeChar.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.types; + +import jdk.test.lib.jittester.BuiltInType; +import jdk.test.lib.jittester.Type; + +public class TypeChar extends BuiltInType { + + public TypeChar() { + super("char"); + } + + @Override + public boolean canImplicitlyCastTo(Type t) { + if (equals(t)) { + return true; + } + try { + BuiltInType _t = (BuiltInType) t; + if (_t.isMoreCapaciousThan(this)) { + return true; + } + } catch (Exception e) { + } + return false; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeDouble.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeDouble.java new file mode 100644 index 00000000000..e15701a703d --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeDouble.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.types; + +import jdk.test.lib.jittester.BuiltInType; + +public class TypeDouble extends BuiltInType { + + public TypeDouble() { + super("double"); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeFloat.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeFloat.java new file mode 100644 index 00000000000..77ebcfe9d74 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeFloat.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.types; + +import jdk.test.lib.jittester.BuiltInType; + +public class TypeFloat extends BuiltInType { + + public TypeFloat() { + super("float"); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeInt.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeInt.java new file mode 100644 index 00000000000..2fe10e82e82 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeInt.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.types; + +import jdk.test.lib.jittester.BuiltInType; + +public class TypeInt extends BuiltInType { + + public TypeInt() { + super("int"); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeKlass.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeKlass.java new file mode 100644 index 00000000000..47e4444d749 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeKlass.java @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.types; + +import java.util.Collection; +import java.util.HashSet; +import java.util.TreeSet; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.SymbolTable; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; + +public class TypeKlass extends Type { + + private TypeKlass parent; + private HashSet parentsList; + private HashSet childrenList; + private final HashSet symbolsSet; + public static final int NONE = 0x00; + public static final int FINAL = 0x01; + public static final int INTERFACE = 0x02; + public static final int ABSTRACT = 0x04; + private int flags = NONE; + + public TypeKlass(String name) { + this(name, 0); + } + + public TypeKlass(String name, int flags) { + super(name); + this.flags = flags; + symbolsSet = new HashSet<>(); + } + + public boolean addSymbol(Symbol s) { + return symbolsSet.add(s); + } + + public boolean addAllSymbols(Collection symbols) { + return symbolsSet.addAll(symbols); + } + + public boolean containsSymbol(Symbol s) { + return symbolsSet.contains(s); + } + + public boolean removeSymbol(Symbol s) { + return symbolsSet.remove(s); + } + + public boolean removeAllSymbols(Collection symbols) { + return symbolsSet.removeAll(symbols); + } + + @Override + protected void exportSymbols() { + symbolsSet.stream().forEach(symbol -> { + SymbolTable.add(symbol); + }); + } + + public void setParent(TypeKlass p) { + parent = p; + } + + public void addParent(String p) { + if (parentsList == null) { + parentsList = new HashSet<>(); + } + parentsList.add(p); + } + + public void addChild(String c) { + if (childrenList == null) { + childrenList = new HashSet<>(); + } + childrenList.add(c); + } + + protected void removeParent(String p) { + if (parentsList != null) { + parentsList.remove(p); + } + } + + protected void removeChild(String c) { + if (childrenList != null) { + childrenList.remove(c); + } + } + + public HashSet getParentsNames() { + return parentsList; + } + + public HashSet getChildrenNames() { + return childrenList; + } + + @Override + public boolean canCompareTo(Type t) { + return false; + } + + @Override + public boolean canEquateTo(Type t) { + return true; + } + + public TreeSet getAllParents() { + TreeSet result = new TreeSet<>(); + if (parentsList != null) { + for (String parentName : parentsList) { + Type _parentKlass = TypeList.find(new TypeKlass(parentName)); + if (_parentKlass != null) { + try { + TypeKlass parentKlass = (TypeKlass) _parentKlass; + result.add(parentKlass); + result.addAll(parentKlass.getAllParents()); + } catch (Exception e) { + } + } + } + } + return result; + } + + public TreeSet getAllChildren() { + TreeSet r = new TreeSet<>(); + if (childrenList != null) { + for (String childName : childrenList) { + Type _childKlass = TypeList.find(new TypeKlass(childName)); + if (_childKlass != null) { + try { + TypeKlass childKlass = (TypeKlass) _childKlass; + r.add(childKlass); + r.addAll(childKlass.getAllChildren()); + } catch (Exception e) { + } + } + } + } + return r; + } + + @Override + public boolean canImplicitlyCastTo(Type t) { + // We can implicitly cast to anything up the hierarchy and to self + if (t instanceof TypeKlass) { + return equals(t) || getAllParents().contains(t); + } + return false; + } + + // If canExplicitlyCastTo() returns true in this case it doesn't mean that + // it would really be successful. Since explicit casts are inherintly dynamic + // we cannot guarantee that no exception will occur. + @Override + public boolean canExplicitlyCastTo(Type t) { + if (t instanceof TypeKlass && !ProductionParams.disableDowncasts.value()) { + return equals(t) || getAllChildren().contains(t); + } + + return false; + } + + public boolean isFinal() { + return (flags & FINAL) > 0; + } + + public void setFinal() { + flags |= FINAL; + } + + public boolean isAbstract() { + return (flags & ABSTRACT) > 0; + } + + public void setAbstract() { + flags |= ABSTRACT; + } + + public boolean isInterface() { + return (flags & INTERFACE) > 0; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeLong.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeLong.java new file mode 100644 index 00000000000..45aa0f9aa4b --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeLong.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.types; + +import jdk.test.lib.jittester.BuiltInType; + +public class TypeLong extends BuiltInType { + + public TypeLong() { + super("long"); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeShort.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeShort.java new file mode 100644 index 00000000000..13e2cc8c59f --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeShort.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.types; + +import jdk.test.lib.jittester.BuiltInType; + +public class TypeShort extends BuiltInType { + + public TypeShort() { + super("short"); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeVoid.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeVoid.java new file mode 100644 index 00000000000..1479de192a6 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/types/TypeVoid.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.types; + +import jdk.test.lib.jittester.BuiltInType; +import jdk.test.lib.jittester.Type; + +public class TypeVoid extends BuiltInType { + + public TypeVoid() { + super("void"); + } + + @Override + public boolean canImplicitlyCastTo(Type t) { + return false; + } + + @Override + public boolean canExplicitlyCastTo(Type t) { + return false; + } + + @Override + public boolean isMoreCapaciousThan(BuiltInType t) { + return false; + } + + @Override + public boolean canCompareTo(Type t) { + return false; + } + + @Override + public boolean canEquateTo(Type t) { + return false; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/utils/OptionResolver.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/utils/OptionResolver.java new file mode 100644 index 00000000000..4366684c14d --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/utils/OptionResolver.java @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.utils; + +import java.io.FileReader; +import java.io.IOException; +import java.util.*; + +public class OptionResolver { + private final Map> options = new LinkedHashMap<>(20); + private Map, Object> values = Collections.emptyMap(); + + public OptionResolver() { + } + + public final void parse(String[] argv) { + parse(argv, null); + } + + public final void parse(String[] argv, Option propertyFileOption) { + int position = 0; + this.values = new HashMap<>(argv.length / 2); + + while (position < argv.length) { + String curArg = argv[position]; + if (curArg.startsWith("-")) { + String valueArg = null; + int opt; + if (curArg.startsWith("--")) { + opt = curArg.indexOf("="); + if (opt != -1) { + valueArg = curArg.substring(opt + 1); + curArg = curArg.substring(0, opt); + } + } else if (curArg.length() > 2) { + for (opt = 1; opt < curArg.length(); ++opt) { + final char key = curArg.charAt(opt); + Option flagOption = this.options.get("-" + key); + + if (!flagOption.isFlag()) { + throw new IllegalArgumentException("Unknown flag option " + key); + } + + values.put(flagOption, Boolean.TRUE); + } + + ++position; + continue; + } + + Option currentOption = this.options.get(curArg); + if (currentOption == null) { + throw new IllegalArgumentException("Unknown option " + curArg); + } + + Object value; + if (!currentOption.isFlag()) { + if (valueArg == null) { + ++position; + if (position < argv.length) { + valueArg = argv[position]; + } + } + } + try { + value = currentOption.parseFromString(valueArg); + } catch (Exception ex) { + throw new IllegalArgumentException("Error parsing " + valueArg + ", option " + curArg, ex); + } + values.put(currentOption, value); + } + ++position; + } + + if (propertyFileOption != null && values.containsKey(propertyFileOption)) { + parseProperties(propertyFileOption.value()); + } + } + + private void parseProperties(String fileName) { + Properties properties = new Properties(); + try { + properties.load(new FileReader(fileName)); + } catch (IOException e) { + throw new RuntimeException(e); + } + for (String optionName : properties.stringPropertyNames()) { + Option currentOption = this.options.get("--" + optionName); + if (currentOption == null) { + throw new IllegalArgumentException("Unknown option in property file" + optionName); + } + + final String propertyValue = properties.getProperty(optionName); + try { + values.putIfAbsent(currentOption, currentOption.parseFromString(propertyValue)); + } catch (Exception ex) { + throw new IllegalArgumentException("Error parsing " + propertyValue + ", property " + optionName, ex); + } + } + } + + public Option addIntegerOption(Character key, String name, int defaultValue, String description) { + final Option option = new IntOption(key, name, defaultValue, description); + register(option); + return option; + } + + public Option addLongOption(Character key, String name, long defaultValue, String description) { + final Option option = new LongOption(key, name, defaultValue, description); + register(option); + return option; + } + + public Option addStringOption(Character key, String name, String defaultValue, String description) { + final Option option = new StringOption(key, name, defaultValue, description); + register(option); + return option; + } + + public Option addBooleanOption(Character key, String name, boolean defaultValue, String description) { + final Option option = new BooleanOption(key, name, defaultValue, description); + register(option); + return option; + } + + public Option addIntegerOption(String name, int defaultValue, String description) { + return addIntegerOption(null, name, defaultValue, description); + } + + public Option addStringOption(String name, String defaultValue, String description) { + return addStringOption(null, name, defaultValue, description); + } + + public Option addBooleanOption(String name, String description) { + return addBooleanOption(null, name, false, description); + } + + private void register(Option option) { + if (options.put("--" + option.longName, option) != null) { + throw new RuntimeException("Option is already registered for key " + option.longName); + } + if (option.shortName != null && options.put("-" + option.shortName, option) != null) { + throw new RuntimeException("Option is already registered for key " + option.shortName); + } + } + + public abstract class Option { + + Character shortName; + String longName; + protected T defaultValue; + protected String description; + + public Option(Character shortName, String longName, T defaultValue, String description) { + this.shortName = shortName; + this.longName = longName; + this.defaultValue = defaultValue; + this.description = description; + } + + public Character getShortName() { + return shortName; + } + + public String getLongName() { + return longName; + } + + public T getDefaultValue() { + return defaultValue; + } + + public String getDescription() { + return description; + } + + @SuppressWarnings("unchecked") + public T value() { + return (T) values.getOrDefault(this, defaultValue); + } + + public boolean isFlag() { + return false; + } + + public abstract T parseFromString(String arg); + } + + private class StringOption extends Option { + + StringOption(Character s, String l, String v, String d) { + super(s, l, v, d); + } + + @Override + public String parseFromString(String arg) { + return arg; + } + } + + private class LongOption extends Option { + + LongOption(Character s, String l, long v, String d) { + super(s, l, v, d); + } + + @Override + public Long parseFromString(String arg) { + return new Long(arg); + } + } + + private class IntOption extends Option { + + IntOption(Character s, String l, int v, String d) { + super(s, l, v, d); + } + + @Override + public Integer parseFromString(String arg) { + return new Integer(arg); + } + } + + private class BooleanOption extends Option { + + BooleanOption(Character s, String l, boolean v, String d) { + super(s, l, v, d); + } + + @Override + public boolean isFlag() { + return true; + } + + @Override + public Boolean parseFromString(String arg) { + //null and empty value is considered true, as option is flag and value could be absent + return arg == null || "".equals(arg) || "1".equalsIgnoreCase(arg) || "true".equalsIgnoreCase(arg); + } + } + + public Collection> getRegisteredOptions() { + return Collections.unmodifiableSet(new LinkedHashSet<>(options.values())); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/utils/PrintingUtils.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/utils/PrintingUtils.java new file mode 100644 index 00000000000..cd68d4162e4 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/utils/PrintingUtils.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +package jdk.test.lib.jittester.utils; + +public class PrintingUtils { + + static public String align(int l) { + String shift = ""; + for (int i = 0; i < l; i++) { + shift += " "; + } + return shift; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/utils/PseudoRandom.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/utils/PseudoRandom.java new file mode 100644 index 00000000000..891d5495c27 --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/utils/PseudoRandom.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.utils; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * This class is used for any random generation operations. + */ +public class PseudoRandom { + + private static java.util.Random random = null; + + public static void reset(String seed) { + if (seed == null || seed.length() == 0) { + seed = String.valueOf(System.currentTimeMillis()); + } + random = new java.util.Random(seed.hashCode()); + } + + public static double random() { + return random.nextDouble(); + } + + // uniformly distributed boolean + public static boolean randomBoolean() { + return random.nextBoolean(); + } + + // non-uniformly distributed boolean. 0 probability - never true, 1 - always true + public static boolean randomBoolean(double probability) { + return random.nextDouble() < probability; + } + + public static long randomNotZero(long limit) { + long result = (long) (limit * random.nextDouble()); + return result > 0L ? result : 1L; + } + + public static int randomNotZero(int limit) { + int result = (int) (limit * random.nextDouble()); + return result > 0 ? result : 1; + } + + public static void shuffle(List list) { + Collections.shuffle(list, random); + } + + public static byte randomNotNegative(byte limit) { + byte result = (byte) (limit * random.nextDouble()); + return (byte)Math.abs(result); + } + + public static T randomElement(Collection collection) { + if (collection.isEmpty()) + throw new NoSuchElementException("Empty, no element can be randomly selected"); + if (collection instanceof List) + return randomElement((List) collection); + else { + int ix = random.nextInt(collection.size()); + final Iterator iterator = collection.iterator(); + while (ix > 0) { + ix--; + iterator.next(); + } + return iterator.next(); + } + } + + public static T randomElement(List list) { + if (list.isEmpty()) + throw new NoSuchElementException("Empty, no element can be randomly selected"); + return list.get(random.nextInt(list.size())); + } + + public static T randomElement(T[] array) { + if (array.length == 0) + throw new NoSuchElementException("Empty, no element can be randomly selected"); + return array[random.nextInt(array.length)]; + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/visitors/JavaCodeVisitor.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/visitors/JavaCodeVisitor.java new file mode 100644 index 00000000000..037d4ab2f9b --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/visitors/JavaCodeVisitor.java @@ -0,0 +1,1044 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.visitors; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.stream.Collectors; +import jdk.test.lib.jittester.BinaryOperator; +import jdk.test.lib.jittester.Block; +import jdk.test.lib.jittester.Break; +import jdk.test.lib.jittester.CastOperator; +import jdk.test.lib.jittester.CatchBlock; +import jdk.test.lib.jittester.Continue; +import jdk.test.lib.jittester.Declaration; +import jdk.test.lib.jittester.IRNode; +import jdk.test.lib.jittester.If; +import jdk.test.lib.jittester.Initialization; +import jdk.test.lib.jittester.Literal; +import jdk.test.lib.jittester.LocalVariable; +import jdk.test.lib.jittester.LogicOperator; +import jdk.test.lib.jittester.NonStaticMemberVariable; +import jdk.test.lib.jittester.Nothing; +import jdk.test.lib.jittester.Operator; +import jdk.test.lib.jittester.PrintVariables; +import jdk.test.lib.jittester.ProductionParams; +import jdk.test.lib.jittester.Statement; +import jdk.test.lib.jittester.StaticMemberVariable; +import jdk.test.lib.jittester.Switch; +import jdk.test.lib.jittester.Symbol; +import jdk.test.lib.jittester.TernaryOperator; +import jdk.test.lib.jittester.Throw; +import jdk.test.lib.jittester.TryCatchBlock; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.TypeList; +import jdk.test.lib.jittester.UnaryOperator; +import jdk.test.lib.jittester.VariableBase; +import jdk.test.lib.jittester.VariableDeclaration; +import jdk.test.lib.jittester.VariableDeclarationBlock; +import jdk.test.lib.jittester.VariableInfo; +import jdk.test.lib.jittester.arrays.ArrayCreation; +import jdk.test.lib.jittester.arrays.ArrayElement; +import jdk.test.lib.jittester.arrays.ArrayExtraction; +import jdk.test.lib.jittester.classes.ClassDefinitionBlock; +import jdk.test.lib.jittester.classes.Interface; +import jdk.test.lib.jittester.classes.Klass; +import jdk.test.lib.jittester.classes.MainKlass; +import jdk.test.lib.jittester.functions.ArgumentDeclaration; +import jdk.test.lib.jittester.functions.ConstructorDefinition; +import jdk.test.lib.jittester.functions.ConstructorDefinitionBlock; +import jdk.test.lib.jittester.functions.Function; +import jdk.test.lib.jittester.functions.FunctionDeclaration; +import jdk.test.lib.jittester.functions.FunctionDeclarationBlock; +import jdk.test.lib.jittester.functions.FunctionDefinition; +import jdk.test.lib.jittester.functions.FunctionDefinitionBlock; +import jdk.test.lib.jittester.functions.FunctionInfo; +import jdk.test.lib.jittester.functions.FunctionRedefinition; +import jdk.test.lib.jittester.functions.FunctionRedefinitionBlock; +import jdk.test.lib.jittester.functions.Return; +import jdk.test.lib.jittester.functions.StaticConstructorDefinition; +import jdk.test.lib.jittester.loops.CounterInitializer; +import jdk.test.lib.jittester.loops.CounterManipulator; +import jdk.test.lib.jittester.loops.DoWhile; +import jdk.test.lib.jittester.loops.For; +import jdk.test.lib.jittester.loops.Loop; +import jdk.test.lib.jittester.loops.LoopingCondition; +import jdk.test.lib.jittester.loops.While; +import jdk.test.lib.jittester.types.TypeArray; +import jdk.test.lib.jittester.types.TypeByte; +import jdk.test.lib.jittester.types.TypeKlass; +import jdk.test.lib.jittester.types.TypeChar; +import jdk.test.lib.jittester.types.TypeDouble; +import jdk.test.lib.jittester.types.TypeFloat; +import jdk.test.lib.jittester.types.TypeLong; +import jdk.test.lib.jittester.types.TypeShort; +import jdk.test.lib.jittester.utils.PrintingUtils; + +public class JavaCodeVisitor implements Visitor { + + public static String funcAttributes(FunctionInfo fi) { + String attrs = attributes(fi); + if (fi.isSynchronized()) { + attrs += "synchronized "; + } + return attrs; + } + + public static String attributes(Symbol s) { + String attrs = ""; + if (s.isPrivate()) { + attrs += "private "; + } + if (s.isProtected()) { + attrs += "protected "; + } + if (s.isPublic()) { + attrs += "public "; + } + if (s.isFinal()) { + attrs += "final "; + } + if (s.isStatic()) { + attrs += "static "; + } + return attrs; + } + + public String expressionToJavaCode(Operator t, IRNode p, Operator.Order o) { + String result; + try { + if ((o == Operator.Order.LEFT && ((Operator) p).getPriority() < t.getPriority()) + || (o == Operator.Order.RIGHT && ((Operator) p).getPriority() <= t.getPriority())) { + result = "(" + p.accept(this)+ ")"; + } else { + result = p.accept(this); + } + } catch (Exception e) { + result = p.accept(this); + } + return result; + } + + @Override + public String visit(ArgumentDeclaration node) { + VariableInfo vi = node.variableInfo; + return attributes(vi) + vi.type.accept(this) + " " + vi.name; + } + + @Override + public String visit(ArrayCreation node) { + Type arrayType = node.getArrayType(); + String type = arrayType.accept(this); + String name = node.getVariable().getName(); + StringBuilder code = new StringBuilder() + .append(node.getVariable().accept(this)) + .append(";\n") + .append(PrintingUtils.align(node.getParent().getLevel())) + .append(name) + .append(" = new ") + .append(type); + code.append(node.getChildren().stream() + .map(p -> p.accept(this)) + .collect(Collectors.joining("][", "[", "]"))); + code.append(";\n") + .append(PrintingUtils.align(node.getParent().getLevel())) + .append("java.util.Arrays.fill(") + .append(name) + .append(", "); + if (TypeList.find("boolean") == arrayType) { + code.append("false"); + } else if (TypeList.isBuiltIn(arrayType)) { + code.append("0"); + } else { + code.append("new ") + .append(type) + .append("()"); + } + code.append(");\n"); + return code.toString(); + } + + @Override + public String visit(ArrayElement node) { + IRNode array = node.getChild(0); + StringBuilder code = new StringBuilder(); + if (array instanceof VariableBase || array instanceof Function) { + code.append(array.accept(this)); + } else { + code.append("(") + .append(array.accept(this)) + .append(")"); + } + code.append(node.getChildren().stream() + .skip(1) + .map(c -> c.accept(this)) + .collect(Collectors.joining("][", "[", "]"))); + return code.toString(); + } + + @Override + public String visit(ArrayExtraction node) { + IRNode array = node.getChild(0); + StringBuilder code = new StringBuilder(); + if (array instanceof VariableBase || array instanceof Function) { + code.append(array.accept(this)); + } else { + code.append("(") + .append(array.accept(this)) + .append(")"); + } + code.append(node.getChildren().stream() + .skip(1) + .map(c -> c.accept(this)) + .collect(Collectors.joining("][", "[", "]"))); + return code.toString(); + } + + @Override + public String visit(BinaryOperator node) { + IRNode left = node.getChild(Operator.Order.LEFT.ordinal()); + IRNode right = node.getChild(Operator.Order.RIGHT.ordinal()); + if (left == null || right == null) { + return "null"; + } + return expressionToJavaCode(node, left, Operator.Order.LEFT) + + " " + node.getOperationCode() + " " + + expressionToJavaCode(node, right, Operator.Order.RIGHT); + } + + @Override + public String visit(Block node) { + StringBuilder code = new StringBuilder(); + for (IRNode i : node.getChildren()) { + String s = i.accept(this); + if (!s.isEmpty()) { + int level = node.getLevel(); + if (i instanceof Block) { + code.append(PrintingUtils.align(level + 1)) + .append("{\n") + .append(s) + .append(PrintingUtils.align(level + 1)) + .append("}"); + } else { + code.append(PrintingUtils.align(level + 1)) + .append(s); + } + code.append(addComplexityInfo(i)); + code.append("\n"); + } + } + return code.toString(); + } + + private String addComplexityInfo(IRNode node) { + if (ProductionParams.printComplexity.value()) { + return " /* " + node.complexity() + " */"; + } + return ""; + } + + @Override + public String visit(Break node) { + return "break;"; + } + + @Override + public String visit(CastOperator node) { + return "(" + node.getResultType().accept(this)+ ")" + + expressionToJavaCode(node, node.getChild(0), Operator.Order.LEFT); + } + + @Override + public String visit(ClassDefinitionBlock node) { + StringBuilder code = new StringBuilder(); + for (IRNode i : node.getChildren()) { + code.append("\n") + .append(PrintingUtils.align(node.getLevel())) + .append(i.accept(this)) + .append("\n"); + } + + return code.toString(); + } + + @Override + public String visit(ConstructorDefinition node) { + String args = node.getChildren().stream() + .skip(1) + .map(c -> c.accept(this)) + .collect(Collectors.joining(", ")); + IRNode body = node.getChild(0); + StringBuilder code = new StringBuilder(); + code.append(funcAttributes(node.getFunctionInfo())) + .append(node.getFunctionInfo().name) + .append("(") + .append(args) + .append(")\n") + .append(PrintingUtils.align(node.getLevel() + 1)) + .append("{\n") + .append(body != null ? body.accept(this) : "") + .append(PrintingUtils.align(node.getLevel() + 1)) + .append("}"); + return code.toString(); + } + + @Override + public String visit(ConstructorDefinitionBlock node) { + StringBuilder code = new StringBuilder(); + for (IRNode i : node.getChildren()) { + code.append("\n") + .append(PrintingUtils.align(node.getLevel())) + .append(i.accept(this)) + .append(addComplexityInfo(i)) + .append("\n"); + } + return code.toString(); + } + + @Override + public String visit(Continue node) { + return "continue;"; + } + + @Override + public String visit(CounterInitializer node) { + VariableInfo vi = node.get(); + return vi.type.accept(this) + " " + vi.name + " = " + node.getChild(0).accept(this)+ ";"; + } + + @Override + public String visit(CounterManipulator node) { + return node.getChild(0).accept(this); + } + + @Override + public String visit(Declaration node) { + return node.getChild(0).accept(this)+ ";"; + } + + @Override + public String visit(DoWhile node) { + IRNode header = node.getChild(DoWhile.DoWhilePart.HEADER.ordinal()); + IRNode body1 = node.getChild(DoWhile.DoWhilePart.BODY1.ordinal()); + IRNode body2 = node.getChild(DoWhile.DoWhilePart.BODY2.ordinal()); + StringBuilder code = new StringBuilder(); + Loop loop = node.getLoop(); + int level = node.getLevel(); + code.append(loop.initialization.accept(this)) + .append("\n") + .append(header.accept(this)) + .append(PrintingUtils.align(level)) + .append("do\n") + .append(PrintingUtils.align(level)) + .append("{\n") + .append(body1.accept(this)) + .append(PrintingUtils.align(level + 1)) + .append(loop.manipulator.accept(this)) + .append(";\n") + .append(body2.accept(this)) + .append(PrintingUtils.align(level)) + .append("} while (") + .append(loop.condition.accept(this)) + .append(");"); + return code.toString(); + } + + @Override + public String visit(For node) { + IRNode header = node.getChild(For.ForPart.HEADER.ordinal()); + IRNode statement1 = node.getChild(For.ForPart.STATEMENT1.ordinal()); + IRNode statement2 = node.getChild(For.ForPart.STATEMENT2.ordinal()); + IRNode body1 = node.getChild(For.ForPart.BODY1.ordinal()); + IRNode body2 = node.getChild(For.ForPart.BODY2.ordinal()); + IRNode body3 = node.getChild(For.ForPart.BODY3.ordinal()); + Loop loop = node.getLoop(); + StringBuilder code = new StringBuilder(); + int level = node.getLevel(); + code.append(loop.initialization.accept(this)) + .append("\n") + .append(header.accept(this)) + .append(PrintingUtils.align(level)) + .append("for (") + .append(statement1.accept(this)) + .append("; ") + .append(loop.condition.accept(this)) + .append("; ") + .append(statement2.accept(this)) + .append(")\n") + .append(PrintingUtils.align(level)) + .append("{\n") + .append(body1.accept(this)) + .append(PrintingUtils.align(level + 1)) + .append(loop.manipulator.accept(this)) + .append(";\n") + .append(body2.accept(this)) + .append(body3.accept(this)) + .append(PrintingUtils.align(level)) + .append("}"); + return code.toString(); + } + + @Override + public String visit(Function node) { + FunctionInfo value = node.getValue(); + String nameAndArgs = value.name + "(" + + node.getChildren().stream() + .skip(value.isStatic() || value.isConstructor() ? 0 : 1) + .map(c -> c.accept(this)) + .collect(Collectors.joining(", ")) + + ")"; + String prefix = ""; + if (value.isStatic()) { + if(!node.getKlass().equals(value.klass)) { + prefix = value.klass.getName() + "."; + } + } else if (value.isConstructor()) { + prefix = "new "; + } else { + IRNode object = node.getChild(0); + String objectString = object.accept(this); + if (!objectString.equals("this")) { + if (object instanceof VariableBase || object instanceof Function + || object instanceof Literal) { + prefix = objectString + "."; + } else { + prefix = "(" + objectString + ")" + "."; + } + } + } + return prefix + nameAndArgs; + } + + @Override + public String visit(FunctionDeclaration node) { + String args = node.getChildren().stream() + .map(c -> c.accept(this)) + .collect(Collectors.joining(", ")); + + FunctionInfo functionInfo = node.getFunctionInfo(); + return (functionInfo.klass.isInterface() ? "" : "abstract ") + + funcAttributes(functionInfo) + functionInfo.type.accept(this)+ " " + + functionInfo.name + "(" + args + ");"; + } + + @Override + public String visit(FunctionDeclarationBlock node) { + StringBuilder code = new StringBuilder(); + for (IRNode i : node.getChildren()) { + code.append(PrintingUtils.align(node.getLevel())) + .append(i.accept(this)) + .append(addComplexityInfo(i)) + .append("\n"); + } + return code.toString(); + } + + @Override + public String visit(FunctionDefinition node) { + String args = node.getChildren().stream() + .skip(2) + .map(c -> c.accept(this)) + .collect(Collectors.joining(", ")); + IRNode body = node.getChild(0); + IRNode ret = node.getChild(1); + FunctionInfo functionInfo = node.getFunctionInfo(); + return funcAttributes(functionInfo) + functionInfo.type.accept(this) + " " + functionInfo.name + "(" + args + ")" + "\n" + + PrintingUtils.align(node.getLevel() + 1) + "{\n" + + body.accept(this) + + (ret != null ? PrintingUtils.align(node.getLevel() + 2) + ret.accept(this) + "\n" : "") + + PrintingUtils.align(node.getLevel() + 1) + "}"; + } + + @Override + public String visit(FunctionDefinitionBlock node) { + StringBuilder code = new StringBuilder(); + for (IRNode i : node.getChildren()) { + code.append("\n") + .append(PrintingUtils.align(node.getLevel())) + .append(i.accept(this)) + .append(addComplexityInfo(i)) + .append("\n"); + } + return code.toString(); + } + + @Override + public String visit(FunctionRedefinition node) { + String args = node.getChildren().stream() + .map(c -> c.accept(this)) + .collect(Collectors.joining(", ")); + + IRNode body = node.getChild(0); + IRNode ret = node.getChild(1); + int level = node.getLevel(); + FunctionInfo functionInfo = node.getFunctionInfo(); + return funcAttributes(functionInfo) + functionInfo.type + " " + functionInfo.name + "(" + args + ")" + "\n" + + PrintingUtils.align(level + 1) + "{\n" + + body + + (ret != null ? PrintingUtils.align(level + 2) + ret + "\n" : "") + + PrintingUtils.align(level + 1) + "}"; + } + + @Override + public String visit(FunctionRedefinitionBlock node) { + StringBuilder code = new StringBuilder(); + for (IRNode i : node.getChildren()) { + code.append("\n") + .append(PrintingUtils.align(node.getLevel())) + .append(i.accept(this)) + .append(addComplexityInfo(i)) + .append("\n"); + } + return code.toString(); + } + + @Override + public String visit(If node) { + int level = node.getLevel(); + String thenBlockString = PrintingUtils.align(level) + "{\n" + + node.getChild(If.IfPart.THEN.ordinal()).accept(this) + + PrintingUtils.align(level) + "}"; + + String elseBlockString = null; + if (node.getChild(If.IfPart.ELSE.ordinal()) != null) { + elseBlockString = PrintingUtils.align(level) + "{\n" + + node.getChild(If.IfPart.ELSE.ordinal()).accept(this) + + PrintingUtils.align(level) + "}"; + } + + return "if (" + node.getChild(If.IfPart.CONDITION.ordinal()).accept(this)+ ")\n" + + thenBlockString + (elseBlockString != null ? "\n" + + PrintingUtils.align(level) + "else\n" + elseBlockString : ""); + } + + @Override + public String visit(Initialization node) { + VariableInfo vi = node.getVariableInfo(); + return attributes(vi) + vi.type.accept(this)+ " " + vi.name + " = " + + node.getChild(0).accept(this); + } + + @Override + public String visit(Interface node) { + return "interface " + node.getName() + (node.getParentKlass() != null ? " extends " + + node.getParentKlass().getName() : "") + " {\n" + + (node.getChildren().size() > 0 ? node.getChild(0).accept(this) : "") + + "}\n"; + } + + @Override + public String visit(Klass node) { + TypeKlass thisKlass = node.getThisKlass(); + String r = (ProductionParams.enableStrictFP.value() ? "strictfp " : "") + + (thisKlass.isFinal() ? "final " : "") + + (thisKlass.isAbstract() ? "abstract " : "") + + "class " + node.getName() + + (node.getParentKlass()!= null ? " extends " + node.getParentKlass().getName() : ""); + List interfaces = node.getInterfaces(); + r += interfaces.stream() + .map(c -> c.getName()) + .collect(Collectors.joining(", ", (interfaces.isEmpty() ? "" : " implements "), "")); + IRNode dataMembers = node.getChild(Klass.KlassPart.DATA_MEMBERS.ordinal()); + IRNode constructors = node.getChild(Klass.KlassPart.CONSTRUCTORS.ordinal()); + IRNode redefinedFunctions = node.getChild(Klass.KlassPart.REDEFINED_FUNCTIONS.ordinal()); + IRNode overridenFunctions = node.getChild(Klass.KlassPart.OVERRIDEN_FUNCTIONS.ordinal()); + IRNode memberFunctions = node.getChild(Klass.KlassPart.MEMBER_FUNCTIONS.ordinal()); + IRNode memberFunctionDecls = node.getChild(Klass.KlassPart.MEMBER_FUNCTIONS_DECLARATIONS.ordinal()); + IRNode printVariables = node.getChild(Klass.KlassPart.PRINT_VARIABLES.ordinal()); + r += " {\n" + + (dataMembers != null ? (dataMembers.accept(this)+ "\n") : "") + + (constructors != null ? (constructors.accept(this)+ "\n") : "") + + (redefinedFunctions != null ? (redefinedFunctions.accept(this)+ "\n") : "") + + (overridenFunctions != null ? (overridenFunctions.accept(this)+ "\n") : "") + + (memberFunctionDecls != null ? (memberFunctionDecls.accept(this)+ "\n") : "") + + (memberFunctions != null ? (memberFunctions.accept(this)+ "\n") : "") + + " public String toString()\n" + + " {\n" + + printVariables.accept(this) + + " }\n" + + "}\n"; + return r; + } + + @Override + public String visit(Literal node) { + Type resultType = node.getResultType(); + Object value = node.getValue(); + if (resultType.equals(new TypeLong())) { + return value.toString() + "L"; + } + if (resultType.equals(new TypeFloat())) { + return String.format((Locale) null, + "%." + ProductionParams.floatingPointPrecision.value() + "EF", + Double.parseDouble(value.toString())); + } + if (resultType.equals(new TypeDouble())) { + return String.format((Locale) null, + "%." + 2 * ProductionParams.floatingPointPrecision.value() + "E", + Double.parseDouble(value.toString())); + } + if (resultType.equals(new TypeChar())) { + if (((Character) value).charValue() == '\\') { + return "\'" + "\\\\" + "\'"; + } else { + return "\'" + value.toString() + "\'"; + } + } + if (resultType.equals(new TypeShort())) { + return "(short) " + value.toString(); + } + if (resultType.equals(new TypeByte())) { + return "(byte) " + value.toString(); + } + return value.toString(); + } + + @Override + public String visit(LocalVariable node) { + return node.get().name; + } + + @Override + public String visit(LogicOperator node) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public String visit(LoopingCondition node) { + return node.getCondition().accept(this); + } + + @Override + public String visit(MainKlass node) { + String name = node.getName(); + IRNode dataMembers = node.getChild(MainKlass.MainKlassPart.DATA_MEMBERS.ordinal()); + IRNode memberFunctions = node.getChild(MainKlass.MainKlassPart.MEMBER_FUNCTIONS.ordinal()); + IRNode testFunction = node.getChild(MainKlass.MainKlassPart.TEST_FUNCTION.ordinal()); + IRNode printVariables = node.getChild(MainKlass.MainKlassPart.PRINT_VARIABLES.ordinal()); + String executeFunction = " public static String execute()\n" + + " {\n" + + " try {\n" + + " " + name + " t = new " + name + "();\n" + + " try { t.test(); }\n" + + " catch(Throwable e) { }\n" + + " try { return t.toString(); }\n" + + " catch (Throwable e) { return \"Error during result conversion to String\"; }\n" + + " } catch (Throwable e) { return \"Error during test execution\"; }\n" + + " }\n"; + String mainFunction = " public static void main(String[] args)\n" + + " {\n" + + " try {\n" + + " " + name + " t = new " + name + "();\n" + + " try {\n" + + " for (int i = 0; i < 150000; ++i) {\n" + + " t.test();\n" + + " }\n" + + " }\n" + + " catch(Throwable e) { e.printStackTrace(); }\n" + + " try { System.out.println(t); }\n" + + " catch(Throwable e) { e.printStackTrace();}\n" + + " } catch (Throwable e) { e.printStackTrace(); }\n" + + " }\n"; + String printerClass = " static class Printer\n" + + " {\n" + + " public static String print(boolean arg) { return String.valueOf(arg); }\n" + + " public static String print(byte arg) { return String.valueOf(arg); }\n" + + " public static String print(short arg) { return String.valueOf(arg); }\n" + + " public static String print(char arg) { return String.valueOf((int)arg); }\n" + + " public static String print(int arg) { return String.valueOf(arg); }\n" + + " public static String print(long arg) { return String.valueOf(arg); }\n" + + " public static String print(float arg) { return String.valueOf(arg); }\n" + + " public static String print(double arg) { return String.valueOf(arg); }\n" + + "\n" + + "\n" + + " public static String print(Object arg)\n" + + " {\n" + + " return print_r(new java.util.Stack(), arg);\n" + + " }\n" + + "\n" + + " private static String print_r(java.util.Stack visitedObjects, Object arg)\n" + + " {\n" + + " String result = \"\";\n" + + " if (arg == null)\n" + + " result += \"null\";\n" + + " else\n" + + " if (arg.getClass().isArray())\n" + + " {\n" + + " for (int i = 0; i < visitedObjects.size(); i++)\n" + + " if (visitedObjects.elementAt(i) == arg) return \"\";\n" + + "\n" + + " visitedObjects.push(arg);\n" + + "\n" + + " final String delimiter = \", \";\n" + + " result += \"[\";\n" + + "\n" + + " if (arg instanceof Object[])\n" + + " {\n" + + " Object[] array = (Object[]) arg;\n" + + " for (int i = 0; i < array.length; i++)\n" + + " {\n" + + " result += print_r(visitedObjects, array[i]);\n" + + " if (i < array.length - 1) result += delimiter;\n" + + " }\n" + + " }\n" + + " else\n" + + " if (arg instanceof boolean[])\n" + + " {\n" + + " boolean[] array = (boolean[]) arg;\n" + + " for (int i = 0; i < array.length; i++)\n" + + " {\n" + + " result += print(array[i]);\n" + + " if (i < array.length - 1) result += delimiter;\n" + + " }\n" + + " }\n" + + " else\n" + + " if (arg instanceof byte[])\n" + + " {\n" + + " byte[] array = (byte[]) arg;\n" + + " for (int i = 0; i < array.length; i++)\n" + + " {\n" + + " result += print(array[i]);\n" + + " if (i < array.length - 1) result += delimiter;\n" + + " }\n" + + " }\n" + + " else\n" + + " if (arg instanceof short[])\n" + + " {\n" + + " short[] array = (short[]) arg;\n" + + " for (int i = 0; i < array.length; i++)\n" + + " {\n" + + " result += print(array[i]);\n" + + " if (i < array.length - 1) result += delimiter;\n" + + " }\n" + + " }\n" + + " else\n" + + " if (arg instanceof char[])\n" + + " {\n" + + " char[] array = (char[]) arg;\n" + + " for (int i = 0; i < array.length; i++)\n" + + " {\n" + + " result += print(array[i]);\n" + + " if (i < array.length - 1) result += delimiter;\n" + + " }\n" + + " }\n" + + " else\n" + + " if (arg instanceof int[])\n" + + " {\n" + + " int[] array = (int[]) arg;\n" + + " for (int i = 0; i < array.length; i++)\n" + + " {\n" + + " result += print(array[i]);\n" + + " if (i < array.length - 1) result += delimiter;\n" + + " }\n" + + " }\n" + + " else\n" + + " if (arg instanceof long[])\n" + + " {\n" + + " long[] array = (long[]) arg;\n" + + " for (int i = 0; i < array.length; i++)\n" + + " {\n" + + " result += print(array[i]);\n" + + " if (i < array.length - 1) result += delimiter;\n" + + " }\n" + + " }\n" + + " else\n" + + " if (arg instanceof float[])\n" + + " {\n" + + " float[] array = (float[]) arg;\n" + + " for (int i = 0; i < array.length; i++)\n" + + " {\n" + + " result += print(array[i]);\n" + + " if (i < array.length - 1) result += delimiter;\n" + + " }\n" + + " }\n" + + " else\n" + + " if (arg instanceof double[])\n" + + " {\n" + + " double[] array = (double[]) arg;\n" + + " for (int i = 0; i < array.length; i++)\n" + + " {\n" + + " result += print(array[i]);\n" + + " if (i < array.length - 1) result += delimiter;\n" + + " }\n" + + " }\n" + + "\n" + + " result += \"]\";\n" + + " visitedObjects.pop();\n" + + "\n" + + " } else\n" + + " {\n" + + " result += arg.toString();\n" + + " }\n" + + "\n" + + " return result;\n" + + " }\n" + + " }\n"; + + return (ProductionParams.enableStrictFP.value() ? "strictfp " : "") + + "public class " + name + " {\n" + + dataMembers.accept(this)+ "\n" + + (memberFunctions != null ? memberFunctions.accept(this): "") + "\n" + + executeFunction + + "\n" + + mainFunction + + "\n" + + " private void test()\n" + + " {\n" + + testFunction.accept(this) + + " }" + addComplexityInfo(testFunction) + "\n" + + " public String toString()\n" + + " {\n" + + printVariables.accept(this) + + " }\n" + + printerClass + + "}\n\n"; + } + + @Override + public String visit(NonStaticMemberVariable node) { + IRNode object = node.getChild(0); + String objectString = object.accept(this); + VariableInfo value = node.getValue(); + if (objectString.equals("this")) { + return value.name; + } else { + if (object instanceof VariableBase || object instanceof Function || object instanceof Literal) { + return objectString + "." + value.name; + } else { + return "(" + objectString + ")" + "." + value.name; + } + } + } + + @Override + public String visit(Nothing node) { + return ""; + } + + @Override + public String visit(PrintVariables node) { + int level = node.getLevel(); + List vars = node.getVars(); + StringBuilder result = new StringBuilder() + .append(PrintingUtils.align(level)) + .append("String result = \"[\\n\";\n"); + if (!vars.isEmpty()) { + for (int i = 0; i < vars.size(); i++) { + Symbol v = vars.get(i); + result.append(PrintingUtils.align(level)) + .append("result += \"").append(v.klass.getName()) + .append(".") + .append(v.name) + .append(" = \"; ") + .append("result += ") + .append(node.getPrinterName()) + .append(".print(") + .append(v.name) + .append(");\n") + .append(PrintingUtils.align(level)); + if (i < vars.size() - 1) { + result.append("result += \"\\n\";"); + } else { + result.append("result += \"\";"); + } + result.append("\n"); + } + } + result.append(PrintingUtils.align(level)) + .append("result += \"\\n]\";\n") + .append(PrintingUtils.align(level)) + .append("return result;\n"); + return result.toString(); + } + + @Override + public String visit(Return node) { + return "return " + node.getExpression().accept(this) + ";"; + } + + @Override + public String visit(Throw node) { + return "throw " + node.getThowable().accept(this) + ";"; + } + + @Override + public String visit(Statement node) { + return node.getChild(0).accept(this)+ (node.isSemicolonNeeded() ? ";" : ""); + } + + @Override + public String visit(StaticConstructorDefinition node) { + IRNode body = node.getChild(0); + return "static {\n" + + (body != null ? body.accept(this): "") + + PrintingUtils.align(node.getLevel()) + "}"; + } + + @Override + public String visit(StaticMemberVariable node) { + IRNode klass = node.getKlass(); + VariableInfo value = node.get(); + if (klass.equals(value.klass)) { + return value.name; + } else { + return value.klass.getName() + "." + value.name; + } + } + + @Override + public String visit(Switch node) { + int level = node.getLevel(); + int caseBlockIdx = node.getCaseBlockIndex(); + String cases = ""; + for (int i = 0; i < caseBlockIdx - 1; ++i) { + cases += PrintingUtils.align(level + 1); + if (node.getChild(i + 1) != null) { + cases += "case " + node.getChild(i + 1).accept(this)+ ":\n"; + } else { + cases += "default:\n"; + } + + cases += node.getChild(i + caseBlockIdx).accept(this)+ "\n"; + } + return "switch (" + node.getChild(0).accept(this)+ ")\n" + + PrintingUtils.align(level) + "{\n" + + cases + + PrintingUtils.align(level) + "}"; + } + + @Override + public String visit(TernaryOperator node) { + IRNode conditionalExp = node.getChild(TernaryOperator.TernaryPart.CONDITION.ordinal()); + IRNode leftExp = node.getChild(TernaryOperator.TernaryPart.TRUE.ordinal()); + IRNode rightExp = node.getChild(TernaryOperator.TernaryPart.FALSE.ordinal()); + if (Objects.isNull(conditionalExp) || Objects.isNull(leftExp) || Objects.isNull(rightExp)) { + return "null"; + } + return expressionToJavaCode(node, conditionalExp, Operator.Order.RIGHT) + " ? " + + expressionToJavaCode(node, leftExp, Operator.Order.RIGHT) + " : " + + expressionToJavaCode(node, rightExp, Operator.Order.RIGHT); + } + + @Override + public String visit(Type node) { + return node.getName(); + } + + @Override + public String visit(TypeArray node) { + String r = node.getType().accept(this); + for (int i = 0; i < node.getDimensions(); i++) { + r += "[]"; + } + return r; + } + + @Override + public String visit(UnaryOperator node) { + IRNode exp = node.getChild(0); + if (node.isPrefix()) { + return node.getOperatorText() + (exp instanceof Operator ? " " : "") + + expressionToJavaCode(node, exp, Operator.Order.LEFT); + } else { + return expressionToJavaCode(node, exp, Operator.Order.RIGHT) + + (exp instanceof Operator ? " " : "") + node.getOperatorText(); + } + } + + @Override + public String visit(VariableDeclaration node) { + VariableInfo vi = node.getVariableInfo(); + return attributes(vi) + vi.type.accept(this)+ " " + vi.name; + } + + @Override + public String visit(VariableDeclarationBlock node) { + StringBuilder code = new StringBuilder(); + for (IRNode i : node.getChildren()) { + code.append(PrintingUtils.align(node.getLevel())) + .append(i.accept(this)) + .append(addComplexityInfo(i)) + .append("\n"); + } + return code.toString(); + } + + @Override + public String visit(While node) { + IRNode header = node.getChild(While.WhilePart.HEADER.ordinal()); + IRNode body1 = node.getChild(While.WhilePart.BODY1.ordinal()); + IRNode body2 = node.getChild(While.WhilePart.BODY2.ordinal()); + IRNode body3 = node.getChild(While.WhilePart.BODY3.ordinal()); + int level = node.getLevel(); + Loop loop = node.getLoop(); + return loop.initialization.accept(this)+ "\n" + + header.accept(this) + + PrintingUtils.align(level) + "while (" + loop.condition.accept(this)+ ")\n" + + PrintingUtils.align(level) + "{\n" + + body1.accept(this) + + PrintingUtils.align(level + 1) + loop.manipulator.accept(this)+ ";\n" + + body2.accept(this) + + body3.accept(this) + + PrintingUtils.align(level) + "}"; + } + + @Override + public String visit(CatchBlock node) { + StringBuilder result = new StringBuilder(); + int level = node.getLevel(); + result.append(PrintingUtils.align(level)).append("catch("); + result.append(node.throwables.get(0).accept(this)); + for (int i = 1; i < node.throwables.size(); i++) { + result.append(" | ").append(node.throwables.get(i).accept(this)); + } + result.append(" ex) {\n"); + result.append(node.getChild(0).accept(this)); + result.append(PrintingUtils.align(level)).append("}\n"); + return result.toString(); + } + + @Override + public String visit(TryCatchBlock node) { + StringBuilder result = new StringBuilder(); + List childs = node.getChildren(); + IRNode body = childs.get(0); + IRNode finallyBody = childs.get(1); + int level = node.getLevel(); + result.append("try {\n") + .append(body.accept(this)).append("\n") + .append(PrintingUtils.align(level)).append("}\n"); + for (int i = 2; i < childs.size(); i++) { + result.append(childs.get(i).accept(this)); + } + if (finallyBody != null) { + result.append(PrintingUtils.align(level)).append("finally {\n") + .append(finallyBody.accept(this)).append("\n") + .append(PrintingUtils.align(level)).append("}\n"); + } + return result.toString(); + } +} diff --git a/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/visitors/Visitor.java b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/visitors/Visitor.java new file mode 100644 index 00000000000..9502d89a4ac --- /dev/null +++ b/hotspot/test/testlibrary/jittester/src/jdk/test/lib/jittester/visitors/Visitor.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.test.lib.jittester.visitors; + +import jdk.test.lib.jittester.BinaryOperator; +import jdk.test.lib.jittester.Block; +import jdk.test.lib.jittester.Break; +import jdk.test.lib.jittester.CastOperator; +import jdk.test.lib.jittester.CatchBlock; +import jdk.test.lib.jittester.Continue; +import jdk.test.lib.jittester.Declaration; +import jdk.test.lib.jittester.If; +import jdk.test.lib.jittester.Initialization; +import jdk.test.lib.jittester.Literal; +import jdk.test.lib.jittester.LocalVariable; +import jdk.test.lib.jittester.LogicOperator; +import jdk.test.lib.jittester.NonStaticMemberVariable; +import jdk.test.lib.jittester.Nothing; +import jdk.test.lib.jittester.PrintVariables; +import jdk.test.lib.jittester.Statement; +import jdk.test.lib.jittester.StaticMemberVariable; +import jdk.test.lib.jittester.Switch; +import jdk.test.lib.jittester.TernaryOperator; +import jdk.test.lib.jittester.Throw; +import jdk.test.lib.jittester.TryCatchBlock; +import jdk.test.lib.jittester.Type; +import jdk.test.lib.jittester.UnaryOperator; +import jdk.test.lib.jittester.VariableDeclaration; +import jdk.test.lib.jittester.VariableDeclarationBlock; +import jdk.test.lib.jittester.arrays.ArrayCreation; +import jdk.test.lib.jittester.arrays.ArrayElement; +import jdk.test.lib.jittester.arrays.ArrayExtraction; +import jdk.test.lib.jittester.classes.ClassDefinitionBlock; +import jdk.test.lib.jittester.classes.Interface; +import jdk.test.lib.jittester.classes.Klass; +import jdk.test.lib.jittester.classes.MainKlass; +import jdk.test.lib.jittester.functions.ArgumentDeclaration; +import jdk.test.lib.jittester.functions.ConstructorDefinition; +import jdk.test.lib.jittester.functions.ConstructorDefinitionBlock; +import jdk.test.lib.jittester.functions.Function; +import jdk.test.lib.jittester.functions.FunctionDeclaration; +import jdk.test.lib.jittester.functions.FunctionDeclarationBlock; +import jdk.test.lib.jittester.functions.FunctionDefinition; +import jdk.test.lib.jittester.functions.FunctionDefinitionBlock; +import jdk.test.lib.jittester.functions.FunctionRedefinition; +import jdk.test.lib.jittester.functions.FunctionRedefinitionBlock; +import jdk.test.lib.jittester.functions.Return; +import jdk.test.lib.jittester.functions.StaticConstructorDefinition; +import jdk.test.lib.jittester.loops.CounterInitializer; +import jdk.test.lib.jittester.loops.CounterManipulator; +import jdk.test.lib.jittester.loops.DoWhile; +import jdk.test.lib.jittester.loops.For; +import jdk.test.lib.jittester.loops.LoopingCondition; +import jdk.test.lib.jittester.loops.While; +import jdk.test.lib.jittester.types.TypeArray; + +public interface Visitor { + T visit(ArgumentDeclaration node); + T visit(ArrayCreation node); + T visit(ArrayElement node); + T visit(ArrayExtraction node); + T visit(BinaryOperator node); + T visit(Block node); + T visit(Break node); + T visit(CastOperator node); + T visit(ClassDefinitionBlock node); + T visit(ConstructorDefinition node); + T visit(ConstructorDefinitionBlock node); + T visit(Continue node); + T visit(CounterInitializer node); + T visit(CounterManipulator node); + T visit(Declaration node); + T visit(DoWhile node); + T visit(For node); + T visit(Function node); + T visit(FunctionDeclaration node); + T visit(FunctionDeclarationBlock node); + T visit(FunctionDefinition node); + T visit(FunctionDefinitionBlock node); + T visit(FunctionRedefinition node); + T visit(FunctionRedefinitionBlock node); + T visit(If node); + T visit(Initialization node); + T visit(Interface node); + T visit(Klass node); + T visit(Literal node); + T visit(LocalVariable node); + T visit(LogicOperator node); + T visit(LoopingCondition node); + T visit(MainKlass node); + T visit(NonStaticMemberVariable node); + T visit(Nothing node); + T visit(PrintVariables node); + T visit(Return node); + T visit(Throw node); + T visit(Statement node); + T visit(StaticConstructorDefinition node); + T visit(StaticMemberVariable node); + T visit(Switch node); + T visit(TernaryOperator node); + T visit(Type node); + T visit(TypeArray node); + T visit(UnaryOperator node); + T visit(VariableDeclaration node); + T visit(VariableDeclarationBlock node); + T visit(While node); + T visit(CatchBlock node); + T visit(TryCatchBlock node); +} diff --git a/jaxp/.hgtags b/jaxp/.hgtags index a1e0320a6bf..ad660fbccf5 100644 --- a/jaxp/.hgtags +++ b/jaxp/.hgtags @@ -334,3 +334,5 @@ eb435c878c2cbbfb043d0b205f4d5bd6faffd44a jdk9-b87 5021da4c949690e5c2578c073c36fb161e4b35e5 jdk9-b89 35f68242b624112cb6ef7e6226059674d6b499f4 jdk9-b90 ffaff3d0ad0e0ca1e632b80826afa8729ee72a48 jdk9-b91 +fcabfb3c38ac1da99394e821902537d92e45222d jdk9-b92 +b9c50c63305cf1120263f6b7c6993021b53c2c40 jdk9-b93 diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/XPath.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/XPath.java index be2e311c560..f6df2030c0c 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/XPath.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/XPath.java @@ -25,9 +25,12 @@ import com.sun.org.apache.xerces.internal.util.XMLChar; import com.sun.org.apache.xerces.internal.util.XMLSymbols; import com.sun.org.apache.xerces.internal.xni.NamespaceContext; import com.sun.org.apache.xerces.internal.xni.QName; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.Vector; +import java.util.stream.Collectors; /** * Bare minimum XPath parser. @@ -47,20 +50,18 @@ public class XPath { private static final boolean DEBUG_XPATH_PARSE = DEBUG_ALL || false; - private static final boolean DEBUG_ANY = DEBUG_XPATH_PARSE; - // // Data // /** Expression. */ - protected String fExpression; + protected final String fExpression; /** Symbol table. */ - protected SymbolTable fSymbolTable; + protected final SymbolTable fSymbolTable; /** Location paths. */ - protected LocationPath[] fLocationPaths; + protected final LocationPath[] fLocationPaths; // // Constructors @@ -72,7 +73,7 @@ public class XPath { throws XPathException { fExpression = xpath; fSymbolTable = symbolTable; - parseExpression(context); + fLocationPaths = parseExpression(context); } // (String,SymbolTable,NamespaceContext) // @@ -101,15 +102,14 @@ public class XPath { // /** Returns a string representation of this object. */ + @Override public String toString() { - StringBuffer buf=new StringBuffer(); - for (int i=0;i0){ - buf.append("|"); - } - buf.append(fLocationPaths[i].toString()); - } - return buf.toString(); + final List l = Arrays.asList(fLocationPaths); + final String s = l.stream() + .map(aPath -> aPath.toString()) + .collect(Collectors.joining("|")); + + return s; } // toString():String // @@ -132,12 +132,12 @@ public class XPath { * to build a {@link LocationPath} object from the accumulated * {@link Step}s. */ - private LocationPath buildLocationPath( Vector stepsVector ) throws XPathException { + private LocationPath buildLocationPath( ArrayList stepsVector ) throws XPathException { int size = stepsVector.size(); check(size!=0); Step[] steps = new Step[size]; - stepsVector.copyInto(steps); - stepsVector.removeAllElements(); + steps = stepsVector.toArray(steps); + stepsVector.clear(); return new LocationPath(steps); } @@ -146,7 +146,7 @@ public class XPath { * This method is implemented by using the XPathExprScanner and * examining the list of tokens that it returns. */ - private void parseExpression(final NamespaceContext context) + private LocationPath[] parseExpression(final NamespaceContext context) throws XPathException { // tokens @@ -184,8 +184,8 @@ public class XPath { throw new XPathException("c-general-xpath"); //fTokens.dumpTokens(); - Vector stepsVector = new Vector(); - Vector locationPathsVector= new Vector(); + ArrayList stepsVector = new ArrayList<>(); + ArrayList locationPathsVector= new ArrayList<>(); // true when the next token should be 'Step' (as defined in // the production rule [3] of XML Schema P1 section 3.11.6 @@ -194,28 +194,39 @@ public class XPath { // this is to make sure we can detect a token list like // 'abc' '/' '/' 'def' 'ghi' boolean expectingStep = true; - boolean expectingDoubleColon = false; - while(xtokens.hasMore()) { + while (xtokens.hasMore()) { final int token = xtokens.nextToken(); switch (token) { case XPath.Tokens.EXPRTOKEN_OPERATOR_UNION :{ check(!expectingStep); - locationPathsVector.addElement(buildLocationPath(stepsVector)); + locationPathsVector.add(buildLocationPath(stepsVector)); expectingStep=true; break; } - case XPath.Tokens.EXPRTOKEN_ATSIGN: { check(expectingStep); Step step = new Step( new Axis(Axis.ATTRIBUTE), parseNodeTest(xtokens.nextToken(),xtokens,context)); - stepsVector.addElement(step); + stepsVector.add(step); expectingStep=false; break; } + case XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE: { + check(expectingStep); + // If we got here we're expecting attribute:: + if (xtokens.nextToken() != XPath.Tokens.EXPRTOKEN_DOUBLE_COLON) { + throw new XPathException("c-general-xpath"); + } + Step step = new Step( + new Axis(Axis.ATTRIBUTE), + parseNodeTest(xtokens.nextToken(),xtokens,context)); + stepsVector.add(step); + expectingStep = false; + break; + } case XPath.Tokens.EXPRTOKEN_NAMETEST_ANY: case XPath.Tokens.EXPRTOKEN_NAMETEST_NAMESPACE: case XPath.Tokens.EXPRTOKEN_NAMETEST_QNAME: { @@ -223,11 +234,23 @@ public class XPath { Step step = new Step( new Axis(Axis.CHILD), parseNodeTest(token,xtokens,context)); - stepsVector.addElement(step); + stepsVector.add(step); expectingStep=false; break; } - + case XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD: { + check(expectingStep); + // If we got here we're expecting child:: + if (xtokens.nextToken() != XPath.Tokens.EXPRTOKEN_DOUBLE_COLON) { + throw new XPathException("c-general-xpath"); + } + Step step = new Step( + new Axis(Axis.CHILD), + parseNodeTest(xtokens.nextToken(),xtokens,context)); + stepsVector.add(step); + expectingStep = false; + break; + } case XPath.Tokens.EXPRTOKEN_PERIOD: { check(expectingStep); expectingStep=false; @@ -237,12 +260,12 @@ public class XPath { // This amounts to shorten "a/././b/./c" to "a/b/c". // Also, the matcher fails to work correctly if XPath // has those redundant dots. - if (stepsVector.size()==0) { + if (stepsVector.isEmpty()) { // build step Axis axis = new Axis(Axis.SELF); NodeTest nodeTest = new NodeTest(NodeTest.NODE); Step step = new Step(axis, nodeTest); - stepsVector.addElement(step); + stepsVector.add(step); if( xtokens.hasMore() && xtokens.peekToken() == XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH){ @@ -253,67 +276,41 @@ public class XPath { axis = new Axis(Axis.DESCENDANT); nodeTest = new NodeTest(NodeTest.NODE); step = new Step(axis, nodeTest); - stepsVector.addElement(step); + stepsVector.add(step); expectingStep=true; } } break; } - case XPath.Tokens.EXPRTOKEN_OPERATOR_DOUBLE_SLASH:{ - // this cannot appear in arbitrary position. + // this cannot appear in an arbitrary position. // it is only allowed right after '.' when // '.' is the first token of a location path. throw new XPathException("c-general-xpath"); } + case XPath.Tokens.EXPRTOKEN_DOUBLE_COLON: { + // :: cannot appear in an arbitrary position. + // We only expect this token if the xpath + // contains child:: or attribute:: + throw new XPathException("c-general-xpath"); + } case XPath.Tokens.EXPRTOKEN_OPERATOR_SLASH: { check(!expectingStep); expectingStep=true; break; } - case XPath.Tokens.EXPRTOKEN_AXISNAME_ATTRIBUTE: { - check(expectingStep); - expectingDoubleColon = true; - - if (xtokens.nextToken() == XPath.Tokens.EXPRTOKEN_DOUBLE_COLON){ - Step step = new Step( - new Axis(Axis.ATTRIBUTE), - parseNodeTest(xtokens.nextToken(),xtokens,context)); - stepsVector.addElement(step); - expectingStep=false; - expectingDoubleColon = false; - } - break; - } - case XPath.Tokens.EXPRTOKEN_AXISNAME_CHILD:{ - check(expectingStep); - expectingDoubleColon = true; - break; - } - case XPath.Tokens.EXPRTOKEN_DOUBLE_COLON :{ - check(expectingStep); - check(expectingDoubleColon); - expectingDoubleColon = false; - break; - } default: // we should have covered all the tokens that we can possibly see. - throw new XPathException("c-general-xpath"); - } + throw new InternalError(); + } } check(!expectingStep); - locationPathsVector.addElement(buildLocationPath(stepsVector)); + locationPathsVector.add(buildLocationPath(stepsVector)); - // save location path - fLocationPaths=new LocationPath[locationPathsVector.size()]; - locationPathsVector.copyInto(fLocationPaths); - - - if (DEBUG_XPATH_PARSE) { - System.out.println(">>> "+fLocationPaths); - } + // return location path + return locationPathsVector.toArray(new LocationPath[locationPathsVector.size()]); } // parseExpression(SymbolTable,NamespaceContext) @@ -378,7 +375,7 @@ public class XPath { // /** List of steps. */ - public Step[] steps; + public final Step[] steps; // // Constructors @@ -445,10 +442,10 @@ public class XPath { // /** Axis. */ - public Axis axis; + public final Axis axis; /** Node test. */ - public NodeTest nodeTest; + public final NodeTest nodeTest; // // Constructors @@ -525,7 +522,7 @@ public class XPath { // /** Axis type. */ - public short type; + public final short type; // // Constructors @@ -594,7 +591,7 @@ public class XPath { // /** Node test type. */ - public short type; + public final short type; /** Node qualified name. */ public final QName name = new QName(); @@ -856,13 +853,13 @@ public class XPath { private int[] fTokens = new int[INITIAL_TOKEN_COUNT]; private int fTokenCount = 0; // for writing - private SymbolTable fSymbolTable; + private final SymbolTable fSymbolTable; // REVISIT: Code something better here. -Ac - private Map fSymbolMapping = new HashMap<>(); + private final Map fSymbolMapping = new HashMap<>(); // REVISIT: Code something better here. -Ac - private Map fTokenNames = new HashMap<>(); + private final Map fTokenNames = new HashMap<>(); /** * Current position in the token list. @@ -1888,6 +1885,10 @@ public class XPath { tokens.addToken(nameHandle); } break; + default: + // CHARTYPE_INVALID or CHARTYPE_OTHER + // We're not expecting to find either of these in a valid expression. + return false; } } if (XPath.Tokens.DUMP_TOKENS) { diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/XPathException.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/XPathException.java index 5e68d8bf021..719559d7a20 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/XPathException.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/XPathException.java @@ -3,11 +3,12 @@ * DO NOT REMOVE OR ALTER! */ /* - * Copyright 2001, 2002,2004 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -37,7 +38,7 @@ public class XPathException // Data // hold the value of the key this Exception refers to. - private String fKey; + private final String fKey; // // Constructors // diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/BMPattern.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/BMPattern.java index 5fd796af1c7..49e7766cae2 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/BMPattern.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/BMPattern.java @@ -3,11 +3,12 @@ * DO NOT REMOVE OR ALTER! */ /* - * Copyright 1999-2002,2004 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -29,9 +30,9 @@ import java.text.CharacterIterator; * */ public class BMPattern { - char[] pattern; - int[] shiftTable; - boolean ignoreCase; + final char[] pattern; + final int[] shiftTable; + final boolean ignoreCase; public BMPattern(String pat, boolean ignoreCase) { this(pat, 256, ignoreCase); diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/CaseInsensitiveMap.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/CaseInsensitiveMap.java index 3f5da982fa4..65f81d95c6c 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/CaseInsensitiveMap.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/CaseInsensitiveMap.java @@ -24,32 +24,27 @@ package com.sun.org.apache.xerces.internal.impl.xpath.regex; /** */ -public class CaseInsensitiveMap { +final class CaseInsensitiveMap { - private static int CHUNK_SHIFT = 10; /* 2^10 = 1k */ - private static int CHUNK_SIZE = (1< branches; UnionOp(int type, int size) { super(type); - this.branches = new Vector(size); + this.branches = new ArrayList<>(size); } void addElement(Op op) { - this.branches.addElement(op); + this.branches.add(op); } int size() { return this.branches.size(); } Op elementAt(int index) { - return (Op)this.branches.elementAt(index); + return this.branches.get(index); } } @@ -201,8 +202,8 @@ class Op { } // ================================================================ static class ModifierOp extends ChildOp { - int v1; - int v2; + final int v1; + final int v2; ModifierOp(int type, int v1, int v2) { super(type); this.v1 = v1; @@ -217,7 +218,7 @@ class Op { } // ================================================================ static class RangeOp extends Op { - Token tok; + final Token tok; RangeOp(int type, Token tok) { super(type); this.tok = tok; @@ -228,7 +229,7 @@ class Op { } // ================================================================ static class StringOp extends Op { - String string; + final String string; StringOp(int type, String literal) { super(type); this.string = literal; @@ -239,10 +240,10 @@ class Op { } // ================================================================ static class ConditionOp extends Op { - int refNumber; - Op condition; - Op yes; - Op no; + final int refNumber; + final Op condition; + final Op yes; + final Op no; ConditionOp(int type, int refno, Op conditionflow, Op yesflow, Op noflow) { super(type); this.refNumber = refno; diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/ParseException.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/ParseException.java index ac4041e8b13..fba6de52143 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/ParseException.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/ParseException.java @@ -3,11 +3,12 @@ * DO NOT REMOVE OR ALTER! */ /* - * Copyright 1999-2002,2004 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -30,7 +31,7 @@ public class ParseException extends RuntimeException { /** Serialization version. */ static final long serialVersionUID = -7012400318097691370L; - int location; + final int location; /* public ParseException(String mes) { diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/ParserForXMLSchema.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/ParserForXMLSchema.java index 3be13114a73..bc0ff0fea29 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/ParserForXMLSchema.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/ParserForXMLSchema.java @@ -252,7 +252,7 @@ class ParserForXMLSchema extends RegexParser { if (c == ']') throw this.ex("parser.cc.7", this.offset-2); if (c == '-' && this.chardata != ']' && !firstloop) throw this.ex("parser.cc.8", this.offset-2); // if regex = '[-]' then invalid } - if (this.read() != T_CHAR || this.chardata != '-' || c == '-' && firstloop) { // Here is no '-'. + if (this.read() != T_CHAR || this.chardata != '-' || c == '-' && !wasDecoded && firstloop) { // Here is no '-'. if (!this.isSet(RegularExpression.IGNORE_CASE) || c > 0xffff) { tok.addRange(c, c); } @@ -382,17 +382,20 @@ class ParserForXMLSchema extends RegexParser { ranges2.put("xml:isSpace", Token.complementRanges(tok)); tok = Token.createRange(); - setupRange(tok, DIGITS); - setupRange(tok, DIGITS_INT); + setupRange(tok, DIGITS_INTS); ranges.put("xml:isDigit", tok); ranges2.put("xml:isDigit", Token.complementRanges(tok)); + /* + * \w is defined by the XML Schema specification to be: + * [#x0000-#x10FFFF]-[\p{P}\p{Z}\p{C}] (all characters except the set of "punctuation", "separator" and "other" characters) + */ tok = Token.createRange(); - setupRange(tok, LETTERS); - setupRange(tok, LETTERS_INT); - tok.mergeRanges(ranges.get("xml:isDigit")); - ranges.put("xml:isWord", tok); - ranges2.put("xml:isWord", Token.complementRanges(tok)); + tok.mergeRanges(Token.getRange("P", true)); + tok.mergeRanges(Token.getRange("Z", true)); + tok.mergeRanges(Token.getRange("C", true)); + ranges2.put("xml:isWord", tok); + ranges.put("xml:isWord", Token.complementRanges(tok)); tok = Token.createRange(); setupRange(tok, NAMECHARS); @@ -401,6 +404,7 @@ class ParserForXMLSchema extends RegexParser { tok = Token.createRange(); setupRange(tok, LETTERS); + setupRange(tok, LETTERS_INT); tok.addRange('_', '_'); tok.addRange(':', ':'); ranges.put("xml:isInitialNameChar", tok); @@ -502,11 +506,12 @@ class ParserForXMLSchema extends RegexParser { private static final int[] LETTERS_INT = {0x1d790, 0x1d7a8, 0x1d7aa, 0x1d7c9, 0x2fa1b, 0x2fa1d}; - private static final String DIGITS = - "\u0030\u0039\u0660\u0669\u06F0\u06F9\u0966\u096F\u09E6\u09EF\u0A66\u0A6F\u0AE6\u0AEF" - +"\u0B66\u0B6F\u0BE7\u0BEF\u0C66\u0C6F\u0CE6\u0CEF\u0D66\u0D6F\u0E50\u0E59\u0ED0\u0ED9" - +"\u0F20\u0F29\u1040\u1049\u1369\u1371\u17E0\u17E9\u1810\u1819\uFF10\uFF19"; - - private static final int[] DIGITS_INT = {0x1D7CE, 0x1D7FF}; - + private static final int[] DIGITS_INTS = { + 0x0030, 0x0039, 0x0660, 0x0669, 0x06F0, 0x06F9, 0x0966, 0x096F, + 0x09E6, 0x09EF, 0x0A66, 0x0A6F, 0x0AE6, 0x0AEF, 0x0B66, 0x0B6F, + 0x0BE7, 0x0BEF, 0x0C66, 0x0C6F, 0x0CE6, 0x0CEF, 0x0D66, 0x0D6F, + 0x0E50, 0x0E59, 0x0ED0, 0x0ED9, 0x0F20, 0x0F29, 0x1040, 0x1049, + 0x1369, 0x1371, 0x17E0, 0x17E9, 0x1810, 0x1819, 0xFF10, 0xFF19, + 0x1D7CE, 0x1D7FF + }; } diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/REUtil.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/REUtil.java index 8f0a7d60794..dc96c08118f 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/REUtil.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/REUtil.java @@ -1,13 +1,13 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. */ /* - * Copyright 1999-2002,2004 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -110,27 +110,27 @@ public final class REUtil { } static final String createOptionString(int options) { - StringBuffer sb = new StringBuffer(9); + StringBuilder sb = new StringBuilder(9); if ((options & RegularExpression.PROHIBIT_FIXED_STRING_OPTIMIZATION) != 0) - sb.append((char)'F'); + sb.append('F'); if ((options & RegularExpression.PROHIBIT_HEAD_CHARACTER_OPTIMIZATION) != 0) - sb.append((char)'H'); + sb.append('H'); if ((options & RegularExpression.XMLSCHEMA_MODE) != 0) - sb.append((char)'X'); + sb.append('X'); if ((options & RegularExpression.IGNORE_CASE) != 0) - sb.append((char)'i'); + sb.append('i'); if ((options & RegularExpression.MULTIPLE_LINES) != 0) - sb.append((char)'m'); + sb.append('m'); if ((options & RegularExpression.SINGLE_LINE) != 0) - sb.append((char)'s'); + sb.append('s'); if ((options & RegularExpression.USE_UNICODE_CATEGORY) != 0) - sb.append((char)'u'); + sb.append('u'); if ((options & RegularExpression.UNICODE_WORD_BOUNDARY) != 0) - sb.append((char)'w'); + sb.append('w'); if ((options & RegularExpression.EXTENDED_COMMENT) != 0) - sb.append((char)'x'); + sb.append('x'); if ((options & RegularExpression.SPECIAL_COMMA) != 0) - sb.append((char)','); + sb.append(','); return sb.toString().intern(); } @@ -138,13 +138,19 @@ public final class REUtil { static String stripExtendedComment(String regex) { int len = regex.length(); - StringBuffer buffer = new StringBuffer(len); + StringBuilder buffer = new StringBuilder(len); int offset = 0; + int charClass = 0; while (offset < len) { int ch = regex.charAt(offset++); // Skips a white space. - if (ch == '\t' || ch == '\n' || ch == '\f' || ch == '\r' || ch == ' ') + if (ch == '\t' || ch == '\n' || ch == '\f' || ch == '\r' || ch == ' ') { + // if we are inside a character class, we keep the white space + if (charClass > 0) { + buffer.append((char)ch); + } continue; + } if (ch == '#') { // Skips chracters between '#' and a line end. while (offset < len) { @@ -163,12 +169,36 @@ public final class REUtil { buffer.append((char)next); offset ++; } else { // Other escaped character. - buffer.append((char)'\\'); + buffer.append('\\'); buffer.append((char)next); offset ++; } - } else // As is. + } + else if (ch == '[') { + charClass++; buffer.append((char)ch); + if (offset < len) { + next = regex.charAt(offset); + if (next == '[' || next ==']') { + buffer.append((char)next); + offset ++; + } + else if (next == '^' && offset + 1 < len) { + next = regex.charAt(offset + 1); + if (next == '[' || next ==']') { + buffer.append('^'); + buffer.append((char)next); + offset += 2; + } + } + } + } + else { + if (charClass > 0 && ch == ']') { + --charClass; + } + buffer.append((char)ch); + } } return buffer.toString(); } @@ -307,15 +337,15 @@ public final class REUtil { */ public static String quoteMeta(String literal) { int len = literal.length(); - StringBuffer buffer = null; + StringBuilder buffer = null; for (int i = 0; i < len; i ++) { int ch = literal.charAt(i); if (".*+?{[()|\\^$".indexOf(ch) >= 0) { if (buffer == null) { - buffer = new StringBuffer(i+(len-i)*2); + buffer = new StringBuilder(i+(len-i)*2); if (i > 0) buffer.append(literal.substring(0, i)); } - buffer.append((char)'\\'); + buffer.append('\\'); buffer.append((char)ch); } else if (buffer != null) buffer.append((char)ch); diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/RangeToken.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/RangeToken.java index 192be06a35c..cd57eab9d15 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/RangeToken.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/RangeToken.java @@ -1,6 +1,5 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! + * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -43,7 +42,7 @@ final class RangeToken extends Token implements java.io.Serializable { this.setSorted(false); } - // for RANGE or NRANGE + // for RANGE or NRANGE protected void addRange(int start, int end) { this.icaseCache = null; //System.err.println("Token#addRange(): "+start+" "+end); @@ -560,7 +559,7 @@ final class RangeToken extends Token implements java.io.Serializable { sb.append(escapeCharInCharClass(this.ranges[i])); } else { sb.append(escapeCharInCharClass(this.ranges[i])); - sb.append((char)'-'); + sb.append('-'); sb.append(escapeCharInCharClass(this.ranges[i+1])); } } diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/RegexParser.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/RegexParser.java index 5435fc799d9..96d16ee3bfc 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/RegexParser.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/RegexParser.java @@ -1,13 +1,13 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. */ /* - * Copyright 1999-2004 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -24,7 +24,7 @@ import com.sun.org.apache.xerces.internal.utils.SecuritySupport; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; -import java.util.Vector; +import java.util.ArrayList; /** * A Regular Expression Parser. @@ -82,8 +82,7 @@ class RegexParser { int parenOpened = 1; int parennumber = 1; boolean hasBackReferences; - Vector references = null; - int parenCount = 0; + ArrayList references = null; public RegexParser() { this.setLocale(Locale.getDefault()); @@ -115,7 +114,7 @@ class RegexParser { return (this.options & flag) == flag; } - synchronized Token parse(String regex, int options) throws ParseException { + Token parse(String regex, int options) throws ParseException { this.options = options; this.offset = 0; this.setContext(S_NORMAL); @@ -132,15 +131,16 @@ class RegexParser { Token ret = this.parseRegex(); if (this.offset != this.regexlen) throw ex("parser.parse.1", this.offset); - if (parenCount < 0) - throw ex("parser.factor.0", this.offset); + if (this.read() != T_EOF) { + throw ex("parser.parse.1", this.offset-1); + } if (this.references != null) { for (int i = 0; i < this.references.size(); i ++) { - ReferencePosition position = (ReferencePosition)this.references.elementAt(i); + ReferencePosition position = this.references.get(i); if (this.parennumber <= position.refNumber) throw ex("parser.parse.2", position.position); } - this.references.removeAllElements(); + this.references.clear(); } return ret; } @@ -160,6 +160,7 @@ class RegexParser { return this.nexttoken; } + @SuppressWarnings("fallthrough") final void next() { if (this.offset >= this.regexlen) { this.chardata = -1; @@ -239,7 +240,6 @@ class RegexParser { break; case '(': ret = T_LPAREN; - parenCount++; if (this.offset >= this.regexlen) break; if (this.regex.charAt(this.offset) != '?') @@ -328,11 +328,10 @@ class RegexParser { */ Token parseTerm() throws ParseException { int ch = this.read(); - Token tok = null; if (ch == T_OR || ch == T_RPAREN || ch == T_EOF) { - tok = Token.createEmpty(); + return Token.createEmpty(); } else { - tok = this.parseFactor(); + Token tok = this.parseFactor(); Token concat = null; while ((ch = this.read()) != T_OR && ch != T_RPAREN && ch != T_EOF) { if (concat == null) { @@ -343,11 +342,8 @@ class RegexParser { concat.addChild(this.parseFactor()); //tok = Token.createConcat(tok, this.parseFactor()); } + return tok; } - if (ch == T_RPAREN) { - parenCount--; - } - return tok; } // ---------------------------------------------------------------- @@ -482,7 +478,7 @@ class RegexParser { while (this.offset + 1 < this.regexlen) { ch = this.regex.charAt(this.offset + 1); - if ('1' <= ch && ch <= '9') { + if ('0' <= ch && ch <= '9') { refno = (refno * 10) + (ch - '0'); if (refno < this.parennumber) { finalRefno= refno; @@ -498,8 +494,8 @@ class RegexParser { } this.hasBackReferences = true; - if (this.references == null) this.references = new Vector(); - this.references.addElement(new ReferencePosition(finalRefno, this.offset)); + if (this.references == null) this.references = new ArrayList<>(); + this.references.add(new ReferencePosition(finalRefno, this.offset)); this.offset ++; if (this.regex.charAt(this.offset) != ')') throw ex("parser.factor.1", this.offset); this.offset ++; @@ -615,7 +611,7 @@ class RegexParser { while (this.offset < this.regexlen) { final int ch = this.regex.charAt(this.offset); - if ('1' <= ch && ch <= '9') { + if ('0' <= ch && ch <= '9') { refnum = (refnum * 10) + (ch - '0'); if (refnum < this.parennumber) { ++this.offset; @@ -633,8 +629,8 @@ class RegexParser { Token tok = Token.createBackReference(finalRefnum); this.hasBackReferences = true; - if (this.references == null) this.references = new Vector(); - this.references.addElement(new ReferencePosition(finalRefnum, this.offset-2)); + if (this.references == null) this.references = new ArrayList<>(); + this.references.add(new ReferencePosition(finalRefnum, this.offset-2)); this.next(); return tok; } diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/RegularExpression.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/RegularExpression.java index 0a996b27c10..b45d497e19f 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/RegularExpression.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/RegularExpression.java @@ -1,13 +1,13 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. */ /* - * Copyright 1999-2002,2004,2005 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -1041,9 +1041,10 @@ public class RegularExpression implements java.io.Serializable { /** * @return -1 when not match; offset of the end of matched string when match. */ + @SuppressWarnings("fallthrough") private int match(Context con, Op op, int offset, int dx, int opts) { final ExpressionTarget target = con.target; - final Stack opStack = new Stack(); + final Stack opStack = new Stack<>(); final IntStack dataStack = new IntStack(); final boolean isSetIgnoreCase = isSet(opts, IGNORE_CASE); int retValue = -1; @@ -1322,7 +1323,7 @@ public class RegularExpression implements java.io.Serializable { return retValue; } - op = (Op) opStack.pop(); + op = opStack.pop(); offset = dataStack.pop(); switch (op.type) { diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/Token.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/Token.java index 3373a4c110d..49cc67203c1 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/Token.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xpath/regex/Token.java @@ -859,7 +859,7 @@ class Token implements java.io.Serializable { buffer.append("Is"); if (n.indexOf(' ') >= 0) { for (int ci = 0; ci < n.length(); ci ++) - if (n.charAt(ci) != ' ') buffer.append((char)n.charAt(ci)); + if (n.charAt(ci) != ' ') buffer.append(n.charAt(ci)); } else { buffer.append(n); @@ -995,8 +995,8 @@ class Token implements java.io.Serializable { } private static void setAlias(String newName, String name, boolean positive) { - Token t1 = (Token)Token.categories.get(name); - Token t2 = (Token)Token.categories2.get(name); + Token t1 = Token.categories.get(name); + Token t2 = Token.categories2.get(name); if (positive) { Token.categories.put(newName, t1); Token.categories2.put(newName, t2); @@ -1525,7 +1525,7 @@ class Token implements java.io.Serializable { this.children.stream().forEach((children1) -> { sb.append((children1).toString(options)); }); - ret = new String(sb); + ret = sb.toString(); } return ret; } @@ -1538,10 +1538,10 @@ class Token implements java.io.Serializable { StringBuilder sb = new StringBuilder(); sb.append((this.children.get(0)).toString(options)); for (int i = 1; i < this.children.size(); i ++) { - sb.append((char)'|'); + sb.append('|'); sb.append((this.children.get(i)).toString(options)); } - ret = new String(sb); + ret = sb.toString(); } return ret; } @@ -1557,7 +1557,7 @@ class Token implements java.io.Serializable { ObjectOutputStream.PutField pf = out.putFields(); pf.put("children", vChildren); out.writeFields(); - } + } @SuppressWarnings("unchecked") private void readObject(ObjectInputStream in) diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/utils/SafeThread.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/utils/SafeThread.java index abb5b3cffc1..4718d34cae6 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/utils/SafeThread.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/utils/SafeThread.java @@ -24,8 +24,6 @@ */ package com.sun.org.apache.xml.internal.utils; -import sun.misc.Unsafe; - /** * This is a combination of ThreadControllerWrapper's inner class SafeThread * that was introduced as a fix for CR 6607339 @@ -35,7 +33,7 @@ import sun.misc.Unsafe; */ public class SafeThread extends Thread { - private static final Unsafe UNSAFE; + private static final jdk.internal.misc.Unsafe UNSAFE; private static final long THREAD_LOCALS; private static final long INHERITABLE_THREAD_LOCALS; @@ -81,7 +79,7 @@ public class SafeThread extends Thread { } static { - UNSAFE = Unsafe.getUnsafe(); + UNSAFE = jdk.internal.misc.Unsafe.getUnsafe(); Class t = Thread.class; try { THREAD_LOCALS = UNSAFE.objectFieldOffset(t.getDeclaredField("threadLocals")); diff --git a/jaxp/test/TEST.ROOT b/jaxp/test/TEST.ROOT index a90bd60ac34..b78892aa12d 100644 --- a/jaxp/test/TEST.ROOT +++ b/jaxp/test/TEST.ROOT @@ -18,4 +18,4 @@ othervm.dirs=javax/xml/jaxp groups=TEST.groups # Minimum jtreg version -requiredVersion=4.1 b11 +requiredVersion=4.1 b12 diff --git a/jaxp/test/javax/xml/jaxp/unittest/validation/tck/RegexWord.java b/jaxp/test/javax/xml/jaxp/unittest/validation/tck/RegexWord.java new file mode 100644 index 00000000000..dd59b93a378 --- /dev/null +++ b/jaxp/test/javax/xml/jaxp/unittest/validation/tck/RegexWord.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, 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. + */ +package validation.tck; + +import java.io.IOException; +import javax.xml.XMLConstants; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.Validator; +import org.testng.annotations.Test; +import org.xml.sax.SAXException; + +/* + * @bug 8142900 + * @summary Verifies that all characters except the set of "punctuation", + * "separator" and "other" characters are accepted by \w [#x0000-#x10FFFF]-[\p{P}\p{Z}\p{C}] + * @author Joe Wang + */ +public class RegexWord { + static final String SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; + static final String SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource"; + + /* + The original reZ003v.xml contains a full list of word characters that \w should accept. + However, U+2308..U+230B were changed from Sm to either Ps or Pe in Unicode 7.0. + They are therefore excluded from the test. + + The test throws an Exception (and fails) if it fails to recognize any of characters. + */ + @Test + public void test() throws SAXException, IOException { + SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + Schema schema = schemaFactory.newSchema(new StreamSource(RegexWord.class.getResourceAsStream("reZ003.xsd"))); + Validator validator = schema.newValidator(); + + validator.validate(new StreamSource(RegexWord.class.getResourceAsStream("reZ003vExc23082309.xml"))); + } +} diff --git a/jaxp/test/javax/xml/jaxp/unittest/validation/tck/reZ003.xsd b/jaxp/test/javax/xml/jaxp/unittest/validation/tck/reZ003.xsd new file mode 100644 index 00000000000..c18a586613c --- /dev/null +++ b/jaxp/test/javax/xml/jaxp/unittest/validation/tck/reZ003.xsd @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/jaxp/test/javax/xml/jaxp/unittest/validation/tck/reZ003vExc23082309.xml b/jaxp/test/javax/xml/jaxp/unittest/validation/tck/reZ003vExc23082309.xml new file mode 100644 index 00000000000..63253cffe42 --- /dev/null +++ b/jaxp/test/javax/xml/jaxp/unittest/validation/tck/reZ003vExc23082309.xml @@ -0,0 +1,9273 @@ + +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F +G +H +I +P +Q +R +S +T +U +V +W +X +Y +` +a +b +c +d +e +f +g +h +i +p +q +r +s +t +u +v +w +x +y +Ā +ā +Ă +ă +Ą +ą +Ć +ć +Ĉ +ĉ +Đ +đ +Ē +ē +Ĕ +ĕ +Ė +ė +Ę +ę +Ġ +ġ +Ģ +ģ +Ĥ +ĥ +Ħ +ħ +Ĩ +ĩ +İ +ı +IJ +ij +Ĵ +ĵ +Ķ +ķ +ĸ +Ĺ +ŀ +Ł +ł +Ń +ń +Ņ +ņ +Ň +ň +ʼn +Ő +ő +Œ +œ +Ŕ +ŕ +Ŗ +ŗ +Ř +ř +Š +š +Ţ +ţ +Ť +ť +Ŧ +ŧ +Ũ +ũ +Ű +ű +Ų +ų +Ŵ +ŵ +Ŷ +ŷ +Ÿ +Ź +ƀ +Ɓ +Ƃ +ƃ +Ƅ +ƅ +Ɔ +Ƈ +ƈ +Ɖ +Ɛ +Ƒ +ƒ +Ɠ +Ɣ +ƕ +Ɩ +Ɨ +Ƙ +ƙ +Ȁ +ȁ +Ȃ +ȃ +Ȅ +ȅ +Ȇ +ȇ +Ȉ +ȉ +Ȑ +ȑ +Ȓ +ȓ +Ȕ +ȕ +Ȗ +ȗ +Ș +ș +Ƞ +Ȣ +ȣ +Ȥ +ȥ +Ȧ +ȧ +Ȩ +ȩ +Ȱ +ȱ +Ȳ +ȳ +ɐ +ɑ +ɒ +ɓ +ɔ +ɕ +ɖ +ɗ +ɘ +ə +ɠ +ɡ +ɢ +ɣ +ɤ +ɥ +ɦ +ɧ +ɨ +ɩ +ɰ +ɱ +ɲ +ɳ +ɴ +ɵ +ɶ +ɷ +ɸ +ɹ +ʀ +ʁ +ʂ +ʃ +ʄ +ʅ +ʆ +ʇ +ʈ +ʉ +ʐ +ʑ +ʒ +ʓ +ʔ +ʕ +ʖ +ʗ +ʘ +ʙ +̀ +́ +̂ +̃ +̄ +̅ +̆ +̇ +̈ +̉ +̐ +̑ +̒ +̓ +̔ +̕ +̖ +̗ +̘ +̙ +̠ +̡ +̢ +̣ +̤ +̥ +̦ +̧ +̨ +̩ +̰ +̱ +̲ +̳ +̴ +̵ +̶ +̷ +̸ +̹ +̀ +́ +͂ +̓ +̈́ +ͅ +͆ +͇ +͈ +͉ +͠ +͡ +͢ +ͣ +ͤ +ͥ +ͦ +ͧ +ͨ +ͩ +ʹ +͵ +΄ +΅ +Ά +Έ +Ή +ΐ +Α +Β +Γ +Δ +Ε +Ζ +Η +Θ +Ι +Ѐ +Ё +Ђ +Ѓ +Є +Ѕ +І +Ї +Ј +Љ +А +Б +В +Г +Д +Е +Ж +З +И +Й +Р +С +Т +У +Ф +Х +Ц +Ч +Ш +Щ +а +б +в +г +д +е +ж +з +и +й +р +с +т +у +ф +х +ц +ч +ш +щ +ѐ +ё +ђ +ѓ +є +ѕ +і +ї +ј +љ +Ѡ +ѡ +Ѣ +ѣ +Ѥ +ѥ +Ѧ +ѧ +Ѩ +ѩ +Ѱ +ѱ +Ѳ +ѳ +Ѵ +ѵ +Ѷ +ѷ +Ѹ +ѹ +Ҁ +ҁ +҂ +҃ +҄ +҅ +҆ +҈ +҉ +Ґ +ґ +Ғ +ғ +Ҕ +ҕ +Җ +җ +Ҙ +ҙ +Ԁ +ԁ +Ԃ +ԃ +Ԅ +ԅ +Ԇ +ԇ +Ԉ +ԉ +Ա +Բ +Գ +Դ +Ե +Զ +Է +Ը +Թ +Հ +Ձ +Ղ +Ճ +Մ +Յ +Ն +Շ +Ո +Չ +Ր +Ց +Ւ +Փ +Ք +Օ +Ֆ +ՙ +ա +բ +գ +դ +ե +զ +է +ը +թ +հ +ձ +ղ +ճ +մ +յ +ն +շ +ո +չ +ր +ց +ւ +փ +ք +օ +ֆ +և +֑ +֒ +֓ +֔ +֕ +֖ +֗ +֘ +֙ +ء +آ +أ +ؤ +إ +ئ +ا +ب +ة +ذ +ر +ز +س +ش +ص +ض +ط +ظ +ع +ـ +ف +ق +ك +ل +م +ن +ه +و +ى +ِ +ّ +ْ +ٓ +ٔ +ٕ +٠ +١ +٢ +٣ +٤ +٥ +٦ +٧ +٨ +٩ +ٰ +ٱ +ٲ +ٳ +ٴ +ٵ +ٶ +ٷ +ٸ +ٹ +ڀ +ځ +ڂ +ڃ +ڄ +څ +چ +ڇ +ڈ +ډ +ڐ +ڑ +ڒ +ړ +ڔ +ڕ +ږ +ڗ +ژ +ڙ +ܐ +ܑ +ܒ +ܓ +ܔ +ܕ +ܖ +ܗ +ܘ +ܙ +ܠ +ܡ +ܢ +ܣ +ܤ +ܥ +ܦ +ܧ +ܨ +ܩ +ܰ +ܱ +ܲ +ܳ +ܴ +ܵ +ܶ +ܷ +ܸ +ܹ +݀ +݁ +݂ +݃ +݄ +݅ +݆ +݇ +݈ +݉ +ހ +ށ +ނ +ރ +ބ +ޅ +ކ +އ +ވ +މ +ސ +ޑ +ޒ +ޓ +ޔ +ޕ +ޖ +ޗ +ޘ +ޙ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +က + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +䀀 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +倀 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +怀 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +瀀 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +耀 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +退 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jaxws/.hgtags b/jaxws/.hgtags index 96337533829..240b4dc2ec3 100644 --- a/jaxws/.hgtags +++ b/jaxws/.hgtags @@ -337,3 +337,5 @@ f6425fec60abe3c096c5251be61e4621c817be80 jdk9-b88 2d84c6f4cbbac5871b099e97f8f968d9de6b52bc jdk9-b89 b3e45213d574618f6520fa6978e4a14ba577c2db jdk9-b90 3b2a3cb658e41618bd152a7598d12e1f0c10e8f7 jdk9-b91 +fe772cbc64f4e0418c5bf694e9e7123f02e1808f jdk9-b92 +5e94fbbb7032b3bba8254ddb1af8fc45a4d1448b jdk9-b93 diff --git a/jdk/.hgtags b/jdk/.hgtags index db3016620bf..8ff4e003184 100644 --- a/jdk/.hgtags +++ b/jdk/.hgtags @@ -334,3 +334,5 @@ e8a66c0b05d786a282a7ff1d7eb4989afa30c891 jdk9-b86 0d0a63b325592607974612f9cfb48590975aa2d6 jdk9-b89 b433e4dfb830fea60e5187e4580791b62cc362d2 jdk9-b90 97624df5026a2fb191793697dbd2c604c4d5c66e jdk9-b91 +6a5c99506f44538b879d8635a3979849ed587130 jdk9-b92 +2f12392d0dde768150c83087cdbdd0d33a4d866c jdk9-b93 diff --git a/jdk/make/Import.gmk b/jdk/make/Import.gmk index ffe2869897f..25560f02ff0 100644 --- a/jdk/make/Import.gmk +++ b/jdk/make/Import.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2015, 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 @@ -45,8 +45,16 @@ endif # # Import hotspot # + +# Don't import jsig library for static builds +ifneq ($(STATIC_BUILD), true) + JSIG_IMPORT = jsig.* +else + JSIG_IMPORT = +endif + HOTSPOT_BASE_IMPORT_FILES := \ - $(addprefix $(LIBRARY_PREFIX), jvm.* jsig.* jvm_db.* jvm_dtrace.*) \ + $(addprefix $(LIBRARY_PREFIX), jvm.* $(JSIG_IMPORT) jvm_db.* jvm_dtrace.*) \ Xusage.txt \ # @@ -79,32 +87,34 @@ SA_TARGETS := $(COPY_HOTSPOT_SA) ################################################################################ -ifeq ($(OPENJDK_TARGET_OS), macosx) - JSIG_DEBUGINFO := $(strip $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig$(SHARED_LIBRARY_SUFFIX).dSYM) \ - $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig.diz) ) -else - JSIG_DEBUGINFO := $(strip $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig.debuginfo) \ - $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig.diz) ) -endif +ifneq ($(STATIC_BUILD), true) + ifeq ($(OPENJDK_TARGET_OS), macosx) + JSIG_DEBUGINFO := $(strip $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig$(SHARED_LIBRARY_SUFFIX).dSYM) \ + $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig.diz) ) + else + JSIG_DEBUGINFO := $(strip $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig.debuginfo) \ + $(wildcard $(HOTSPOT_DIST)/lib$(OPENJDK_TARGET_CPU_LIBDIR)/libjsig.diz) ) + endif -ifneq ($(OPENJDK_TARGET_OS), windows) - ifeq ($(JVM_VARIANT_SERVER), true) - BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/server/$(LIBRARY_PREFIX)jsig$(SHARED_LIBRARY_SUFFIX) - ifneq (, $(JSIG_DEBUGINFO)) - BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/server/$(foreach I,$(JSIG_DEBUGINFO),$(notdir $I)) + ifneq ($(OPENJDK_TARGET_OS), windows) + ifeq ($(JVM_VARIANT_SERVER), true) + BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/server/$(LIBRARY_PREFIX)jsig$(SHARED_LIBRARY_SUFFIX) + ifneq (, $(JSIG_DEBUGINFO)) + BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/server/$(foreach I,$(JSIG_DEBUGINFO),$(notdir $I)) + endif endif - endif - ifeq ($(JVM_VARIANT_CLIENT), true) - BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/client/$(LIBRARY_PREFIX)jsig$(SHARED_LIBRARY_SUFFIX) - ifneq (, $(JSIG_DEBUGINFO)) - BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/client/$(foreach I,$(JSIG_DEBUGINFO),$(notdir $I)) + ifeq ($(JVM_VARIANT_CLIENT), true) + BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/client/$(LIBRARY_PREFIX)jsig$(SHARED_LIBRARY_SUFFIX) + ifneq (, $(JSIG_DEBUGINFO)) + BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/client/$(foreach I,$(JSIG_DEBUGINFO),$(notdir $I)) + endif endif - endif - ifneq ($(OPENJDK_TARGET_OS), macosx) - ifeq ($(JVM_VARIANT_MINIMAL1), true) - BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/minimal/$(LIBRARY_PREFIX)jsig$(SHARED_LIBRARY_SUFFIX) - ifneq (,$(JSIG_DEBUGINFO)) - BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/minimal/$(foreach I,$(JSIG_DEBUGINFO),$(notdir $I)) + ifneq ($(OPENJDK_TARGET_OS), macosx) + ifeq ($(JVM_VARIANT_MINIMAL1), true) + BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/minimal/$(LIBRARY_PREFIX)jsig$(SHARED_LIBRARY_SUFFIX) + ifneq (,$(JSIG_DEBUGINFO)) + BASE_TARGETS += $(BASE_INSTALL_LIBRARIES_HERE)/minimal/$(foreach I,$(JSIG_DEBUGINFO),$(notdir $I)) + endif endif endif endif diff --git a/jdk/make/data/charsetmapping/DoubleByte-X.java.template b/jdk/make/data/charsetmapping/DoubleByte-X.java.template index 4ef582c9317..d97e2c46f4b 100644 --- a/jdk/make/data/charsetmapping/DoubleByte-X.java.template +++ b/jdk/make/data/charsetmapping/DoubleByte-X.java.template @@ -50,12 +50,12 @@ public class $NAME_CLZ$ extends Charset public CharsetDecoder newDecoder() { initb2c(); - return new DoubleByte.Decoder$DECTYPE$(this, b2c, b2cSB, $B2MIN$, $B2MAX$); + return new DoubleByte.Decoder$DECTYPE$(this, b2c, b2cSB, $B2MIN$, $B2MAX$, $ASCIICOMPATIBLE$); } public CharsetEncoder newEncoder() { initc2b(); - return new DoubleByte.Encoder$ENCTYPE$(this, $ENC_REPLACEMENT$ c2b, c2bIndex); + return new DoubleByte.Encoder$ENCTYPE$(this, $ENC_REPLACEMENT$ c2b, c2bIndex, $ASCIICOMPATIBLE$); } $B2C$ diff --git a/jdk/make/data/charsetmapping/SingleByte-X.java.template b/jdk/make/data/charsetmapping/SingleByte-X.java.template index 82af0521809..2acb1ef256d 100644 --- a/jdk/make/data/charsetmapping/SingleByte-X.java.template +++ b/jdk/make/data/charsetmapping/SingleByte-X.java.template @@ -48,11 +48,11 @@ public class $NAME_CLZ$ extends Charset implements HistoricallyNamedCharset } public CharsetDecoder newDecoder() { - return new SingleByte.Decoder(this, b2c); + return new SingleByte.Decoder(this, b2c, $ASCIICOMPATIBLE$); } public CharsetEncoder newEncoder() { - return new SingleByte.Encoder(this, c2b, c2bIndex); + return new SingleByte.Encoder(this, c2b, c2bIndex, $ASCIICOMPATIBLE$); } private final static String b2cTable = $B2CTABLE$ diff --git a/jdk/make/gensrc/Gensrc-java.base.gmk b/jdk/make/gensrc/Gensrc-java.base.gmk index 134edea72b0..e176d88ea7e 100644 --- a/jdk/make/gensrc/Gensrc-java.base.gmk +++ b/jdk/make/gensrc/Gensrc-java.base.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2015, 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 @@ -38,15 +38,15 @@ include GensrcExceptions.gmk include GensrcProperties.gmk -$(eval $(call SetupCompileProperties,LIST_RESOURCE_BUNDLE, \ - $(filter %.properties, \ - $(call CacheFind, $(JDK_TOPDIR)/src/java.base/share/classes/sun/launcher/resources)), \ - ListResourceBundle)) +$(eval $(call SetupCompileProperties, LIST_RESOURCE_BUNDLE, \ + SRC_DIRS := $(JDK_TOPDIR)/src/java.base/share/classes/sun/launcher/resources, \ + CLASS := ListResourceBundle, \ +)) -$(eval $(call SetupCompileProperties,SUN_UTIL, \ - $(filter %.properties, \ - $(call CacheFind, $(JDK_TOPDIR)/src/java.base/share/classes/sun/util/resources)), \ - sun.util.resources.LocaleNamesBundle)) +$(eval $(call SetupCompileProperties, SUN_UTIL, \ + SRC_DIRS := $(JDK_TOPDIR)/src/java.base/share/classes/sun/util/resources, \ + CLASS := sun.util.resources.LocaleNamesBundle, \ +)) GENSRC_JAVA_BASE += $(LIST_RESOURCE_BUNDLE) $(SUN_UTIL) diff --git a/jdk/make/gensrc/Gensrc-java.desktop.gmk b/jdk/make/gensrc/Gensrc-java.desktop.gmk index d2269150336..05e93c66e3f 100644 --- a/jdk/make/gensrc/Gensrc-java.desktop.gmk +++ b/jdk/make/gensrc/Gensrc-java.desktop.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2015, 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 @@ -66,11 +66,11 @@ else PROP_SRC_DIRS += $(JDK_TOPDIR)/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/resources endif -PROP_SRC_FILES := $(filter-out %cursors.properties, \ - $(filter %.properties, $(call CacheFind, $(PROP_SRC_DIRS)))) - -$(eval $(call SetupCompileProperties,COMPILE_PROPERTIES, \ - $(PROP_SRC_FILES), ListResourceBundle)) +$(eval $(call SetupCompileProperties, COMPILE_PROPERTIES, \ + SRC_DIRS := $(PROP_SRC_DIRS), \ + EXCLUDE := %cursors.properties, \ + CLASS := ListResourceBundle, \ +)) GENSRC_JAVA_DESKTOP += $(COMPILE_PROPERTIES) diff --git a/jdk/make/gensrc/Gensrc-java.logging.gmk b/jdk/make/gensrc/Gensrc-java.logging.gmk index 18ad984f859..ea44f5701a6 100644 --- a/jdk/make/gensrc/Gensrc-java.logging.gmk +++ b/jdk/make/gensrc/Gensrc-java.logging.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 2015, 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 @@ -29,10 +29,10 @@ include GensrcCommon.gmk include GensrcProperties.gmk -$(eval $(call SetupCompileProperties,COMPILE_PROPERTIES, \ - $(filter %.properties, \ - $(call CacheFind, $(JDK_TOPDIR)/src/java.logging/share/classes/sun/util/logging/resources)), \ - ListResourceBundle)) +$(eval $(call SetupCompileProperties, COMPILE_PROPERTIES, \ + SRC_DIRS := $(JDK_TOPDIR)/src/java.logging/share/classes/sun/util/logging/resources, \ + CLASS := ListResourceBundle, \ +)) TARGETS += $(COMPILE_PROPERTIES) diff --git a/jdk/make/gensrc/Gensrc-java.management.gmk b/jdk/make/gensrc/Gensrc-java.management.gmk index b0a3751631e..771392466fd 100644 --- a/jdk/make/gensrc/Gensrc-java.management.gmk +++ b/jdk/make/gensrc/Gensrc-java.management.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2015, 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 @@ -32,10 +32,10 @@ $(eval $(call IncludeCustomExtension, jdk, gensrc/Gensrc-java.management.gmk)) include GensrcProperties.gmk -$(eval $(call SetupCompileProperties,COMPILE_PROPERTIES, \ - $(filter %.properties, \ - $(call CacheFind, $(JDK_TOPDIR)/src/java.management/share/classes/sun/management/resources)), \ - ListResourceBundle)) +$(eval $(call SetupCompileProperties, COMPILE_PROPERTIES, \ + SRC_DIRS := $(JDK_TOPDIR)/src/java.management/share/classes/sun/management/resources, \ + CLASS := ListResourceBundle, \ +)) TARGETS += $(COMPILE_PROPERTIES) diff --git a/jdk/make/gensrc/Gensrc-jdk.dev.gmk b/jdk/make/gensrc/Gensrc-jdk.dev.gmk index b8f57aa529e..4316c485883 100644 --- a/jdk/make/gensrc/Gensrc-jdk.dev.gmk +++ b/jdk/make/gensrc/Gensrc-jdk.dev.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 2015, 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 @@ -29,11 +29,10 @@ include GensrcCommon.gmk include GensrcProperties.gmk -$(eval $(call SetupCompileProperties,COMPILE_PROPERTIES, \ - $(filter %.properties, \ - $(call CacheFind, \ - $(JDK_TOPDIR)/src/jdk.dev/share/classes/jdk/tools/jimage/resources)), \ - ListResourceBundle)) +$(eval $(call SetupCompileProperties, COMPILE_PROPERTIES, \ + SRC_DIRS := $(JDK_TOPDIR)/src/jdk.dev/share/classes/jdk/tools/jimage/resources, \ + CLASS := ListResourceBundle, \ +)) TARGETS += $(COMPILE_PROPERTIES) diff --git a/jdk/make/gensrc/Gensrc-jdk.jartool.gmk b/jdk/make/gensrc/Gensrc-jdk.jartool.gmk index 0fb8eabc621..8cb04f33f7f 100644 --- a/jdk/make/gensrc/Gensrc-jdk.jartool.gmk +++ b/jdk/make/gensrc/Gensrc-jdk.jartool.gmk @@ -29,11 +29,10 @@ include GensrcCommon.gmk include GensrcProperties.gmk -$(eval $(call SetupCompileProperties,COMPILE_PROPERTIES, \ - $(filter %.properties, \ - $(call CacheFind, \ - $(JDK_TOPDIR)/src/jdk.jartool/share/classes/sun/tools/jar/resources)), \ - ListResourceBundle)) +$(eval $(call SetupCompileProperties, COMPILE_PROPERTIES, \ + SRC_DIRS := $(JDK_TOPDIR)/src/jdk.jartool/share/classes/sun/tools/jar/resources, \ + CLASS := ListResourceBundle, \ +)) TARGETS += $(COMPILE_PROPERTIES) diff --git a/jdk/make/gensrc/Gensrc-jdk.jdi.gmk b/jdk/make/gensrc/Gensrc-jdk.jdi.gmk index 7c36f4e6999..416ec40852b 100644 --- a/jdk/make/gensrc/Gensrc-jdk.jdi.gmk +++ b/jdk/make/gensrc/Gensrc-jdk.jdi.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2015, 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 @@ -34,7 +34,7 @@ HEADER_FILE := $(SUPPORT_OUTPUTDIR)/headers/jdk.jdwp.agent/JDWPCommands.h JAVA_FILE := $(SUPPORT_OUTPUTDIR)/gensrc/jdk.jdi/com/sun/tools/jdi/JDWP.java # Both the header and java file are created using the same recipe. By declaring -# this rule and adding header file to dependencies for java file, both are +# this rule and adding header file to dependencies for java file, both are # rebuilt if either is missing $(HEADER_FILE): $(JDWP_SPEC_FILE) $(BUILD_TOOLS_JDK) @@ -90,10 +90,10 @@ endif include GensrcProperties.gmk -$(eval $(call SetupCompileProperties,COMPILE_PROPERTIES, \ - $(filter %.properties, \ - $(call CacheFind, $(JDK_TOPDIR)/src/jdk.jdi/share/classes/com/sun/tools/jdi/resources)), \ - ListResourceBundle)) +$(eval $(call SetupCompileProperties, COMPILE_PROPERTIES, \ + SRC_DIRS := $(JDK_TOPDIR)/src/jdk.jdi/share/classes/com/sun/tools/jdi/resources, \ + CLASS := ListResourceBundle, \ +)) GENSRC_JDK_JDI += $(COMPILE_PROPERTIES) diff --git a/jdk/make/gensrc/Gensrc-jdk.localedata.gmk b/jdk/make/gensrc/Gensrc-jdk.localedata.gmk index 5759cb6093d..5bbe512dc18 100644 --- a/jdk/make/gensrc/Gensrc-jdk.localedata.gmk +++ b/jdk/make/gensrc/Gensrc-jdk.localedata.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 2015, 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 @@ -35,10 +35,10 @@ include GensrcCLDR.gmk include GensrcProperties.gmk -$(eval $(call SetupCompileProperties,COMPILE_PROPERTIES, \ - $(filter %.properties, \ - $(call CacheFind, $(JDK_TOPDIR)/src/jdk.localedata/share/classes/sun/util/resources)), \ - sun.util.resources.LocaleNamesBundle)) +$(eval $(call SetupCompileProperties, COMPILE_PROPERTIES, \ + SRC_DIRS := $(JDK_TOPDIR)/src/jdk.localedata/share/classes/sun/util/resources, \ + CLASS := sun.util.resources.LocaleNamesBundle, \ +)) # Skip generating zh_HK from zh_TW for this module. GENSRC_JDK_LOCALEDATA += $(filter-out %_zh_HK.java, $(COMPILE_PROPERTIES)) diff --git a/jdk/make/gensrc/GensrcProperties.gmk b/jdk/make/gensrc/GensrcProperties.gmk index 220b8509904..7293a19bbeb 100644 --- a/jdk/make/gensrc/GensrcProperties.gmk +++ b/jdk/make/gensrc/GensrcProperties.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2015, 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 @@ -49,30 +49,40 @@ define SetupCopy-zh_HK endef ################################################################################ -# Creates a rule that runs CompileProperties on a set of properties files. -# Param 1 - Variable to add targets to, must not contain space -# Param 2 - Properties files to process -# Param 3 - The super class for the generated classes -# Param 4 - Module path root, defaults to $(JDK_TOPDIR)/src -define SetupCompileProperties - $1_SRCS := $2 - $1_CLASS := $3 - $1_MODULE_PATH_ROOT := $4 - +# Setup make rules that runs CompileProperties on a set of properties files. +# +# Parameter 1 is the name of the rule. This name is used as variable prefix, +# and the targets generated are listed in a variable by that name. +# +# Remaining parameters are named arguments. These include: +# SRC_DIRS Directories containing properties files to process. +# EXCLUDE Exclude files matching this pattern. +# CLASS The super class for the generated classes. +# MODULE_PATH_ROOT Module path root, defaults to $(JDK_TOPDIR)/src. +SetupCompileProperties = $(NamedParamsMacroTemplate) +define SetupCompilePropertiesBody + # Set default value unless overridden ifeq ($$($1_MODULE_PATH_ROOT), ) $1_MODULE_PATH_ROOT := $(JDK_TOPDIR)/src endif + # Locate all properties files in the given source dirs. + $1_SRC_FILES := $$(filter %.properties, $$(call CacheFind, $$($1_SRC_DIRS))) + + ifneq ($$($1_EXCLUDE), ) + $1_SRC_FILES := $$(filter-out $$($1_EXCLUDE), $$($1_SRC_FILES)) + endif + # Convert .../src//share/classes/com/sun/tools/javac/resources/javac_zh_CN.properties # to .../support/gensrc//com/sun/tools/javac/resources/javac_zh_CN.java - # Strip away prefix and suffix, leaving for example only: + # Strip away prefix and suffix, leaving for example only: # "/share/classes/com/sun/tools/javac/resources/javac_zh_CN" $1_JAVAS := $$(patsubst $$($1_MODULE_PATH_ROOT)/%, \ $(SUPPORT_OUTPUTDIR)/gensrc/%, \ $$(patsubst %.properties, %.java, \ $$(subst /$(OPENJDK_TARGET_OS)/classes,, \ $$(subst /$(OPENJDK_TARGET_OS_TYPE)/classes,, \ - $$(subst /share/classes,, $$($1_SRCS)))))) + $$(subst /share/classes,, $$($1_SRC_FILES)))))) # Generate the package dirs for the to be generated java files. Sort to remove # duplicates. @@ -82,22 +92,22 @@ define SetupCompileProperties # "-compile ...javac_zh_CN.properties ...javac_zh_CN.java java.util.ListResourceBundle" # suitable to be fed into the CompileProperties command. $1_CMDLINE := $$(subst _SPACE_, $(SPACE), \ - $$(join $$(addprefix -compile_SPACE_, $$($1_SRCS)), \ + $$(join $$(addprefix -compile_SPACE_, $$($1_SRC_FILES)), \ $$(addsuffix _SPACE_$$($1_CLASS), \ $$(addprefix _SPACE_, $$($1_JAVAS))))) - $1_TARGET := $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/_the.$1.done + $1_TARGET := $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/_the.$1.marker $1_CMDLINE_FILE := $(SUPPORT_OUTPUTDIR)/gensrc/$(MODULE)/_the.$1.cmdline # Now setup the rule for the generation of the resource bundles. - $$($1_TARGET): $$($1_SRCS) $$($1_JAVAS) $(BUILD_TOOLS_JDK) + $$($1_TARGET): $$($1_SRC_FILES) $$($1_JAVAS) $(BUILD_TOOLS_JDK) $(MKDIR) -p $$(@D) $$($1_DIRS) - $(ECHO) Compiling $$(words $$($1_SRCS)) properties into resource bundles for $(MODULE) + $(ECHO) Compiling $$(words $$($1_SRC_FILES)) properties into resource bundles for $(MODULE) $$(eval $$(call ListPathsSafely, $1_CMDLINE, $$($1_CMDLINE_FILE))) $(TOOL_COMPILEPROPERTIES) -quiet @$$($1_CMDLINE_FILE) $(TOUCH) $$@ - $$($1_JAVAS): $$($1_SRCS) + $$($1_JAVAS): $$($1_SRC_FILES) # Create zh_HK versions of all zh_TW files created above $$(eval $$(call SetupCopy-zh_HK,$1_HK,$$(filter %_zh_TW.java, $$($1_JAVAS)))) diff --git a/jdk/make/launcher/LauncherCommon.gmk b/jdk/make/launcher/LauncherCommon.gmk index 942224f4697..debc6337b60 100644 --- a/jdk/make/launcher/LauncherCommon.gmk +++ b/jdk/make/launcher/LauncherCommon.gmk @@ -122,8 +122,23 @@ define SetupBuildLauncherBody endif $1_CFLAGS += -DPACKAGE_PATH='"$(PACKAGE_PATH)"' - $1_LDFLAGS += -Wl,-all_load $(SUPPORT_OUTPUTDIR)/native/java.base/libjli_static.a \ + $1_LDFLAGS += -Wl,-all_load \ -sectcreate __TEXT __info_plist $(MACOSX_PLIST_DIR)/$$($1_PLIST_FILE) + ifeq ($(STATIC_BUILD), true) + $1_LDFLAGS += -exported_symbols_list \ + $(SUPPORT_OUTPUTDIR)/build-static/exported.symbols + $1_LIBS += \ + $(shell $(FIND) $(SUPPORT_OUTPUTDIR)/modules_libs/java.base -name "*.a") \ + $(SUPPORT_OUTPUTDIR)/modules_libs/jdk.jdwp.agent/libdt_socket.a \ + $(SUPPORT_OUTPUTDIR)/modules_libs/jdk.jdwp.agent/libjdwp.a \ + $(SUPPORT_OUTPUTDIR)/native/java.base/$(LIBRARY_PREFIX)fdlibm$(STATIC_LIBRARY_SUFFIX) \ + -framework CoreFoundation \ + -framework Foundation \ + -framework SystemConfiguration \ + -lstdc++ -liconv + else + $1_LIBS += $(SUPPORT_OUTPUTDIR)/native/java.base/libjli_static.a + endif $1_LIBS += -framework Cocoa -framework Security \ -framework ApplicationServices endif diff --git a/jdk/make/lib/Awt2dLibraries.gmk b/jdk/make/lib/Awt2dLibraries.gmk index ad60e8a8146..21e61c5c679 100644 --- a/jdk/make/lib/Awt2dLibraries.gmk +++ b/jdk/make/lib/Awt2dLibraries.gmk @@ -476,7 +476,7 @@ endif ifeq ($(USE_EXTERNAL_LIBJPEG), true) LIBJPEG_LIBS := -ljpeg BUILD_LIBJAVAJPEG_INCLUDE_FILES := \ - imageIOJPEG.c \ + imageioJPEG.c \ jpegdecoder.c BUILD_LIBJAVAJPEG_HEADERS := else diff --git a/jdk/make/lib/CoreLibraries.gmk b/jdk/make/lib/CoreLibraries.gmk index 01ae7467e5d..9fe9305fcd7 100644 --- a/jdk/make/lib/CoreLibraries.gmk +++ b/jdk/make/lib/CoreLibraries.gmk @@ -435,10 +435,14 @@ else ifeq ($(OPENJDK_TARGET_OS), macosx) OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjli_static, \ DEBUG_SYMBOLS := $(DEBUG_ALL_BINARIES))) - $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjli_static.a: $(BUILD_LIBJLI_STATIC) + ifeq ($(STATIC_BUILD), true) + TARGETS += $(BUILD_LIBJLI_STATIC) + else + $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjli_static.a: $(BUILD_LIBJLI_STATIC) $(call install-file) - TARGETS += $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjli_static.a + TARGETS += $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjli_static.a + endif else ifeq ($(OPENJDK_TARGET_OS), aix) # AIX also requires a static libjli because the compiler doesn't support '-rpath' diff --git a/jdk/make/lib/Lib-java.base.gmk b/jdk/make/lib/Lib-java.base.gmk index c5949d57138..0699a42dd34 100644 --- a/jdk/make/lib/Lib-java.base.gmk +++ b/jdk/make/lib/Lib-java.base.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2015, 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 @@ -33,3 +33,29 @@ include CoreLibraries.gmk include NetworkingLibraries.gmk include NioLibraries.gmk include SecurityLibraries.gmk + +ifeq ($(STATIC_BUILD), true) + JAVA_BASE_EXPORT_SYMBOLS_SRC := \ + $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/jli/$(LIBRARY_PREFIX)jli.symbols \ + $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/$(LIBRARY_PREFIX)java.symbols \ + $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/$(LIBRARY_PREFIX)net.symbols \ + $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/$(LIBRARY_PREFIX)nio.symbols \ + $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/$(LIBRARY_PREFIX)verify.symbols \ + $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/$(LIBRARY_PREFIX)zip.symbols \ + $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/$(LIBRARY_PREFIX)jimage.symbols \ + $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/server/$(LIBRARY_PREFIX)jvm.symbols \ + # + + JAVA_BASE_EXPORT_SYMBOL_FILE := $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/java.base.symbols + + $(JAVA_BASE_EXPORT_SYMBOL_FILE): $(JAVA_BASE_EXPORT_SYMBOLS_SRC) + $(ECHO) $(LOG_INFO) "Generating java.base.symbols file" + $(CAT) $^ > $@ + + # The individual symbol files is generated when the respective lib is built + $(JAVA_BASE_EXPORT_SYMBOLS_SRC): $(BUILD_LIBJLI) $(BUILD_LIBJAVA) \ + $(BUILD_LIBNET) $(BUILD_LIBNIO) $(BUILD_LIBVERIFY) $(BUILD_LIBZIP) \ + $(BUILD_LIBJIMAGE) + + TARGETS += $(JAVA_BASE_EXPORT_SYMBOL_FILE) +endif diff --git a/jdk/make/lib/Lib-jdk.jdwp.agent.gmk b/jdk/make/lib/Lib-jdk.jdwp.agent.gmk index 504df6dd4d0..14784d3796a 100644 --- a/jdk/make/lib/Lib-jdk.jdwp.agent.gmk +++ b/jdk/make/lib/Lib-jdk.jdwp.agent.gmk @@ -102,3 +102,23 @@ $(BUILD_LIBJDWP): $(call FindLib, java.base, java) TARGETS += $(BUILD_LIBJDWP) ################################################################################ + +ifeq ($(STATIC_BUILD), true) + JDK_JDWP_AGENT_EXPORT_SYMBOLS_SRC := \ + $(SUPPORT_OUTPUTDIR)/modules_libs/jdk.jdwp.agent/$(LIBRARY_PREFIX)dt_socket.symbols \ + $(SUPPORT_OUTPUTDIR)/modules_libs/jdk.jdwp.agent/$(LIBRARY_PREFIX)jdwp.symbols + + JDK_JDWP_AGENT_EXPORT_SYMBOL_FILE := $(SUPPORT_OUTPUTDIR)/modules_libs/jdk.jdwp.agent/jdk.jdwp.agent.symbols + + $(JDK_JDWP_AGENT_EXPORT_SYMBOL_FILE): $(JDK_JDWP_AGENT_EXPORT_SYMBOLS_SRC) + $(ECHO) $(LOG_INFO) "Generating jdk.jdwp.agent symbols file" + $(CAT) $^ > $@ + + # The individual symbol files is generated when the respective lib is built + $(JDK_JDWP_AGENT_EXPORT_SYMBOLS_SRC): $(BUILD_LIBDT_SOCKET) $(BUILD_LIBJDWP) + + TARGETS += $(JDK_JDWP_AGENT_EXPORT_SYMBOL_FILE) + +endif + +################################################################################ diff --git a/jdk/make/lib/SecurityLibraries.gmk b/jdk/make/lib/SecurityLibraries.gmk index a1c51b73ae3..9c1c65211ca 100644 --- a/jdk/make/lib/SecurityLibraries.gmk +++ b/jdk/make/lib/SecurityLibraries.gmk @@ -26,38 +26,41 @@ include LibCommon.gmk ifeq ($(OPENJDK_TARGET_OS), macosx) + # JavaNativeFoundation framework not supported in static builds + ifneq ($(STATIC_BUILD), true) ################################################################################ - LIBOSXSECURITY_DIRS := $(JDK_TOPDIR)/src/java.base/macosx/native/libosxsecurity - LIBOSXSECURITY_CFLAGS := -I$(LIBOSXSECURITY_DIRS) \ - $(LIBJAVA_HEADER_FLAGS) \ - -I$(SUPPORT_OUTPUTDIR)/headers/java.base \ + LIBOSXSECURITY_DIRS := $(JDK_TOPDIR)/src/java.base/macosx/native/libosxsecurity + LIBOSXSECURITY_CFLAGS := -I$(LIBOSXSECURITY_DIRS) \ + $(LIBJAVA_HEADER_FLAGS) \ + -I$(SUPPORT_OUTPUTDIR)/headers/java.base \ - $(eval $(call SetupNativeCompilation,BUILD_LIBOSXSECURITY, \ - LIBRARY := osxsecurity, \ - OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ - SRC := $(LIBOSXSECURITY_DIRS), \ - OPTIMIZATION := LOW, \ - CFLAGS := $(CFLAGS_JDKLIB) \ - $(LIBOSXSECURITY_CFLAGS), \ - DISABLED_WARNINGS_clang := deprecated-declarations, \ - LDFLAGS := $(LDFLAGS_JDKLIB) \ - -L$(SUPPORT_OUTPUTDIR)/modules_libs/java.base \ - $(call SET_SHARED_LIBRARY_ORIGIN) \ - -fobjc-link-runtime, \ - LIBS := \ - -framework JavaNativeFoundation \ - -framework CoreServices \ - -framework Security \ - $(JDKLIB_LIBS), \ - OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libosxsecurity, \ - DEBUG_SYMBOLS := $(DEBUG_ALL_BINARIES))) + $(eval $(call SetupNativeCompilation,BUILD_LIBOSXSECURITY, \ + LIBRARY := osxsecurity, \ + OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ + SRC := $(LIBOSXSECURITY_DIRS), \ + OPTIMIZATION := LOW, \ + CFLAGS := $(CFLAGS_JDKLIB) \ + $(LIBOSXSECURITY_CFLAGS), \ + DISABLED_WARNINGS_clang := deprecated-declarations, \ + LDFLAGS := $(LDFLAGS_JDKLIB) \ + -L$(SUPPORT_OUTPUTDIR)/modules_libs/java.base \ + $(call SET_SHARED_LIBRARY_ORIGIN) \ + -fobjc-link-runtime, \ + LIBS := \ + -framework JavaNativeFoundation \ + -framework CoreServices \ + -framework Security \ + $(JDKLIB_LIBS), \ + OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libosxsecurity, \ + DEBUG_SYMBOLS := $(DEBUG_ALL_BINARIES))) - $(BUILD_LIBOSXSECURITY): $(BUILD_LIBJAVA) + $(BUILD_LIBOSXSECURITY): $(BUILD_LIBJAVA) - TARGETS += $(BUILD_LIBOSXSECURITY) + TARGETS += $(BUILD_LIBOSXSECURITY) ################################################################################ + endif endif diff --git a/jdk/make/mapfiles/libjava/mapfile-vers b/jdk/make/mapfiles/libjava/mapfile-vers index c2e022c4ae9..7ae48fec8e3 100644 --- a/jdk/make/mapfiles/libjava/mapfile-vers +++ b/jdk/make/mapfiles/libjava/mapfile-vers @@ -211,6 +211,7 @@ SUNWprivate_1.1 { Java_java_lang_SecurityManager_getClassContext; Java_java_lang_Shutdown_halt0; Java_java_lang_String_intern; + Java_java_lang_StringUTF16_isBigEndian; Java_java_lang_System_identityHashCode; Java_java_lang_System_initProperties; Java_java_lang_System_mapLibraryName; diff --git a/jdk/make/mapfiles/libjava/reorder-sparc b/jdk/make/mapfiles/libjava/reorder-sparc index 5ae7ccabd3f..3994c916c2d 100644 --- a/jdk/make/mapfiles/libjava/reorder-sparc +++ b/jdk/make/mapfiles/libjava/reorder-sparc @@ -57,6 +57,7 @@ text: .text%Java_java_io_UnixFileSystem_list; text: .text%JNU_ClassString; text: .text%JNU_CopyObjectArray; text: .text%Java_java_lang_String_intern; +text: .text%Java_java_lang_StringUTF16_isBigEndian; text: .text%Java_java_lang_ClassLoader_findLoadedClass0; text: .text%Java_java_lang_ClassLoader_findBootstrapClass; text: .text%Java_java_lang_Throwable_fillInStackTrace; diff --git a/jdk/make/mapfiles/libjava/reorder-sparcv9 b/jdk/make/mapfiles/libjava/reorder-sparcv9 index f10986626b0..63a667f0124 100644 --- a/jdk/make/mapfiles/libjava/reorder-sparcv9 +++ b/jdk/make/mapfiles/libjava/reorder-sparcv9 @@ -29,6 +29,7 @@ text: .text%Java_sun_reflect_Reflection_getCallerClass__; text: .text%Java_sun_reflect_Reflection_getCallerClass__I; text: .text%Java_java_lang_Class_forName0; text: .text%Java_java_lang_String_intern; +text: .text%Java_java_lang_StringUTF16_isBigEndian; text: .text%Java_java_lang_Float_floatToRawIntBits; text: .text%Java_java_lang_Double_doubleToRawLongBits; text: .text%Java_java_lang_ClassLoader_findLoadedClass0; diff --git a/jdk/make/mapfiles/libjava/reorder-x86 b/jdk/make/mapfiles/libjava/reorder-x86 index 03609c18916..c6c3fced9f6 100644 --- a/jdk/make/mapfiles/libjava/reorder-x86 +++ b/jdk/make/mapfiles/libjava/reorder-x86 @@ -31,6 +31,7 @@ text: .text%Java_sun_reflect_Reflection_getCallerClass__; text: .text%Java_sun_reflect_Reflection_getCallerClass__I; text: .text%Java_java_lang_Class_forName0; text: .text%Java_java_lang_String_intern; +text: .text%Java_java_lang_StringUTF16_isBigEndian; text: .text%Java_sun_reflect_NativeConstructorAccessorImpl_newInstance0; text: .text%Java_java_lang_Throwable_fillInStackTrace; text: .text%Java_java_lang_System_setOut0; diff --git a/jdk/make/mapfiles/libnio/mapfile-macosx b/jdk/make/mapfiles/libnio/mapfile-macosx index 7ce9c50fde5..97207e2c816 100644 --- a/jdk/make/mapfiles/libnio/mapfile-macosx +++ b/jdk/make/mapfiles/libnio/mapfile-macosx @@ -75,6 +75,7 @@ SUNWprivate_1.1 { Java_sun_nio_ch_IOUtil_makePipe; Java_sun_nio_ch_IOUtil_randomBytes; Java_sun_nio_ch_IOUtil_setfdVal; + Java_sun_nio_ch_IOUtil_iovMax; Java_sun_nio_ch_KQueue_kqueue; Java_sun_nio_ch_KQueue_keventRegister; Java_sun_nio_ch_KQueue_keventPoll; diff --git a/jdk/make/src/classes/build/tools/charsetmapping/DBCS.java b/jdk/make/src/classes/build/tools/charsetmapping/DBCS.java index bd4bd7140f6..9ebadde8c43 100644 --- a/jdk/make/src/classes/build/tools/charsetmapping/DBCS.java +++ b/jdk/make/src/classes/build/tools/charsetmapping/DBCS.java @@ -197,6 +197,7 @@ public class DBCS { .replace("$B1MAX$" , "0x" + Integer.toString(b1Max, 16)) .replace("$B2MIN$" , "0x" + Integer.toString(b2Min, 16)) .replace("$B2MAX$" , "0x" + Integer.toString(b2Max, 16)) + .replace("$ASCIICOMPATIBLE$", isASCII ? "true" : "false") .replace("$B2C$", b2c) .replace("$C2BLENGTH$", "0x" + Integer.toString(c2bOff, 16)) .replace("$NONROUNDTRIP_B2C$", b2cNR) diff --git a/jdk/make/src/classes/build/tools/charsetmapping/SBCS.java b/jdk/make/src/classes/build/tools/charsetmapping/SBCS.java index fa294f13b9b..02c8d63fb6a 100644 --- a/jdk/make/src/classes/build/tools/charsetmapping/SBCS.java +++ b/jdk/make/src/classes/build/tools/charsetmapping/SBCS.java @@ -175,6 +175,9 @@ public class SBCS { else line = " return (cs instanceof " + clzName + ");"; } + if (line.indexOf("$ASCIICOMPATIBLE$") != -1) { + line = line.replace("$ASCIICOMPATIBLE$", isASCII ? "true" : "false"); + } if (line.indexOf("$B2CTABLE$") != -1) { line = line.replace("$B2CTABLE$", b2c); } diff --git a/jdk/make/src/classes/build/tools/x11wrappergen/WrapperGenerator.java b/jdk/make/src/classes/build/tools/x11wrappergen/WrapperGenerator.java index e3a68c42afb..60888904b89 100644 --- a/jdk/make/src/classes/build/tools/x11wrappergen/WrapperGenerator.java +++ b/jdk/make/src/classes/build/tools/x11wrappergen/WrapperGenerator.java @@ -841,7 +841,7 @@ public class WrapperGenerator { pw.println("// This file is an automatically generated file, please do not edit this file, modify the WrapperGenerator.java file instead !\n" ); pw.println("package "+package_name+";\n"); - pw.println("import sun.misc.*;\n"); + pw.println("import jdk.internal.misc.Unsafe;\n"); pw.println("import sun.util.logging.PlatformLogger;"); String baseClass = stp.getBaseClass(); if (baseClass == null) { @@ -941,7 +941,7 @@ public class WrapperGenerator { pw.println("// This file is an automatically generated file, please do not edit this file, modify the WrapperGenerator.java file instead !\n" ); pw.println("package "+package_name+";\n"); - pw.println("import sun.misc.Unsafe;\n"); + pw.println("import jdk.internal.misc.Unsafe;\n"); pw.println("class " + ft.getName() + " {"); pw.println("\tprivate static Unsafe unsafe = XlibWrapper.unsafe;"); pw.println("\tprivate boolean __executed = false;"); diff --git a/jdk/src/demo/share/jvmti/agent_util/agent_util.h b/jdk/src/demo/share/jvmti/agent_util/agent_util.h index 44882d71216..2237097ab30 100644 --- a/jdk/src/demo/share/jvmti/agent_util/agent_util.h +++ b/jdk/src/demo/share/jvmti/agent_util/agent_util.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -65,6 +65,51 @@ void deallocate(jvmtiEnv *jvmti, void *ptr); void *allocate(jvmtiEnv *jvmti, jint len); void add_demo_jar_to_bootclasspath(jvmtiEnv *jvmti, char *demo_name); +#ifdef STATIC_BUILD +/* Macros for handling declaration of static/dynamic + * Agent library Load/Attach/Unload functions + * + * DEF_Agent_OnLoad, DEF_Agent_OnAttach or DEF_Agent_OnUnload + * generate the appropriate entrypoint names based on static + * versus dynamic builds. + * + * STATIC_BUILD must be defined to build static versions of these libraries. + * LIBRARY_NAME must be set to the name of the library for static builds. + */ +#define ADD_LIB_NAME3(name, lib) name ## lib +#define ADD_LIB_NAME2(name, lib) ADD_LIB_NAME3(name, lib) +#define ADD_LIB_NAME(entry) ADD_LIB_NAME2(entry, LIBRARY_NAME) + +#define DEF_Agent_OnLoad \ +ADD_LIB_NAME(Agent_OnLoad_)(JavaVM *vm, char *options, void *reserved) \ +{ \ + jint JNICALL ADD_LIB_NAME(Agent_OnLoad_dynamic_)(JavaVM *vm, char *options, void *reserved); \ + return ADD_LIB_NAME(Agent_OnLoad_dynamic_)(vm, options, reserved); \ +} \ +jint JNICALL ADD_LIB_NAME(Agent_OnLoad_dynamic_) + +#define DEF_Agent_OnAttach \ +ADD_LIB_NAME(Agent_OnAttach_)(JavaVM *vm, char *options, void *reserved) \ +{ \ + jint JNICALL ADD_LIB_NAME(Agent_OnAttach_dynamic_)(JavaVM *vm, char *options, void *reserved); \ + return ADD_LIB_NAME(Agent_OnAttach_dynamic_)(vm, options, reserved); \ +} \ +jint JNICALL ADD_LIB_NAME(Agent_OnAttach_dynamic_) + +#define DEF_Agent_OnUnload \ +ADD_LIB_NAME(Agent_OnUnload_)(JavaVM *vm) \ +{ \ + void JNICALL ADD_LIB_NAME(Agent_OnUnload_dynamic_)(JavaVM *vm); \ + ADD_LIB_NAME(Agent_OnUnload_dynamic_)(vm); \ +} \ +void JNICALL ADD_LIB_NAME(Agent_OnUnload_dynamic_) + +#else +#define DEF_Agent_OnLoad Agent_OnLoad +#define DEF_Agent_OnAttach Agent_OnAttach +#define DEF_Agent_OnUnload Agent_OnUnload +#endif + #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ diff --git a/jdk/src/demo/share/jvmti/compiledMethodLoad/compiledMethodLoad.c b/jdk/src/demo/share/jvmti/compiledMethodLoad/compiledMethodLoad.c index 72c9717a5cb..92d123ea3ce 100644 --- a/jdk/src/demo/share/jvmti/compiledMethodLoad/compiledMethodLoad.c +++ b/jdk/src/demo/share/jvmti/compiledMethodLoad/compiledMethodLoad.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -225,7 +225,7 @@ compiled_method_load(jvmtiEnv *jvmti, jmethodID method, jint code_size, * event here. */ JNIEXPORT jint JNICALL -Agent_OnLoad(JavaVM *vm, char *options, void *reserved) +DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { jint rc; jvmtiError err; @@ -272,6 +272,6 @@ Agent_OnLoad(JavaVM *vm, char *options, void *reserved) /* Agent_OnUnload() is called last */ JNIEXPORT void JNICALL -Agent_OnUnload(JavaVM *vm) +DEF_Agent_OnUnload(JavaVM *vm) { } diff --git a/jdk/src/demo/share/jvmti/gctest/gctest.c b/jdk/src/demo/share/jvmti/gctest/gctest.c index 96fc83d0cb7..848e7e07c1a 100644 --- a/jdk/src/demo/share/jvmti/gctest/gctest.c +++ b/jdk/src/demo/share/jvmti/gctest/gctest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -148,7 +148,7 @@ gc_finish(jvmtiEnv* jvmti_env) /* Agent_OnLoad() is called first, we prepare for a VM_INIT event here. */ JNIEXPORT jint JNICALL -Agent_OnLoad(JavaVM *vm, char *options, void *reserved) +DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { jint rc; jvmtiError err; @@ -193,6 +193,6 @@ Agent_OnLoad(JavaVM *vm, char *options, void *reserved) /* Agent_OnUnload() is called last */ JNIEXPORT void JNICALL -Agent_OnUnload(JavaVM *vm) +DEF_Agent_OnUnload(JavaVM *vm) { } diff --git a/jdk/src/demo/share/jvmti/heapTracker/heapTracker.c b/jdk/src/demo/share/jvmti/heapTracker/heapTracker.c index ebbcf73de5a..3af21846245 100644 --- a/jdk/src/demo/share/jvmti/heapTracker/heapTracker.c +++ b/jdk/src/demo/share/jvmti/heapTracker/heapTracker.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -894,7 +894,7 @@ parse_agent_options(char *options) * loaded. This is the first code executed. */ JNIEXPORT jint JNICALL -Agent_OnLoad(JavaVM *vm, char *options, void *reserved) +DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { static GlobalAgentData data; jvmtiEnv *jvmti; @@ -1010,7 +1010,7 @@ Agent_OnLoad(JavaVM *vm, char *options, void *reserved) * unloaded. This is the last code executed. */ JNIEXPORT void JNICALL -Agent_OnUnload(JavaVM *vm) +DEF_Agent_OnUnload(JavaVM *vm) { /* Skip any cleanup, VM is about to die anyway */ } diff --git a/jdk/src/demo/share/jvmti/heapTracker/heapTracker.h b/jdk/src/demo/share/jvmti/heapTracker/heapTracker.h index dcfc4fd8514..8d63f156b71 100644 --- a/jdk/src/demo/share/jvmti/heapTracker/heapTracker.h +++ b/jdk/src/demo/share/jvmti/heapTracker/heapTracker.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -64,9 +64,4 @@ #include "agent_util.h" -/* Agent library externals to export. */ - -JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved); -JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm); - #endif diff --git a/jdk/src/demo/share/jvmti/heapViewer/heapViewer.c b/jdk/src/demo/share/jvmti/heapViewer/heapViewer.c index f6a6e5eb488..35ed907b84a 100644 --- a/jdk/src/demo/share/jvmti/heapViewer/heapViewer.c +++ b/jdk/src/demo/share/jvmti/heapViewer/heapViewer.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -235,7 +235,7 @@ vmDeath(jvmtiEnv *jvmti, JNIEnv *env) /* Agent_OnLoad() is called first, we prepare for a VM_INIT event here. */ JNIEXPORT jint JNICALL -Agent_OnLoad(JavaVM *vm, char *options, void *reserved) +DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { jint rc; jvmtiError err; @@ -283,6 +283,6 @@ Agent_OnLoad(JavaVM *vm, char *options, void *reserved) /* Agent_OnUnload() is called last */ JNIEXPORT void JNICALL -Agent_OnUnload(JavaVM *vm) +DEF_Agent_OnUnload(JavaVM *vm) { } diff --git a/jdk/src/demo/share/jvmti/minst/minst.c b/jdk/src/demo/share/jvmti/minst/minst.c index 45da43ea458..8317c1d3d61 100644 --- a/jdk/src/demo/share/jvmti/minst/minst.c +++ b/jdk/src/demo/share/jvmti/minst/minst.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -373,7 +373,7 @@ parse_agent_options(char *options) * loaded. This is the first code executed. */ JNIEXPORT jint JNICALL -Agent_OnLoad(JavaVM *vm, char *options, void *reserved) +DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { static GlobalAgentData data; jvmtiEnv *jvmti; @@ -467,7 +467,7 @@ Agent_OnLoad(JavaVM *vm, char *options, void *reserved) * unloaded. This is the last code executed. */ JNIEXPORT void JNICALL -Agent_OnUnload(JavaVM *vm) +DEF_Agent_OnUnload(JavaVM *vm) { /* Make sure all malloc/calloc/strdup space is freed */ if ( gdata->include != NULL ) { diff --git a/jdk/src/demo/share/jvmti/minst/minst.h b/jdk/src/demo/share/jvmti/minst/minst.h index b7cf45a89bf..d852ad4dcb8 100644 --- a/jdk/src/demo/share/jvmti/minst/minst.h +++ b/jdk/src/demo/share/jvmti/minst/minst.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -64,9 +64,4 @@ #include "agent_util.h" -/* Agent library externals to export. */ - -JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved); -JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm); - #endif diff --git a/jdk/src/demo/share/jvmti/mtrace/mtrace.c b/jdk/src/demo/share/jvmti/mtrace/mtrace.c index 05686a7ec70..82b9e662e40 100644 --- a/jdk/src/demo/share/jvmti/mtrace/mtrace.c +++ b/jdk/src/demo/share/jvmti/mtrace/mtrace.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -697,7 +697,7 @@ parse_agent_options(char *options) * loaded. This is the first code executed. */ JNIEXPORT jint JNICALL -Agent_OnLoad(JavaVM *vm, char *options, void *reserved) +DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { static GlobalAgentData data; jvmtiEnv *jvmti; @@ -795,7 +795,7 @@ Agent_OnLoad(JavaVM *vm, char *options, void *reserved) * unloaded. This is the last code executed. */ JNIEXPORT void JNICALL -Agent_OnUnload(JavaVM *vm) +DEF_Agent_OnUnload(JavaVM *vm) { /* Make sure all malloc/calloc/strdup space is freed */ if ( gdata->include != NULL ) { diff --git a/jdk/src/demo/share/jvmti/mtrace/mtrace.h b/jdk/src/demo/share/jvmti/mtrace/mtrace.h index c3130b88952..39f483ddf14 100644 --- a/jdk/src/demo/share/jvmti/mtrace/mtrace.h +++ b/jdk/src/demo/share/jvmti/mtrace/mtrace.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -64,9 +64,4 @@ #include "agent_util.h" -/* Agent library externals to export. */ - -JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved); -JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm); - #endif diff --git a/jdk/src/demo/share/jvmti/versionCheck/versionCheck.c b/jdk/src/demo/share/jvmti/versionCheck/versionCheck.c index 441e488a821..0ed58263b26 100644 --- a/jdk/src/demo/share/jvmti/versionCheck/versionCheck.c +++ b/jdk/src/demo/share/jvmti/versionCheck/versionCheck.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -89,7 +89,7 @@ vm_init(jvmtiEnv *jvmti, JNIEnv *env, jthread thread) /* Agent_OnLoad() is called first, we prepare for a VM_INIT event here. */ JNIEXPORT jint JNICALL -Agent_OnLoad(JavaVM *vm, char *options, void *reserved) +DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { jint rc; jvmtiError err; @@ -116,6 +116,6 @@ Agent_OnLoad(JavaVM *vm, char *options, void *reserved) /* Agent_OnUnload() is called last */ JNIEXPORT void JNICALL -Agent_OnUnload(JavaVM *vm) +DEF_Agent_OnUnload(JavaVM *vm) { } diff --git a/jdk/src/demo/share/jvmti/waiters/Monitor.hpp b/jdk/src/demo/share/jvmti/waiters/Monitor.hpp index f067e8c65c8..2906e5779f6 100644 --- a/jdk/src/demo/share/jvmti/waiters/Monitor.hpp +++ b/jdk/src/demo/share/jvmti/waiters/Monitor.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,6 +37,10 @@ * this sample code. */ +#ifdef STATIC_BUILD +#define Monitor WaiterMonitor +#endif + /* C++ Monitor class */ diff --git a/jdk/src/demo/share/jvmti/waiters/Thread.cpp b/jdk/src/demo/share/jvmti/waiters/Thread.cpp index 065a40dd516..589976963e0 100644 --- a/jdk/src/demo/share/jvmti/waiters/Thread.cpp +++ b/jdk/src/demo/share/jvmti/waiters/Thread.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,6 +37,9 @@ * this sample code. */ +#ifdef STATIC_BUILD +#define Thread WaiterThread +#endif #include #include diff --git a/jdk/src/demo/share/jvmti/waiters/waiters.cpp b/jdk/src/demo/share/jvmti/waiters/waiters.cpp index 34b79f38308..cf38e00e160 100644 --- a/jdk/src/demo/share/jvmti/waiters/waiters.cpp +++ b/jdk/src/demo/share/jvmti/waiters/waiters.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -243,7 +243,7 @@ extern "C" { /* Agent_OnLoad() is called first, we prepare for a VM_INIT event here. */ JNIEXPORT jint JNICALL - Agent_OnLoad(JavaVM *vm, char *options, void *reserved) + DEF_Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { jvmtiEnv *jvmti; jint rc; @@ -288,7 +288,7 @@ extern "C" { /* Agent_OnUnload() is called last */ JNIEXPORT void JNICALL - Agent_OnUnload(JavaVM *vm) + DEF_Agent_OnUnload(JavaVM *vm) { } diff --git a/jdk/src/java.base/aix/classes/sun/nio/ch/AixPollPort.java b/jdk/src/java.base/aix/classes/sun/nio/ch/AixPollPort.java index a7a954597e5..9cb294f67f0 100644 --- a/jdk/src/java.base/aix/classes/sun/nio/ch/AixPollPort.java +++ b/jdk/src/java.base/aix/classes/sun/nio/ch/AixPollPort.java @@ -34,7 +34,7 @@ import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** * AsynchronousChannelGroup implementation based on the AIX pollset framework. diff --git a/jdk/src/java.base/linux/classes/sun/nio/ch/EPoll.java b/jdk/src/java.base/linux/classes/sun/nio/ch/EPoll.java index bb2b9e5171f..72fe9950621 100644 --- a/jdk/src/java.base/linux/classes/sun/nio/ch/EPoll.java +++ b/jdk/src/java.base/linux/classes/sun/nio/ch/EPoll.java @@ -26,7 +26,7 @@ package sun.nio.ch; import java.io.IOException; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** * Provides access to the Linux epoll facility. diff --git a/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxDosFileAttributeView.java b/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxDosFileAttributeView.java index 11a40a0e862..dba638e8da9 100644 --- a/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxDosFileAttributeView.java +++ b/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxDosFileAttributeView.java @@ -29,7 +29,7 @@ import java.nio.file.attribute.*; import java.util.Map; import java.util.Set; import java.io.IOException; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import static sun.nio.fs.UnixNativeDispatcher.*; import static sun.nio.fs.UnixConstants.*; diff --git a/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java b/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java index d3960b27701..18b880c550f 100644 --- a/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java +++ b/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java @@ -29,7 +29,7 @@ import java.nio.file.*; import java.nio.ByteBuffer; import java.io.IOException; import java.util.*; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import static sun.nio.fs.UnixConstants.*; import static sun.nio.fs.LinuxNativeDispatcher.*; diff --git a/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxWatchService.java b/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxWatchService.java index 19079d72878..02af6a539d6 100644 --- a/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxWatchService.java +++ b/jdk/src/java.base/linux/classes/sun/nio/fs/LinuxWatchService.java @@ -30,7 +30,7 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.util.*; import java.io.IOException; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import static sun.nio.fs.UnixNativeDispatcher.*; import static sun.nio.fs.UnixConstants.*; diff --git a/jdk/src/java.base/macosx/classes/sun/nio/ch/KQueue.java b/jdk/src/java.base/macosx/classes/sun/nio/ch/KQueue.java index b98dacdfc53..f2be5339a76 100644 --- a/jdk/src/java.base/macosx/classes/sun/nio/ch/KQueue.java +++ b/jdk/src/java.base/macosx/classes/sun/nio/ch/KQueue.java @@ -26,7 +26,7 @@ package sun.nio.ch; import java.io.IOException; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** * Provides access to the BSD kqueue facility. diff --git a/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c b/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c index 7482d6ccc4c..82f63169edd 100644 --- a/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c +++ b/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2015, 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 @@ -35,13 +35,15 @@ #include "java_props_macosx.h" - // need dlopen/dlsym trick to avoid pulling in JavaRuntimeSupport before libjava.dylib is loaded static void *getJRSFramework() { static void *jrsFwk = NULL; +#ifndef STATIC_BUILD +// JavaRuntimeSupport doesn't support static Java runtimes if (jrsFwk == NULL) { jrsFwk = dlopen("/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/JavaRuntimeSupport", RTLD_LAZY | RTLD_LOCAL); } +#endif return jrsFwk; } diff --git a/jdk/src/java.base/macosx/native/libjli/java_md_macosx.c b/jdk/src/java.base/macosx/native/libjli/java_md_macosx.c index 2f3409dc0da..46a4a06beae 100644 --- a/jdk/src/java.base/macosx/native/libjli/java_md_macosx.c +++ b/jdk/src/java.base/macosx/native/libjli/java_md_macosx.c @@ -245,6 +245,8 @@ static InvocationFunctions *GetExportedJNIFunctions() { return sExportedJNIFunctions = fxns; } +#ifndef STATIC_BUILD + JNIEXPORT jint JNICALL JNI_GetDefaultJavaVMInitArgs(void *args) { InvocationFunctions *ifn = GetExportedJNIFunctions(); @@ -265,6 +267,7 @@ JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen, jsize *nVMs) { if (ifn == NULL) return JNI_ERR; return ifn->GetCreatedJavaVMs(vmBuf, bufLen, nVMs); } +#endif /* * Allow JLI-aware launchers to specify a client/server preference @@ -303,7 +306,12 @@ static void *apple_main (void *arg) objc_registerThreadWithCollector(); if (main_fptr == NULL) { +#ifdef STATIC_BUILD + extern int main(int argc, char **argv); + main_fptr = &main; +#else main_fptr = (int (*)())dlsym(RTLD_DEFAULT, "main"); +#endif if (main_fptr == NULL) { JLI_ReportErrorMessageSys("error locating main entrypoint\n"); exit(1); @@ -588,6 +596,9 @@ GetJVMPath(const char *jrepath, const char *jvmtype, JLI_TraceLauncher("Does `%s' exist ... ", jvmpath); +#ifdef STATIC_BUILD + return JNI_TRUE; +#else if (stat(jvmpath, &s) == 0) { JLI_TraceLauncher("yes.\n"); return JNI_TRUE; @@ -595,6 +606,7 @@ GetJVMPath(const char *jrepath, const char *jvmtype, JLI_TraceLauncher("no.\n"); return JNI_FALSE; } +#endif } /* @@ -607,10 +619,18 @@ GetJREPath(char *path, jint pathsize, const char * arch, jboolean speculative) if (GetApplicationHome(path, pathsize)) { /* Is JRE co-located with the application? */ +#ifdef STATIC_BUILD + char jvm_cfg[MAXPATHLEN]; + JLI_Snprintf(jvm_cfg, sizeof(jvm_cfg), "%s/lib/jvm.cfg", path); + if (access(jvm_cfg, F_OK) == 0) { + return JNI_TRUE; + } +#else JLI_Snprintf(libjava, sizeof(libjava), "%s/lib/" JAVA_DLL, path); if (access(libjava, F_OK) == 0) { return JNI_TRUE; } +#endif /* ensure storage for path + /jre + NULL */ if ((JLI_StrLen(path) + 4 + 1) > (size_t) pathsize) { JLI_TraceLauncher("Insufficient space to store JRE path\n"); @@ -629,6 +649,24 @@ GetJREPath(char *path, jint pathsize, const char * arch, jboolean speculative) Dl_info selfInfo; dladdr(&GetJREPath, &selfInfo); +#ifdef STATIC_BUILD + char jvm_cfg[MAXPATHLEN]; + char *p = NULL; + strncpy(jvm_cfg, selfInfo.dli_fname, MAXPATHLEN); + p = strrchr(jvm_cfg, '/'); *p = '\0'; + p = strrchr(jvm_cfg, '/'); + if (strcmp(p, "/.") == 0) { + *p = '\0'; + p = strrchr(jvm_cfg, '/'); *p = '\0'; + } + else *p = '\0'; + strncpy(path, jvm_cfg, pathsize); + strncat(jvm_cfg, "/lib/jvm.cfg", MAXPATHLEN); + if (access(jvm_cfg, F_OK) == 0) { + return JNI_TRUE; + } +#endif + char *realPathToSelf = realpath(selfInfo.dli_fname, path); if (realPathToSelf != path) { return JNI_FALSE; @@ -664,7 +702,11 @@ LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn) JLI_TraceLauncher("JVM path is %s\n", jvmpath); +#ifndef STATIC_BUILD libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL); +#else + libjvm = dlopen(NULL, RTLD_FIRST); +#endif if (libjvm == NULL) { JLI_ReportErrorMessage(DLL_ERROR1, __LINE__); JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); @@ -714,9 +756,14 @@ SetExecname(char **argv) char* exec_path = NULL; { Dl_info dlinfo; - int (*fptr)(); +#ifdef STATIC_BUILD + void *fptr; + fptr = (void *)&SetExecname; +#else + int (*fptr)(); fptr = (int (*)())dlsym(RTLD_DEFAULT, "main"); +#endif if (fptr == NULL) { JLI_ReportErrorMessage(DLL_ERROR3, dlerror()); return JNI_FALSE; diff --git a/jdk/src/java.base/share/classes/java/io/File.java b/jdk/src/java.base/share/classes/java/io/File.java index 63e6d571ed2..6137d0acd29 100644 --- a/jdk/src/java.base/share/classes/java/io/File.java +++ b/jdk/src/java.base/share/classes/java/io/File.java @@ -2184,10 +2184,10 @@ public class File private static final long PATH_OFFSET; private static final long PREFIX_LENGTH_OFFSET; - private static final sun.misc.Unsafe UNSAFE; + private static final jdk.internal.misc.Unsafe UNSAFE; static { try { - sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); + jdk.internal.misc.Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe(); PATH_OFFSET = unsafe.objectFieldOffset( File.class.getDeclaredField("path")); PREFIX_LENGTH_OFFSET = unsafe.objectFieldOffset( diff --git a/jdk/src/java.base/share/classes/java/io/ObjectInputStream.java b/jdk/src/java.base/share/classes/java/io/ObjectInputStream.java index f2bf5f01795..779257e87c0 100644 --- a/jdk/src/java.base/share/classes/java/io/ObjectInputStream.java +++ b/jdk/src/java.base/share/classes/java/io/ObjectInputStream.java @@ -40,7 +40,7 @@ import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static java.io.ObjectStreamClass.processQueue; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import sun.reflect.misc.ReflectUtil; /** diff --git a/jdk/src/java.base/share/classes/java/io/ObjectStreamClass.java b/jdk/src/java.base/share/classes/java/io/ObjectStreamClass.java index aa516a15d67..762706c1e88 100644 --- a/jdk/src/java.base/share/classes/java/io/ObjectStreamClass.java +++ b/jdk/src/java.base/share/classes/java/io/ObjectStreamClass.java @@ -48,7 +48,7 @@ import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import sun.reflect.CallerSensitive; import sun.reflect.Reflection; import sun.reflect.ReflectionFactory; diff --git a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java index a9216000731..3cd12eeac15 100644 --- a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java +++ b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java @@ -31,6 +31,12 @@ import java.util.Spliterator; import java.util.stream.IntStream; import java.util.stream.StreamSupport; +import static java.lang.String.COMPACT_STRINGS; +import static java.lang.String.UTF16; +import static java.lang.String.LATIN1; +import static java.lang.String.checkIndex; +import static java.lang.String.checkOffset; + /** * A mutable sequence of characters. *

@@ -51,7 +57,12 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ - char[] value; + byte[] value; + + /** + * The id of the encoding used to encode the bytes in {@code value}. + */ + byte coder; /** * The count is the number of characters used. @@ -68,7 +79,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * Creates an AbstractStringBuilder of the specified capacity. */ AbstractStringBuilder(int capacity) { - value = new char[capacity]; + if (COMPACT_STRINGS) { + value = new byte[capacity]; + coder = LATIN1; + } else { + value = StringUTF16.newBytesFor(capacity); + coder = UTF16; + } } /** @@ -90,7 +107,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * @return the current capacity */ public int capacity() { - return value.length; + return value.length >> coder; } /** @@ -110,8 +127,9 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * @param minimumCapacity the minimum desired capacity. */ public void ensureCapacity(int minimumCapacity) { - if (minimumCapacity > 0) + if (minimumCapacity > 0) { ensureCapacityInternal(minimumCapacity); + } } /** @@ -120,24 +138,48 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { */ private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code - if (minimumCapacity - value.length > 0) + int capacity = value.length >> coder; + if (minimumCapacity - capacity > 0) { expandCapacity(minimumCapacity); + } } /** * This implements the expansion semantics of ensureCapacity with no * size check or synchronization. */ - void expandCapacity(int minimumCapacity) { - int newCapacity = value.length * 2 + 2; - if (newCapacity - minimumCapacity < 0) + private void expandCapacity(int minimumCapacity) { + int newCapacity = (value.length >> coder) * 2 + 2; + if (newCapacity - minimumCapacity < 0) { newCapacity = minimumCapacity; + } if (newCapacity < 0) { - if (minimumCapacity < 0) // overflow + if (minimumCapacity < 0) {// overflow throw new OutOfMemoryError(); + } newCapacity = Integer.MAX_VALUE; } - value = Arrays.copyOf(value, newCapacity); + if (coder != LATIN1 && newCapacity > StringUTF16.MAX_LENGTH) { + if (minimumCapacity >= StringUTF16.MAX_LENGTH) { + throw new OutOfMemoryError(); + } + newCapacity = StringUTF16.MAX_LENGTH; + } + this.value = Arrays.copyOf(value, newCapacity << coder); + } + + /** + * If the coder is "isLatin1", this inflates the internal 8-bit storage + * to 16-bit pair storage. + */ + private void inflate() { + if (!isLatin1()) { + return; + } + byte[] buf = StringUTF16.newBytesFor(value.length); + StringLatin1.inflateSB(value, buf, 0, count); + this.value = buf; + this.coder = UTF16; } /** @@ -148,8 +190,9 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * returned by a subsequent call to the {@link #capacity()} method. */ public void trimToSize() { - if (count < value.length) { - value = Arrays.copyOf(value, count); + int length = count << coder; + if (length < value.length) { + value = Arrays.copyOf(value, length); } } @@ -179,14 +222,17 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * {@code newLength} argument is negative. */ public void setLength(int newLength) { - if (newLength < 0) + if (newLength < 0) { throw new StringIndexOutOfBoundsException(newLength); - ensureCapacityInternal(newLength); - - if (count < newLength) { - Arrays.fill(value, count, newLength, '\0'); } - + ensureCapacityInternal(newLength); + if (count < newLength) { + if (isLatin1()) { + StringLatin1.fillNull(value, count, newLength); + } else { + StringUTF16.fillNull(value, count, newLength); + } + } count = newLength; } @@ -209,9 +255,11 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { */ @Override public char charAt(int index) { - if ((index < 0) || (index >= count)) - throw new StringIndexOutOfBoundsException(index); - return value[index]; + checkIndex(index, count); + if (isLatin1()) { + return (char)(value[index] & 0xff); + } + return StringUTF16.charAt(value, index); } /** @@ -236,10 +284,11 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * sequence. */ public int codePointAt(int index) { - if ((index < 0) || (index >= count)) { - throw new StringIndexOutOfBoundsException(index); + checkIndex(index, count); + if (isLatin1()) { + return value[index] & 0xff; } - return Character.codePointAtImpl(value, index, count); + return StringUTF16.codePointAtSB(value, index, count); } /** @@ -265,10 +314,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { */ public int codePointBefore(int index) { int i = index - 1; - if ((i < 0) || (i >= count)) { + if (i < 0 || i >= count) { throw new StringIndexOutOfBoundsException(index); } - return Character.codePointBeforeImpl(value, index, 0); + if (isLatin1()) { + return value[i] & 0xff; + } + return StringUTF16.codePointBeforeSB(value, index); } /** @@ -295,7 +347,10 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) { throw new IndexOutOfBoundsException(); } - return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex); + if (isLatin1()) { + return endIndex - beginIndex; + } + return StringUTF16.codePointCountSB(value, beginIndex, endIndex); } /** @@ -321,8 +376,8 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { if (index < 0 || index > count) { throw new IndexOutOfBoundsException(); } - return Character.offsetByCodePointsImpl(value, 0, count, - index, codePointOffset); + return Character.offsetByCodePoints(this, + index, codePointOffset); } /** @@ -355,13 +410,14 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { */ public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) { - if (srcBegin < 0) - throw new StringIndexOutOfBoundsException(srcBegin); - if ((srcEnd < 0) || (srcEnd > count)) - throw new StringIndexOutOfBoundsException(srcEnd); - if (srcBegin > srcEnd) - throw new StringIndexOutOfBoundsException("srcBegin > srcEnd"); - System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); + checkRangeSIOOBE(srcBegin, srcEnd, count); // compatible to old version + int n = srcEnd - srcBegin; + checkRange(dstBegin, dstBegin + n, dst.length); + if (isLatin1()) { + StringLatin1.getCharsSB(value, srcBegin, srcEnd, dst, dstBegin); + } else { + StringUTF16.getCharsSB(value, srcBegin, srcEnd, dst, dstBegin); + } } /** @@ -379,9 +435,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * negative or greater than or equal to {@code length()}. */ public void setCharAt(int index, char ch) { - if ((index < 0) || (index >= count)) - throw new StringIndexOutOfBoundsException(index); - value[index] = ch; + checkIndex(index, count); + if (isLatin1() && StringLatin1.canEncode(ch)) { + value[index] = (byte)ch; + } else { + if (isLatin1()) { + inflate(); + } + StringUTF16.putCharSB(value, index, ch); + } } /** @@ -418,35 +480,34 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * @return a reference to this object. */ public AbstractStringBuilder append(String str) { - if (str == null) + if (str == null) { return appendNull(); + } int len = str.length(); ensureCapacityInternal(count + len); - str.getChars(0, len, value, count); + putStringAt(count, str); count += len; return this; } // Documentation in subclasses because of synchro difference public AbstractStringBuilder append(StringBuffer sb) { - if (sb == null) - return appendNull(); - int len = sb.length(); - ensureCapacityInternal(count + len); - sb.getChars(0, len, value, count); - count += len; - return this; + return this.append((AbstractStringBuilder)sb); } /** * @since 1.8 */ AbstractStringBuilder append(AbstractStringBuilder asb) { - if (asb == null) + if (asb == null) { return appendNull(); + } int len = asb.length(); ensureCapacityInternal(count + len); - asb.getChars(0, len, value, count); + if (getCoder() != asb.getCoder()) { + inflate(); + } + asb.getBytes(value, count, coder); count += len; return this; } @@ -454,25 +515,35 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { // Documentation in subclasses because of synchro difference @Override public AbstractStringBuilder append(CharSequence s) { - if (s == null) + if (s == null) { return appendNull(); - if (s instanceof String) + } + if (s instanceof String) { return this.append((String)s); - if (s instanceof AbstractStringBuilder) + } + if (s instanceof AbstractStringBuilder) { return this.append((AbstractStringBuilder)s); - + } return this.append(s, 0, s.length()); } private AbstractStringBuilder appendNull() { - int c = count; - ensureCapacityInternal(c + 4); - final char[] value = this.value; - value[c++] = 'n'; - value[c++] = 'u'; - value[c++] = 'l'; - value[c++] = 'l'; - count = c; + ensureCapacityInternal(count + 4); + int count = this.count; + byte[] val = this.value; + if (isLatin1()) { + val[count++] = 'n'; + val[count++] = 'u'; + val[count++] = 'l'; + val[count++] = 'l'; + } else { + checkOffset(count + 4, val.length >> 1); + StringUTF16.putChar(val, count++, 'n'); + StringUTF16.putChar(val, count++, 'u'); + StringUTF16.putChar(val, count++, 'l'); + StringUTF16.putChar(val, count++, 'l'); + } + this.count = count; return this; } @@ -507,21 +578,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { */ @Override public AbstractStringBuilder append(CharSequence s, int start, int end) { - if (s == null) + if (s == null) { s = "null"; - if ((start < 0) || (start > end) || (end > s.length())) - throw new IndexOutOfBoundsException( - "start " + start + ", end " + end + ", s.length() " - + s.length()); + } + checkRange(start, end, s.length()); int len = end - start; ensureCapacityInternal(count + len); - if (s instanceof String) { - ((String)s).getChars(start, end, value, count); - } else { - for (int i = start, j = count; i < end; i++, j++) - value[j] = s.charAt(i); - } - count += len; + appendChars(s, start, end); return this; } @@ -544,8 +607,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { public AbstractStringBuilder append(char[] str) { int len = str.length; ensureCapacityInternal(count + len); - System.arraycopy(str, 0, value, count, len); - count += len; + appendChars(str, 0, len); return this; } @@ -572,10 +634,10 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * or {@code offset+len > str.length} */ public AbstractStringBuilder append(char str[], int offset, int len) { - if (len > 0) // let arraycopy report AIOOBE for len < 0 - ensureCapacityInternal(count + len); - System.arraycopy(str, offset, value, count, len); - count += len; + int end = offset + len; + checkRange(offset, end, str.length); + ensureCapacityInternal(count + len); + appendChars(str, offset, end); return this; } @@ -592,20 +654,39 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * @return a reference to this object. */ public AbstractStringBuilder append(boolean b) { - if (b) { - ensureCapacityInternal(count + 4); - value[count++] = 't'; - value[count++] = 'r'; - value[count++] = 'u'; - value[count++] = 'e'; + ensureCapacityInternal(count + (b ? 4 : 5)); + int count = this.count; + byte[] val = this.value; + if (isLatin1()) { + if (b) { + val[count++] = 't'; + val[count++] = 'r'; + val[count++] = 'u'; + val[count++] = 'e'; + } else { + val[count++] = 'f'; + val[count++] = 'a'; + val[count++] = 'l'; + val[count++] = 's'; + val[count++] = 'e'; + } } else { - ensureCapacityInternal(count + 5); - value[count++] = 'f'; - value[count++] = 'a'; - value[count++] = 'l'; - value[count++] = 's'; - value[count++] = 'e'; + if (b) { + checkOffset(count + 4, val.length >> 1); + StringUTF16.putChar(val, count++, 't'); + StringUTF16.putChar(val, count++, 'r'); + StringUTF16.putChar(val, count++, 'u'); + StringUTF16.putChar(val, count++, 'e'); + } else { + checkOffset(count + 5, val.length >> 1); + StringUTF16.putChar(val, count++, 'f'); + StringUTF16.putChar(val, count++, 'a'); + StringUTF16.putChar(val, count++, 'l'); + StringUTF16.putChar(val, count++, 's'); + StringUTF16.putChar(val, count++, 'e'); + } } + this.count = count; return this; } @@ -627,7 +708,14 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { @Override public AbstractStringBuilder append(char c) { ensureCapacityInternal(count + 1); - value[count++] = c; + if (isLatin1() && StringLatin1.canEncode(c)) { + value[count++] = (byte)c; + } else { + if (isLatin1()) { + inflate(); + } + StringUTF16.putCharSB(value, count++, c); + } return this; } @@ -652,7 +740,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { : Integer.stringSize(i); int spaceNeeded = count + appendedLength; ensureCapacityInternal(spaceNeeded); - Integer.getChars(i, spaceNeeded, value); + if (isLatin1()) { + Integer.getChars(i, spaceNeeded, value); + } else { + byte[] val = this.value; + checkOffset(spaceNeeded, val.length >> 1); + Integer.getCharsUTF16(i, spaceNeeded, val); + } count = spaceNeeded; return this; } @@ -678,7 +772,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { : Long.stringSize(l); int spaceNeeded = count + appendedLength; ensureCapacityInternal(spaceNeeded); - Long.getChars(l, spaceNeeded, value); + if (isLatin1()) { + Long.getChars(l, spaceNeeded, value); + } else { + byte[] val = this.value; + checkOffset(spaceNeeded, val.length >> 1); + Long.getCharsUTF16(l, spaceNeeded, val); + } count = spaceNeeded; return this; } @@ -732,15 +832,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * greater than {@code end}. */ public AbstractStringBuilder delete(int start, int end) { - if (start < 0) - throw new StringIndexOutOfBoundsException(start); - if (end > count) + if (end > count) { end = count; - if (start > end) - throw new StringIndexOutOfBoundsException(); + } + checkRangeSIOOBE(start, end, count); int len = end - start; if (len > 0) { - System.arraycopy(value, start+len, value, start, count-end); + shift(end, -len); count -= len; } return this; @@ -766,20 +864,10 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * {@code codePoint} isn't a valid Unicode code point */ public AbstractStringBuilder appendCodePoint(int codePoint) { - final int count = this.count; - if (Character.isBmpCodePoint(codePoint)) { - ensureCapacityInternal(count + 1); - value[count] = (char) codePoint; - this.count = count + 1; - } else if (Character.isValidCodePoint(codePoint)) { - ensureCapacityInternal(count + 2); - Character.toSurrogates(codePoint, value, count); - this.count = count + 2; - } else { - throw new IllegalArgumentException(); + return append((char)codePoint); } - return this; + return append(Character.toChars(codePoint)); } /** @@ -800,9 +888,8 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * {@code length()}. */ public AbstractStringBuilder deleteCharAt(int index) { - if ((index < 0) || (index >= count)) - throw new StringIndexOutOfBoundsException(index); - System.arraycopy(value, index+1, value, index, count-index-1); + checkIndex(index, count); + shift(index + 1, -1); count--; return this; } @@ -827,22 +914,16 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * greater than {@code end}. */ public AbstractStringBuilder replace(int start, int end, String str) { - if (start < 0) - throw new StringIndexOutOfBoundsException(start); - if (start > count) - throw new StringIndexOutOfBoundsException("start > length()"); - if (start > end) - throw new StringIndexOutOfBoundsException("start > end"); - - if (end > count) + if (end > count) { end = count; + } + checkRangeSIOOBE(start, end, count); int len = str.length(); int newCount = count + len - (end - start); ensureCapacityInternal(newCount); - - System.arraycopy(value, end, value, start + len, count - end); - str.getChars(value, start); + shift(end, newCount - count); count = newCount; + putStringAt(start, str); return this; } @@ -907,13 +988,16 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * greater than {@code end}. */ public String substring(int start, int end) { - if (start < 0) - throw new StringIndexOutOfBoundsException(start); - if (end > count) - throw new StringIndexOutOfBoundsException(end); - if (start > end) - throw new StringIndexOutOfBoundsException(end - start); - return new String(value, start, end - start); + checkRangeSIOOBE(start, end, count); + if (isLatin1()) { + return StringLatin1.newString(value, start, end - start); + } + return StringUTF16.newStringSB(value, start, end - start); + } + + private void shift(int offset, int n) { + System.arraycopy(value, offset << coder, + value, (offset + n) << coder, (count - offset) << coder); } /** @@ -940,16 +1024,12 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { public AbstractStringBuilder insert(int index, char[] str, int offset, int len) { - if ((index < 0) || (index > length())) - throw new StringIndexOutOfBoundsException(index); - if ((offset < 0) || (len < 0) || (offset > str.length - len)) - throw new StringIndexOutOfBoundsException( - "offset " + offset + ", len " + len + ", str.length " - + str.length); + checkOffset(index, count); + checkRangeSIOOBE(offset, offset + len, str.length); ensureCapacityInternal(count + len); - System.arraycopy(value, index, value, index + len, count - index); - System.arraycopy(str, offset, value, index, len); + shift(index, len); count += len; + putCharsAt(index, str, offset, offset + len); return this; } @@ -1008,15 +1088,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * @throws StringIndexOutOfBoundsException if the offset is invalid. */ public AbstractStringBuilder insert(int offset, String str) { - if ((offset < 0) || (offset > length())) - throw new StringIndexOutOfBoundsException(offset); - if (str == null) + checkOffset(offset, count); + if (str == null) { str = "null"; + } int len = str.length(); ensureCapacityInternal(count + len); - System.arraycopy(value, offset, value, offset + len, count - offset); - str.getChars(value, offset); + shift(offset, len); count += len; + putStringAt(offset, str); return this; } @@ -1045,13 +1125,12 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * @throws StringIndexOutOfBoundsException if the offset is invalid. */ public AbstractStringBuilder insert(int offset, char[] str) { - if ((offset < 0) || (offset > length())) - throw new StringIndexOutOfBoundsException(offset); + checkOffset(offset, count); int len = str.length; ensureCapacityInternal(count + len); - System.arraycopy(value, offset, value, offset + len, count - offset); - System.arraycopy(str, 0, value, offset, len); + shift(offset, len); count += len; + putCharsAt(offset, str, 0, len); return this; } @@ -1077,10 +1156,12 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * @throws IndexOutOfBoundsException if the offset is invalid. */ public AbstractStringBuilder insert(int dstOffset, CharSequence s) { - if (s == null) + if (s == null) { s = "null"; - if (s instanceof String) + } + if (s instanceof String) { return this.insert(dstOffset, (String)s); + } return this.insert(dstOffset, s, 0, s.length()); } @@ -1128,23 +1209,19 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * {@code start} is greater than {@code end} or * {@code end} is greater than {@code s.length()} */ - public AbstractStringBuilder insert(int dstOffset, CharSequence s, - int start, int end) { - if (s == null) + public AbstractStringBuilder insert(int dstOffset, CharSequence s, + int start, int end) + { + if (s == null) { s = "null"; - if ((dstOffset < 0) || (dstOffset > this.length())) - throw new IndexOutOfBoundsException("dstOffset "+dstOffset); - if ((start < 0) || (end < 0) || (start > end) || (end > s.length())) - throw new IndexOutOfBoundsException( - "start " + start + ", end " + end + ", s.length() " - + s.length()); + } + checkOffset(dstOffset, count); + checkRange(start, end, s.length()); int len = end - start; ensureCapacityInternal(count + len); - System.arraycopy(value, dstOffset, value, dstOffset + len, - count - dstOffset); - for (int i=start; i> 1; j >= 0; j--) { - int k = n - j; - char cj = value[j]; - char ck = value[k]; - value[j] = ck; - value[k] = cj; - if (Character.isSurrogate(cj) || - Character.isSurrogate(ck)) { - hasSurrogates = true; + if (COMPACT_STRINGS && coder == LATIN1) { + for (int j = (n-1) >> 1; j >= 0; j--) { + int k = n - j; + byte cj = val[j]; + val[j] = val[k]; + val[k] = cj; + } + } else { + checkOffset(count, val.length >> 1); + boolean hasSurrogates = false; + for (int j = (n-1) >> 1; j >= 0; j--) { + int k = n - j; + char cj = StringUTF16.getChar(val, j); + char ck = StringUTF16.getChar(val, k); + StringUTF16.putChar(val, j, ck); + StringUTF16.putChar(val, k, cj); + if (Character.isSurrogate(cj) || + Character.isSurrogate(ck)) { + hasSurrogates = true; + } + } + if (hasSurrogates) { + reverseAllValidSurrogatePairs(val, count); } - } - if (hasSurrogates) { - reverseAllValidSurrogatePairs(); } return this; } /** Outlined helper method for reverse() */ - private void reverseAllValidSurrogatePairs() { + private void reverseAllValidSurrogatePairs(byte[] val, int count) { for (int i = 0; i < count - 1; i++) { - char c2 = value[i]; + char c2 = StringUTF16.getChar(val, i); if (Character.isLowSurrogate(c2)) { - char c1 = value[i + 1]; + char c1 = StringUTF16.getChar(val, i + 1); if (Character.isHighSurrogate(c1)) { - value[i++] = c1; - value[i] = c2; + StringUTF16.putChar(val, i++, c1); + StringUTF16.putChar(val, i, c2); } } } @@ -1444,10 +1542,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { */ @Override public IntStream chars() { + byte[] val = this.value; int count = this.count; byte coder = this.coder; + checkOffset(count, val.length >> coder); // Reuse String-based spliterator. This requires a supplier to // capture the value and count when the terminal operation is executed return StreamSupport.intStream( - () -> new String.IntCharArraySpliterator(value, 0, count, 0), + () -> coder == LATIN1 ? new StringLatin1.CharsSpliterator(val, 0, count, 0) + : new StringUTF16.CharsSpliterator(val, 0, count, 0), Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED, false); } @@ -1458,10 +1559,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { */ @Override public IntStream codePoints() { + byte[] val = this.value; int count = this.count; byte coder = this.coder; + checkOffset(count, val.length >> coder); // Reuse String-based spliterator. This requires a supplier to // capture the value and count when the terminal operation is executed return StreamSupport.intStream( - () -> new String.CodePointsSpliterator(value, 0, count, 0), + () -> coder == LATIN1 ? new StringLatin1.CharsSpliterator(val, 0, count, 0) + : new StringUTF16.CodePointsSpliterator(val, 0, count, 0), Spliterator.ORDERED, false); } @@ -1469,8 +1573,147 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * Needed by {@code String} for the contentEquals method. */ - final char[] getValue() { + final byte[] getValue() { return value; } + /* + * Invoker guarantees it is in UTF16 (inflate itself for asb), if two + * coders are different and the dstBegin has enough space + * + * @param dstBegin the char index, not offset of byte[] + * @param coder the coder of dst[] + */ + void getBytes(byte dst[], int dstBegin, byte coder) { + if (this.coder == coder) { + System.arraycopy(value, 0, dst, dstBegin << coder, count << coder); + } else { // this.coder == LATIN && coder == UTF16 + StringLatin1.inflateSB(value, dst, dstBegin, count); + } + } + + /* for readObject() */ + void initBytes(char[] value, int off, int len) { + if (String.COMPACT_STRINGS) { + this.value = StringUTF16.compress(value, off, len); + if (this.value != null) { + this.coder = LATIN1; + return; + } + } + this.coder = UTF16; + this.value = StringUTF16.toBytes(value, off, len); + } + + final byte getCoder() { + return COMPACT_STRINGS ? coder : UTF16; + } + + final boolean isLatin1() { + return COMPACT_STRINGS && coder == LATIN1; + } + + private final void putCharsAt(int index, char[] s, int off, int end) { + if (isLatin1()) { + byte[] val = this.value; + for (int i = off, j = index; i < end; i++) { + char c = s[i]; + if (StringLatin1.canEncode(c)) { + val[j++] = (byte)c; + } else { + inflate(); + StringUTF16.putCharsSB(this.value, j, s, i, end); + return; + } + } + } else { + StringUTF16.putCharsSB(this.value, index, s, off, end); + } + } + + private final void putCharsAt(int index, CharSequence s, int off, int end) { + if (isLatin1()) { + byte[] val = this.value; + for (int i = off, j = index; i < end; i++) { + char c = s.charAt(i); + if (StringLatin1.canEncode(c)) { + val[j++] = (byte)c; + } else { + inflate(); + StringUTF16.putCharsSB(this.value, j, s, i, end); + return; + } + } + } else { + StringUTF16.putCharsSB(this.value, index, s, off, end); + } + } + + private final void putStringAt(int index, String str) { + if (getCoder() != str.coder()) { + inflate(); + } + byte[] val = this.value; + byte coder = this.coder; + checkOffset(index + str.length(), val.length >> coder); + str.getBytes(val, index, coder); + } + + private final void appendChars(char[] s, int off, int end) { + if (isLatin1()) { + byte[] val = this.value; + for (int i = off, j = count; i < end; i++) { + char c = s[i]; + if (StringLatin1.canEncode(c)) { + val[j++] = (byte)c; + } else { + count = j; + inflate(); + StringUTF16.putCharsSB(this.value, j, s, i, end); + count += end - i; + return; + } + } + } else { + StringUTF16.putCharsSB(this.value, count, s, off, end); + } + count += end - off; + } + + private final void appendChars(CharSequence s, int off, int end) { + if (isLatin1()) { + byte[] val = this.value; + for (int i = off, j = count; i < end; i++) { + char c = s.charAt(i); + if (StringLatin1.canEncode(c)) { + val[j++] = (byte)c; + } else { + count = j; + inflate(); + StringUTF16.putCharsSB(this.value, j, s, i, end); + count += end - i; + return; + } + } + } else { + StringUTF16.putCharsSB(this.value, count, s, off, end); + } + count += end - off; + } + + /* IndexOutOfBoundsException, if out of bounds */ + private static void checkRange(int start, int end, int len) { + if (start < 0 || start > end || end > len) { + throw new IndexOutOfBoundsException( + "start " + start + ", end " + end + ", length " + len); + } + } + + /* StringIndexOutOfBoundsException, if out of bounds */ + private static void checkRangeSIOOBE(int start, int end, int len) { + if (start < 0 || start > end || end > len) { + throw new StringIndexOutOfBoundsException( + "start " + start + ", end " + end + ", length " + len); + } + } } diff --git a/jdk/src/java.base/share/classes/java/lang/Class.java b/jdk/src/java.base/share/classes/java/lang/Class.java index fc1e6b9f0f3..77913c3187d 100644 --- a/jdk/src/java.base/share/classes/java/lang/Class.java +++ b/jdk/src/java.base/share/classes/java/lang/Class.java @@ -55,7 +55,7 @@ import java.util.Map; import java.util.HashMap; import java.util.Objects; import java.util.StringJoiner; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import jdk.internal.HotSpotIntrinsicCandidate; import sun.reflect.CallerSensitive; import sun.reflect.ConstantPool; diff --git a/jdk/src/java.base/share/classes/java/lang/Integer.java b/jdk/src/java.base/share/classes/java/lang/Integer.java index ed401dba13f..e956c3571ff 100644 --- a/jdk/src/java.base/share/classes/java/lang/Integer.java +++ b/jdk/src/java.base/share/classes/java/lang/Integer.java @@ -29,6 +29,10 @@ import java.lang.annotation.Native; import java.util.Objects; import jdk.internal.HotSpotIntrinsicCandidate; +import static java.lang.String.COMPACT_STRINGS; +import static java.lang.String.LATIN1; +import static java.lang.String.UTF16; + /** * The {@code Integer} class wraps a value of the primitive type * {@code int} in an object. An object of type {@code Integer} @@ -138,25 +142,47 @@ public final class Integer extends Number implements Comparable { return toString(i); } - char buf[] = new char[33]; + if (COMPACT_STRINGS) { + byte[] buf = new byte[33]; + boolean negative = (i < 0); + int charPos = 32; + + if (!negative) { + i = -i; + } + + while (i <= -radix) { + buf[charPos--] = (byte)digits[-(i % radix)]; + i = i / radix; + } + buf[charPos] = (byte)digits[-i]; + + if (negative) { + buf[--charPos] = '-'; + } + + return StringLatin1.newString(buf, charPos, (33 - charPos)); + } + return toStringUTF16(i, radix); + } + + private static String toStringUTF16(int i, int radix) { + byte[] buf = new byte[33 * 2]; boolean negative = (i < 0); int charPos = 32; - if (!negative) { i = -i; } - while (i <= -radix) { - buf[charPos--] = digits[-(i % radix)]; + StringUTF16.putChar(buf, charPos--, digits[-(i % radix)]); i = i / radix; } - buf[charPos] = digits[-i]; + StringUTF16.putChar(buf, charPos, digits[-i]); if (negative) { - buf[--charPos] = '-'; + StringUTF16.putChar(buf, --charPos, '-'); } - - return new String(buf, charPos, (33 - charPos)); + return StringUTF16.newString(buf, charPos, (33 - charPos)); } /** @@ -312,12 +338,16 @@ public final class Integer extends Number implements Comparable { // assert shift > 0 && shift <=5 : "Illegal shift value"; int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val); int chars = Math.max(((mag + (shift - 1)) / shift), 1); - char[] buf = new char[chars]; - formatUnsignedInt(val, shift, buf, 0, chars); - - // Use special constructor which takes over "buf". - return new String(buf, true); + if (COMPACT_STRINGS) { + byte[] buf = new byte[chars]; + formatUnsignedInt(val, shift, buf, 0, chars); + return new String(buf, LATIN1); + } else { + byte[] buf = new byte[chars * 2]; + formatUnsignedIntUTF16(val, shift, buf, 0, chars); + return new String(buf, UTF16); + } } /** @@ -331,7 +361,7 @@ public final class Integer extends Number implements Comparable { * @param offset the offset in the destination buffer to start at * @param len the number of characters to write */ - static void formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) { + static void formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) { // assert shift > 0 && shift <=5 : "Illegal shift value"; // assert offset >= 0 && offset < buf.length : "illegal offset"; // assert len > 0 && (offset + len) <= buf.length : "illegal length"; @@ -344,6 +374,28 @@ public final class Integer extends Number implements Comparable { } while (charPos > offset); } + /** byte[]/LATIN1 version */ + static void formatUnsignedInt(int val, int shift, byte[] buf, int offset, int len) { + int charPos = offset + len; + int radix = 1 << shift; + int mask = radix - 1; + do { + buf[--charPos] = (byte)Integer.digits[val & mask]; + val >>>= shift; + } while (charPos > offset); + } + + /** byte[]/UTF16 version */ + static void formatUnsignedIntUTF16(int val, int shift, byte[] buf, int offset, int len) { + int charPos = offset + len; + int radix = 1 << shift; + int mask = radix - 1; + do { + StringUTF16.putChar(buf, --charPos, Integer.digits[val & mask]); + val >>>= shift; + } while (charPos > offset); + } + static final char [] DigitTens = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', @@ -401,9 +453,15 @@ public final class Integer extends Number implements Comparable { if (i == Integer.MIN_VALUE) return "-2147483648"; int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); - char[] buf = new char[size]; - getChars(i, size, buf); - return new String(buf, true); + if (COMPACT_STRINGS) { + byte[] buf = new byte[size]; + getChars(i, size, buf); + return new String(buf, LATIN1); + } else { + byte[] buf = new byte[size * 2]; + getCharsUTF16(i, size, buf); + return new String(buf, UTF16); + } } /** @@ -433,7 +491,7 @@ public final class Integer extends Number implements Comparable { * * Will fail if i == Integer.MIN_VALUE */ - static void getChars(int i, int index, char[] buf) { + static void getChars(int i, int index, byte[] buf) { int q, r; int charPos = index; char sign = 0; @@ -449,8 +507,8 @@ public final class Integer extends Number implements Comparable { // really: r = i - (q * 100); r = i - ((q << 6) + (q << 5) + (q << 2)); i = q; - buf [--charPos] = DigitOnes[r]; - buf [--charPos] = DigitTens[r]; + buf [--charPos] = (byte)DigitOnes[r]; + buf [--charPos] = (byte)DigitTens[r]; } // Fall thru to fast mode for smaller numbers @@ -458,12 +516,46 @@ public final class Integer extends Number implements Comparable { for (;;) { q = (i * 52429) >>> (16+3); r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ... - buf [--charPos] = digits [r]; + buf [--charPos] = (byte)digits [r]; i = q; if (i == 0) break; } if (sign != 0) { - buf [--charPos] = sign; + buf [--charPos] = (byte)sign; + } + } + + static void getCharsUTF16(int i, int index, byte[] buf) { + int q, r; + int charPos = index; + char sign = 0; + + if (i < 0) { + sign = '-'; + i = -i; + } + + // Generate two digits per iteration + while (i >= 65536) { + q = i / 100; + // really: r = i - (q * 100); + r = i - ((q << 6) + (q << 5) + (q << 2)); + i = q; + StringUTF16.putChar(buf, --charPos, DigitOnes[r]); + StringUTF16.putChar(buf, --charPos, DigitTens[r]); + } + + // Fall thru to fast mode for smaller numbers + // assert(i <= 65536, i); + for (;;) { + q = (i * 52429) >>> (16+3); + r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ... + StringUTF16.putChar(buf, --charPos, Integer.digits[r]); + i = q; + if (i == 0) break; + } + if (sign != 0) { + StringUTF16.putChar(buf, --charPos, sign); } } diff --git a/jdk/src/java.base/share/classes/java/lang/Long.java b/jdk/src/java.base/share/classes/java/lang/Long.java index 4f248d71003..b68e2c29b7d 100644 --- a/jdk/src/java.base/share/classes/java/lang/Long.java +++ b/jdk/src/java.base/share/classes/java/lang/Long.java @@ -30,6 +30,9 @@ import java.math.*; import java.util.Objects; import jdk.internal.HotSpotIntrinsicCandidate; +import static java.lang.String.COMPACT_STRINGS; +import static java.lang.String.LATIN1; +import static java.lang.String.UTF16; /** * The {@code Long} class wraps a value of the primitive type {@code @@ -124,25 +127,46 @@ public final class Long extends Number implements Comparable { radix = 10; if (radix == 10) return toString(i); - char[] buf = new char[65]; + + if (COMPACT_STRINGS) { + byte[] buf = new byte[65]; + int charPos = 64; + boolean negative = (i < 0); + + if (!negative) { + i = -i; + } + + while (i <= -radix) { + buf[charPos--] = (byte)Integer.digits[(int)(-(i % radix))]; + i = i / radix; + } + buf[charPos] = (byte)Integer.digits[(int)(-i)]; + + if (negative) { + buf[--charPos] = '-'; + } + return StringLatin1.newString(buf, charPos, (65 - charPos)); + } + return toStringUTF16(i, radix); + } + + private static String toStringUTF16(long i, int radix) { + byte[] buf = new byte[65 * 2]; int charPos = 64; boolean negative = (i < 0); - if (!negative) { i = -i; } - while (i <= -radix) { - buf[charPos--] = Integer.digits[(int)(-(i % radix))]; + StringUTF16.putChar(buf, charPos--, Integer.digits[(int)(-(i % radix))]); i = i / radix; } - buf[charPos] = Integer.digits[(int)(-i)]; - + StringUTF16.putChar(buf, charPos, Integer.digits[(int)(-i)]); if (negative) { - buf[--charPos] = '-'; + StringUTF16.putChar(buf, --charPos, '-'); } - - return new String(buf, charPos, (65 - charPos)); + return StringUTF16.newString(buf, charPos, (65 - charPos)); } /** @@ -355,10 +379,16 @@ public final class Long extends Number implements Comparable { // assert shift > 0 && shift <=5 : "Illegal shift value"; int mag = Long.SIZE - Long.numberOfLeadingZeros(val); int chars = Math.max(((mag + (shift - 1)) / shift), 1); - char[] buf = new char[chars]; - formatUnsignedLong(val, shift, buf, 0, chars); - return new String(buf, true); + if (COMPACT_STRINGS) { + byte[] buf = new byte[chars]; + formatUnsignedLong0(val, shift, buf, 0, chars); + return new String(buf, LATIN1); + } else { + byte[] buf = new byte[chars * 2]; + formatUnsignedLong0UTF16(val, shift, buf, 0, chars); + return new String(buf, UTF16); + } } /** @@ -385,6 +415,28 @@ public final class Long extends Number implements Comparable { } while (charPos > offset); } + /** byte[]/LATIN1 version */ + static void formatUnsignedLong0(long val, int shift, byte[] buf, int offset, int len) { + int charPos = offset + len; + int radix = 1 << shift; + int mask = radix - 1; + do { + buf[--charPos] = (byte)Integer.digits[((int) val) & mask]; + val >>>= shift; + } while (charPos > offset); + } + + /** byte[]/UTF16 version */ + static void formatUnsignedLong0UTF16(long val, int shift, byte[] buf, int offset, int len) { + int charPos = offset + len; + int radix = 1 << shift; + int mask = radix - 1; + do { + StringUTF16.putChar(buf, --charPos, Integer.digits[((int) val) & mask]); + val >>>= shift; + } while (charPos > offset); + } + /** * Returns a {@code String} object representing the specified * {@code long}. The argument is converted to signed decimal @@ -399,9 +451,15 @@ public final class Long extends Number implements Comparable { if (i == Long.MIN_VALUE) return "-9223372036854775808"; int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); - char[] buf = new char[size]; - getChars(i, size, buf); - return new String(buf, true); + if (COMPACT_STRINGS) { + byte[] buf = new byte[size]; + getChars(i, size, buf); + return new String(buf, LATIN1); + } else { + byte[] buf = new byte[size * 2]; + getCharsUTF16(i, size, buf); + return new String(buf, UTF16); + } } /** @@ -431,7 +489,7 @@ public final class Long extends Number implements Comparable { * * Will fail if i == Long.MIN_VALUE */ - static void getChars(long i, int index, char[] buf) { + static void getChars(long i, int index, byte[] buf) { long q; int r; int charPos = index; @@ -448,8 +506,8 @@ public final class Long extends Number implements Comparable { // really: r = i - (q * 100); r = (int)(i - ((q << 6) + (q << 5) + (q << 2))); i = q; - buf[--charPos] = Integer.DigitOnes[r]; - buf[--charPos] = Integer.DigitTens[r]; + buf[--charPos] = (byte)Integer.DigitOnes[r]; + buf[--charPos] = (byte)Integer.DigitTens[r]; } // Get 2 digits/iteration using ints @@ -460,8 +518,8 @@ public final class Long extends Number implements Comparable { // really: r = i2 - (q * 100); r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2)); i2 = q2; - buf[--charPos] = Integer.DigitOnes[r]; - buf[--charPos] = Integer.DigitTens[r]; + buf[--charPos] = (byte)Integer.DigitOnes[r]; + buf[--charPos] = (byte)Integer.DigitTens[r]; } // Fall thru to fast mode for smaller numbers @@ -469,12 +527,59 @@ public final class Long extends Number implements Comparable { for (;;) { q2 = (i2 * 52429) >>> (16+3); r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ... - buf[--charPos] = Integer.digits[r]; + buf[--charPos] = (byte)Integer.digits[r]; i2 = q2; if (i2 == 0) break; } if (sign != 0) { - buf[--charPos] = sign; + buf[--charPos] = (byte)sign; + } + } + + static void getCharsUTF16(long i, int index, byte[] buf) { + long q; + int r; + int charPos = index; + char sign = 0; + + if (i < 0) { + sign = '-'; + i = -i; + } + + // Get 2 digits/iteration using longs until quotient fits into an int + while (i > Integer.MAX_VALUE) { + q = i / 100; + // really: r = i - (q * 100); + r = (int)(i - ((q << 6) + (q << 5) + (q << 2))); + i = q; + StringUTF16.putChar(buf, --charPos, Integer.DigitOnes[r]); + StringUTF16.putChar(buf, --charPos, Integer.DigitTens[r]); + } + + // Get 2 digits/iteration using ints + int q2; + int i2 = (int)i; + while (i2 >= 65536) { + q2 = i2 / 100; + // really: r = i2 - (q * 100); + r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2)); + i2 = q2; + StringUTF16.putChar(buf, --charPos, Integer.DigitOnes[r]); + StringUTF16.putChar(buf, --charPos, Integer.DigitTens[r]); + } + + // Fall thru to fast mode for smaller numbers + // assert(i2 <= 65536, i2); + for (;;) { + q2 = (i2 * 52429) >>> (16+3); + r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ... + StringUTF16.putChar(buf, --charPos, Integer.digits[r]); + i2 = q2; + if (i2 == 0) break; + } + if (sign != 0) { + StringUTF16.putChar(buf, --charPos, sign); } } diff --git a/jdk/src/java.base/share/classes/java/lang/Process.java b/jdk/src/java.base/share/classes/java/lang/Process.java index e5077f9dcc0..b537bb416d3 100644 --- a/jdk/src/java.base/share/classes/java/lang/Process.java +++ b/jdk/src/java.base/share/classes/java/lang/Process.java @@ -83,7 +83,7 @@ import java.util.stream.Stream; * {@link #getPid() process id}, * {@link #info() information about the process}, * {@link #children() direct children}, and - * {@link #allChildren() direct and indirect children} of the process. + * {@link #descendants() direct children plus descendants of those children} of the process. * Delegating to the underlying Process or ProcessHandle is typically * easiest and most efficient. * @@ -351,7 +351,7 @@ public abstract class Process { * The {@link java.util.concurrent.CompletableFuture} provides the ability * to trigger dependent functions or actions that may be run synchronously * or asynchronously upon process termination. - * When the process terminates the CompletableFuture is + * When the process has terminated the CompletableFuture is * {@link java.util.concurrent.CompletableFuture#complete completed} regardless * of the exit status of the process. *

@@ -362,9 +362,6 @@ public abstract class Process { * {@link java.util.concurrent.CompletableFuture#cancel(boolean) Cancelling} * the CompletableFuture does not affect the Process. *

- * If the process is {@link #isAlive not alive} the {@link CompletableFuture} - * returned has been {@link java.util.concurrent.CompletableFuture#complete completed}. - *

* Processes returned from {@link ProcessBuilder#start} override the * default implementation to provide an efficient mechanism to wait * for process exit. @@ -406,6 +403,9 @@ public abstract class Process { * return delegate.onExit().thenApply(p -> this); * } * } + * @apiNote + * The process may be observed to have terminated with {@link #isAlive} + * before the ComputableFuture is completed and dependent actions are invoked. * * @return a new {@code CompletableFuture} for the Process * @@ -464,7 +464,7 @@ public abstract class Process { * {@link java.lang.UnsupportedOperationException} and performs no other action. * Subclasses should override this method to provide a ProcessHandle for the * process. The methods {@link #getPid}, {@link #info}, {@link #children}, - * and {@link #allChildren}, unless overridden, operate on the ProcessHandle. + * and {@link #descendants}, unless overridden, operate on the ProcessHandle. * * @return Returns a ProcessHandle for the Process * @throws UnsupportedOperationException if the Process implementation @@ -481,9 +481,8 @@ public abstract class Process { /** * Returns a snapshot of information about the process. * - *

An {@link ProcessHandle.Info} instance has various accessor methods - * that return information about the process, if the process is alive and - * the information is available, otherwise {@code null} is returned. + *

A {@link ProcessHandle.Info} instance has accessor methods + * that return information about the process if it is available. * * @implSpec * This implementation returns information about the process as: @@ -524,9 +523,9 @@ public abstract class Process { } /** - * Returns a snapshot of the direct and indirect children of the process. - * An indirect child is one whose parent is either a direct child or - * another indirect child. + * Returns a snapshot of the descendants of the process. + * The descendants of a process are the children of the process + * plus the descendants of those children, recursively. * Typically, a process that is {@link #isAlive not alive} has no children. *

* Note that processes are created and terminate asynchronously. @@ -535,18 +534,18 @@ public abstract class Process { * * @implSpec * This implementation returns all children as: - * {@link #toHandle toHandle().allChildren()}. + * {@link #toHandle toHandle().descendants()}. * - * @return a sequential Stream of ProcessHandles for processes that are - * direct and indirect children of the process + * @return a sequential Stream of ProcessHandles for processes that + * are descendants of the process * @throws UnsupportedOperationException if the Process implementation * does not support this operation * @throws SecurityException if a security manager has been installed and * it denies RuntimePermission("manageProcess") * @since 1.9 */ - public Stream allChildren() { - return toHandle().allChildren(); + public Stream descendants() { + return toHandle().descendants(); } diff --git a/jdk/src/java.base/share/classes/java/lang/ProcessBuilder.java b/jdk/src/java.base/share/classes/java/lang/ProcessBuilder.java index b102eb404da..8610465c1ee 100644 --- a/jdk/src/java.base/share/classes/java/lang/ProcessBuilder.java +++ b/jdk/src/java.base/share/classes/java/lang/ProcessBuilder.java @@ -26,9 +26,11 @@ package java.lang; import java.io.File; +import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.channels.Pipe; import java.util.Arrays; import java.util.ArrayList; import java.util.List; @@ -43,6 +45,11 @@ import java.security.PrivilegedAction; * {@link Process} instance with those attributes. The {@link * #start()} method can be invoked repeatedly from the same instance * to create new subprocesses with identical or related attributes. + *

+ * The {@link #startPipeline startPipeline} method can be invoked to create + * a pipeline of new processes that send the output of each process + * directly to the next process. Each process has the attributes of + * its respective ProcessBuilder. * *

Each process builder manages these process attributes: * @@ -696,11 +703,37 @@ public final class ProcessBuilder private Redirect() {} } + /** + * Private implementation subclass of Redirect that holds a FileDescriptor for the + * output of a previously started Process. + * The FileDescriptor is used as the standard input of the next Process + * to be started. + */ + static class RedirectPipeImpl extends Redirect { + final FileDescriptor fd; + + RedirectPipeImpl() { + this.fd = new FileDescriptor(); + } + @Override + public Type type() { return Type.PIPE; } + + @Override + public String toString() { return type().toString();} + + FileDescriptor getFd() { return fd; } + } + + /** + * Return the array of redirects, creating the default as needed. + * @return the array of redirects + */ private Redirect[] redirects() { - if (redirects == null) + if (redirects == null) { redirects = new Redirect[] { - Redirect.PIPE, Redirect.PIPE, Redirect.PIPE + Redirect.PIPE, Redirect.PIPE, Redirect.PIPE }; + } return redirects; } @@ -1039,6 +1072,18 @@ public final class ProcessBuilder * @see Runtime#exec(String[], String[], java.io.File) */ public Process start() throws IOException { + return start(redirects); + } + + /** + * Start a new Process using an explicit array of redirects. + * See {@link #start} for details of starting each Process. + * + * @param redirect array of redirects for stdin, stdout, stderr + * @return the new Process + * @throws IOException if an I/O error occurs + */ + private Process start(Redirect[] redirects) throws IOException { // Must convert to array first -- a malicious user-supplied // list might try to circumvent the security check. String[] cmdarray = command.toArray(new String[command.size()]); @@ -1089,4 +1134,171 @@ public final class ProcessBuilder cause); } } + + /** + * Starts a Process for each ProcessBuilder, creating a pipeline of + * processes linked by their standard output and standard input streams. + * The attributes of each ProcessBuilder are used to start the respective + * process except that as each process is started, its standard output + * is directed to the standard input of the next. The redirects for standard + * input of the first process and standard output of the last process are + * initialized using the redirect settings of the respective ProcessBuilder. + * All other {@code ProcessBuilder} redirects should be + * {@link Redirect#PIPE Redirect.PIPE}. + *

+ * All input and output streams between the intermediate processes are + * not accessible. + * The {@link Process#getOutputStream standard input} of all processes + * except the first process are null output streams + * The {@link Process#getInputStream standard output} of all processes + * except the last process are null input streams. + *

+ * The {@link #redirectErrorStream} of each ProcessBuilder applies to the + * respective process. If set to {@code true}, the error stream is written + * to the same stream as standard output. + *

+ * If starting any of the processes throws an Exception, all processes + * are forcibly destroyed. + *

+ * The {@code startPipeline} method performs the same checks on + * each ProcessBuilder as does the {@link #start} method. The new process + * will invoke the command and arguments given by {@link #command()}, + * in a working directory as given by {@link #directory()}, + * with a process environment as given by {@link #environment()}. + *

+ * This method checks that the command is a valid operating + * system command. Which commands are valid is system-dependent, + * but at the very least the command must be a non-empty list of + * non-null strings. + *

+ * A minimal set of system dependent environment variables may + * be required to start a process on some operating systems. + * As a result, the subprocess may inherit additional environment variable + * settings beyond those in the process builder's {@link #environment()}. + *

+ * If there is a security manager, its + * {@link SecurityManager#checkExec checkExec} + * method is called with the first component of this object's + * {@code command} array as its argument. This may result in + * a {@link SecurityException} being thrown. + *

+ * Starting an operating system process is highly system-dependent. + * Among the many things that can go wrong are: + *

    + *
  • The operating system program file was not found. + *
  • Access to the program file was denied. + *
  • The working directory does not exist. + *
  • Invalid character in command argument, such as NUL. + *
+ *

+ * In such cases an exception will be thrown. The exact nature + * of the exception is system-dependent, but it will always be a + * subclass of {@link IOException}. + *

+ * If the operating system does not support the creation of + * processes, an {@link UnsupportedOperationException} will be thrown. + *

+ * Subsequent modifications to this process builder will not + * affect the returned {@link Process}. + * @apiNote + * For example to count the unique imports for all the files in a file hierarchy + * on a Unix compatible platform: + *

{@code
+     * String directory = "/home/duke/src";
+     * ProcessBuilder[] builders = {
+     *              new ProcessBuilder("find", directory, "-type", "f"),
+                    new ProcessBuilder("xargs", "grep", "-h", "^import "),
+                    new ProcessBuilder("awk", "{print $2;}"),
+                    new ProcessBuilder("sort", "-u")};
+     * List processes = ProcessBuilder.startPipeline(
+     *         Arrays.asList(builders));
+     * Process last = processes.get(processes.size()-1);
+     * try (InputStream is = last.getInputStream();
+     *         Reader isr = new InputStreamReader(is);
+     *         BufferedReader r = new BufferedReader(isr)) {
+     *     long count = r.lines().count();
+     * }
+     * }
+ * + * @param builders a List of ProcessBuilders + * @return a {@code List}es started from the corresponding + * ProcessBuilder + * @throws IllegalArgumentException any of the redirects except the + * standard input of the first builder and the standard output of + * the last builder are not {@link Redirect#PIPE}. + * @throws NullPointerException + * if an element of the command list is null or + * if an element of the ProcessBuilder list is null or + * the builders argument is null + * @throws IndexOutOfBoundsException + * if the command is an empty list (has size {@code 0}) + * @throws SecurityException + * if a security manager exists and + *
    + *
  • its + * {@link SecurityManager#checkExec checkExec} + * method doesn't allow creation of the subprocess, or + *
  • the standard input to the subprocess was + * {@linkplain #redirectInput redirected from a file} + * and the security manager's + * {@link SecurityManager#checkRead(String) checkRead} method + * denies read access to the file, or + *
  • the standard output or standard error of the + * subprocess was + * {@linkplain #redirectOutput redirected to a file} + * and the security manager's + * {@link SecurityManager#checkWrite(String) checkWrite} method + * denies write access to the file + *
+ * + * @throws UnsupportedOperationException + * If the operating system does not support the creation of processes + * + * @throws IOException if an I/O error occurs + */ + public static List startPipeline(List builders) throws IOException { + // Accumulate and check the builders + final int numBuilders = builders.size(); + List processes = new ArrayList<>(numBuilders); + try { + Redirect prevOutput = null; + for (int index = 0; index < builders.size(); index++) { + ProcessBuilder builder = builders.get(index); + Redirect[] redirects = builder.redirects(); + if (index > 0) { + // check the current Builder to see if it can take input from the previous + if (builder.redirectInput() != Redirect.PIPE) { + throw new IllegalArgumentException("builder redirectInput()" + + " must be PIPE except for the first builder: " + + builder.redirectInput()); + } + redirects[0] = prevOutput; + } + if (index < numBuilders - 1) { + // check all but the last stage has output = PIPE + if (builder.redirectOutput() != Redirect.PIPE) { + throw new IllegalArgumentException("builder redirectOutput()" + + " must be PIPE except for the last builder: " + + builder.redirectOutput()); + } + redirects[1] = new RedirectPipeImpl(); // placeholder for new output + } + processes.add(builder.start(redirects)); + prevOutput = redirects[1]; + } + } catch (Exception ex) { + // Cleanup processes already started + processes.forEach(Process::destroyForcibly); + processes.forEach(p -> { + try { + p.waitFor(); // Wait for it to exit + } catch (InterruptedException ie) { + // If interrupted; continue with next Process + Thread.currentThread().interrupt(); + } + }); + throw ex; + } + return processes; + } } diff --git a/jdk/src/java.base/share/classes/java/lang/ProcessHandle.java b/jdk/src/java.base/share/classes/java/lang/ProcessHandle.java index 95d77fe68ca..a7e650fee04 100644 --- a/jdk/src/java.base/share/classes/java/lang/ProcessHandle.java +++ b/jdk/src/java.base/share/classes/java/lang/ProcessHandle.java @@ -54,7 +54,7 @@ import java.util.stream.Stream; * Each ProcessHandle identifies and allows control of a process in the native * system. ProcessHandles are returned from the factory methods {@link #current()}, * {@link #of(long)}, - * {@link #children}, {@link #allChildren}, {@link #parent()} and + * {@link #children}, {@link #descendants}, {@link #parent()} and * {@link #allProcesses()}. *

* The {@link Process} instances created by {@link ProcessBuilder} can be queried @@ -164,21 +164,21 @@ public interface ProcessHandle extends Comparable { Stream children(); /** - * Returns a snapshot of the current direct and indirect children of the process. - * An indirect child is one whose parent is either a direct child or - * another indirect child. + * Returns a snapshot of the descendants of the process. + * The descendants of a process are the children of the process + * plus the descendants of those children, recursively. * Typically, a process that is {@link #isAlive not alive} has no children. *

* Note that processes are created and terminate asynchronously. * There is no guarantee that a process is {@link #isAlive alive}. * * - * @return a sequential Stream of ProcessHandles for processes that are - * direct and indirect children of the process + * @return a sequential Stream of ProcessHandles for processes that + * are descendants of the process * @throws SecurityException if a security manager has been installed and * it denies RuntimePermission("manageProcess") */ - Stream allChildren(); + Stream descendants(); /** * Returns a snapshot of all processes visible to the current process. @@ -201,9 +201,8 @@ public interface ProcessHandle extends Comparable { /** * Returns a snapshot of information about the process. * - *

An {@code Info} instance has various accessor methods that return - * information about the process, if the process is alive and the - * information is available. + *

A {@link ProcessHandle.Info} instance has accessor methods that return + * information about the process if it is available. * * @return a snapshot of information about the process, always non-null */ @@ -288,7 +287,7 @@ public interface ProcessHandle extends Comparable { * The {@link java.util.concurrent.CompletableFuture} provides the ability * to trigger dependent functions or actions that may be run synchronously * or asynchronously upon process termination. - * When the process terminates the CompletableFuture is + * When the process has terminated the CompletableFuture is * {@link java.util.concurrent.CompletableFuture#complete completed} regardless * of the exit status of the process. * The {@code onExit} method can be called multiple times to invoke @@ -300,9 +299,9 @@ public interface ProcessHandle extends Comparable { * {@link java.util.concurrent.Future#get() wait} for it to terminate. * {@link java.util.concurrent.Future#cancel(boolean) Cancelling} * the CompleteableFuture does not affect the Process. - *

- * If the process is {@link #isAlive not alive} the {@link CompletableFuture} - * returned has been {@link java.util.concurrent.CompletableFuture#complete completed}. + * @apiNote + * The process may be observed to have terminated with {@link #isAlive} + * before the ComputableFuture is completed and dependent actions are invoked. * * @return a new {@code CompletableFuture} for the ProcessHandle * diff --git a/jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java b/jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java index b3d03683e4a..92b175b0957 100644 --- a/jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java +++ b/jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java @@ -389,7 +389,7 @@ final class ProcessHandleImpl implements ProcessHandle { } @Override - public Stream allChildren() { + public Stream descendants() { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("manageProcess")); diff --git a/jdk/src/java.base/share/classes/java/lang/RuntimePermission.java b/jdk/src/java.base/share/classes/java/lang/RuntimePermission.java index 36b48b75d39..d014ddb3378 100644 --- a/jdk/src/java.base/share/classes/java/lang/RuntimePermission.java +++ b/jdk/src/java.base/share/classes/java/lang/RuntimePermission.java @@ -348,6 +348,19 @@ import java.util.StringTokenizer; * {@code java.util.spi.LocaleServiceProvider} for more * information. * + * + * + * loggerFinder + * This {@code RuntimePermission} is required to be granted to + * classes which subclass or call methods on + * {@code java.lang.System.LoggerFinder}. The permission is + * checked during invocation of the abstract base class constructor, as + * well as on the invocation of its public methods. + * This permission ensures trust in classes which provide loggers + * to system classes. + * See {@link java.lang.System.LoggerFinder java.lang.System.LoggerFinder} + * for more information. + * * * * @implNote diff --git a/jdk/src/java.base/share/classes/java/lang/String.java b/jdk/src/java.base/share/classes/java/lang/String.java index ce4262eee19..4d4ab358b9f 100644 --- a/jdk/src/java.base/share/classes/java/lang/String.java +++ b/jdk/src/java.base/share/classes/java/lang/String.java @@ -36,7 +36,6 @@ import java.util.Locale; import java.util.Objects; import java.util.Spliterator; import java.util.StringJoiner; -import java.util.function.IntConsumer; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -119,8 +118,19 @@ import jdk.internal.HotSpotIntrinsicCandidate; public final class String implements java.io.Serializable, Comparable, CharSequence { + /** The value is used for character storage. */ - private final char value[]; + private final byte[] value; + + /** + * The identifier of the encoding used to encode the bytes in + * {@code value}. The supported values in this implementation are + * + * LATIN1 + * UTF16 + * + */ + private final byte coder; /** Cache the hash code for the string */ private int hash; // Default to 0 @@ -128,6 +138,49 @@ public final class String /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = -6849794470754667710L; + /** + * If String compaction is disabled, the bytes in {@code value} are + * always encoded in UTF16. + * + * For methods with several possible implementation paths, when String + * compaction is disabled, only one code path is taken. + * + * The instance field value is generally opaque to optimizing JIT + * compilers. Therefore, in performance-sensitive place, an explicit + * check of the static boolean {@code COMPACT_STRINGS} is done first + * before checking the {@code coder} field since the static boolean + * {@code COMPACT_STRINGS} would be constant folded away by an + * optimizing JIT compiler. The idioms for these cases are as follows. + * + * For code such as: + * + * if (coder == LATIN1) { ... } + * + * can be written more optimally as + * + * if (coder() == LATIN1) { ... } + * + * or: + * + * if (COMPACT_STRINGS && coder == LATIN1) { ... } + * + * An optimizing JIT compiler can fold the above conditional as: + * + * COMPACT_STRINGS == true => if (coder == LATIN1) { ... } + * COMPACT_STRINGS == false => if (false) { ... } + * + * @implNote + * The actual value for this field is injected by JVM. The static + * initialization block is used to set the value here to communicate + * that this static final field is not statically foldable, and to + * avoid any possible circular dependency during vm initialization. + */ + static final boolean COMPACT_STRINGS; + + static { + COMPACT_STRINGS = true; + } + /** * Class String is special cased within the Serialization Stream Protocol. * @@ -145,6 +198,7 @@ public final class String */ public String() { this.value = "".value; + this.coder = "".coder; } /** @@ -160,6 +214,7 @@ public final class String @HotSpotIntrinsicCandidate public String(String original) { this.value = original.value; + this.coder = original.coder; this.hash = original.hash; } @@ -173,7 +228,7 @@ public final class String * The initial value of the string */ public String(char value[]) { - this.value = Arrays.copyOf(value, value.length); + this(value, 0, value.length, null); } /** @@ -198,23 +253,12 @@ public final class String * {@code offset} is greater than {@code value.length - count} */ public String(char value[], int offset, int count) { - if (offset < 0) { - throw new StringIndexOutOfBoundsException(offset); - } - if (count <= 0) { - if (count < 0) { - throw new StringIndexOutOfBoundsException(count); - } - if (offset <= value.length) { - this.value = "".value; - return; - } - } - // Note: offset or count might be near -1>>>1. - if (offset > value.length - count) { - throw new StringIndexOutOfBoundsException(offset + count); - } - this.value = Arrays.copyOfRange(value, offset, offset + count); + this(value, offset, count, rangeCheck(value, offset, count)); + } + + private static Void rangeCheck(char[] value, int offset, int count) { + checkBoundsOffCount(offset, count, value.length); + return null; } /** @@ -246,48 +290,22 @@ public final class String * @since 1.5 */ public String(int[] codePoints, int offset, int count) { - if (offset < 0) { - throw new StringIndexOutOfBoundsException(offset); + checkBoundsOffCount(offset, count, codePoints.length); + if (count == 0) { + this.value = "".value; + this.coder = "".coder; + return; } - if (count <= 0) { - if (count < 0) { - throw new StringIndexOutOfBoundsException(count); - } - if (offset <= codePoints.length) { - this.value = "".value; + if (COMPACT_STRINGS) { + byte[] val = StringLatin1.toBytes(codePoints, offset, count); + if (val != null) { + this.coder = LATIN1; + this.value = val; return; } } - // Note: offset or count might be near -1>>>1. - if (offset > codePoints.length - count) { - throw new StringIndexOutOfBoundsException(offset + count); - } - - final int end = offset + count; - - // Pass 1: Compute precise size of char[] - int n = count; - for (int i = offset; i < end; i++) { - int c = codePoints[i]; - if (Character.isBmpCodePoint(c)) - continue; - else if (Character.isValidCodePoint(c)) - n++; - else throw new IllegalArgumentException(Integer.toString(c)); - } - - // Pass 2: Allocate and fill in char[] - final char[] v = new char[n]; - - for (int i = offset, j = 0; i < end; i++, j++) { - int c = codePoints[i]; - if (Character.isBmpCodePoint(c)) - v[j] = (char)c; - else - Character.toSurrogates(c, v, j++); - } - - this.value = v; + this.coder = UTF16; + this.value = StringUTF16.toBytes(codePoints, offset, count); } /** @@ -332,20 +350,24 @@ public final class String */ @Deprecated public String(byte ascii[], int hibyte, int offset, int count) { - checkBounds(ascii, offset, count); - char[] value = new char[count]; - - if (hibyte == 0) { - for (int i = count; i-- > 0;) { - value[i] = (char)(ascii[i + offset] & 0xff); - } + checkBoundsOffCount(offset, count, ascii.length); + if (count == 0) { + this.value = "".value; + this.coder = "".coder; + return; + } + if (COMPACT_STRINGS && (byte)hibyte == 0) { + this.value = Arrays.copyOfRange(ascii, offset, offset + count); + this.coder = LATIN1; } else { hibyte <<= 8; - for (int i = count; i-- > 0;) { - value[i] = (char)(hibyte | (ascii[i + offset] & 0xff)); + byte[] val = StringUTF16.newBytesFor(count); + for (int i = 0; i < count; i++) { + StringUTF16.putChar(val, i, hibyte | (ascii[offset++] & 0xff)); } + this.value = val; + this.coder = UTF16; } - this.value = value; } /** @@ -383,19 +405,6 @@ public final class String this(ascii, hibyte, 0, ascii.length); } - /* Common private utility method used to bounds check the byte array - * and requested offset & length values used by the String(byte[],..) - * constructors. - */ - private static void checkBounds(byte[] bytes, int offset, int length) { - if (length < 0) - throw new StringIndexOutOfBoundsException(length); - if (offset < 0) - throw new StringIndexOutOfBoundsException(offset); - if (offset > bytes.length - length) - throw new StringIndexOutOfBoundsException(offset + length); - } - /** * Constructs a new {@code String} by decoding the specified subarray of * bytes using the specified charset. The length of the new {@code String} @@ -433,8 +442,11 @@ public final class String throws UnsupportedEncodingException { if (charsetName == null) throw new NullPointerException("charsetName"); - checkBounds(bytes, offset, length); - this.value = StringCoding.decode(charsetName, bytes, offset, length); + checkBoundsOffCount(offset, length, bytes.length); + StringCoding.Result ret = + StringCoding.decode(charsetName, bytes, offset, length); + this.value = ret.value; + this.coder = ret.coder; } /** @@ -470,8 +482,11 @@ public final class String public String(byte bytes[], int offset, int length, Charset charset) { if (charset == null) throw new NullPointerException("charset"); - checkBounds(bytes, offset, length); - this.value = StringCoding.decode(charset, bytes, offset, length); + checkBoundsOffCount(offset, length, bytes.length); + StringCoding.Result ret = + StringCoding.decode(charset, bytes, offset, length); + this.value = ret.value; + this.coder = ret.coder; } /** @@ -553,8 +568,10 @@ public final class String * @since 1.1 */ public String(byte bytes[], int offset, int length) { - checkBounds(bytes, offset, length); - this.value = StringCoding.decode(bytes, offset, length); + checkBoundsOffCount(offset, length, bytes.length); + StringCoding.Result ret = StringCoding.decode(bytes, offset, length); + this.value = ret.value; + this.coder = ret.coder; } /** @@ -587,9 +604,7 @@ public final class String * A {@code StringBuffer} */ public String(StringBuffer buffer) { - synchronized(buffer) { - this.value = Arrays.copyOf(buffer.getValue(), buffer.length()); - } + this(buffer.toString()); } /** @@ -608,18 +623,20 @@ public final class String * @since 1.5 */ public String(StringBuilder builder) { - this.value = Arrays.copyOf(builder.getValue(), builder.length()); + this(builder, null); } - /* + /* * Package private constructor which shares value array for speed. * this constructor is always expected to be called with share==true. * a separate constructor is needed because we already have a public * String(char[]) constructor that makes a copy of the given char[]. */ - String(char[] value, boolean share) { + // TBD: this is kept for package internal use (Thread/System), + // should be removed if they all have a byte[] version + String(char[] val, boolean share) { // assert share : "unshared not supported"; - this.value = value; + this(val, 0, val.length, null); } /** @@ -631,7 +648,7 @@ public final class String * object. */ public int length() { - return value.length; + return value.length >> coder(); } /** @@ -665,10 +682,11 @@ public final class String * string. */ public char charAt(int index) { - if ((index < 0) || (index >= value.length)) { - throw new StringIndexOutOfBoundsException(index); + if (isLatin1()) { + return StringLatin1.charAt(value, index); + } else { + return StringUTF16.charAt(value, index); } - return value[index]; } /** @@ -694,10 +712,13 @@ public final class String * @since 1.5 */ public int codePointAt(int index) { - if ((index < 0) || (index >= value.length)) { - throw new StringIndexOutOfBoundsException(index); + if (isLatin1()) { + checkIndex(index, value.length); + return value[index] & 0xff; } - return Character.codePointAtImpl(value, index, value.length); + int length = value.length >> 1; + checkIndex(index, length); + return StringUTF16.codePointAt(value, index, length); } /** @@ -724,10 +745,13 @@ public final class String */ public int codePointBefore(int index) { int i = index - 1; - if ((i < 0) || (i >= value.length)) { + if (i < 0 || i >= length()) { throw new StringIndexOutOfBoundsException(index); } - return Character.codePointBeforeImpl(value, index, 0); + if (isLatin1()) { + return (value[i] & 0xff); + } + return StringUTF16.codePointBefore(value, index); } /** @@ -752,10 +776,14 @@ public final class String * @since 1.5 */ public int codePointCount(int beginIndex, int endIndex) { - if (beginIndex < 0 || endIndex > value.length || beginIndex > endIndex) { + if (beginIndex < 0 || beginIndex > endIndex || + endIndex > length()) { throw new IndexOutOfBoundsException(); } - return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex); + if (isLatin1()) { + return endIndex - beginIndex; + } + return StringUTF16.codePointCount(value, beginIndex, endIndex); } /** @@ -779,19 +807,10 @@ public final class String * @since 1.5 */ public int offsetByCodePoints(int index, int codePointOffset) { - if (index < 0 || index > value.length) { + if (index < 0 || index > length()) { throw new IndexOutOfBoundsException(); } - return Character.offsetByCodePointsImpl(value, 0, value.length, - index, codePointOffset); - } - - /** - * Copy characters from this string into dst starting at dstBegin. - * This method doesn't perform any range checking. - */ - void getChars(char dst[], int dstBegin) { - System.arraycopy(value, 0, dst, dstBegin, value.length); + return Character.offsetByCodePoints(this, index, codePointOffset); } /** @@ -825,16 +844,13 @@ public final class String * {@code dst.length} */ public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) { - if (srcBegin < 0) { - throw new StringIndexOutOfBoundsException(srcBegin); + checkBoundsBeginEnd(srcBegin, srcEnd, length()); + checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length); + if (isLatin1()) { + StringLatin1.getChars(value, srcBegin, srcEnd, dst, dstBegin); + } else { + StringUTF16.getChars(value, srcBegin, srcEnd, dst, dstBegin); } - if (srcEnd > value.length) { - throw new StringIndexOutOfBoundsException(srcEnd); - } - if (srcBegin > srcEnd) { - throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); - } - System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); } /** @@ -882,24 +898,13 @@ public final class String */ @Deprecated public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) { - if (srcBegin < 0) { - throw new StringIndexOutOfBoundsException(srcBegin); - } - if (srcEnd > value.length) { - throw new StringIndexOutOfBoundsException(srcEnd); - } - if (srcBegin > srcEnd) { - throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); - } + checkBoundsBeginEnd(srcBegin, srcEnd, length()); Objects.requireNonNull(dst); - - int j = dstBegin; - int n = srcEnd; - int i = srcBegin; - char[] val = value; /* avoid getfield opcode */ - - while (i < n) { - dst[j++] = (byte)val[i++]; + checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length); + if (isLatin1()) { + StringLatin1.getBytes(value, srcBegin, srcEnd, dst, dstBegin); + } else { + StringUTF16.getBytes(value, srcBegin, srcEnd, dst, dstBegin); } } @@ -926,7 +931,7 @@ public final class String public byte[] getBytes(String charsetName) throws UnsupportedEncodingException { if (charsetName == null) throw new NullPointerException(); - return StringCoding.encode(charsetName, value, 0, value.length); + return StringCoding.encode(charsetName, coder(), value); } /** @@ -949,8 +954,8 @@ public final class String */ public byte[] getBytes(Charset charset) { if (charset == null) throw new NullPointerException(); - return StringCoding.encode(charset, value, 0, value.length); - } + return StringCoding.encode(charset, coder(), value); + } /** * Encodes this {@code String} into a sequence of bytes using the @@ -966,7 +971,7 @@ public final class String * @since 1.1 */ public byte[] getBytes() { - return StringCoding.encode(value, 0, value.length); + return StringCoding.encode(coder(), value); } /** @@ -987,23 +992,15 @@ public final class String * @see #compareTo(String) * @see #equalsIgnoreCase(String) */ - @HotSpotIntrinsicCandidate public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { - char[] v1 = value; - char[] v2 = ((String)anObject).value; - int n = v1.length; - if (n == v2.length) { - int i = 0; - while (n-- != 0) { - if (v1[i] != v2[i]) - return false; - i++; - } - return true; + String aString = (String)anObject; + if (coder() == aString.coder()) { + return isLatin1() ? StringLatin1.equals(value, aString.value) + : StringUTF16.equals(value, aString.value); } } return false; @@ -1032,16 +1029,28 @@ public final class String } private boolean nonSyncContentEquals(AbstractStringBuilder sb) { - char[] v1 = value; - char[] v2 = sb.getValue(); - int n = v1.length; - if (n != sb.length()) { + int len = length(); + if (len != sb.length()) { return false; } - for (int i = 0; i < n; i++) { - if (v1[i] != v2[i]) { + byte v1[] = value; + byte v2[] = sb.getValue(); + if (coder() == sb.getCoder()) { + int n = v1.length; + for (int i = 0; i < n; i++) { + if (v1[i] != v2[i]) { + return false; + } + } + } else { + if (!isLatin1()) { // utf16 str and latin1 abs can never be "equal" return false; } + for (int i = 0; i < len; i++) { + if ((char)(v1[i] & 0xff) != StringUTF16.getChar(v2, i)) { + return false; + } + } } return true; } @@ -1081,14 +1090,22 @@ public final class String return equals(cs); } // Argument is a generic CharSequence - char[] v1 = value; - int n = v1.length; - if (n != cs.length()) { + int n = cs.length(); + if (n != length()) { return false; } - for (int i = 0; i < n; i++) { - if (v1[i] != cs.charAt(i)) { - return false; + byte[] val = this.value; + if (isLatin1()) { + for (int i = 0; i < n; i++) { + if ((val[i] & 0xff) != cs.charAt(i)) { + return false; + } + } + } else { + for (int i = 0; i < n; i++) { + if (StringUTF16.getChar(val, i) != cs.charAt(i)) { + return false; + } } } return true; @@ -1125,8 +1142,8 @@ public final class String public boolean equalsIgnoreCase(String anotherString) { return (this == anotherString) ? true : (anotherString != null) - && (anotherString.value.length == value.length) - && regionMatches(true, 0, anotherString, 0, value.length); + && (anotherString.length() == length()) + && regionMatches(true, 0, anotherString, 0, length()); } /** @@ -1173,23 +1190,16 @@ public final class String * value greater than {@code 0} if this string is * lexicographically greater than the string argument. */ - @HotSpotIntrinsicCandidate public int compareTo(String anotherString) { - char[] v1 = value; - char[] v2 = anotherString.value; - int len1 = v1.length; - int len2 = v2.length; - int lim = Math.min(len1, len2); - - for (int k = 0; k < lim; k++) { - char c1 = v1[k]; - char c2 = v2[k]; - if (c1 != c2) { - return c1 - c2; - } + byte v1[] = value; + byte v2[] = anotherString.value; + if (coder() == anotherString.coder()) { + return isLatin1() ? StringLatin1.compareTo(v1, v2) + : StringUTF16.compareTo(v1, v2); } - return len1 - len2; - } + return isLatin1() ? StringLatin1.compareToUTF16(v1, v2) + : StringUTF16.compareToLatin1(v1, v2); + } /** * A Comparator that orders {@code String} objects as by @@ -1210,12 +1220,18 @@ public final class String private static final long serialVersionUID = 8575799808933029326L; public int compare(String s1, String s2) { + byte v1[] = s1.value; + byte v2[] = s2.value; int n1 = s1.length(); int n2 = s2.length(); + boolean s1IsLatin1 = s1.isLatin1(); + boolean s2IsLatin1 = s2.isLatin1(); int min = Math.min(n1, n2); for (int i = 0; i < min; i++) { - char c1 = s1.charAt(i); - char c2 = s2.charAt(i); + char c1 = s1IsLatin1 ? StringLatin1.getChar(v1, i) + : StringUTF16.getChar(v1, i); + char c2 = s2IsLatin1 ? StringLatin1.getChar(v2, i) + : StringUTF16.getChar(v2, i); if (c1 != c2) { c1 = Character.toUpperCase(c1); c2 = Character.toUpperCase(c2); @@ -1294,21 +1310,41 @@ public final class String * exactly matches the specified subregion of the string argument; * {@code false} otherwise. */ - public boolean regionMatches(int toffset, String other, int ooffset, - int len) { - char[] ta = value; - int to = toffset; - char[] pa = other.value; - int po = ooffset; + public boolean regionMatches(int toffset, String other, int ooffset, int len) { + byte tv[] = value; + byte ov[] = other.value; // Note: toffset, ooffset, or len might be near -1>>>1. - if ((ooffset < 0) || (toffset < 0) - || (toffset > (long)ta.length - len) - || (ooffset > (long)pa.length - len)) { + if ((ooffset < 0) || (toffset < 0) || + (toffset > (long)length() - len) || + (ooffset > (long)other.length() - len)) { return false; } - while (len-- > 0) { - if (ta[to++] != pa[po++]) { - return false; + if (coder() == other.coder()) { + if (!isLatin1() && (len > 0)) { + toffset = toffset << 1; + ooffset = ooffset << 1; + len = len << 1; + } + while (len-- > 0) { + if (tv[toffset++] != ov[ooffset++]) { + return false; + } + } + } else { + if (coder() == LATIN1) { + while (len-- > 0) { + if (StringLatin1.getChar(tv, toffset++) != + StringUTF16.getChar(ov, ooffset++)) { + return false; + } + } + } else { + while (len-- > 0) { + if (StringUTF16.getChar(tv, toffset++) != + StringLatin1.getChar(ov, ooffset++)) { + return false; + } + } } } return true; @@ -1366,43 +1402,25 @@ public final class String */ public boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) { - char[] ta = value; - int to = toffset; - char[] pa = other.value; - int po = ooffset; + if (!ignoreCase) { + return regionMatches(toffset, other, ooffset, len); + } // Note: toffset, ooffset, or len might be near -1>>>1. if ((ooffset < 0) || (toffset < 0) - || (toffset > (long)ta.length - len) - || (ooffset > (long)pa.length - len)) { + || (toffset > (long)length() - len) + || (ooffset > (long)other.length() - len)) { return false; } - while (len-- > 0) { - char c1 = ta[to++]; - char c2 = pa[po++]; - if (c1 == c2) { - continue; - } - if (ignoreCase) { - // If characters don't match but case may be ignored, - // try converting both characters to uppercase. - // If the results match, then the comparison scan should - // continue. - char u1 = Character.toUpperCase(c1); - char u2 = Character.toUpperCase(c2); - if (u1 == u2) { - continue; - } - // Unfortunately, conversion to uppercase does not work properly - // for the Georgian alphabet, which has strange rules about case - // conversion. So we need to make one last check before - // exiting. - if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { - continue; - } - } - return false; + byte tv[] = value; + byte ov[] = other.value; + if (coder() == other.coder()) { + return isLatin1() + ? StringLatin1.regionMatchesCI(tv, toffset, ov, ooffset, len) + : StringUTF16.regionMatchesCI(tv, toffset, ov, ooffset, len); } - return true; + return isLatin1() + ? StringLatin1.regionMatchesCI_UTF16(tv, toffset, ov, ooffset, len) + : StringUTF16.regionMatchesCI_Latin1(tv, toffset, ov, ooffset, len); } /** @@ -1423,19 +1441,31 @@ public final class String * */ public boolean startsWith(String prefix, int toffset) { - char[] ta = value; - int to = toffset; - char[] pa = prefix.value; - int po = 0; - int pc = pa.length; // Note: toffset might be near -1>>>1. - if ((toffset < 0) || (toffset > ta.length - pc)) { + if (toffset < 0 || toffset > length() - prefix.length()) { return false; } - while (--pc >= 0) { - if (ta[to++] != pa[po++]) { + byte ta[] = value; + byte pa[] = prefix.value; + int po = 0; + int pc = pa.length; + if (coder() == prefix.coder()) { + int to = isLatin1() ? toffset : toffset << 1; + while (po < pc) { + if (ta[to++] != pa[po++]) { + return false; + } + } + } else { + if (isLatin1()) { // && pcoder == UTF16 return false; } + // coder == UTF16 && pcoder == LATIN1) + while (po < pc) { + if (StringUTF16.getChar(ta, toffset++) != (pa[po++] & 0xff)) { + return false; + } + } } return true; } @@ -1469,7 +1499,7 @@ public final class String * as determined by the {@link #equals(Object)} method. */ public boolean endsWith(String suffix) { - return startsWith(suffix, value.length - suffix.value.length); + return startsWith(suffix, length() - suffix.length()); } /** @@ -1486,16 +1516,11 @@ public final class String * @return a hash code value for this object. */ public int hashCode() { - int h = hash; - if (h == 0) { - for (char v : value) { - h = 31 * h + v; - } - if (h != 0) { - hash = h; - } + if (hash == 0 && value.length > 0) { + hash = isLatin1() ? StringLatin1.hashCode(value) + : StringUTF16.hashCode(value); } - return h; + return hash; } /** @@ -1566,45 +1591,8 @@ public final class String * if the character does not occur. */ public int indexOf(int ch, int fromIndex) { - final int max = value.length; - if (fromIndex < 0) { - fromIndex = 0; - } else if (fromIndex >= max) { - // Note: fromIndex might be near -1>>>1. - return -1; - } - - if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { - // handle most cases here (ch is a BMP code point or a - // negative value (invalid code point)) - final char[] value = this.value; - for (int i = fromIndex; i < max; i++) { - if (value[i] == ch) { - return i; - } - } - return -1; - } else { - return indexOfSupplementary(ch, fromIndex); - } - } - - /** - * Handles (rare) calls of indexOf with a supplementary character. - */ - private int indexOfSupplementary(int ch, int fromIndex) { - if (Character.isValidCodePoint(ch)) { - final char[] value = this.value; - final char hi = Character.highSurrogate(ch); - final char lo = Character.lowSurrogate(ch); - final int max = value.length - 1; - for (int i = fromIndex; i < max; i++) { - if (value[i] == hi && value[i + 1] == lo) { - return i; - } - } - } - return -1; + return isLatin1() ? StringLatin1.indexOf(value, ch, fromIndex) + : StringUTF16.indexOf(value, ch, fromIndex); } /** @@ -1631,7 +1619,7 @@ public final class String * {@code -1} if the character does not occur. */ public int lastIndexOf(int ch) { - return lastIndexOf(ch, value.length - 1); + return lastIndexOf(ch, length() - 1); } /** @@ -1669,38 +1657,8 @@ public final class String * if the character does not occur before that point. */ public int lastIndexOf(int ch, int fromIndex) { - if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { - // handle most cases here (ch is a BMP code point or a - // negative value (invalid code point)) - final char[] value = this.value; - int i = Math.min(fromIndex, value.length - 1); - for (; i >= 0; i--) { - if (value[i] == ch) { - return i; - } - } - return -1; - } else { - return lastIndexOfSupplementary(ch, fromIndex); - } - } - - /** - * Handles (rare) calls of lastIndexOf with a supplementary character. - */ - private int lastIndexOfSupplementary(int ch, int fromIndex) { - if (Character.isValidCodePoint(ch)) { - final char[] value = this.value; - char hi = Character.highSurrogate(ch); - char lo = Character.lowSurrogate(ch); - int i = Math.min(fromIndex, value.length - 2); - for (; i >= 0; i--) { - if (value[i] == hi && value[i + 1] == lo) { - return i; - } - } - } - return -1; + return isLatin1() ? StringLatin1.lastIndexOf(value, ch, fromIndex) + : StringUTF16.lastIndexOf(value, ch, fromIndex); } /** @@ -1717,9 +1675,15 @@ public final class String * @return the index of the first occurrence of the specified substring, * or {@code -1} if there is no such occurrence. */ - @HotSpotIntrinsicCandidate public int indexOf(String str) { - return indexOf(str, 0); + if (coder() == str.coder()) { + return isLatin1() ? StringLatin1.indexOf(value, str.value) + : StringUTF16.indexOf(value, str.value); + } + if (coder() == LATIN1) { // str.coder == UTF16 + return -1; + } + return StringUTF16.indexOfLatin1(value, str.value); } /** @@ -1740,8 +1704,7 @@ public final class String * or {@code -1} if there is no such occurrence. */ public int indexOf(String str, int fromIndex) { - return indexOf(value, 0, value.length, - str.value, 0, str.value.length, fromIndex); + return indexOf(value, coder(), length(), str, fromIndex); } /** @@ -1749,68 +1712,38 @@ public final class String * source is the character array being searched, and the target * is the string being searched for. * - * @param source the characters being searched. - * @param sourceOffset offset of the source string. - * @param sourceCount count of the source string. - * @param target the characters being searched for. - * @param fromIndex the index to begin searching from. + * @param src the characters being searched. + * @param srcCoder the coder of the source string. + * @param srcCount length of the source string. + * @param tgtStr the characters being searched for. + * @param fromIndex the index to begin searching from. */ - static int indexOf(char[] source, int sourceOffset, int sourceCount, - String target, int fromIndex) { - return indexOf(source, sourceOffset, sourceCount, - target.value, 0, target.value.length, - fromIndex); - } + static int indexOf(byte[] src, byte srcCoder, int srcCount, + String tgtStr, int fromIndex) { - /** - * Code shared by String and StringBuffer to do searches. The - * source is the character array being searched, and the target - * is the string being searched for. - * - * @param source the characters being searched. - * @param sourceOffset offset of the source string. - * @param sourceCount count of the source string. - * @param target the characters being searched for. - * @param targetOffset offset of the target string. - * @param targetCount count of the target string. - * @param fromIndex the index to begin searching from. - */ - static int indexOf(char[] source, int sourceOffset, int sourceCount, - char[] target, int targetOffset, int targetCount, - int fromIndex) { - if (fromIndex >= sourceCount) { - return (targetCount == 0 ? sourceCount : -1); + byte[] tgt = tgtStr.value; + byte tgtCoder = tgtStr.coder(); + int tgtCount = tgtStr.length(); + + if (fromIndex >= srcCount) { + return (tgtCount == 0 ? srcCount : -1); } if (fromIndex < 0) { fromIndex = 0; } - if (targetCount == 0) { + if (tgtCount == 0) { return fromIndex; } - - char first = target[targetOffset]; - int max = sourceOffset + (sourceCount - targetCount); - - for (int i = sourceOffset + fromIndex; i <= max; i++) { - /* Look for first character. */ - if (source[i] != first) { - while (++i <= max && source[i] != first); - } - - /* Found first character, now look at the rest of v2 */ - if (i <= max) { - int j = i + 1; - int end = j + targetCount - 1; - for (int k = targetOffset + 1; j < end && source[j] - == target[k]; j++, k++); - - if (j == end) { - /* Found whole string. */ - return i - sourceOffset; - } - } + if (srcCoder == tgtCoder) { + return srcCoder == LATIN1 + ? StringLatin1.indexOf(src, srcCount, tgt, tgtCount, fromIndex) + : StringUTF16.indexOf(src, srcCount, tgt, tgtCount, fromIndex); } - return -1; + if (srcCoder == LATIN1) { // && tgtCoder == UTF16 + return -1; + } + // srcCoder == UTF16 && tgtCoder == LATIN1) { + return StringUTF16.indexOfLatin1(src, srcCount, tgt, tgtCount, fromIndex); } /** @@ -1829,7 +1762,7 @@ public final class String * or {@code -1} if there is no such occurrence. */ public int lastIndexOf(String str) { - return lastIndexOf(str, value.length); + return lastIndexOf(str, length()); } /** @@ -1850,8 +1783,7 @@ public final class String * or {@code -1} if there is no such occurrence. */ public int lastIndexOf(String str, int fromIndex) { - return lastIndexOf(value, 0, value.length, - str.value, 0, str.value.length, fromIndex); + return lastIndexOf(value, coder(), length(), str, fromIndex); } /** @@ -1859,40 +1791,22 @@ public final class String * source is the character array being searched, and the target * is the string being searched for. * - * @param source the characters being searched. - * @param sourceOffset offset of the source string. - * @param sourceCount count of the source string. - * @param target the characters being searched for. - * @param fromIndex the index to begin searching from. + * @param src the characters being searched. + * @param srcCoder coder handles the mapping between bytes/chars + * @param srcCount count of the source string. + * @param tgt the characters being searched for. + * @param fromIndex the index to begin searching from. */ - static int lastIndexOf(char[] source, int sourceOffset, int sourceCount, - String target, int fromIndex) { - return lastIndexOf(source, sourceOffset, sourceCount, - target.value, 0, target.value.length, - fromIndex); - } - - /** - * Code shared by String and StringBuffer to do searches. The - * source is the character array being searched, and the target - * is the string being searched for. - * - * @param source the characters being searched. - * @param sourceOffset offset of the source string. - * @param sourceCount count of the source string. - * @param target the characters being searched for. - * @param targetOffset offset of the target string. - * @param targetCount count of the target string. - * @param fromIndex the index to begin searching from. - */ - static int lastIndexOf(char[] source, int sourceOffset, int sourceCount, - char[] target, int targetOffset, int targetCount, - int fromIndex) { + static int lastIndexOf(byte[] src, byte srcCoder, int srcCount, + String tgtStr, int fromIndex) { + byte[] tgt = tgtStr.value; + byte tgtCoder = tgtStr.coder(); + int tgtCount = tgtStr.length(); /* * Check arguments; return immediately where possible. For * consistency, don't check for null str. */ - int rightIndex = sourceCount - targetCount; + int rightIndex = srcCount - tgtCount; if (fromIndex < 0) { return -1; } @@ -1900,34 +1814,41 @@ public final class String fromIndex = rightIndex; } /* Empty string always matches. */ - if (targetCount == 0) { + if (tgtCount == 0) { return fromIndex; } - - int strLastIndex = targetOffset + targetCount - 1; - char strLastChar = target[strLastIndex]; - int min = sourceOffset + targetCount - 1; + if (srcCoder == tgtCoder) { + return srcCoder == LATIN1 + ? StringLatin1.lastIndexOf(src, srcCount, tgt, tgtCount, fromIndex) + : StringUTF16.lastIndexOf(src, srcCount, tgt, tgtCount, fromIndex); + } + if (srcCoder == LATIN1) { // && tgtCoder == UTF16 + return -1; + } + // srcCoder == UTF16 && tgtCoder == LATIN1 + int min = tgtCount - 1; int i = min + fromIndex; + int strLastIndex = tgtCount - 1; + char strLastChar = (char)(tgt[strLastIndex] & 0xff); startSearchForLastChar: while (true) { - while (i >= min && source[i] != strLastChar) { + while (i >= min && StringUTF16.getChar(src, i) != strLastChar) { i--; } if (i < min) { return -1; } int j = i - 1; - int start = j - (targetCount - 1); + int start = j - strLastIndex; int k = strLastIndex - 1; - while (j > start) { - if (source[j--] != target[k--]) { + if (StringUTF16.getChar(src, j--) != (tgt[k--] & 0xff)) { i--; continue startSearchForLastChar; } } - return start - sourceOffset + 1; + return start + 1; } } @@ -1949,17 +1870,18 @@ public final class String * length of this {@code String} object. */ public String substring(int beginIndex) { - if (beginIndex <= 0) { - if (beginIndex < 0) { - throw new StringIndexOutOfBoundsException(beginIndex); - } - return this; + if (beginIndex < 0) { + throw new StringIndexOutOfBoundsException(beginIndex); } - int subLen = value.length - beginIndex; + int subLen = length() - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } - return new String(value, beginIndex, subLen); + if (beginIndex == 0) { + return this; + } + return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen) + : StringUTF16.newString(value, beginIndex, subLen); } /** @@ -1985,22 +1907,14 @@ public final class String * {@code endIndex}. */ public String substring(int beginIndex, int endIndex) { - if (beginIndex <= 0) { - if (beginIndex < 0) { - throw new StringIndexOutOfBoundsException(beginIndex); - } - if (endIndex == value.length) { - return this; - } - } - if (endIndex > value.length) { - throw new StringIndexOutOfBoundsException(endIndex); - } + int length = length(); + checkBoundsBeginEnd(beginIndex, endIndex, length); int subLen = endIndex - beginIndex; - if (subLen < 0) { - throw new StringIndexOutOfBoundsException(subLen); + if (beginIndex == 0 && endIndex == length) { + return this; } - return new String(value, beginIndex, subLen); + return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen) + : StringUTF16.newString(value, beginIndex, subLen); } /** @@ -2057,14 +1971,23 @@ public final class String * characters followed by the string argument's characters. */ public String concat(String str) { - int otherLen = str.length(); - if (otherLen == 0) { + int olen = str.length(); + if (olen == 0) { return this; } - int len = value.length; - char[] buf = Arrays.copyOf(value, len + otherLen); - str.getChars(buf, len); - return new String(buf, true); + if (coder() == str.coder()) { + byte[] val = this.value; + byte[] oval = str.value; + int len = val.length + oval.length; + byte[] buf = Arrays.copyOf(val, len); + System.arraycopy(oval, 0, buf, val.length, oval.length); + return new String(buf, coder); + } + int len = length(); + byte[] buf = StringUTF16.newBytesFor(len + olen); + getBytes(buf, 0, UTF16); + str.getBytes(buf, len, UTF16); + return new String(buf, UTF16); } /** @@ -2098,26 +2021,10 @@ public final class String */ public String replace(char oldChar, char newChar) { if (oldChar != newChar) { - char[] val = value; /* avoid getfield opcode */ - int len = val.length; - int i = -1; - - while (++i < len) { - if (val[i] == oldChar) { - break; - } - } - if (i < len) { - char[] buf = new char[len]; - for (int j = 0; j < i; j++) { - buf[j] = val[j]; - } - while (i < len) { - char c = val[i]; - buf[i] = (c == oldChar) ? newChar : c; - i++; - } - return new String(buf, true); + String ret = isLatin1() ? StringLatin1.replace(value, oldChar, newChar) + : StringUTF16.replace(value, oldChar, newChar); + if (ret != null) { + return ret; } } return this; @@ -2269,29 +2176,27 @@ public final class String * @since 1.5 */ public String replace(CharSequence target, CharSequence replacement) { - String starget = target.toString(); - String srepl = replacement.toString(); - int j = indexOf(starget); + String tgtStr = target.toString(); + String replStr = replacement.toString(); + int j = indexOf(tgtStr); if (j < 0) { return this; } - int targLen = starget.length(); - int targLen1 = Math.max(targLen, 1); - final char[] value = this.value; - final char[] replValue = srepl.value; - int newLenHint = value.length - targLen + replValue.length; + int tgtLen = tgtStr.length(); + int tgtLen1 = Math.max(tgtLen, 1); + int thisLen = length(); + + int newLenHint = thisLen - tgtLen + replStr.length(); if (newLenHint < 0) { throw new OutOfMemoryError(); } StringBuilder sb = new StringBuilder(newLenHint); int i = 0; do { - sb.append(value, i, j - i) - .append(replValue); - i = j + targLen; - } while (j < value.length && (j = indexOf(starget, j + targLen1)) > 0); - - return sb.append(value, i, value.length - i).toString(); + sb.append(this, i, j).append(replStr); + i = j + tgtLen; + } while (j < thisLen && (j = indexOf(tgtStr, j + tgtLen1)) > 0); + return sb.append(this, i, thisLen).toString(); } /** @@ -2388,7 +2293,7 @@ public final class String the second is not the ascii digit or ascii letter. */ char ch = 0; - if (((regex.value.length == 1 && + if (((regex.length() == 1 && ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) || (regex.length() == 2 && regex.charAt(0) == '\\' && @@ -2408,8 +2313,9 @@ public final class String off = next + 1; } else { // last one //assert (list.size() == limit - 1); - list.add(substring(off, value.length)); - off = value.length; + int last = length(); + list.add(substring(off, last)); + off = last; break; } } @@ -2419,7 +2325,7 @@ public final class String // Add remaining segment if (!limited || list.size() < limit) - list.add(substring(off, value.length)); + list.add(substring(off, length())); // Construct result int resultSize = list.size(); @@ -2613,95 +2519,8 @@ public final class String * @since 1.1 */ public String toLowerCase(Locale locale) { - if (locale == null) { - throw new NullPointerException(); - } - int first; - boolean hasSurr = false; - final int len = value.length; - - // Now check if there are any characters that need to be changed, or are surrogate - for (first = 0 ; first < len; first++) { - int cp = (int)value[first]; - if (Character.isSurrogate((char)cp)) { - hasSurr = true; - break; - } - if (cp != Character.toLowerCase(cp)) { // no need to check Character.ERROR - break; - } - } - if (first == len) - return this; - char[] result = new char[len]; - System.arraycopy(value, 0, result, 0, first); // Just copy the first few - // lowerCase characters. - String lang = locale.getLanguage(); - if (lang == "tr" || lang == "az" || lang == "lt") { - return toLowerCaseEx(result, first, locale, true); - } - if (hasSurr) { - return toLowerCaseEx(result, first, locale, false); - } - for (int i = first; i < len; i++) { - int cp = (int)value[i]; - if (cp == '\u03A3' || // GREEK CAPITAL LETTER SIGMA - Character.isSurrogate((char)cp)) { - return toLowerCaseEx(result, i, locale, false); - } - if (cp == '\u0130') { // LATIN CAPITAL LETTER I WITH DOT ABOVE - return toLowerCaseEx(result, i, locale, true); - } - cp = Character.toLowerCase(cp); - if (!Character.isBmpCodePoint(cp)) { - return toLowerCaseEx(result, i, locale, false); - } - result[i] = (char)cp; - } - return new String(result, true); - } - - private String toLowerCaseEx(char[] result, int first, Locale locale, boolean localeDependent) { - int resultOffset = first; - int srcCount; - for (int i = first; i < value.length; i += srcCount) { - int srcChar = (int)value[i]; - int lowerChar; - char[] lowerCharArray; - srcCount = 1; - if (Character.isSurrogate((char)srcChar)) { - srcChar = codePointAt(i); - srcCount = Character.charCount(srcChar); - } - if (localeDependent || srcChar == '\u03A3') { // GREEK CAPITAL LETTER SIGMA - lowerChar = ConditionalSpecialCasing.toLowerCaseEx(this, i, locale); - } else { - lowerChar = Character.toLowerCase(srcChar); - } - if (Character.isBmpCodePoint(lowerChar)) { // Character.ERROR is not a bmp - result[resultOffset++] = (char)lowerChar; - } else { - if (lowerChar == Character.ERROR) { - lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(this, i, locale); - } else if (srcCount == 2) { - resultOffset += Character.toChars(lowerChar, result, resultOffset); - continue; - } else { - lowerCharArray = Character.toChars(lowerChar); - } - /* Grow result if needed */ - int mapLen = lowerCharArray.length; - if (mapLen > srcCount) { - char[] result2 = new char[result.length + mapLen - srcCount]; - System.arraycopy(result, 0, result2, 0, resultOffset); - result = result2; - } - for (int x = 0; x < mapLen; ++x) { - result[resultOffset++] = lowerCharArray[x]; - } - } - } - return new String(result, 0, resultOffset); + return isLatin1() ? StringLatin1.toLowerCase(this, value, locale) + : StringUTF16.toLowerCase(this, value, locale); } /** @@ -2776,98 +2595,8 @@ public final class String * @since 1.1 */ public String toUpperCase(Locale locale) { - if (locale == null) { - throw new NullPointerException(); - } - int first; - boolean hasSurr = false; - final int len = value.length; - - // Now check if there are any characters that need to be changed, or are surrogate - for (first = 0 ; first < len; first++ ) { - int cp = (int)value[first]; - if (Character.isSurrogate((char)cp)) { - hasSurr = true; - break; - } - if (cp != Character.toUpperCaseEx(cp)) { // no need to check Character.ERROR - break; - } - } - if (first == len) { - return this; - } - char[] result = new char[len]; - System.arraycopy(value, 0, result, 0, first); // Just copy the first few - // upperCase characters. - String lang = locale.getLanguage(); - if (lang == "tr" || lang == "az" || lang == "lt") { - return toUpperCaseEx(result, first, locale, true); - } - if (hasSurr) { - return toUpperCaseEx(result, first, locale, false); - } - for (int i = first; i < len; i++) { - int cp = (int)value[i]; - if (Character.isSurrogate((char)cp)) { - return toUpperCaseEx(result, i, locale, false); - } - cp = Character.toUpperCaseEx(cp); - if (!Character.isBmpCodePoint(cp)) { // Character.ERROR is not bmp - return toUpperCaseEx(result, i, locale, false); - } - result[i] = (char)cp; - } - return new String(result, true); - } - - private String toUpperCaseEx(char[] result, int first, Locale locale, - boolean localeDependent) { - int resultOffset = first; - int srcCount; - for (int i = first; i < value.length; i += srcCount) { - int srcChar = (int)value[i]; - int upperChar; - char[] upperCharArray; - srcCount = 1; - if (Character.isSurrogate((char)srcChar)) { - srcChar = codePointAt(i); - srcCount = Character.charCount(srcChar); - } - if (localeDependent) { - upperChar = ConditionalSpecialCasing.toUpperCaseEx(this, i, locale); - } else { - upperChar = Character.toUpperCaseEx(srcChar); - } - if (Character.isBmpCodePoint(upperChar)) { - result[resultOffset++] = (char)upperChar; - } else { - if (upperChar == Character.ERROR) { - if (localeDependent) { - upperCharArray = - ConditionalSpecialCasing.toUpperCaseCharArray(this, i, locale); - } else { - upperCharArray = Character.toUpperCaseCharArray(srcChar); - } - } else if (srcCount == 2) { - resultOffset += Character.toChars(upperChar, result, resultOffset); - continue; - } else { - upperCharArray = Character.toChars(upperChar); - } - /* Grow result if needed */ - int mapLen = upperCharArray.length; - if (mapLen > srcCount) { - char[] result2 = new char[result.length + mapLen - srcCount]; - System.arraycopy(result, 0, result2, 0, resultOffset); - result = result2; - } - for (int x = 0; x < mapLen; ++x) { - result[resultOffset++] = upperCharArray[x]; - } - } - } - return new String(result, 0, resultOffset); + return isLatin1() ? StringLatin1.toUpperCase(this, value, locale) + : StringUTF16.toUpperCase(this, value, locale); } /** @@ -2925,17 +2654,9 @@ public final class String * trailing white space. */ public String trim() { - char[] val = value; /* avoid getfield opcode */ - int end = val.length; - int beg = 0; - - while ((beg < end) && (val[beg] <= ' ')) { - beg++; - } - while ((beg < end) && (val[end - 1] <= ' ')) { - end--; - } - return substring(beg, end); + String ret = isLatin1() ? StringLatin1.trim(value) + : StringUTF16.trim(value); + return ret == null ? this : ret; } /** @@ -2947,63 +2668,6 @@ public final class String return this; } - static class IntCharArraySpliterator implements Spliterator.OfInt { - private final char[] array; - private int index; // current index, modified on advance/split - private final int fence; // one past last index - private final int cs; - - IntCharArraySpliterator(char[] array, int acs) { - this(array, 0, array.length, acs); - } - - IntCharArraySpliterator(char[] array, int origin, int fence, int acs) { - this.array = array; - this.index = origin; - this.fence = fence; - this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED - | Spliterator.SUBSIZED; - } - - @Override - public OfInt trySplit() { - int lo = index, mid = (lo + fence) >>> 1; - return (lo >= mid) - ? null - : new IntCharArraySpliterator(array, lo, index = mid, cs); - } - - @Override - public void forEachRemaining(IntConsumer action) { - char[] a; int i, hi; // hoist accesses and checks from loop - if (action == null) - throw new NullPointerException(); - if ((a = array).length >= (hi = fence) && - (i = index) >= 0 && i < (index = hi)) { - do { action.accept(a[i]); } while (++i < hi); - } - } - - @Override - public boolean tryAdvance(IntConsumer action) { - if (action == null) - throw new NullPointerException(); - if (index >= 0 && index < fence) { - action.accept(array[index++]); - return true; - } - return false; - } - - @Override - public long estimateSize() { return (long)(fence - index); } - - @Override - public int characteristics() { - return cs; - } - } - /** * Returns a stream of {@code int} zero-extending the {@code char} values * from this sequence. Any char which maps to a >> 1; - if (lo >= mid) - return null; - - int midOneLess; - // If the mid-point intersects a surrogate pair - if (Character.isLowSurrogate(array[mid]) && - Character.isHighSurrogate(array[midOneLess = (mid -1)])) { - // If there is only one pair it cannot be split - if (lo >= midOneLess) - return null; - // Shift the mid-point to align with the surrogate pair - return new CodePointsSpliterator(array, lo, index = midOneLess, cs); - } - return new CodePointsSpliterator(array, lo, index = mid, cs); - } - - @Override - public void forEachRemaining(IntConsumer action) { - char[] a; int i, hi; // hoist accesses and checks from loop - if (action == null) - throw new NullPointerException(); - if ((a = array).length >= (hi = fence) && - (i = index) >= 0 && i < (index = hi)) { - do { - i = advance(a, i, hi, action); - } while (i < hi); - } - } - - @Override - public boolean tryAdvance(IntConsumer action) { - if (action == null) - throw new NullPointerException(); - if (index >= 0 && index < fence) { - index = advance(array, index, fence, action); - return true; - } - return false; - } - - // Advance one code point from the index, i, and return the next - // index to advance from - private static int advance(char[] a, int i, int hi, IntConsumer action) { - char c1 = a[i++]; - int cp = c1; - if (Character.isHighSurrogate(c1) && i < hi) { - char c2 = a[i]; - if (Character.isLowSurrogate(c2)) { - i++; - cp = Character.toCodePoint(c1, c2); - } - } - action.accept(cp); - return i; - } - - @Override - public long estimateSize() { return (long)(fence - index); } - - @Override - public int characteristics() { - return cs; - } - } /** * Returns a stream of code point values from this sequence. Any surrogate @@ -3118,7 +2700,9 @@ public final class String @Override public IntStream codePoints() { return StreamSupport.intStream( - new CodePointsSpliterator(value, Spliterator.IMMUTABLE), false); + isLatin1() ? new StringLatin1.CharsSpliterator(value, Spliterator.IMMUTABLE) + : new StringUTF16.CodePointsSpliterator(value, Spliterator.IMMUTABLE), + false); } /** @@ -3129,10 +2713,8 @@ public final class String * the character sequence represented by this string. */ public char[] toCharArray() { - // Cannot use Arrays.copyOf because of class initialization order issues - char[] result = new char[value.length]; - System.arraycopy(value, 0, result, 0, value.length); - return result; + return isLatin1() ? StringLatin1.toChars(value) + : StringUTF16.toChars(value); } /** @@ -3315,7 +2897,10 @@ public final class String * as its single character the argument {@code c}. */ public static String valueOf(char c) { - return new String(new char[]{c}, true); + if (COMPACT_STRINGS && StringLatin1.canEncode(c)) { + return new String(StringLatin1.toBytes(c), LATIN1); + } + return new String(StringUTF16.toBytes(c), UTF16); } /** @@ -3398,4 +2983,145 @@ public final class String * guaranteed to be from a pool of unique strings. */ public native String intern(); + + //////////////////////////////////////////////////////////////// + + /** + * Copy character bytes from this string into dst starting at dstBegin. + * This method doesn't perform any range checking. + * + * Invoker guarantees: dst is in UTF16 (inflate itself for asb), if two + * coders are different, and dst is big enough (range check) + * + * @param dstBegin the char index, not offset of byte[] + * @param coder the coder of dst[] + */ + void getBytes(byte dst[], int dstBegin, byte coder) { + if (coder() == coder) { + System.arraycopy(value, 0, dst, dstBegin << coder, value.length); + } else { // this.coder == LATIN && coder == UTF16 + StringLatin1.inflate(value, 0, dst, dstBegin, value.length); + } + } + + /* + * Package private constructor. Trailing Void argument is there for + * disambiguating it against other (public) constructors. + * + * Stores the char[] value into a byte[] that each byte represents + * the8 low-order bits of the corresponding character, if the char[] + * contains only latin1 character. Or a byte[] that stores all + * characters in their byte sequences defined by the {@code StringUTF16}. + */ + String(char[] value, int off, int len, Void sig) { + if (len == 0) { + this.value = "".value; + this.coder = "".coder; + return; + } + if (COMPACT_STRINGS) { + byte[] val = StringUTF16.compress(value, off, len); + if (val != null) { + this.value = val; + this.coder = LATIN1; + return; + } + } + this.coder = UTF16; + this.value = StringUTF16.toBytes(value, off, len); + } + + /* + * Package private constructor. Trailing Void argument is there for + * disambiguating it against other (public) constructors. + */ + String(AbstractStringBuilder asb, Void sig) { + byte[] val = asb.getValue(); + int length = asb.length(); + if (asb.isLatin1()) { + this.coder = LATIN1; + this.value = Arrays.copyOfRange(val, 0, length); + } else { + if (COMPACT_STRINGS) { + byte[] buf = StringUTF16.compress(val, 0, length); + if (buf != null) { + this.coder = LATIN1; + this.value = buf; + return; + } + } + this.coder = UTF16; + this.value = Arrays.copyOfRange(val, 0, length << 1); + } + } + + /* + * Package private constructor which shares value array for speed. + */ + String(byte[] value, byte coder) { + this.value = value; + this.coder = coder; + } + + byte coder() { + return COMPACT_STRINGS ? coder : UTF16; + } + + private boolean isLatin1() { + return COMPACT_STRINGS && coder == LATIN1; + } + + static final byte LATIN1 = 0; + static final byte UTF16 = 1; + + /* + * StringIndexOutOfBoundsException if {@code index} is + * negative or greater than or equal to {@code length}. + */ + static void checkIndex(int index, int length) { + if (index < 0 || index >= length) { + throw new StringIndexOutOfBoundsException("index " + index); + } + } + + /* + * StringIndexOutOfBoundsException if {@code offset} + * is negative or greater than {@code length}. + */ + static void checkOffset(int offset, int length) { + if (offset < 0 || offset > length) { + throw new StringIndexOutOfBoundsException("offset " + offset + + ",length " + length); + } + } + + /* + * Check {@code offset}, {@code count} against {@code 0} and {@code length} + * bounds. + * + * @throws StringIndexOutOfBoundsException + * If {@code offset} is negative, {@code count} is negative, + * or {@code offset} is greater than {@code length - count} + */ + private static void checkBoundsOffCount(int offset, int count, int length) { + if (offset < 0 || count < 0 || offset > length - count) { + throw new StringIndexOutOfBoundsException( + "offset " + offset + ", count " + count + ", length " + length); + } + } + + /* + * Check {@code begin}, {@code end} against {@code 0} and {@code length} + * bounds. + * + * @throws StringIndexOutOfBoundsException + * If {@code begin} is negative, {@code begin} is greater than + * {@code end}, or {@code end} is greater than {@code length}. + */ + private static void checkBoundsBeginEnd(int begin, int end, int length) { + if (begin < 0 || begin > end || end > length) { + throw new StringIndexOutOfBoundsException( + "begin " + begin + ", end " + end + ", length " + length); + } + } } diff --git a/jdk/src/java.base/share/classes/java/lang/StringBuffer.java b/jdk/src/java.base/share/classes/java/lang/StringBuffer.java index a20645463fa..65bbe14e34e 100644 --- a/jdk/src/java.base/share/classes/java/lang/StringBuffer.java +++ b/jdk/src/java.base/share/classes/java/lang/StringBuffer.java @@ -104,7 +104,7 @@ import jdk.internal.HotSpotIntrinsicCandidate; * A cache of the last value returned by toString. Cleared * whenever the StringBuffer is modified. */ - private transient char[] toStringCache; + private transient String toStringCache; /** use serialVersionUID from JDK 1.0.2 for interoperability */ static final long serialVersionUID = 3388685877147921107L; @@ -169,15 +169,13 @@ import jdk.internal.HotSpotIntrinsicCandidate; @Override public synchronized int capacity() { - return value.length; + return super.capacity(); } @Override public synchronized void ensureCapacity(int minimumCapacity) { - if (minimumCapacity > value.length) { - expandCapacity(minimumCapacity); - } + super.ensureCapacity(minimumCapacity); } /** @@ -204,9 +202,7 @@ import jdk.internal.HotSpotIntrinsicCandidate; */ @Override public synchronized char charAt(int index) { - if ((index < 0) || (index >= count)) - throw new StringIndexOutOfBoundsException(index); - return value[index]; + return super.charAt(index); } /** @@ -261,10 +257,8 @@ import jdk.internal.HotSpotIntrinsicCandidate; */ @Override public synchronized void setCharAt(int index, char ch) { - if ((index < 0) || (index >= count)) - throw new StringIndexOutOfBoundsException(index); toStringCache = null; - value[index] = ch; + super.setCharAt(index, ch); } @Override @@ -680,9 +674,11 @@ import jdk.internal.HotSpotIntrinsicCandidate; @HotSpotIntrinsicCandidate public synchronized String toString() { if (toStringCache == null) { - toStringCache = Arrays.copyOfRange(value, 0, count); + return toStringCache = + isLatin1() ? StringLatin1.newString(value, 0, count) + : StringUTF16.newString(value, 0, count); } - return new String(toStringCache, true); + return new String(toStringCache); } /** @@ -710,7 +706,13 @@ import jdk.internal.HotSpotIntrinsicCandidate; private synchronized void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { java.io.ObjectOutputStream.PutField fields = s.putFields(); - fields.put("value", value); + char[] val = new char[capacity()]; + if (isLatin1()) { + StringLatin1.getChars(value, 0, count, val, 0); + } else { + StringUTF16.getChars(value, 0, count, val, 0); + } + fields.put("value", val); fields.put("count", count); fields.put("shared", false); s.writeFields(); @@ -723,7 +725,12 @@ import jdk.internal.HotSpotIntrinsicCandidate; private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { java.io.ObjectInputStream.GetField fields = s.readFields(); - value = (char[])fields.get("value", null); + char[] val = (char[])fields.get("value", null); + initBytes(val, 0, val.length); count = fields.get("count", 0); } + + protected synchronized void getBytes(byte dst[], int dstBegin, byte coder) { + super.getBytes(dst, dstBegin, coder); + } } diff --git a/jdk/src/java.base/share/classes/java/lang/StringBuilder.java b/jdk/src/java.base/share/classes/java/lang/StringBuilder.java index 9d4ccf3dbbe..7d1e46a423f 100644 --- a/jdk/src/java.base/share/classes/java/lang/StringBuilder.java +++ b/jdk/src/java.base/share/classes/java/lang/StringBuilder.java @@ -412,7 +412,8 @@ public final class StringBuilder @HotSpotIntrinsicCandidate public String toString() { // Create a copy, don't share the array - return new String(value, 0, count); + return isLatin1() ? StringLatin1.newString(value, 0, count) + : StringUTF16.newStringSB(value, 0, count); } /** @@ -430,7 +431,13 @@ public final class StringBuilder throws java.io.IOException { s.defaultWriteObject(); s.writeInt(count); - s.writeObject(value); + char[] val = new char[capacity()]; + if (isLatin1()) { + StringLatin1.getChars(value, 0, count, val, 0); + } else { + StringUTF16.getChars(value, 0, count, val, 0); + } + s.writeObject(val); } /** @@ -441,7 +448,8 @@ public final class StringBuilder throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); count = s.readInt(); - value = (char[]) s.readObject(); + char[] val = (char[]) s.readObject(); + initBytes(val, 0, val.length); } } diff --git a/jdk/src/java.base/share/classes/java/lang/StringCoding.java b/jdk/src/java.base/share/classes/java/lang/StringCoding.java index d770156da2c..b1e25d5128f 100644 --- a/jdk/src/java.base/share/classes/java/lang/StringCoding.java +++ b/jdk/src/java.base/share/classes/java/lang/StringCoding.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2015, 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 @@ -38,11 +38,19 @@ import java.nio.charset.CodingErrorAction; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; import java.util.Arrays; +import jdk.internal.HotSpotIntrinsicCandidate; import sun.misc.MessageUtils; import sun.nio.cs.HistoricallyNamedCharset; import sun.nio.cs.ArrayDecoder; import sun.nio.cs.ArrayEncoder; +import static java.lang.String.LATIN1; +import static java.lang.String.UTF16; +import static java.lang.String.COMPACT_STRINGS; +import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static java.nio.charset.StandardCharsets.UTF_8; + /** * Utility class for string encoding and decoding. */ @@ -72,23 +80,13 @@ class StringCoding { // Trim the given byte array to the given length // - private static byte[] safeTrim(byte[] ba, int len, Charset cs, boolean isTrusted) { + private static byte[] safeTrim(byte[] ba, int len, boolean isTrusted) { if (len == ba.length && (isTrusted || System.getSecurityManager() == null)) return ba; else return Arrays.copyOf(ba, len); } - // Trim the given char array to the given length - // - private static char[] safeTrim(char[] ca, int len, - Charset cs, boolean isTrusted) { - if (len == ca.length && (isTrusted || System.getSecurityManager() == null)) - return ca; - else - return Arrays.copyOf(ca, len); - } - private static int scale(int len, float expansionFactor) { // We need to perform double, not float, arithmetic; otherwise // we lose low order bits when len is larger than 2**24. @@ -117,21 +115,64 @@ class StringCoding { } } + static class Result { + byte[] value; + byte coder; + + Result with() { + coder = COMPACT_STRINGS ? LATIN1 : UTF16; + value = new byte[0]; + return this; + } + + Result with(char[] val, int off, int len) { + if (String.COMPACT_STRINGS) { + byte[] bs = StringUTF16.compress(val, off, len); + if (bs != null) { + value = bs; + coder = LATIN1; + return this; + } + } + coder = UTF16; + value = StringUTF16.toBytes(val, off, len); + return this; + } + + Result with(byte[] val, byte coder) { + this.coder = coder; + value = val; + return this; + } + } + + @HotSpotIntrinsicCandidate + private static boolean hasNegatives(byte[] ba, int off, int len) { + for (int i = off; i < off + len; i++) { + if (ba[i] < 0) { + return true; + } + } + return false; + } // -- Decoding -- - private static class StringDecoder { + static class StringDecoder { private final String requestedCharsetName; private final Charset cs; + private final boolean isASCIICompatible; private final CharsetDecoder cd; - private final boolean isTrusted; + protected final Result result; - private StringDecoder(Charset cs, String rcn) { + StringDecoder(Charset cs, String rcn) { this.requestedCharsetName = rcn; this.cs = cs; this.cd = cs.newDecoder() .onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE); - this.isTrusted = (cs.getClass().getClassLoader0() == null); + this.result = new Result(); + this.isASCIICompatible = (cd instanceof ArrayDecoder) && + ((ArrayDecoder)cd).isASCIICompatible(); } String charsetName() { @@ -144,36 +185,58 @@ class StringCoding { return requestedCharsetName; } - char[] decode(byte[] ba, int off, int len) { + Result decode(byte[] ba, int off, int len) { + if (len == 0) { + return result.with(); + } + // fastpath for ascii compatible + if (isASCIICompatible && !hasNegatives(ba, off, len)) { + if (COMPACT_STRINGS) { + return result.with(Arrays.copyOfRange(ba, off, off + len), + LATIN1); + } else { + return result.with(StringLatin1.inflate(ba, off, len), UTF16); + } + } int en = scale(len, cd.maxCharsPerByte()); char[] ca = new char[en]; - if (len == 0) - return ca; if (cd instanceof ArrayDecoder) { int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca); - return safeTrim(ca, clen, cs, isTrusted); + return result.with(ca, 0, clen); + } + cd.reset(); + ByteBuffer bb = ByteBuffer.wrap(ba, off, len); + CharBuffer cb = CharBuffer.wrap(ca); + try { + CoderResult cr = cd.decode(bb, cb, true); + if (!cr.isUnderflow()) + cr.throwException(); + cr = cd.flush(cb); + if (!cr.isUnderflow()) + cr.throwException(); + } catch (CharacterCodingException x) { + // Substitution is always enabled, + // so this shouldn't happen + throw new Error(x); + } + return result.with(ca, 0, cb.position()); + } + } + + private static class StringDecoder8859_1 extends StringDecoder { + StringDecoder8859_1(Charset cs, String rcn) { + super(cs, rcn); + } + Result decode(byte[] ba, int off, int len) { + if (COMPACT_STRINGS) { + return result.with(Arrays.copyOfRange(ba, off, off + len), LATIN1); } else { - cd.reset(); - ByteBuffer bb = ByteBuffer.wrap(ba, off, len); - CharBuffer cb = CharBuffer.wrap(ca); - try { - CoderResult cr = cd.decode(bb, cb, true); - if (!cr.isUnderflow()) - cr.throwException(); - cr = cd.flush(cb); - if (!cr.isUnderflow()) - cr.throwException(); - } catch (CharacterCodingException x) { - // Substitution is always enabled, - // so this shouldn't happen - throw new Error(x); - } - return safeTrim(ca, cb.position(), cs, isTrusted); + return result.with(StringLatin1.inflate(ba, off, len), UTF16); } } } - static char[] decode(String charsetName, byte[] ba, int off, int len) + static Result decode(String charsetName, byte[] ba, int off, int len) throws UnsupportedEncodingException { StringDecoder sd = deref(decoder); @@ -183,8 +246,15 @@ class StringCoding { sd = null; try { Charset cs = lookupCharset(csn); - if (cs != null) - sd = new StringDecoder(cs, csn); + if (cs != null) { + if (cs == UTF_8) { + sd = new StringDecoderUTF8(cs, csn); + } else if (cs == ISO_8859_1) { + sd = new StringDecoder8859_1(cs, csn); + } else { + sd = new StringDecoder(cs, csn); + } + } } catch (IllegalCharsetNameException x) {} if (sd == null) throw new UnsupportedEncodingException(csn); @@ -193,7 +263,7 @@ class StringCoding { return sd.decode(ba, off, len); } - static char[] decode(Charset cs, byte[] ba, int off, int len) { + static Result decode(Charset cs, byte[] ba, int off, int len) { // (1)We never cache the "external" cs, the only benefit of creating // an additional StringDe/Encoder object to wrap it is to share the // de/encode() method. These SD/E objects are short-lived, the young-gen @@ -210,44 +280,57 @@ class StringCoding { // check (... && (isTrusted || SM == null || getClassLoader0())) in trim // but it then can be argued that the SM is null when the operation // is started... + if (cs == UTF_8) { + return StringDecoderUTF8.decode(ba, off, len, new Result()); + } CharsetDecoder cd = cs.newDecoder(); + // ascii fastpath + if (cs == ISO_8859_1 || ((cd instanceof ArrayDecoder) && + ((ArrayDecoder)cd).isASCIICompatible() && + !hasNegatives(ba, off, len))) { + if (COMPACT_STRINGS) { + return new Result().with(Arrays.copyOfRange(ba, off, off + len), + LATIN1); + } else { + return new Result().with(StringLatin1.inflate(ba, off, len), UTF16); + } + } int en = scale(len, cd.maxCharsPerByte()); - char[] ca = new char[en]; - if (len == 0) - return ca; - boolean isTrusted = false; - if (System.getSecurityManager() != null) { - if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) { - ba = Arrays.copyOfRange(ba, off, off + len); - off = 0; - } + if (len == 0) { + return new Result().with(); + } + if (System.getSecurityManager() != null && + cs.getClass().getClassLoader0() != null) { + ba = Arrays.copyOfRange(ba, off, off + len); + off = 0; } cd.onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE) .reset(); + + char[] ca = new char[en]; if (cd instanceof ArrayDecoder) { int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca); - return safeTrim(ca, clen, cs, isTrusted); - } else { - ByteBuffer bb = ByteBuffer.wrap(ba, off, len); - CharBuffer cb = CharBuffer.wrap(ca); - try { - CoderResult cr = cd.decode(bb, cb, true); - if (!cr.isUnderflow()) - cr.throwException(); - cr = cd.flush(cb); - if (!cr.isUnderflow()) - cr.throwException(); - } catch (CharacterCodingException x) { - // Substitution is always enabled, - // so this shouldn't happen - throw new Error(x); - } - return safeTrim(ca, cb.position(), cs, isTrusted); + return new Result().with(ca, 0, clen); } + ByteBuffer bb = ByteBuffer.wrap(ba, off, len); + CharBuffer cb = CharBuffer.wrap(ca); + try { + CoderResult cr = cd.decode(bb, cb, true); + if (!cr.isUnderflow()) + cr.throwException(); + cr = cd.flush(cb); + if (!cr.isUnderflow()) + cr.throwException(); + } catch (CharacterCodingException x) { + // Substitution is always enabled, + // so this shouldn't happen + throw new Error(x); + } + return new Result().with(ca, 0, cb.position()); } - static char[] decode(byte[] ba, int off, int len) { + static Result decode(byte[] ba, int off, int len) { String csn = Charset.defaultCharset().name(); try { // use charset name decode() variant which provides caching. @@ -273,6 +356,7 @@ class StringCoding { private static class StringEncoder { private Charset cs; private CharsetEncoder ce; + private final boolean isASCIICompatible; private final String requestedCharsetName; private final boolean isTrusted; @@ -283,6 +367,8 @@ class StringCoding { .onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE); this.isTrusted = (cs.getClass().getClassLoader0() == null); + this.isASCIICompatible = (ce instanceof ArrayEncoder) && + ((ArrayEncoder)ce).isASCIICompatible(); } String charsetName() { @@ -295,36 +381,186 @@ class StringCoding { return requestedCharsetName; } - byte[] encode(char[] ca, int off, int len) { + byte[] encode(byte coder, byte[] val) { + // fastpath for ascii compatible + if (coder == LATIN1 && isASCIICompatible && + !hasNegatives(val, 0, val.length)) { + return Arrays.copyOf(val, val.length); + } + int len = val.length >> coder; // assume LATIN1=0/UTF16=1; int en = scale(len, ce.maxBytesPerChar()); byte[] ba = new byte[en]; - if (len == 0) + if (len == 0) { return ba; - if (ce instanceof ArrayEncoder) { - int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba); - return safeTrim(ba, blen, cs, isTrusted); - } else { - ce.reset(); - ByteBuffer bb = ByteBuffer.wrap(ba); - CharBuffer cb = CharBuffer.wrap(ca, off, len); - try { - CoderResult cr = ce.encode(cb, bb, true); - if (!cr.isUnderflow()) - cr.throwException(); - cr = ce.flush(bb); - if (!cr.isUnderflow()) - cr.throwException(); - } catch (CharacterCodingException x) { - // Substitution is always enabled, - // so this shouldn't happen - throw new Error(x); - } - return safeTrim(ba, bb.position(), cs, isTrusted); } + if (ce instanceof ArrayEncoder) { + if (!isTrusted) { + val = Arrays.copyOf(val, val.length); + } + int blen = (coder == LATIN1 ) ? ((ArrayEncoder)ce).encodeFromLatin1(val, 0, len, ba) + : ((ArrayEncoder)ce).encodeFromUTF16(val, 0, len, ba); + if (blen != -1) { + return safeTrim(ba, blen, isTrusted); + } + } + char[] ca = (coder == LATIN1 ) ? StringLatin1.toChars(val) + : StringUTF16.toChars(val); + ce.reset(); + ByteBuffer bb = ByteBuffer.wrap(ba); + CharBuffer cb = CharBuffer.wrap(ca, 0, len); + try { + CoderResult cr = ce.encode(cb, bb, true); + if (!cr.isUnderflow()) + cr.throwException(); + cr = ce.flush(bb); + if (!cr.isUnderflow()) + cr.throwException(); + } catch (CharacterCodingException x) { + // Substitution is always enabled, + // so this shouldn't happen + throw new Error(x); + } + return safeTrim(ba, bb.position(), isTrusted); } } - static byte[] encode(String charsetName, char[] ca, int off, int len) + @HotSpotIntrinsicCandidate + private static int implEncodeISOArray(byte[] sa, int sp, + byte[] da, int dp, int len) { + int i = 0; + for (; i < len; i++) { + char c = StringUTF16.getChar(sa, sp++); + if (c > '\u00FF') + break; + da[dp++] = (byte)c; + } + return i; + } + + static byte[] encode8859_1(byte coder, byte[] val) { + if (coder == LATIN1) { + return Arrays.copyOf(val, val.length); + } + int len = val.length >> 1; + byte[] dst = new byte[len]; + int dp = 0; + int sp = 0; + int sl = len; + while (sp < sl) { + int ret = implEncodeISOArray(val, sp, dst, dp, len); + sp = sp + ret; + dp = dp + ret; + if (ret != len) { + char c = StringUTF16.getChar(val, sp++); + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(StringUTF16.getChar(val, sp))) { + sp++; + } + dst[dp++] = '?'; + len = sl - sp; + } + } + if (dp == dst.length) { + return dst; + } + return Arrays.copyOf(dst, dp); + } + + static byte[] encodeASCII(byte coder, byte[] val) { + if (coder == LATIN1) { + byte[] dst = new byte[val.length]; + for (int i = 0; i < val.length; i++) { + if (val[i] < 0) { + dst[i] = '?'; + } else { + dst[i] = val[i]; + } + } + return dst; + } + int len = val.length >> 1; + byte[] dst = new byte[len]; + int dp = 0; + for (int i = 0; i < len; i++) { + char c = StringUTF16.getChar(val, i); + if (c < 0x80) { + dst[dp++] = (byte)c; + continue; + } + if (Character.isHighSurrogate(c) && i + 1 < len && + Character.isLowSurrogate(StringUTF16.getChar(val, i + 1))) { + i++; + } + dst[dp++] = '?'; + } + if (len == dp) { + return dst; + } + return Arrays.copyOf(dst, dp); + } + + static byte[] encodeUTF8(byte coder, byte[] val) { + int dp = 0; + byte[] dst; + if (coder == LATIN1) { + dst = new byte[val.length << 1]; + for (int sp = 0; sp < val.length; sp++) { + byte c = val[sp]; + if (c < 0) { + dst[dp++] = (byte)(0xc0 | ((c & 0xff) >> 6)); + dst[dp++] = (byte)(0x80 | (c & 0x3f)); + } else { + dst[dp++] = c; + } + } + } else { + int sp = 0; + int sl = val.length >> 1; + dst = new byte[sl * 3]; + char c; + while (sp < sl && (c = StringUTF16.getChar(val, sp)) < '\u0080') { + // ascii fast loop; + dst[dp++] = (byte)c; + sp++; + } + while (sp < sl) { + c = StringUTF16.getChar(val, sp++); + if (c < 0x80) { + dst[dp++] = (byte)c; + } else if (c < 0x800) { + dst[dp++] = (byte)(0xc0 | (c >> 6)); + dst[dp++] = (byte)(0x80 | (c & 0x3f)); + } else if (Character.isSurrogate(c)) { + int uc = -1; + char c2; + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(c2 = StringUTF16.getChar(val, sp))) { + uc = Character.toCodePoint(c, c2); + } + if (uc < 0) { + dst[dp++] = '?'; + } else { + dst[dp++] = (byte)(0xf0 | ((uc >> 18))); + dst[dp++] = (byte)(0x80 | ((uc >> 12) & 0x3f)); + dst[dp++] = (byte)(0x80 | ((uc >> 6) & 0x3f)); + dst[dp++] = (byte)(0x80 | (uc & 0x3f)); + sp++; // 2 chars + } + } else { + // 3 bytes, 16 bits + dst[dp++] = (byte)(0xe0 | ((c >> 12))); + dst[dp++] = (byte)(0x80 | ((c >> 6) & 0x3f)); + dst[dp++] = (byte)(0x80 | (c & 0x3f)); + } + } + } + if (dp == dst.length) { + return dst; + } + return Arrays.copyOf(dst, dp); + } + + static byte[] encode(String charsetName, byte coder, byte[] val) throws UnsupportedEncodingException { StringEncoder se = deref(encoder); @@ -334,62 +570,88 @@ class StringCoding { se = null; try { Charset cs = lookupCharset(csn); - if (cs != null) + if (cs != null) { + if (cs == UTF_8) { + return encodeUTF8(coder, val); + } else if (cs == ISO_8859_1) { + return encode8859_1(coder, val); + } else if (cs == US_ASCII) { + return encodeASCII(coder, val); + } se = new StringEncoder(cs, csn); + } } catch (IllegalCharsetNameException x) {} - if (se == null) + if (se == null) { throw new UnsupportedEncodingException (csn); + } set(encoder, se); } - return se.encode(ca, off, len); + return se.encode(coder, val); } - static byte[] encode(Charset cs, char[] ca, int off, int len) { + static byte[] encode(Charset cs, byte coder, byte[] val) { + if (cs == UTF_8) { + return encodeUTF8(coder, val); + } else if (cs == ISO_8859_1) { + return encode8859_1(coder, val); + } else if (cs == US_ASCII) { + return encodeASCII(coder, val); + } CharsetEncoder ce = cs.newEncoder(); + // fastpath for ascii compatible + if (coder == LATIN1 && (((ce instanceof ArrayEncoder) && + ((ArrayEncoder)ce).isASCIICompatible() && + !hasNegatives(val, 0, val.length)))) { + return Arrays.copyOf(val, val.length); + } + int len = val.length >> coder; // assume LATIN1=0/UTF16=1; int en = scale(len, ce.maxBytesPerChar()); byte[] ba = new byte[en]; - if (len == 0) + if (len == 0) { return ba; - boolean isTrusted = false; - if (System.getSecurityManager() != null) { - if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) { - ca = Arrays.copyOfRange(ca, off, off + len); - off = 0; - } } + boolean isTrusted = System.getSecurityManager() == null || + cs.getClass().getClassLoader0() == null; ce.onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE) .reset(); if (ce instanceof ArrayEncoder) { - int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba); - return safeTrim(ba, blen, cs, isTrusted); - } else { - ByteBuffer bb = ByteBuffer.wrap(ba); - CharBuffer cb = CharBuffer.wrap(ca, off, len); - try { - CoderResult cr = ce.encode(cb, bb, true); - if (!cr.isUnderflow()) - cr.throwException(); - cr = ce.flush(bb); - if (!cr.isUnderflow()) - cr.throwException(); - } catch (CharacterCodingException x) { - throw new Error(x); + if (!isTrusted) { + val = Arrays.copyOf(val, val.length); + } + int blen = (coder == LATIN1 ) ? ((ArrayEncoder)ce).encodeFromLatin1(val, 0, len, ba) + : ((ArrayEncoder)ce).encodeFromUTF16(val, 0, len, ba); + if (blen != -1) { + return safeTrim(ba, blen, isTrusted); } - return safeTrim(ba, bb.position(), cs, isTrusted); } + char[] ca = (coder == LATIN1 ) ? StringLatin1.toChars(val) + : StringUTF16.toChars(val); + ByteBuffer bb = ByteBuffer.wrap(ba); + CharBuffer cb = CharBuffer.wrap(ca, 0, len); + try { + CoderResult cr = ce.encode(cb, bb, true); + if (!cr.isUnderflow()) + cr.throwException(); + cr = ce.flush(bb); + if (!cr.isUnderflow()) + cr.throwException(); + } catch (CharacterCodingException x) { + throw new Error(x); + } + return safeTrim(ba, bb.position(), isTrusted); } - static byte[] encode(char[] ca, int off, int len) { + static byte[] encode(byte coder, byte[] val) { String csn = Charset.defaultCharset().name(); try { // use charset name encode() variant which provides caching. - return encode(csn, ca, off, len); + return encode(csn, coder, val); } catch (UnsupportedEncodingException x) { warnUnsupportedCharset(csn); } try { - return encode("ISO-8859-1", ca, off, len); + return encode("ISO-8859-1", coder, val); } catch (UnsupportedEncodingException x) { // If this code is hit during VM initialization, MessageUtils is // the only way we will be able to get any kind of error message. diff --git a/jdk/src/java.base/share/classes/java/lang/StringDecoderUTF8.java b/jdk/src/java.base/share/classes/java/lang/StringDecoderUTF8.java new file mode 100644 index 00000000000..1b504df2574 --- /dev/null +++ b/jdk/src/java.base/share/classes/java/lang/StringDecoderUTF8.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2015, 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 java.lang; + +import java.nio.charset.Charset; +import java.util.Arrays; + +import static java.lang.String.LATIN1; +import static java.lang.String.UTF16; +import static java.lang.String.COMPACT_STRINGS; +import static java.lang.Character.isSurrogate; +import static java.lang.Character.highSurrogate; +import static java.lang.Character.lowSurrogate; +import static java.lang.Character.isSupplementaryCodePoint; +import static java.lang.StringUTF16.putChar; + +class StringDecoderUTF8 extends StringCoding.StringDecoder { + + StringDecoderUTF8(Charset cs, String rcn) { + super(cs, rcn); + } + + private static boolean isNotContinuation(int b) { + return (b & 0xc0) != 0x80; + } + + private static boolean isMalformed3(int b1, int b2, int b3) { + return (b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) || + (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80; + } + + private static boolean isMalformed3_2(int b1, int b2) { + return (b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) || + (b2 & 0xc0) != 0x80; + } + + private static boolean isMalformed4(int b2, int b3, int b4) { + return (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || + (b4 & 0xc0) != 0x80; + } + + private static boolean isMalformed4_2(int b1, int b2) { + return (b1 == 0xf0 && (b2 < 0x90 || b2 > 0xbf)) || + (b1 == 0xf4 && (b2 & 0xf0) != 0x80) || + (b2 & 0xc0) != 0x80; + } + + private static boolean isMalformed4_3(int b3) { + return (b3 & 0xc0) != 0x80; + } + + // for nb == 3/4 + private static int malformedN(byte[] src, int sp, int nb) { + if (nb == 3) { + int b1 = src[sp++]; + int b2 = src[sp++]; // no need to lookup b3 + return ((b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) || + isNotContinuation(b2)) ? 1 : 2; + } else if (nb == 4) { // we don't care the speed here + int b1 = src[sp++] & 0xff; + int b2 = src[sp++] & 0xff; + if (b1 > 0xf4 || + (b1 == 0xf0 && (b2 < 0x90 || b2 > 0xbf)) || + (b1 == 0xf4 && (b2 & 0xf0) != 0x80) || + isNotContinuation(b2)) + return 1; + if (isNotContinuation(src[sp++])) + return 2; + return 3; + } + assert false; + return -1; + } + + private static char repl = '\ufffd'; + + StringCoding.Result decode(byte[] src, int sp, int len) { + return decode(src, sp, len, result); + } + + static StringCoding.Result decode(byte[] src, int sp, int len, + StringCoding.Result ret) { + int sl = sp + len; + byte[] dst = new byte[len]; + int dp = 0; + if (COMPACT_STRINGS) { // Latin1 only loop + while (sp < sl) { + int b1 = src[sp]; + if (b1 >= 0) { + dst[dp++] = (byte)b1; + sp++; + continue; + } + if ((b1 == (byte)0xc2 || b1 == (byte)0xc3) && + sp + 1 < sl) { + int b2 = src[sp + 1]; + if (!isNotContinuation(b2)) { + dst[dp++] = (byte)(((b1 << 6) ^ b2)^ + (((byte) 0xC0 << 6) ^ + ((byte) 0x80 << 0))); + sp += 2; + continue; + } + } + // anything not a latin1, including the repl + // we have to go with the utf16 + break; + } + if (sp == sl) { + if (dp != dst.length) { + dst = Arrays.copyOf(dst, dp); + } + return ret.with(dst, LATIN1); + } + } + if (dp == 0) { + dst = new byte[len << 1]; + } else { + byte[] buf = new byte[len << 1]; + StringLatin1.inflate(dst, 0, buf, 0, dp); + dst = buf; + } + while (sp < sl) { + int b1 = src[sp++]; + if (b1 >= 0) { + putChar(dst, dp++, (char) b1); + } else if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) { + if (sp < sl) { + int b2 = src[sp++]; + if (isNotContinuation(b2)) { + putChar(dst, dp++, repl); + sp--; + } else { + putChar(dst, dp++, (char)(((b1 << 6) ^ b2)^ + (((byte) 0xC0 << 6) ^ + ((byte) 0x80 << 0)))); + } + continue; + } + putChar(dst, dp++, repl); + break; + } else if ((b1 >> 4) == -2) { + if (sp + 1 < sl) { + int b2 = src[sp++]; + int b3 = src[sp++]; + if (isMalformed3(b1, b2, b3)) { + putChar(dst, dp++, repl); + sp -= 3; + sp += malformedN(src, sp, 3); + } else { + char c = (char)((b1 << 12) ^ + (b2 << 6) ^ + (b3 ^ + (((byte) 0xE0 << 12) ^ + ((byte) 0x80 << 6) ^ + ((byte) 0x80 << 0)))); + putChar(dst, dp++, isSurrogate(c) ? repl : c); + } + continue; + } + if (sp < sl && isMalformed3_2(b1, src[sp])) { + putChar(dst, dp++, repl); + continue; + } + putChar(dst, dp++, repl); + break; + } else if ((b1 >> 3) == -2) { + if (sp + 2 < sl) { + int b2 = src[sp++]; + int b3 = src[sp++]; + int b4 = src[sp++]; + int uc = ((b1 << 18) ^ + (b2 << 12) ^ + (b3 << 6) ^ + (b4 ^ + (((byte) 0xF0 << 18) ^ + ((byte) 0x80 << 12) ^ + ((byte) 0x80 << 6) ^ + ((byte) 0x80 << 0)))); + if (isMalformed4(b2, b3, b4) || + !isSupplementaryCodePoint(uc)) { // shortest form check + putChar(dst, dp++, repl); + sp -= 4; + sp += malformedN(src, sp, 4); + } else { + putChar(dst, dp++, highSurrogate(uc)); + putChar(dst, dp++, lowSurrogate(uc)); + } + continue; + } + b1 &= 0xff; + if (b1 > 0xf4 || + sp < sl && isMalformed4_2(b1, src[sp] & 0xff)) { + putChar(dst, dp++, repl); + continue; + } + sp++; + putChar(dst, dp++, repl); + if (sp < sl && isMalformed4_3(src[sp])) { + continue; + } + break; + } else { + putChar(dst, dp++, repl); + } + } + if (dp != len) { + dst = Arrays.copyOf(dst, dp << 1); + } + return ret.with(dst, UTF16); + } +} diff --git a/jdk/src/java.base/share/classes/java/lang/StringLatin1.java b/jdk/src/java.base/share/classes/java/lang/StringLatin1.java new file mode 100644 index 00000000000..eb8ddc65121 --- /dev/null +++ b/jdk/src/java.base/share/classes/java/lang/StringLatin1.java @@ -0,0 +1,600 @@ +/* + * Copyright (c) 2015, 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 java.lang; + +import java.util.Arrays; +import java.util.Locale; +import java.util.Objects; +import java.util.Spliterator; +import java.util.function.IntConsumer; +import java.util.stream.IntStream; +import jdk.internal.HotSpotIntrinsicCandidate; + +import static java.lang.String.LATIN1; +import static java.lang.String.UTF16; +import static java.lang.String.checkOffset; + +final class StringLatin1 { + + public static char charAt(byte[] value, int index) { + if (index < 0 || index >= value.length) { + throw new StringIndexOutOfBoundsException(index); + } + return (char)(value[index] & 0xff); + } + + public static boolean canEncode(int cp) { + return cp >>> 8 == 0; + } + + public static int length(byte[] value) { + return value.length; + } + + public static int codePointAt(byte[] value, int index, int end) { + return value[index] & 0xff; + } + + public static int codePointBefore(byte[] value, int index) { + return value[index - 1] & 0xff; + } + + public static int codePointCount(byte[] value, int beginIndex, int endIndex) { + return endIndex - beginIndex; + } + + public static char[] toChars(byte[] value) { + char[] dst = new char[value.length]; + inflate(value, 0, dst, 0, value.length); + return dst; + } + + public static byte[] inflate(byte[] value, int off, int len) { + byte[] ret = StringUTF16.newBytesFor(len); + inflate(value, off, ret, 0, len); + return ret; + } + + public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) { + inflate(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); + } + + public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte dst[], int dstBegin) { + System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); + } + + @HotSpotIntrinsicCandidate + public static boolean equals(byte[] value, byte[] other) { + if (value.length == other.length) { + for (int i = 0; i < value.length; i++) { + if (value[i] != other[i]) { + return false; + } + } + return true; + } + return false; + } + + @HotSpotIntrinsicCandidate + public static int compareTo(byte[] value, byte[] other) { + int len1 = value.length; + int len2 = other.length; + int lim = Math.min(len1, len2); + for (int k = 0; k < lim; k++) { + if (value[k] != other[k]) { + return getChar(value, k) - getChar(other, k); + } + } + return len1 - len2; + } + + @HotSpotIntrinsicCandidate + public static int compareToUTF16(byte[] value, byte[] other) { + int len1 = length(value); + int len2 = StringUTF16.length(other); + int lim = Math.min(len1, len2); + for (int k = 0; k < lim; k++) { + char c1 = getChar(value, k); + char c2 = StringUTF16.getChar(other, k); + if (c1 != c2) { + return c1 - c2; + } + } + return len1 - len2; + } + + public static int hashCode(byte[] value) { + int h = 0; + for (byte v : value) { + h = 31 * h + (v & 0xff); + } + return h; + } + + public static int indexOf(byte[] value, int ch, int fromIndex) { + if (!canEncode(ch)) { + return -1; + } + int max = value.length; + if (fromIndex < 0) { + fromIndex = 0; + } else if (fromIndex >= max) { + // Note: fromIndex might be near -1>>>1. + return -1; + } + byte c = (byte)ch; + for (int i = fromIndex; i < max; i++) { + if (value[i] == c) { + return i; + } + } + return -1; + } + + @HotSpotIntrinsicCandidate + public static int indexOf(byte[] value, byte[] str) { + if (str.length == 0) { + return 0; + } + if (value.length == 0) { + return -1; + } + return indexOf(value, value.length, str, str.length, 0); + } + + @HotSpotIntrinsicCandidate + public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) { + byte first = str[0]; + int max = (valueCount - strCount); + for (int i = fromIndex; i <= max; i++) { + // Look for first character. + if (value[i] != first) { + while (++i <= max && value[i] != first); + } + // Found first character, now look at the rest of value + if (i <= max) { + int j = i + 1; + int end = j + strCount - 1; + for (int k = 1; j < end && value[j] == str[k]; j++, k++); + if (j == end) { + // Found whole string. + return i; + } + } + } + return -1; + } + + public static int lastIndexOf(byte[] src, int srcCount, + byte[] tgt, int tgtCount, int fromIndex) { + int min = tgtCount - 1; + int i = min + fromIndex; + int strLastIndex = tgtCount - 1; + char strLastChar = (char)(tgt[strLastIndex] & 0xff); + + startSearchForLastChar: + while (true) { + while (i >= min && (src[i] & 0xff) != strLastChar) { + i--; + } + if (i < min) { + return -1; + } + int j = i - 1; + int start = j - strLastIndex; + int k = strLastIndex - 1; + while (j > start) { + if ((src[j--] & 0xff) != (tgt[k--] & 0xff)) { + i--; + continue startSearchForLastChar; + } + } + return start + 1; + } + } + + public static int lastIndexOf(final byte[] value, int ch, int fromIndex) { + if (!canEncode(ch)) { + return -1; + } + int off = Math.min(fromIndex, value.length - 1); + for (; off >= 0; off--) { + if (value[off] == (byte)ch) { + return off; + } + } + return -1; + } + + public static String replace(byte[] value, char oldChar, char newChar) { + if (canEncode(oldChar)) { + int len = value.length; + int i = -1; + while (++i < len) { + if (value[i] == (byte)oldChar) { + break; + } + } + if (i < len) { + if (canEncode(newChar)) { + byte buf[] = new byte[len]; + for (int j = 0; j < i; j++) { // TBD arraycopy? + buf[j] = value[j]; + } + while (i < len) { + byte c = value[i]; + buf[i] = (c == (byte)oldChar) ? (byte)newChar : c; + i++; + } + return new String(buf, LATIN1); + } else { + byte[] buf = StringUTF16.newBytesFor(len); + // inflate from latin1 to UTF16 + inflate(value, 0, buf, 0, i); + while (i < len) { + char c = (char)(value[i] & 0xff); + StringUTF16.putChar(buf, i, (c == oldChar) ? newChar : c); + i++; + } + return new String(buf, UTF16); + } + } + } + return null; // for string to return this; + } + + // case insensitive + public static boolean regionMatchesCI(byte[] value, int toffset, + byte[] other, int ooffset, int len) { + int last = toffset + len; + while (toffset < last) { + char c1 = (char)(value[toffset++] & 0xff); + char c2 = (char)(other[ooffset++] & 0xff); + if (c1 == c2) { + continue; + } + char u1 = Character.toUpperCase(c1); + char u2 = Character.toUpperCase(c2); + if (u1 == u2) { + continue; + } + if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { + continue; + } + return false; + } + return true; + } + + public static boolean regionMatchesCI_UTF16(byte[] value, int toffset, + byte[] other, int ooffset, int len) { + int last = toffset + len; + while (toffset < last) { + char c1 = (char)(value[toffset++] & 0xff); + char c2 = StringUTF16.getChar(other, ooffset++); + if (c1 == c2) { + continue; + } + char u1 = Character.toUpperCase(c1); + char u2 = Character.toUpperCase(c2); + if (u1 == u2) { + continue; + } + if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { + continue; + } + return false; + } + return true; + } + + public static String toLowerCase(String str, byte[] value, Locale locale) { + if (locale == null) { + throw new NullPointerException(); + } + int first; + final int len = value.length; + // Now check if there are any characters that need to be changed, or are surrogate + for (first = 0 ; first < len; first++) { + int cp = value[first] & 0xff; + if (cp != Character.toLowerCase(cp)) { // no need to check Character.ERROR + break; + } + } + if (first == len) + return str; + String lang = locale.getLanguage(); + if (lang == "tr" || lang == "az" || lang == "lt") { + return toLowerCaseEx(str, value, first, locale, true); + } + byte[] result = new byte[len]; + System.arraycopy(value, 0, result, 0, first); // Just copy the first few + // lowerCase characters. + for (int i = first; i < len; i++) { + int cp = value[i] & 0xff; + cp = Character.toLowerCase(cp); + if (!canEncode(cp)) { // not a latin1 character + return toLowerCaseEx(str, value, first, locale, false); + } + result[i] = (byte)cp; + } + return new String(result, LATIN1); + } + + private static String toLowerCaseEx(String str, byte[] value, + int first, Locale locale, boolean localeDependent) + { + byte[] result = StringUTF16.newBytesFor(value.length); + int resultOffset = 0; + for (int i = 0; i < first; i++) { + StringUTF16.putChar(result, resultOffset++, value[i] & 0xff); + } + for (int i = first; i < value.length; i++) { + int srcChar = value[i] & 0xff; + int lowerChar; + char[] lowerCharArray; + if (localeDependent) { + lowerChar = ConditionalSpecialCasing.toLowerCaseEx(str, i, locale); + } else { + lowerChar = Character.toLowerCase(srcChar); + } + if (Character.isBmpCodePoint(lowerChar)) { // Character.ERROR is not a bmp + StringUTF16.putChar(result, resultOffset++, lowerChar); + } else { + if (lowerChar == Character.ERROR) { + lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(str, i, locale); + } else { + lowerCharArray = Character.toChars(lowerChar); + } + /* Grow result if needed */ + int mapLen = lowerCharArray.length; + if (mapLen > 1) { + byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1); + System.arraycopy(result, 0, result2, 0, resultOffset << 1); + result = result2; + } + for (int x = 0; x < mapLen; ++x) { + StringUTF16.putChar(result, resultOffset++, lowerCharArray[x]); + } + } + } + return StringUTF16.newString(result, 0, resultOffset); + } + + public static String toUpperCase(String str, byte[] value, Locale locale) { + if (locale == null) { + throw new NullPointerException(); + } + int first; + final int len = value.length; + + // Now check if there are any characters that need to be changed, or are surrogate + for (first = 0 ; first < len; first++ ) { + int cp = value[first] & 0xff; + if (cp != Character.toUpperCaseEx(cp)) { // no need to check Character.ERROR + break; + } + } + if (first == len) { + return str; + } + String lang = locale.getLanguage(); + if (lang == "tr" || lang == "az" || lang == "lt") { + return toUpperCaseEx(str, value, first, locale, true); + } + byte[] result = new byte[len]; + System.arraycopy(value, 0, result, 0, first); // Just copy the first few + // upperCase characters. + for (int i = first; i < len; i++) { + int cp = value[i] & 0xff; + cp = Character.toUpperCaseEx(cp); + if (!canEncode(cp)) { // not a latin1 character + return toUpperCaseEx(str, value, first, locale, false); + } + result[i] = (byte)cp; + } + return new String(result, LATIN1); + } + + private static String toUpperCaseEx(String str, byte[] value, + int first, Locale locale, boolean localeDependent) + { + byte[] result = StringUTF16.newBytesFor(value.length); + int resultOffset = 0; + for (int i = 0; i < first; i++) { + StringUTF16.putChar(result, resultOffset++, value[i] & 0xff); + } + for (int i = first; i < value.length; i++) { + int srcChar = value[i] & 0xff; + int upperChar; + char[] upperCharArray; + if (localeDependent) { + upperChar = ConditionalSpecialCasing.toUpperCaseEx(str, i, locale); + } else { + upperChar = Character.toUpperCaseEx(srcChar); + } + if (Character.isBmpCodePoint(upperChar)) { + StringUTF16.putChar(result, resultOffset++, upperChar); + } else { + if (upperChar == Character.ERROR) { + if (localeDependent) { + upperCharArray = + ConditionalSpecialCasing.toUpperCaseCharArray(str, i, locale); + } else { + upperCharArray = Character.toUpperCaseCharArray(srcChar); + } + } else { + upperCharArray = Character.toChars(upperChar); + } + /* Grow result if needed */ + int mapLen = upperCharArray.length; + if (mapLen > 1) { + byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1); + System.arraycopy(result, 0, result2, 0, resultOffset << 1); + result = result2; + } + for (int x = 0; x < mapLen; ++x) { + StringUTF16.putChar(result, resultOffset++, upperCharArray[x]); + } + } + } + return StringUTF16.newString(result, 0, resultOffset); + } + + public static String trim(byte[] value) { + int len = value.length; + int st = 0; + while ((st < len) && ((value[st] & 0xff) <= ' ')) { + st++; + } + while ((st < len) && ((value[len - 1] & 0xff) <= ' ')) { + len--; + } + return ((st > 0) || (len < value.length)) ? + newString(value, st, len - st) : null; + } + + public static void putChar(byte[] val, int index, int c) { + //assert (canEncode(c)); + val[index] = (byte)(c); + } + + public static char getChar(byte[] val, int index) { + return (char)(val[index] & 0xff); + } + + public static byte[] toBytes(int[] val, int off, int len) { + byte[] ret = new byte[len]; + for (int i = 0; i < len; i++) { + int cp = val[off++]; + if (!canEncode(cp)) { + return null; + } + ret[i] = (byte)cp; + } + return ret; + } + + public static byte[] toBytes(char c) { + return new byte[] { (byte)c }; + } + + public static String newString(byte[] val, int index, int len) { + return new String(Arrays.copyOfRange(val, index, index + len), + LATIN1); + } + + public static void fillNull(byte[] val, int index, int end) { + Arrays.fill(val, index, end, (byte)0); + } + + // inflatedCopy byte[] -> char[] + @HotSpotIntrinsicCandidate + private static void inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len) { + for (int i = 0; i < len; i++) { + dst[dstOff++] = (char)(src[srcOff++] & 0xff); + } + } + + // inflatedCopy byte[] -> byte[] + @HotSpotIntrinsicCandidate + public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { + for (int i = 0; i < len; i++) { + StringUTF16.putChar(dst, dstOff++, src[srcOff++] & 0xff); + } + } + + static class CharsSpliterator implements Spliterator.OfInt { + private final byte[] array; + private int index; // current index, modified on advance/split + private final int fence; // one past last index + private final int cs; + + CharsSpliterator(byte[] array, int acs) { + this(array, 0, array.length, acs); + } + + CharsSpliterator(byte[] array, int origin, int fence, int acs) { + this.array = array; + this.index = origin; + this.fence = fence; + this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED + | Spliterator.SUBSIZED; + } + + @Override + public OfInt trySplit() { + int lo = index, mid = (lo + fence) >>> 1; + return (lo >= mid) + ? null + : new CharsSpliterator(array, lo, index = mid, cs); + } + + @Override + public void forEachRemaining(IntConsumer action) { + byte[] a; int i, hi; // hoist accesses and checks from loop + if (action == null) + throw new NullPointerException(); + if ((a = array).length >= (hi = fence) && + (i = index) >= 0 && i < (index = hi)) { + do { action.accept(a[i] & 0xff); } while (++i < hi); + } + } + + @Override + public boolean tryAdvance(IntConsumer action) { + if (action == null) + throw new NullPointerException(); + if (index >= 0 && index < fence) { + action.accept(array[index++] & 0xff); + return true; + } + return false; + } + + @Override + public long estimateSize() { return (long)(fence - index); } + + @Override + public int characteristics() { + return cs; + } + } + + //////////////////////////////////////////////////////////////// + + public static void getCharsSB(byte[] val, int srcBegin, int srcEnd, char dst[], int dstBegin) { + checkOffset(srcEnd, val.length); + getChars(val, srcBegin, srcEnd, dst, dstBegin); + } + + public static void inflateSB(byte[] val, byte[] dst, int dstOff, int count) { + checkOffset(count, val.length); + checkOffset(dstOff + count, dst.length >> 1); // dst is utf16 + inflate(val, 0, dst, dstOff, count); + } +} diff --git a/jdk/src/java.base/share/classes/java/lang/StringUTF16.java b/jdk/src/java.base/share/classes/java/lang/StringUTF16.java new file mode 100644 index 00000000000..56550d77fc6 --- /dev/null +++ b/jdk/src/java.base/share/classes/java/lang/StringUTF16.java @@ -0,0 +1,971 @@ +/* + * Copyright (c) 2015, 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 java.lang; + +import java.util.Arrays; +import java.util.Locale; +import java.util.Spliterator; +import java.util.function.IntConsumer; +import jdk.internal.HotSpotIntrinsicCandidate; + +import static java.lang.String.UTF16; +import static java.lang.String.LATIN1; +import static java.lang.String.checkIndex; +import static java.lang.String.checkOffset; + +final class StringUTF16 { + + public static byte[] newBytesFor(int len) { + if (len < 0) { + throw new NegativeArraySizeException(); + } + if (len > MAX_LENGTH) { + throw new OutOfMemoryError("UTF16 String size is " + len + + ", should be less than " + MAX_LENGTH); + } + return new byte[len << 1]; + } + + @HotSpotIntrinsicCandidate + public static void putChar(byte[] val, int index, int c) { + index <<= 1; + val[index++] = (byte)(c >> HI_BYTE_SHIFT); + val[index] = (byte)(c >> LO_BYTE_SHIFT); + } + + @HotSpotIntrinsicCandidate + public static char getChar(byte[] val, int index) { + index <<= 1; + return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) | + ((val[index] & 0xff) << LO_BYTE_SHIFT)); + } + + public static char charAt(byte[] value, int index) { + if (index < 0 || index >= value.length >> 1) { + throw new StringIndexOutOfBoundsException(index); + } + return getChar(value, index); + } + + public static int length(byte[] value) { + return value.length >> 1; + } + + public static int codePointAt(byte[] value, int index, int end) { + char c1 = getChar(value, index); + if (Character.isHighSurrogate(c1) && ++index < end) { + char c2 = getChar(value, index); + if (Character.isLowSurrogate(c2)) { + return Character.toCodePoint(c1, c2); + } + } + return c1; + } + + public static int codePointBefore(byte[] value, int index) { + char c2 = getChar(value, --index); + if (Character.isLowSurrogate(c2) && index > 0) { + char c1 = getChar(value, --index); + if (Character.isHighSurrogate(c1)) { + return Character.toCodePoint(c1, c2); + } + } + return c2; + } + + public static int codePointCount(byte[] value, int beginIndex, int endIndex) { + int count = endIndex - beginIndex; + for (int i = beginIndex; i < endIndex; ) { + if (Character.isHighSurrogate(getChar(value, i++)) && + i < endIndex && + Character.isLowSurrogate(getChar(value, i))) { + count--; + i++; + } + } + return count; + } + + public static char[] toChars(byte[] value) { + char[] dst = new char[value.length >> 1]; + getChars(value, 0, dst.length, dst, 0); + return dst; + } + + @HotSpotIntrinsicCandidate + public static byte[] toBytes(char[] value, int off, int len) { + byte[] val = newBytesFor(len); + for (int i = 0; i < len; i++) { + putChar(val, i, value[off++]); + } + return val; + } + + public static byte[] compress(char[] val, int off, int len) { + byte[] ret = new byte[len]; + if (compress(val, off, ret, 0, len) == len) { + return ret; + } + return null; + } + + public static byte[] compress(byte[] val, int off, int len) { + byte[] ret = new byte[len]; + if (compress(val, off, ret, 0, len) == len) { + return ret; + } + return null; + } + + // compressedCopy char[] -> byte[] + @HotSpotIntrinsicCandidate + private static int compress(char[] src, int srcOff, byte[] dst, int dstOff, int len) { + for (int i = 0; i < len; i++) { + int c = src[srcOff++]; + if (c >>> 8 != 0) { + return 0; + } + dst[dstOff++] = (byte)c; + } + return len; + } + + // compressedCopy byte[] -> byte[] + @HotSpotIntrinsicCandidate + public static int compress(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { + for (int i = 0; i < len; i++) { + int c = getChar(src, srcOff++); + if (c >>> 8 != 0) { + return 0; + } + dst[dstOff++] = (byte)c; + } + return len; + } + + public static byte[] toBytes(int[] val, int index, int len) { + final int end = index + len; + // Pass 1: Compute precise size of char[] + int n = len; + for (int i = index; i < end; i++) { + int cp = val[i]; + if (Character.isBmpCodePoint(cp)) + continue; + else if (Character.isValidCodePoint(cp)) + n++; + else throw new IllegalArgumentException(Integer.toString(cp)); + } + // Pass 2: Allocate and fill in pair + byte[] buf = newBytesFor(n); + for (int i = index, j = 0; i < end; i++, j++) { + int cp = val[i]; + if (Character.isBmpCodePoint(cp)) { + putChar(buf, j, cp); + } else { + putChar(buf, j++, Character.highSurrogate(cp)); + putChar(buf, j, Character.lowSurrogate(cp)); + } + } + return buf; + } + + public static byte[] toBytes(char c) { + byte[] result = new byte[2]; + putChar(result, 0, c); + return result; + } + + @HotSpotIntrinsicCandidate + public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) { + for (int i = srcBegin; i < srcEnd; i++) { + dst[dstBegin++] = getChar(value, i); + } + } + + /* @see java.lang.String.getBytes(int, int, byte[], int) */ + public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte dst[], int dstBegin) { + srcBegin <<= 1; + srcEnd <<= 1; + for (int i = srcBegin + (1 >> LO_BYTE_SHIFT); i < srcEnd; i += 2) { + dst[dstBegin++] = value[i]; + } + } + + @HotSpotIntrinsicCandidate + public static boolean equals(byte[] value, byte[] other) { + if (value.length == other.length) { + int len = value.length >> 1; + for (int i = 0; i < len; i++) { + if (getChar(value, i) != getChar(other, i)) { + return false; + } + } + return true; + } + return false; + } + + @HotSpotIntrinsicCandidate + public static int compareTo(byte[] value, byte[] other) { + int len1 = length(value); + int len2 = length(other); + int lim = Math.min(len1, len2); + for (int k = 0; k < lim; k++) { + char c1 = getChar(value, k); + char c2 = getChar(other, k); + if (c1 != c2) { + return c1 - c2; + } + } + return len1 - len2; + } + + @HotSpotIntrinsicCandidate + public static int compareToLatin1(byte[] value, byte[] other) { + int len1 = length(value); + int len2 = StringLatin1.length(other); + int lim = Math.min(len1, len2); + for (int k = 0; k < lim; k++) { + char c1 = getChar(value, k); + char c2 = StringLatin1.getChar(other, k); + if (c1 != c2) { + return c1 - c2; + } + } + return len1 - len2; + } + + public static int hashCode(byte[] value) { + int h = 0; + int length = value.length >> 1; + for (int i = 0; i < length; i++) { + h = 31 * h + getChar(value, i); + } + return h; + } + + public static int indexOf(byte[] value, int ch, int fromIndex) { + int max = value.length >> 1; + if (fromIndex < 0) { + fromIndex = 0; + } else if (fromIndex >= max) { + // Note: fromIndex might be near -1>>>1. + return -1; + } + if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { + // handle most cases here (ch is a BMP code point or a + // negative value (invalid code point)) + return indexOfChar(value, ch, fromIndex, max); + } else { + return indexOfSupplementary(value, ch, fromIndex, max); + } + } + + @HotSpotIntrinsicCandidate + public static int indexOf(byte[] value, byte[] str) { + if (str.length == 0) { + return 0; + } + if (value.length == 0) { + return -1; + } + return indexOf(value, length(value), str, length(str), 0); + } + + @HotSpotIntrinsicCandidate + public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) { + char first = getChar(str, 0); + int max = (valueCount - strCount); + for (int i = fromIndex; i <= max; i++) { + // Look for first character. + if (getChar(value, i) != first) { + while (++i <= max && getChar(value, i) != first); + } + // Found first character, now look at the rest of value + if (i <= max) { + int j = i + 1; + int end = j + strCount - 1; + for (int k = 1; j < end && getChar(value, j) == getChar(str, k); j++, k++); + if (j == end) { + // Found whole string. + return i; + } + } + } + return -1; + } + + /** + * Handles indexOf Latin1 substring in UTF16 string. + */ + @HotSpotIntrinsicCandidate + public static int indexOfLatin1(byte[] value, byte[] str) { + if (str.length == 0) { + return 0; + } + if (value.length == 0) { + return -1; + } + return indexOfLatin1(value, length(value), str, str.length, 0); + } + + @HotSpotIntrinsicCandidate + public static int indexOfLatin1(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) { + char first = (char)(tgt[0] & 0xff); + int max = (srcCount - tgtCount); + for (int i = fromIndex; i <= max; i++) { + // Look for first character. + if (getChar(src, i) != first) { + while (++i <= max && getChar(src, i) != first); + } + // Found first character, now look at the rest of v2 + if (i <= max) { + int j = i + 1; + int end = j + tgtCount - 1; + for (int k = 1; + j < end && getChar(src, j) == (tgt[k] & 0xff); + j++, k++); + if (j == end) { + // Found whole string. + return i; + } + } + } + return -1; + } + + @HotSpotIntrinsicCandidate + private static int indexOfChar(byte[] value, int ch, int fromIndex, int max) { + for (int i = fromIndex; i < max; i++) { + if (getChar(value, i) == ch) { + return i; + } + } + return -1; + } + + /** + * Handles (rare) calls of indexOf with a supplementary character. + */ + private static int indexOfSupplementary(byte[] value, int ch, int fromIndex, int max) { + if (Character.isValidCodePoint(ch)) { + final char hi = Character.highSurrogate(ch); + final char lo = Character.lowSurrogate(ch); + for (int i = fromIndex; i < max - 1; i++) { + if (getChar(value, i) == hi && getChar(value, i + 1 ) == lo) { + return i; + } + } + } + return -1; + } + + public static int lastIndexOf(byte[] src, int srcCount, + byte[] tgt, int tgtCount, int fromIndex) { + int min = tgtCount - 1; + int i = min + fromIndex; + int strLastIndex = tgtCount - 1; + char strLastChar = getChar(tgt, strLastIndex); + + startSearchForLastChar: + while (true) { + while (i >= min && getChar(src, i) != strLastChar) { + i--; + } + if (i < min) { + return -1; + } + int j = i - 1; + int start = j - strLastIndex; + int k = strLastIndex - 1; + while (j > start) { + if (getChar(src, j--) != getChar(tgt, k--)) { + i--; + continue startSearchForLastChar; + } + } + return start + 1; + } + } + + public static int lastIndexOf(byte[] value, int ch, int fromIndex) { + if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { + // handle most cases here (ch is a BMP code point or a + // negative value (invalid code point)) + int i = Math.min(fromIndex, (value.length >> 1) - 1); + for (; i >= 0; i--) { + if (getChar(value, i) == ch) { + return i; + } + } + return -1; + } else { + return lastIndexOfSupplementary(value, ch, fromIndex); + } + } + + /** + * Handles (rare) calls of lastIndexOf with a supplementary character. + */ + private static int lastIndexOfSupplementary(final byte[] value, int ch, int fromIndex) { + if (Character.isValidCodePoint(ch)) { + char hi = Character.highSurrogate(ch); + char lo = Character.lowSurrogate(ch); + int i = Math.min(fromIndex, (value.length >> 1) - 2); + for (; i >= 0; i--) { + if (getChar(value, i) == hi && getChar(value, i + 1) == lo) { + return i; + } + } + } + return -1; + } + + public static String replace(byte[] value, char oldChar, char newChar) { + int len = value.length >> 1; + int i = -1; + while (++i < len) { + if (getChar(value, i) == oldChar) { + break; + } + } + if (i < len) { + byte buf[] = new byte[value.length]; + for (int j = 0; j < i; j++) { + putChar(buf, j, getChar(value, j)); // TBD:arraycopy? + } + while (i < len) { + char c = getChar(value, i); + putChar(buf, i, c == oldChar ? newChar : c); + i++; + } + // Check if we should try to compress to latin1 + if (String.COMPACT_STRINGS && + !StringLatin1.canEncode(oldChar) && + StringLatin1.canEncode(newChar)) { + byte[] val = compress(buf, 0, len); + if (val != null) { + return new String(val, LATIN1); + } + } + return new String(buf, UTF16); + } + return null; + } + + public static boolean regionMatchesCI(byte[] value, int toffset, + byte[] other, int ooffset, int len) { + int last = toffset + len; + while (toffset < last) { + char c1 = getChar(value, toffset++); + char c2 = getChar(other, ooffset++); + if (c1 == c2) { + continue; + } + // try converting both characters to uppercase. + // If the results match, then the comparison scan should + // continue. + char u1 = Character.toUpperCase(c1); + char u2 = Character.toUpperCase(c2); + if (u1 == u2) { + continue; + } + // Unfortunately, conversion to uppercase does not work properly + // for the Georgian alphabet, which has strange rules about case + // conversion. So we need to make one last check before + // exiting. + if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { + continue; + } + return false; + } + return true; + } + + public static boolean regionMatchesCI_Latin1(byte[] value, int toffset, + byte[] other, int ooffset, + int len) { + int last = toffset + len; + while (toffset < last) { + char c1 = getChar(value, toffset++); + char c2 = (char)(other[ooffset++] & 0xff); + if (c1 == c2) { + continue; + } + char u1 = Character.toUpperCase(c1); + char u2 = Character.toUpperCase(c2); + if (u1 == u2) { + continue; + } + if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { + continue; + } + return false; + } + return true; + } + + public static String toLowerCase(String str, byte[] value, Locale locale) { + if (locale == null) { + throw new NullPointerException(); + } + int first; + boolean hasSurr = false; + final int len = value.length >> 1; + + // Now check if there are any characters that need to be changed, or are surrogate + for (first = 0 ; first < len; first++) { + int cp = (int)getChar(value, first); + if (Character.isSurrogate((char)cp)) { + hasSurr = true; + break; + } + if (cp != Character.toLowerCase(cp)) { // no need to check Character.ERROR + break; + } + } + if (first == len) + return str; + byte[] result = new byte[value.length]; + System.arraycopy(value, 0, result, 0, first << 1); // Just copy the first few + // lowerCase characters. + String lang = locale.getLanguage(); + if (lang == "tr" || lang == "az" || lang == "lt") { + return toLowerCaseEx(str, value, result, first, locale, true); + } + if (hasSurr) { + return toLowerCaseEx(str, value, result, first, locale, false); + } + int bits = 0; + for (int i = first; i < len; i++) { + int cp = (int)getChar(value, i); + if (cp == '\u03A3' || // GREEK CAPITAL LETTER SIGMA + Character.isSurrogate((char)cp)) { + return toLowerCaseEx(str, value, result, i, locale, false); + } + if (cp == '\u0130') { // LATIN CAPITAL LETTER I WITH DOT ABOVE + return toLowerCaseEx(str, value, result, i, locale, true); + } + cp = Character.toLowerCase(cp); + if (!Character.isBmpCodePoint(cp)) { + return toLowerCaseEx(str, value, result, i, locale, false); + } + bits |= cp; + putChar(result, i, cp); + } + if (bits >>> 8 != 0) { + return new String(result, UTF16); + } else { + return newString(result, 0, len); + } + } + + private static String toLowerCaseEx(String str, byte[] value, + byte[] result, int first, Locale locale, + boolean localeDependent) { + int resultOffset = first; + int length = value.length >> 1; + int srcCount; + for (int i = first; i < length; i += srcCount) { + int srcChar = getChar(value, i); + int lowerChar; + char[] lowerCharArray; + srcCount = 1; + if (Character.isSurrogate((char)srcChar)) { + srcChar = codePointAt(value, i, length); + srcCount = Character.charCount(srcChar); + } + if (localeDependent || + srcChar == '\u03A3' || // GREEK CAPITAL LETTER SIGMA + srcChar == '\u0130') { // LATIN CAPITAL LETTER I WITH DOT ABOVE + lowerChar = ConditionalSpecialCasing.toLowerCaseEx(str, i, locale); + } else { + lowerChar = Character.toLowerCase(srcChar); + } + if (Character.isBmpCodePoint(lowerChar)) { // Character.ERROR is not a bmp + putChar(result, resultOffset++, lowerChar); + } else { + if (lowerChar == Character.ERROR) { + lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(str, i, locale); + } else { + lowerCharArray = Character.toChars(lowerChar); + } + /* Grow result if needed */ + int mapLen = lowerCharArray.length; + if (mapLen > srcCount) { + byte[] result2 = newBytesFor((result.length >> 1) + mapLen - srcCount); + System.arraycopy(result, 0, result2, 0, resultOffset << 1); + result = result2; + } + for (int x = 0; x < mapLen; ++x) { + putChar(result, resultOffset++, lowerCharArray[x]); + } + } + } + return newString(result, 0, resultOffset); + } + + public static String toUpperCase(String str, byte[] value, Locale locale) { + if (locale == null) { + throw new NullPointerException(); + } + int first; + boolean hasSurr = false; + final int len = value.length >> 1; + + // Now check if there are any characters that need to be changed, or are surrogate + for (first = 0 ; first < len; first++) { + int cp = (int)getChar(value, first); + if (Character.isSurrogate((char)cp)) { + hasSurr = true; + break; + } + if (cp != Character.toUpperCaseEx(cp)) { // no need to check Character.ERROR + break; + } + } + if (first == len) { + return str; + } + byte[] result = new byte[value.length]; + System.arraycopy(value, 0, result, 0, first << 1); // Just copy the first few + // upperCase characters. + String lang = locale.getLanguage(); + if (lang == "tr" || lang == "az" || lang == "lt") { + return toUpperCaseEx(str, value, result, first, locale, true); + } + if (hasSurr) { + return toUpperCaseEx(str, value, result, first, locale, false); + } + int bits = 0; + for (int i = first; i < len; i++) { + int cp = (int)getChar(value, i); + if (Character.isSurrogate((char)cp)) { + return toUpperCaseEx(str, value, result, i, locale, false); + } + cp = Character.toUpperCaseEx(cp); + if (!Character.isBmpCodePoint(cp)) { // Character.ERROR is not bmp + return toUpperCaseEx(str, value, result, i, locale, false); + } + bits |= cp; + putChar(result, i, cp); + } + if (bits >>> 8 != 0) { + return new String(result, UTF16); + } else { + return newString(result, 0, len); + } + } + + private static String toUpperCaseEx(String str, byte[] value, + byte[] result, int first, + Locale locale, boolean localeDependent) + { + int resultOffset = first; + int length = value.length >> 1; + int srcCount; + for (int i = first; i < length; i += srcCount) { + int srcChar = getChar(value, i); + int upperChar; + char[] upperCharArray; + srcCount = 1; + if (Character.isSurrogate((char)srcChar)) { + srcChar = codePointAt(value, i, length); + srcCount = Character.charCount(srcChar); + } + if (localeDependent) { + upperChar = ConditionalSpecialCasing.toUpperCaseEx(str, i, locale); + } else { + upperChar = Character.toUpperCaseEx(srcChar); + } + if (Character.isBmpCodePoint(upperChar)) { + putChar(result, resultOffset++, upperChar); + } else { + if (upperChar == Character.ERROR) { + if (localeDependent) { + upperCharArray = + ConditionalSpecialCasing.toUpperCaseCharArray(str, i, locale); + } else { + upperCharArray = Character.toUpperCaseCharArray(srcChar); + } + } else { + upperCharArray = Character.toChars(upperChar); + } + /* Grow result if needed */ + int mapLen = upperCharArray.length; + if (mapLen > srcCount) { + byte[] result2 = newBytesFor((result.length >> 1) + mapLen - srcCount); + System.arraycopy(result, 0, result2, 0, resultOffset << 1); + result = result2; + } + for (int x = 0; x < mapLen; ++x) { + putChar(result, resultOffset++, upperCharArray[x]); + } + } + } + return newString(result, 0, resultOffset); + } + + public static String trim(byte[] value) { + int length = value.length >> 1; + int len = length; + int st = 0; + while (st < len && getChar(value, st) <= ' ') { + st++; + } + while (st < len && getChar(value, len - 1) <= ' ') { + len--; + } + return ((st > 0) || (len < length )) ? + new String(Arrays.copyOfRange(value, st << 1, len << 1), UTF16) : + null; + } + + public static void putChars(byte[] val, int index, char[] str, int off, int end) { + while (off < end) { + putChar(val, index++, str[off++]); + } + } + + public static String newString(byte[] val, int index, int len) { + if (String.COMPACT_STRINGS) { + byte[] buf = compress(val, index, len); + if (buf != null) { + return new String(buf, LATIN1); + } + } + int last = index + len; + return new String(Arrays.copyOfRange(val, index << 1, last << 1), UTF16); + } + + public static void fillNull(byte[] val, int index, int end) { + Arrays.fill(val, index << 1, end << 1, (byte)0); + } + + static class CharsSpliterator implements Spliterator.OfInt { + private final byte[] array; + private int index; // current index, modified on advance/split + private final int fence; // one past last index + private final int cs; + + CharsSpliterator(byte[] array, int acs) { + this(array, 0, array.length >> 1, acs); + } + + CharsSpliterator(byte[] array, int origin, int fence, int acs) { + this.array = array; + this.index = origin; + this.fence = fence; + this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED + | Spliterator.SUBSIZED; + } + + @Override + public OfInt trySplit() { + int lo = index, mid = (lo + fence) >>> 1; + return (lo >= mid) + ? null + : new CharsSpliterator(array, lo, index = mid, cs); + } + + @Override + public void forEachRemaining(IntConsumer action) { + byte[] a; int i, hi; // hoist accesses and checks from loop + if (action == null) + throw new NullPointerException(); + if (((a = array).length >> 1) >= (hi = fence) && + (i = index) >= 0 && i < (index = hi)) { + do { action.accept(getChar(a, i)); } while (++i < hi); + } + } + + @Override + public boolean tryAdvance(IntConsumer action) { + if (action == null) + throw new NullPointerException(); + if (index >= 0 && index < fence) { + action.accept(getChar(array, index++)); + return true; + } + return false; + } + + @Override + public long estimateSize() { return (long)(fence - index); } + + @Override + public int characteristics() { + return cs; + } + } + + static class CodePointsSpliterator implements Spliterator.OfInt { + private final byte[] array; + private int index; // current index, modified on advance/split + private final int fence; // one past last index + private final int cs; + + CodePointsSpliterator(byte[] array, int acs) { + this(array, 0, array.length >> 1, acs); + } + + CodePointsSpliterator(byte[] array, int origin, int fence, int acs) { + this.array = array; + this.index = origin; + this.fence = fence; + this.cs = acs | Spliterator.ORDERED; + } + + @Override + public OfInt trySplit() { + int lo = index, mid = (lo + fence) >>> 1; + if (lo >= mid) + return null; + + int midOneLess; + // If the mid-point intersects a surrogate pair + if (Character.isLowSurrogate(getChar(array, mid)) && + Character.isHighSurrogate(getChar(array, midOneLess = (mid -1)))) { + // If there is only one pair it cannot be split + if (lo >= midOneLess) + return null; + // Shift the mid-point to align with the surrogate pair + return new CodePointsSpliterator(array, lo, index = midOneLess, cs); + } + return new CodePointsSpliterator(array, lo, index = mid, cs); + } + + @Override + public void forEachRemaining(IntConsumer action) { + byte[] a; int i, hi; // hoist accesses and checks from loop + if (action == null) + throw new NullPointerException(); + if (((a = array).length >> 1) >= (hi = fence) && + (i = index) >= 0 && i < (index = hi)) { + do { + i = advance(a, i, hi, action); + } while (i < hi); + } + } + + @Override + public boolean tryAdvance(IntConsumer action) { + if (action == null) + throw new NullPointerException(); + if (index >= 0 && index < fence) { + index = advance(array, index, fence, action); + return true; + } + return false; + } + + // Advance one code point from the index, i, and return the next + // index to advance from + private static int advance(byte[] a, int i, int hi, IntConsumer action) { + char c1 = getChar(a, i++); + int cp = c1; + if (Character.isHighSurrogate(c1) && i < hi) { + char c2 = getChar(a, i); + if (Character.isLowSurrogate(c2)) { + i++; + cp = Character.toCodePoint(c1, c2); + } + } + action.accept(cp); + return i; + } + + @Override + public long estimateSize() { return (long)(fence - index); } + + @Override + public int characteristics() { + return cs; + } + } + + //////////////////////////////////////////////////////////////// + + public static void getCharsSB(byte[] val, int srcBegin, int srcEnd, char dst[], int dstBegin) { + checkOffset(srcEnd, val.length >> 1); + getChars(val, srcBegin, srcEnd, dst, dstBegin); + } + + public static void putCharSB(byte[] val, int index, int c) { + checkIndex(index, val.length >> 1); + putChar(val, index, c); + } + + public static void putCharsSB(byte[] val, int index, char[] ca, int off, int end) { + checkOffset(index + end - off, val.length >> 1); + putChars(val, index, ca, off, end); + } + + public static void putCharsSB(byte[] val, int index, CharSequence s, int off, int end) { + checkOffset(index + end - off, val.length >> 1); + for (int i = off; i < end; i++) { + putChar(val, index++, s.charAt(i)); + } + } + + public static int codePointAtSB(byte[] val, int index, int end) { + checkOffset(end, val.length >> 1); + return codePointAt(val, index, end); + } + + public static int codePointBeforeSB(byte[] val, int index) { + checkOffset(index, val.length >> 1); + return codePointBefore(val, index); + } + + public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) { + checkOffset(endIndex, val.length >> 1); + return codePointCount(val, beginIndex, endIndex); + } + + public static String newStringSB(byte[] val, int index, int len) { + checkOffset(index + len, val.length >> 1); + return newString(val, index, len); + } + + //////////////////////////////////////////////////////////////// + + private static native boolean isBigEndian(); + + static final int HI_BYTE_SHIFT; + static final int LO_BYTE_SHIFT; + static { + if (isBigEndian()) { + HI_BYTE_SHIFT = 8; + LO_BYTE_SHIFT = 0; + } else { + HI_BYTE_SHIFT = 0; + LO_BYTE_SHIFT = 8; + } + } + + static final int MAX_LENGTH = Integer.MAX_VALUE >> 1; +} diff --git a/jdk/src/java.base/share/classes/java/lang/System.java b/jdk/src/java.base/share/classes/java/lang/System.java index 44336f9ca2b..9a109ce756e 100644 --- a/jdk/src/java.base/share/classes/java/lang/System.java +++ b/jdk/src/java.base/share/classes/java/lang/System.java @@ -30,13 +30,14 @@ import java.lang.annotation.Annotation; import java.security.AccessControlContext; import java.util.Properties; import java.util.PropertyPermission; -import java.util.StringTokenizer; import java.util.Map; import java.security.AccessController; import java.security.PrivilegedAction; -import java.security.AllPermission; import java.nio.channels.Channel; import java.nio.channels.spi.SelectorProvider; +import java.util.Objects; +import java.util.ResourceBundle; +import java.util.function.Supplier; import sun.nio.ch.Interruptible; import sun.reflect.CallerSensitive; import sun.reflect.Reflection; @@ -45,6 +46,9 @@ import sun.reflect.annotation.AnnotationType; import jdk.internal.HotSpotIntrinsicCandidate; import jdk.internal.misc.JavaLangAccess;; import jdk.internal.misc.SharedSecrets;; +import jdk.internal.logger.LoggerFinderLoader; +import jdk.internal.logger.LazyLoggers; +import jdk.internal.logger.LocalizedLoggerWrapper; /** * The System class contains several useful class fields @@ -943,6 +947,648 @@ public final class System { return ProcessEnvironment.getenv(); } + /** + * {@code System.Logger} instances log messages that will be + * routed to the underlying logging framework the {@link System.LoggerFinder + * LoggerFinder} uses. + *

+ * {@code System.Logger} instances are typically obtained from + * the {@link java.lang.System System} class, by calling + * {@link java.lang.System#getLogger(java.lang.String) System.getLogger(loggerName)} + * or {@link java.lang.System#getLogger(java.lang.String, java.util.ResourceBundle) + * System.getLogger(loggerName, bundle)}. + * + * @see java.lang.System#getLogger(java.lang.String) + * @see java.lang.System#getLogger(java.lang.String, java.util.ResourceBundle) + * @see java.lang.System.LoggerFinder + * + * @since 9 + * + */ + public interface Logger { + + /** + * System {@linkplain Logger loggers} levels. + *

+ * A level has a {@linkplain #getName() name} and {@linkplain + * #getSeverity() severity}. + * Level values are {@link #ALL}, {@link #TRACE}, {@link #DEBUG}, + * {@link #INFO}, {@link #WARNING}, {@link #ERROR}, {@link #OFF}, + * by order of increasing severity. + *
+ * {@link #ALL} and {@link #OFF} + * are simple markers with severities mapped respectively to + * {@link java.lang.Integer#MIN_VALUE Integer.MIN_VALUE} and + * {@link java.lang.Integer#MAX_VALUE Integer.MAX_VALUE}. + *

+ * Severity values and Mapping to {@code java.util.logging.Level}. + *

+ * {@linkplain System.Logger.Level System logger levels} are mapped to + * {@linkplain java.util.logging.Level java.util.logging levels} + * of corresponding severity. + *
The mapping is as follows: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
System.Logger Severity Level Mapping
System.Logger Levels{@link Logger.Level#ALL ALL}{@link Logger.Level#TRACE TRACE}{@link Logger.Level#DEBUG DEBUG}{@link Logger.Level#INFO INFO}{@link Logger.Level#WARNING WARNING}{@link Logger.Level#ERROR ERROR}{@link Logger.Level#OFF OFF}
java.util.logging Levels{@link java.util.logging.Level#ALL ALL}{@link java.util.logging.Level#FINER FINER}{@link java.util.logging.Level#FINE FINE}{@link java.util.logging.Level#INFO INFO}{@link java.util.logging.Level#WARNING WARNING}{@link java.util.logging.Level#SEVERE SEVERE}{@link java.util.logging.Level#OFF OFF}
+ * + * @since 9 + * + * @see java.lang.System.LoggerFinder + * @see java.lang.System.Logger + */ + public enum Level { + + // for convenience, we're reusing java.util.logging.Level int values + // the mapping logic in sun.util.logging.PlatformLogger depends + // on this. + /** + * A marker to indicate that all levels are enabled. + * This level {@linkplain #getSeverity() severity} is + * {@link Integer#MIN_VALUE}. + */ + ALL(Integer.MIN_VALUE), // typically mapped to/from j.u.l.Level.ALL + /** + * {@code TRACE} level: usually used to log diagnostic information. + * This level {@linkplain #getSeverity() severity} is + * {@code 400}. + */ + TRACE(400), // typically mapped to/from j.u.l.Level.FINER + /** + * {@code DEBUG} level: usually used to log debug information traces. + * This level {@linkplain #getSeverity() severity} is + * {@code 500}. + */ + DEBUG(500), // typically mapped to/from j.u.l.Level.FINEST/FINE/CONFIG + /** + * {@code INFO} level: usually used to log information messages. + * This level {@linkplain #getSeverity() severity} is + * {@code 800}. + */ + INFO(800), // typically mapped to/from j.u.l.Level.INFO + /** + * {@code WARNING} level: usually used to log warning messages. + * This level {@linkplain #getSeverity() severity} is + * {@code 900}. + */ + WARNING(900), // typically mapped to/from j.u.l.Level.WARNING + /** + * {@code ERROR} level: usually used to log error messages. + * This level {@linkplain #getSeverity() severity} is + * {@code 1000}. + */ + ERROR(1000), // typically mapped to/from j.u.l.Level.SEVERE + /** + * A marker to indicate that all levels are disabled. + * This level {@linkplain #getSeverity() severity} is + * {@link Integer#MAX_VALUE}. + */ + OFF(Integer.MAX_VALUE); // typically mapped to/from j.u.l.Level.OFF + + private final int severity; + + private Level(int severity) { + this.severity = severity; + } + + /** + * Returns the name of this level. + * @return this level {@linkplain #name()}. + */ + public final String getName() { + return name(); + } + + /** + * Returns the severity of this level. + * A higher severity means a more severe condition. + * @return this level severity. + */ + public final int getSeverity() { + return severity; + } + } + + /** + * Returns the name of this logger. + * + * @return the logger name. + */ + public String getName(); + + /** + * Checks if a message of the given level would be logged by + * this logger. + * + * @param level the log message level. + * @return {@code true} if the given log message level is currently + * being logged. + * + * @throws NullPointerException if {@code level} is {@code null}. + */ + public boolean isLoggable(Level level); + + /** + * Logs a message. + * + * @implSpec The default implementation for this method calls + * {@code this.log(level, (ResourceBundle)null, msg, (Object[])null);} + * + * @param level the log message level. + * @param msg the string message (or a key in the message catalog, if + * this logger is a {@link + * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class) + * localized logger}); can be {@code null}. + * + * @throws NullPointerException if {@code level} is {@code null}. + */ + public default void log(Level level, String msg) { + log(level, (ResourceBundle) null, msg, (Object[]) null); + } + + /** + * Logs a lazily supplied message. + *

+ * If the logger is currently enabled for the given log message level + * then a message is logged that is the result produced by the + * given supplier function. Otherwise, the supplier is not operated on. + * + * @implSpec When logging is enabled for the given level, the default + * implementation for this method calls + * {@code this.log(level, (ResourceBundle)null, msgSupplier.get(), (Object[])null);} + * + * @param level the log message level. + * @param msgSupplier a supplier function that produces a message. + * + * @throws NullPointerException if {@code level} is {@code null}, + * or {@code msgSupplier} is {@code null}. + */ + public default void log(Level level, Supplier msgSupplier) { + Objects.requireNonNull(msgSupplier); + if (isLoggable(Objects.requireNonNull(level))) { + log(level, (ResourceBundle) null, msgSupplier.get(), (Object[]) null); + } + } + + /** + * Logs a message produced from the given object. + *

+ * If the logger is currently enabled for the given log message level then + * a message is logged that, by default, is the result produced from + * calling toString on the given object. + * Otherwise, the object is not operated on. + * + * @implSpec When logging is enabled for the given level, the default + * implementation for this method calls + * {@code this.log(level, (ResourceBundle)null, obj.toString(), (Object[])null);} + * + * @param level the log message level. + * @param obj the object to log. + * + * @throws NullPointerException if {@code level} is {@code null}, or + * {@code obj} is {@code null}. + */ + public default void log(Level level, Object obj) { + Objects.requireNonNull(obj); + if (isLoggable(Objects.requireNonNull(level))) { + this.log(level, (ResourceBundle) null, obj.toString(), (Object[]) null); + } + } + + /** + * Logs a message associated with a given throwable. + * + * @implSpec The default implementation for this method calls + * {@code this.log(level, (ResourceBundle)null, msg, thrown);} + * + * @param level the log message level. + * @param msg the string message (or a key in the message catalog, if + * this logger is a {@link + * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class) + * localized logger}); can be {@code null}. + * @param thrown a {@code Throwable} associated with the log message; + * can be {@code null}. + * + * @throws NullPointerException if {@code level} is {@code null}. + */ + public default void log(Level level, String msg, Throwable thrown) { + this.log(level, null, msg, thrown); + } + + /** + * Logs a lazily supplied message associated with a given throwable. + *

+ * If the logger is currently enabled for the given log message level + * then a message is logged that is the result produced by the + * given supplier function. Otherwise, the supplier is not operated on. + * + * @implSpec When logging is enabled for the given level, the default + * implementation for this method calls + * {@code this.log(level, (ResourceBundle)null, msgSupplier.get(), thrown);} + * + * @param level one of the log message level identifiers. + * @param msgSupplier a supplier function that produces a message. + * @param thrown a {@code Throwable} associated with log message; + * can be {@code null}. + * + * @throws NullPointerException if {@code level} is {@code null}, or + * {@code msgSupplier} is {@code null}. + */ + public default void log(Level level, Supplier msgSupplier, + Throwable thrown) { + Objects.requireNonNull(msgSupplier); + if (isLoggable(Objects.requireNonNull(level))) { + this.log(level, null, msgSupplier.get(), thrown); + } + } + + /** + * Logs a message with an optional list of parameters. + * + * @implSpec The default implementation for this method calls + * {@code this.log(level, (ResourceBundle)null, format, params);} + * + * @param level one of the log message level identifiers. + * @param format the string message format in {@link + * java.text.MessageFormat} format, (or a key in the message + * catalog, if this logger is a {@link + * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class) + * localized logger}); can be {@code null}. + * @param params an optional list of parameters to the message (may be + * none). + * + * @throws NullPointerException if {@code level} is {@code null}. + */ + public default void log(Level level, String format, Object... params) { + this.log(level, null, format, params); + } + + /** + * Logs a localized message associated with a given throwable. + *

+ * If the given resource bundle is non-{@code null}, the {@code msg} + * string is localized using the given resource bundle. + * Otherwise the {@code msg} string is not localized. + * + * @param level the log message level. + * @param bundle a resource bundle to localize {@code msg}; can be + * {@code null}. + * @param msg the string message (or a key in the message catalog, + * if {@code bundle} is not {@code null}); can be {@code null}. + * @param thrown a {@code Throwable} associated with the log message; + * can be {@code null}. + * + * @throws NullPointerException if {@code level} is {@code null}. + */ + public void log(Level level, ResourceBundle bundle, String msg, + Throwable thrown); + + /** + * Logs a message with resource bundle and an optional list of + * parameters. + *

+ * If the given resource bundle is non-{@code null}, the {@code format} + * string is localized using the given resource bundle. + * Otherwise the {@code format} string is not localized. + * + * @param level the log message level. + * @param bundle a resource bundle to localize {@code format}; can be + * {@code null}. + * @param format the string message format in {@link + * java.text.MessageFormat} format, (or a key in the message + * catalog if {@code bundle} is not {@code null}); can be {@code null}. + * @param params an optional list of parameters to the message (may be + * none). + * + * @throws NullPointerException if {@code level} is {@code null}. + */ + public void log(Level level, ResourceBundle bundle, String format, + Object... params); + + + } + + /** + * The {@code LoggerFinder} service is responsible for creating, managing, + * and configuring loggers to the underlying framework it uses. + *

+ * A logger finder is a concrete implementation of this class that has a + * zero-argument constructor and implements the abstract methods defined + * by this class. + * The loggers returned from a logger finder are capable of routing log + * messages to the logging backend this provider supports. + * A given invocation of the Java Runtime maintains a single + * system-wide LoggerFinder instance that is loaded as follows: + *

+ *

+ * An application can replace the logging backend + * even when the java.logging module is present, by simply providing + * and declaring an implementation of the {@link LoggerFinder} service. + *

+ * Default Implementation + *

+ * The system default {@code LoggerFinder} implementation uses + * {@code java.util.logging} as the backend framework when the + * {@code java.logging} module is present. + * It returns a {@linkplain System.Logger logger} instance + * that will route log messages to a {@link java.util.logging.Logger + * java.util.logging.Logger}. Otherwise, if {@code java.logging} is not + * present, the default implementation will return a simple logger + * instance that will route log messages of {@code INFO} level and above to + * the console ({@code System.err}). + *

+ * Logging Configuration + *

+ * {@linkplain Logger Logger} instances obtained from the + * {@code LoggerFinder} factory methods are not directly configurable by + * the application. Configuration is the responsibility of the underlying + * logging backend, and usually requires using APIs specific to that backend. + *

For the default {@code LoggerFinder} implementation + * using {@code java.util.logging} as its backend, refer to + * {@link java.util.logging java.util.logging} for logging configuration. + * For the default {@code LoggerFinder} implementation returning simple loggers + * when the {@code java.logging} module is absent, the configuration + * is implementation dependent. + *

+ * Usually an application that uses a logging framework will log messages + * through a logger facade defined (or supported) by that framework. + * Applications that wish to use an external framework should log + * through the facade associated with that framework. + *

+ * A system class that needs to log messages will typically obtain + * a {@link System.Logger} instance to route messages to the logging + * framework selected by the application. + *

+ * Libraries and classes that only need loggers to produce log messages + * should not attempt to configure loggers by themselves, as that + * would make them dependent from a specific implementation of the + * {@code LoggerFinder} service. + *

+ * In addition, when a security manager is present, loggers provided to + * system classes should not be directly configurable through the logging + * backend without requiring permissions. + *
+ * It is the responsibility of the provider of + * the concrete {@code LoggerFinder} implementation to ensure that + * these loggers are not configured by untrusted code without proper + * permission checks, as configuration performed on such loggers usually + * affects all applications in the same Java Runtime. + *

+ * Message Levels and Mapping to backend levels + *

+ * A logger finder is responsible for mapping from a {@code + * System.Logger.Level} to a level supported by the logging backend it uses. + *
The default LoggerFinder using {@code java.util.logging} as the backend + * maps {@code System.Logger} levels to + * {@linkplain java.util.logging.Level java.util.logging} levels + * of corresponding severity - as described in {@link Logger.Level + * Logger.Level}. + * + * @see java.lang.System + * @see java.lang.System.Logger + * + * @since 9 + */ + public static abstract class LoggerFinder { + /** + * The {@code RuntimePermission("loggerFinder")} is + * necessary to subclass and instantiate the {@code LoggerFinder} class, + * as well as to obtain loggers from an instance of that class. + */ + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + + /** + * Creates a new instance of {@code LoggerFinder}. + * + * @implNote It is recommended that a {@code LoggerFinder} service + * implementation does not perform any heavy initialization in its + * constructor, in order to avoid possible risks of deadlock or class + * loading cycles during the instantiation of the service provider. + * + * @throws SecurityException if a security manager is present and its + * {@code checkPermission} method doesn't allow the + * {@code RuntimePermission("loggerFinder")}. + */ + protected LoggerFinder() { + this(checkPermission()); + } + + private LoggerFinder(Void unused) { + // nothing to do. + } + + private static Void checkPermission() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(LOGGERFINDER_PERMISSION); + } + return null; + } + + /** + * Returns an instance of {@link Logger Logger} + * for the given {@code caller}. + * + * @param name the name of the logger. + * @param caller the class for which the logger is being requested; + * can be {@code null}. + * + * @return a {@link Logger logger} suitable for the given caller's + * use. + * @throws NullPointerException if {@code name} is {@code null} or + * {@code caller} is {@code null}. + * @throws SecurityException if a security manager is present and its + * {@code checkPermission} method doesn't allow the + * {@code RuntimePermission("loggerFinder")}. + */ + public abstract Logger getLogger(String name, /* Module */ Class caller); + + /** + * Returns a localizable instance of {@link Logger Logger} + * for the given {@code caller}. + * The returned logger will use the provided resource bundle for + * message localization. + * + * @implSpec By default, this method calls {@link + * #getLogger(java.lang.String, java.lang.Class) + * this.getLogger(name, caller)} to obtain a logger, then wraps that + * logger in a {@link Logger} instance where all methods that do not + * take a {@link ResourceBundle} as parameter are redirected to one + * which does - passing the given {@code bundle} for + * localization. So for instance, a call to {@link + * Logger#log(Level, String) Logger.log(Level.INFO, msg)} + * will end up as a call to {@link + * Logger#log(Level, ResourceBundle, String, Object...) + * Logger.log(Level.INFO, bundle, msg, (Object[])null)} on the wrapped + * logger instance. + * Note however that by default, string messages returned by {@link + * java.util.function.Supplier Supplier<String>} will not be + * localized, as it is assumed that such strings are messages which are + * already constructed, rather than keys in a resource bundle. + *

+ * An implementation of {@code LoggerFinder} may override this method, + * for example, when the underlying logging backend provides its own + * mechanism for localizing log messages, then such a + * {@code LoggerFinder} would be free to return a logger + * that makes direct use of the mechanism provided by the backend. + * + * @param name the name of the logger. + * @param bundle a resource bundle; can be {@code null}. + * @param caller the class for which the logger is being requested. + * @return an instance of {@link Logger Logger} which will use the + * provided resource bundle for message localization. + * + * @throws NullPointerException if {@code name} is {@code null} or + * {@code caller} is {@code null}. + * @throws SecurityException if a security manager is present and its + * {@code checkPermission} method doesn't allow the + * {@code RuntimePermission("loggerFinder")}. + */ + public Logger getLocalizedLogger(String name, ResourceBundle bundle, + /* Module */ Class caller) { + return new LocalizedLoggerWrapper<>(getLogger(name, caller), bundle); + } + + /** + * Returns the {@code LoggerFinder} instance. There is one + * single system-wide {@code LoggerFinder} instance in + * the Java Runtime. See the class specification of how the + * {@link LoggerFinder LoggerFinder} implementation is located and + * loaded. + + * @return the {@link LoggerFinder LoggerFinder} instance. + * @throws SecurityException if a security manager is present and its + * {@code checkPermission} method doesn't allow the + * {@code RuntimePermission("loggerFinder")}. + */ + public static LoggerFinder getLoggerFinder() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(LOGGERFINDER_PERMISSION); + } + return accessProvider(); + } + + + private static volatile LoggerFinder service; + static LoggerFinder accessProvider() { + // We do not need to synchronize: LoggerFinderLoader will + // always return the same instance, so if we don't have it, + // just fetch it again. + if (service == null) { + PrivilegedAction pa = + () -> LoggerFinderLoader.getLoggerFinder(); + service = AccessController.doPrivileged(pa, null, + LOGGERFINDER_PERMISSION); + } + return service; + } + + } + + + /** + * Returns an instance of {@link Logger Logger} for the caller's + * use. + * + * @implSpec + * Instances returned by this method route messages to loggers + * obtained by calling {@link LoggerFinder#getLogger(java.lang.String, java.lang.Class) + * LoggerFinder.getLogger(name, caller)}. + * + * @apiNote + * This method may defer calling the {@link + * LoggerFinder#getLogger(java.lang.String, java.lang.Class) + * LoggerFinder.getLogger} method to create an actual logger supplied by + * the logging backend, for instance, to allow loggers to be obtained during + * the system initialization time. + * + * @param name the name of the logger. + * @return an instance of {@link Logger} that can be used by the calling + * class. + * @throws NullPointerException if {@code name} is {@code null}. + */ + @CallerSensitive + public static Logger getLogger(String name) { + Objects.requireNonNull(name); + final Class caller = Reflection.getCallerClass(); + return LazyLoggers.getLogger(name, caller); + } + + /** + * Returns a localizable instance of {@link Logger + * Logger} for the caller's use. + * The returned logger will use the provided resource bundle for message + * localization. + * + * @implSpec + * The returned logger will perform message localization as specified + * by {@link LoggerFinder#getLocalizedLogger(java.lang.String, + * java.util.ResourceBundle, java.lang.Class) + * LoggerFinder.getLocalizedLogger(name, bundle, caller}. + * + * @apiNote + * This method is intended to be used after the system is fully initialized. + * This method may trigger the immediate loading and initialization + * of the {@link LoggerFinder} service, which may cause issues if the + * Java Runtime is not ready to initialize the concrete service + * implementation yet. + * System classes which may be loaded early in the boot sequence and + * need to log localized messages should create a logger using + * {@link #getLogger(java.lang.String)} and then use the log methods that + * take a resource bundle as parameter. + * + * @param name the name of the logger. + * @param bundle a resource bundle. + * @return an instance of {@link Logger} which will use the provided + * resource bundle for message localization. + * @throws NullPointerException if {@code name} is {@code null} or + * {@code bundle} is {@code null}. + */ + @CallerSensitive + public static Logger getLogger(String name, ResourceBundle bundle) { + final ResourceBundle rb = Objects.requireNonNull(bundle); + Objects.requireNonNull(name); + final Class caller = Reflection.getCallerClass(); + final SecurityManager sm = System.getSecurityManager(); + // We don't use LazyLoggers if a resource bundle is specified. + // Bootstrap sensitive classes in the JDK do not use resource bundles + // when logging. This could be revisited later, if it needs to. + if (sm != null) { + return AccessController.doPrivileged((PrivilegedAction) + () -> LoggerFinder.accessProvider().getLocalizedLogger(name, rb, caller), + null, + LoggerFinder.LOGGERFINDER_PERMISSION); + } + return LoggerFinder.accessProvider().getLocalizedLogger(name, rb, caller); + } + /** * Terminates the currently running Java Virtual Machine. The * argument serves as a status code; by convention, a nonzero status diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java b/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java index b8f9c52d19f..f385d95c4c9 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java @@ -34,14 +34,16 @@ import java.lang.invoke.LambdaForm.NamedFunction; import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.Field; import java.util.Arrays; -import java.util.HashMap; +import java.util.function.Function; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; +import jdk.internal.org.objectweb.asm.FieldVisitor; import sun.invoke.util.ValueConversions; import sun.invoke.util.Wrapper; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.MethodVisitor; -import jdk.internal.org.objectweb.asm.Type; /** * The flavor of method handle which emulates an invoke instruction @@ -217,7 +219,7 @@ import jdk.internal.org.objectweb.asm.Type; /*non-public*/ int fieldCount() { return 1; } - /*non-public*/ static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("L", Species_L.class); + /*non-public*/ static final SpeciesData SPECIES_DATA = new SpeciesData("L", Species_L.class); /*non-public*/ static BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0) { return new Species_L(mt, lf, argL0); } @@ -335,7 +337,7 @@ import jdk.internal.org.objectweb.asm.Type; static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class); - private SpeciesData(String types, Class clazz) { + SpeciesData(String types, Class clazz) { this.typeChars = types; this.typeCodes = basicTypes(types); this.clazz = clazz; @@ -355,26 +357,14 @@ import jdk.internal.org.objectweb.asm.Type; assert(!INIT_DONE); if (constructor() == null) { String types = typeChars; + CACHE.put(types, this); Factory.makeCtors(clazz, types, this.constructor); Factory.makeGetters(clazz, types, this.getters); Factory.makeNominalGetters(types, this.nominalGetters, this.getters); } } - private SpeciesData(String typeChars) { - // Placeholder only. - this.typeChars = typeChars; - this.typeCodes = basicTypes(typeChars); - this.clazz = null; - this.constructor = null; - this.getters = null; - this.nominalGetters = null; - this.extensions = null; - } - private boolean isPlaceholder() { return clazz == null; } - - private static final HashMap CACHE = new HashMap<>(); - static { CACHE.put("", EMPTY); } // make bootstrap predictable + private static final ConcurrentMap CACHE = new ConcurrentHashMap<>(); private static final boolean INIT_DONE; // set after finishes... SpeciesData extendWith(byte type) { @@ -390,62 +380,52 @@ import jdk.internal.org.objectweb.asm.Type; } private static SpeciesData get(String types) { - // Acquire cache lock for query. - SpeciesData d = lookupCache(types); - if (!d.isPlaceholder()) - return d; - synchronized (d) { - // Use synch. on the placeholder to prevent multiple instantiation of one species. - // Creating this class forces a recursive call to getForClass. - if (lookupCache(types).isPlaceholder()) - Factory.generateConcreteBMHClass(types); - } - // Reacquire cache lock. - d = lookupCache(types); - // Class loading must have upgraded the cache. - assert(d != null && !d.isPlaceholder()); - return d; - } - static SpeciesData getForClass(String types, Class clazz) { - // clazz is a new class which is initializing its SPECIES_DATA field - return updateCache(types, new SpeciesData(types, clazz)); - } - private static synchronized SpeciesData lookupCache(String types) { - SpeciesData d = CACHE.get(types); - if (d != null) return d; - d = new SpeciesData(types); - assert(d.isPlaceholder()); - CACHE.put(types, d); - return d; - } - private static synchronized SpeciesData updateCache(String types, SpeciesData d) { - SpeciesData d2; - assert((d2 = CACHE.get(types)) == null || d2.isPlaceholder()); - assert(!d.isPlaceholder()); - CACHE.put(types, d); - return d; + return CACHE.computeIfAbsent(types, new Function() { + @Override + public SpeciesData apply(String types) { + Class bmhcl = Factory.getConcreteBMHClass(types); + // SpeciesData instantiation may throw VirtualMachineError because of + // code cache overflow... + SpeciesData speciesData = new SpeciesData(types, bmhcl); + // CHM.computeIfAbsent ensures only one SpeciesData will be set + // successfully on the concrete BMH class if ever + Factory.setSpeciesDataToConcreteBMHClass(bmhcl, speciesData); + // the concrete BMH class is published via SpeciesData instance + // returned here only after it's SPECIES_DATA field is set + return speciesData; + } + }); } - static { - // pre-fill the BMH speciesdata cache with BMH's inner classes - final Class rootCls = BoundMethodHandle.class; + /** + * This is to be called when assertions are enabled. It checks whether SpeciesData for all of the statically + * defined species subclasses of BoundMethodHandle has been added to the SpeciesData cache. See below in the + * static initializer for + */ + static boolean speciesDataCachePopulated() { + Class rootCls = BoundMethodHandle.class; try { for (Class c : rootCls.getDeclaredClasses()) { if (rootCls.isAssignableFrom(c)) { final Class cbmh = c.asSubclass(BoundMethodHandle.class); - SpeciesData d = Factory.speciesDataFromConcreteBMHClass(cbmh); + SpeciesData d = Factory.getSpeciesDataFromConcreteBMHClass(cbmh); assert(d != null) : cbmh.getName(); assert(d.clazz == cbmh); - assert(d == lookupCache(d.typeChars)); + assert(CACHE.get(d.typeChars) == d); } } } catch (Throwable e) { throw newInternalError(e); } + return true; + } - for (SpeciesData d : CACHE.values()) { - d.initForBootstrap(); - } + static { + // Pre-fill the BMH species-data cache with EMPTY and all BMH's inner subclasses. + EMPTY.initForBootstrap(); + Species_L.SPECIES_DATA.initForBootstrap(); + // check that all static SpeciesData instances have been initialized + assert speciesDataCachePopulated(); // Note: Do not simplify this, because INIT_DONE must not be // a compile-time constant during bootstrapping. INIT_DONE = Boolean.TRUE; @@ -479,6 +459,7 @@ import jdk.internal.org.objectweb.asm.Type; static final String BMH_SIG = "L"+BMH+";"; static final String SPECIES_DATA = "java/lang/invoke/BoundMethodHandle$SpeciesData"; static final String SPECIES_DATA_SIG = "L"+SPECIES_DATA+";"; + static final String STABLE_SIG = "Ljava/lang/invoke/Stable;"; static final String SPECIES_PREFIX_NAME = "Species_"; static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME; @@ -493,6 +474,26 @@ import jdk.internal.org.objectweb.asm.Type; static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" }; + static final ConcurrentMap> CLASS_CACHE = new ConcurrentHashMap<>(); + + /** + * Get a concrete subclass of BMH for a given combination of bound types. + * + * @param types the type signature, wherein reference types are erased to 'L' + * @return the concrete BMH class + */ + static Class getConcreteBMHClass(String types) { + // CHM.computeIfAbsent ensures generateConcreteBMHClass is called + // only once per key. + return CLASS_CACHE.computeIfAbsent( + types, new Function>() { + @Override + public Class apply(String types) { + return generateConcreteBMHClass(types); + } + }); + } + /** * Generate a concrete subclass of BMH for a given combination of bound types. * @@ -529,7 +530,7 @@ import jdk.internal.org.objectweb.asm.Type; * } * final SpeciesData speciesData() { return SPECIES_DATA; } * final int fieldCount() { return 3; } - * static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LLI", Species_LLI.class); + * @Stable static SpeciesData SPECIES_DATA; // injected afterwards * static BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) { * return new Species_LLI(mt, lf, argL0, argL1, argI2); * } @@ -568,7 +569,9 @@ import jdk.internal.org.objectweb.asm.Type; cw.visitSource(sourceFile, null); // emit static types and SPECIES_DATA fields - cw.visitField(NOT_ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null).visitEnd(); + FieldVisitor fw = cw.visitField(NOT_ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null); + fw.visitAnnotation(STABLE_SIG, true); + fw.visitEnd(); // emit bound argument fields for (int i = 0; i < types.length(); ++i) { @@ -694,17 +697,6 @@ import jdk.internal.org.objectweb.asm.Type; mv.visitEnd(); } - // emit class initializer - mv = cw.visitMethod(NOT_ACC_PUBLIC | ACC_STATIC, "", VOID_SIG, null, null); - mv.visitCode(); - mv.visitLdcInsn(types); - mv.visitLdcInsn(Type.getObjectType(className)); - mv.visitMethodInsn(INVOKESTATIC, SPECIES_DATA, "getForClass", BMHSPECIES_DATA_GFC_SIG, false); - mv.visitFieldInsn(PUTSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG); - mv.visitInsn(RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - cw.visitEnd(); // load class @@ -715,7 +707,6 @@ import jdk.internal.org.objectweb.asm.Type; UNSAFE.defineClass(className, classFile, 0, classFile.length, BoundMethodHandle.class.getClassLoader(), null) .asSubclass(BoundMethodHandle.class); - UNSAFE.ensureClassInitialized(bmhClass); return bmhClass; } @@ -785,7 +776,7 @@ import jdk.internal.org.objectweb.asm.Type; // Auxiliary methods. // - static SpeciesData speciesDataFromConcreteBMHClass(Class cbmh) { + static SpeciesData getSpeciesDataFromConcreteBMHClass(Class cbmh) { try { Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA"); return (SpeciesData) F_SPECIES_DATA.get(null); @@ -794,6 +785,16 @@ import jdk.internal.org.objectweb.asm.Type; } } + static void setSpeciesDataToConcreteBMHClass(Class cbmh, SpeciesData speciesData) { + try { + Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA"); + assert F_SPECIES_DATA.getDeclaredAnnotation(Stable.class) != null; + F_SPECIES_DATA.set(null, speciesData); + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + } + /** * Field names in concrete BMHs adhere to this pattern: * arg + type + index diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java index c70b9db3097..23e0aa9410c 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java @@ -25,7 +25,7 @@ package java.lang.invoke; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import java.lang.reflect.Method; import java.util.Arrays; import sun.invoke.util.VerifyAccess; @@ -224,12 +224,12 @@ class DirectMethodHandle extends MethodHandle { assert(names.length == nameCursor); if (doesAlloc) { // names = { argx,y,z,... new C, init method } - names[NEW_OBJ] = new Name(Lazy.NF_allocateInstance, names[DMH_THIS]); - names[GET_MEMBER] = new Name(Lazy.NF_constructorMethod, names[DMH_THIS]); + names[NEW_OBJ] = new Name(NF_allocateInstance, names[DMH_THIS]); + names[GET_MEMBER] = new Name(NF_constructorMethod, names[DMH_THIS]); } else if (needsInit) { - names[GET_MEMBER] = new Name(Lazy.NF_internalMemberNameEnsureInit, names[DMH_THIS]); + names[GET_MEMBER] = new Name(NF_internalMemberNameEnsureInit, names[DMH_THIS]); } else { - names[GET_MEMBER] = new Name(Lazy.NF_internalMemberName, names[DMH_THIS]); + names[GET_MEMBER] = new Name(NF_internalMemberName, names[DMH_THIS]); } assert(findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]); Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class); @@ -250,9 +250,9 @@ class DirectMethodHandle extends MethodHandle { } static Object findDirectMethodHandle(Name name) { - if (name.function == Lazy.NF_internalMemberName || - name.function == Lazy.NF_internalMemberNameEnsureInit || - name.function == Lazy.NF_constructorMethod) { + if (name.function == NF_internalMemberName || + name.function == NF_internalMemberNameEnsureInit || + name.function == NF_constructorMethod) { assert(name.arguments.length == 1); return name.arguments[0]; } @@ -613,18 +613,18 @@ class DirectMethodHandle extends MethodHandle { final int RESULT = nameCursor-1; // either the call or the cast Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); if (needsInit) - names[INIT_BAR] = new Name(Lazy.NF_ensureInitialized, names[DMH_THIS]); + names[INIT_BAR] = new Name(NF_ensureInitialized, names[DMH_THIS]); if (needsCast && !isGetter) - names[PRE_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[SET_VALUE]); + names[PRE_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[SET_VALUE]); Object[] outArgs = new Object[1 + linkerType.parameterCount()]; assert(outArgs.length == (isGetter ? 3 : 4)); outArgs[0] = UNSAFE; if (isStatic) { - outArgs[1] = names[F_HOLDER] = new Name(Lazy.NF_staticBase, names[DMH_THIS]); - outArgs[2] = names[F_OFFSET] = new Name(Lazy.NF_staticOffset, names[DMH_THIS]); + outArgs[1] = names[F_HOLDER] = new Name(NF_staticBase, names[DMH_THIS]); + outArgs[2] = names[F_OFFSET] = new Name(NF_staticOffset, names[DMH_THIS]); } else { - outArgs[1] = names[OBJ_CHECK] = new Name(Lazy.NF_checkBase, names[OBJ_BASE]); - outArgs[2] = names[F_OFFSET] = new Name(Lazy.NF_fieldOffset, names[DMH_THIS]); + outArgs[1] = names[OBJ_CHECK] = new Name(NF_checkBase, names[OBJ_BASE]); + outArgs[2] = names[F_OFFSET] = new Name(NF_fieldOffset, names[DMH_THIS]); } if (!isGetter) { outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]); @@ -632,7 +632,7 @@ class DirectMethodHandle extends MethodHandle { for (Object a : outArgs) assert(a != null); names[LINKER_CALL] = new Name(linker, outArgs); if (needsCast && isGetter) - names[POST_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[LINKER_CALL]); + names[POST_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[LINKER_CALL]); for (Name n : names) assert(n != null); String fieldOrStatic = (isStatic ? "Static" : "Field"); String lambdaName = (linkerName + fieldOrStatic); // significant only for debugging @@ -645,50 +645,45 @@ class DirectMethodHandle extends MethodHandle { * Pre-initialized NamedFunctions for bootstrapping purposes. * Factored in an inner class to delay initialization until first usage. */ - private static class Lazy { - static final NamedFunction - NF_internalMemberName, - NF_internalMemberNameEnsureInit, - NF_ensureInitialized, - NF_fieldOffset, - NF_checkBase, - NF_staticBase, - NF_staticOffset, - NF_checkCast, - NF_allocateInstance, - NF_constructorMethod; - static { - try { - NamedFunction nfs[] = { - NF_internalMemberName = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("internalMemberName", Object.class)), - NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)), - NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("ensureInitialized", Object.class)), - NF_fieldOffset = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("fieldOffset", Object.class)), - NF_checkBase = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("checkBase", Object.class)), - NF_staticBase = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("staticBase", Object.class)), - NF_staticOffset = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("staticOffset", Object.class)), - NF_checkCast = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("checkCast", Object.class, Object.class)), - NF_allocateInstance = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("allocateInstance", Object.class)), - NF_constructorMethod = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("constructorMethod", Object.class)) - }; - for (NamedFunction nf : nfs) { - // Each nf must be statically invocable or we get tied up in our bootstraps. - assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf; - nf.resolve(); - } - } catch (ReflectiveOperationException ex) { - throw newInternalError(ex); - } + static final NamedFunction + NF_internalMemberName, + NF_internalMemberNameEnsureInit, + NF_ensureInitialized, + NF_fieldOffset, + NF_checkBase, + NF_staticBase, + NF_staticOffset, + NF_checkCast, + NF_allocateInstance, + NF_constructorMethod; + static { + try { + NamedFunction nfs[] = { + NF_internalMemberName = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("internalMemberName", Object.class)), + NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)), + NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("ensureInitialized", Object.class)), + NF_fieldOffset = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("fieldOffset", Object.class)), + NF_checkBase = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("checkBase", Object.class)), + NF_staticBase = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("staticBase", Object.class)), + NF_staticOffset = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("staticOffset", Object.class)), + NF_checkCast = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("checkCast", Object.class, Object.class)), + NF_allocateInstance = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("allocateInstance", Object.class)), + NF_constructorMethod = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("constructorMethod", Object.class)) + }; + // Each nf must be statically invocable or we get tied up in our bootstraps. + assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs)); + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); } } } diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/jdk/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java index 5f821977c0f..9a43947d34e 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java @@ -27,7 +27,7 @@ package java.lang.invoke; import jdk.internal.org.objectweb.asm.*; import sun.invoke.util.BytecodeDescriptor; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import sun.security.action.GetPropertyAction; import java.io.FilePermission; diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java index bcfa877fbf1..0f062f59417 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java @@ -750,7 +750,7 @@ class InvokerBytecodeGenerator { assert(!isLinkerMethodInvoke(name)); // should use the static path for these if (true) { // push receiver - MethodHandle target = name.function.resolvedHandle; + MethodHandle target = name.function.resolvedHandle(); assert(target != null) : name.exprString(); mv.visitLdcInsn(constantPlaceholder(target)); emitReferenceCast(MethodHandle.class, target); @@ -775,10 +775,19 @@ class InvokerBytecodeGenerator { // Sample classes from each package we are willing to bind to statically: java.lang.Object.class, java.util.Arrays.class, - sun.misc.Unsafe.class + jdk.internal.misc.Unsafe.class //MethodHandle.class already covered }; + static boolean isStaticallyInvocable(NamedFunction[] functions) { + for (NamedFunction nf : functions) { + if (!isStaticallyInvocable(nf.member())) { + return false; + } + } + return true; + } + static boolean isStaticallyInvocable(Name name) { return isStaticallyInvocable(name.function.member()); } @@ -881,7 +890,7 @@ class InvokerBytecodeGenerator { // The array will be a constant. Object emptyArray; try { - emptyArray = name.function.resolvedHandle.invoke(); + emptyArray = name.function.resolvedHandle().invoke(); } catch (Throwable ex) { throw newInternalError(ex); } @@ -1085,8 +1094,8 @@ class InvokerBytecodeGenerator { Label L_handler = new Label(); Label L_done = new Label(); - Class returnType = result.function.resolvedHandle.type().returnType(); - MethodType type = args.function.resolvedHandle.type() + Class returnType = result.function.resolvedHandle().type().returnType(); + MethodType type = args.function.resolvedHandle().type() .dropParameterTypes(0,1) .changeReturnType(returnType); diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java b/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java index e674829338e..add42889bac 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java @@ -429,11 +429,8 @@ class Invokers { NF_checkCustomized = new NamedFunction(Invokers.class .getDeclaredMethod("checkCustomized", Object.class)) }; - for (NamedFunction nf : nfs) { - // Each nf must be statically invocable or we get tied up in our bootstraps. - assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf; - nf.resolve(); - } + // Each nf must be statically invocable or we get tied up in our bootstraps. + assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs)); } catch (ReflectiveOperationException ex) { throw newInternalError(ex); } diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java index 09a742f55ec..2023bfabfde 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java @@ -1024,7 +1024,7 @@ class LambdaForm { static class NamedFunction { final MemberName member; - @Stable MethodHandle resolvedHandle; + private @Stable MethodHandle resolvedHandle; @Stable MethodHandle invoker; NamedFunction(MethodHandle resolvedHandle) { @@ -1074,8 +1074,10 @@ class LambdaForm { return resolvedHandle; } - void resolve() { - resolvedHandle = DirectMethodHandle.make(member); + synchronized void resolve() { + if (resolvedHandle == null) { + resolvedHandle = DirectMethodHandle.make(member); + } } @Override @@ -1235,6 +1237,7 @@ class LambdaForm { traceInterpreter("| getInvoker", this); invoker(); } + // resolvedHandle might be uninitialized, ok for tracing if (resolvedHandle == null) { traceInterpreter("| resolve", this); resolvedHandle(); @@ -1704,88 +1707,112 @@ class LambdaForm { private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(); static LambdaForm identityForm(BasicType type) { - return LF_identityForm[type.ordinal()]; + int ord = type.ordinal(); + LambdaForm form = LF_identity[ord]; + if (form != null) { + return form; + } + createFormsFor(type); + return LF_identity[ord]; } + static LambdaForm zeroForm(BasicType type) { - return LF_zeroForm[type.ordinal()]; + int ord = type.ordinal(); + LambdaForm form = LF_zero[ord]; + if (form != null) { + return form; + } + createFormsFor(type); + return LF_zero[ord]; } + static NamedFunction identity(BasicType type) { - return NF_identity[type.ordinal()]; + int ord = type.ordinal(); + NamedFunction function = NF_identity[ord]; + if (function != null) { + return function; + } + createFormsFor(type); + return NF_identity[ord]; } + static NamedFunction constantZero(BasicType type) { - return NF_zero[type.ordinal()]; + int ord = type.ordinal(); + NamedFunction function = NF_zero[ord]; + if (function != null) { + return function; + } + createFormsFor(type); + return NF_zero[ord]; } - private static final LambdaForm[] LF_identityForm = new LambdaForm[TYPE_LIMIT]; - private static final LambdaForm[] LF_zeroForm = new LambdaForm[TYPE_LIMIT]; - private static final NamedFunction[] NF_identity = new NamedFunction[TYPE_LIMIT]; - private static final NamedFunction[] NF_zero = new NamedFunction[TYPE_LIMIT]; - private static void createIdentityForms() { - for (BasicType type : BasicType.ALL_TYPES) { - int ord = type.ordinal(); - char btChar = type.basicTypeChar(); - boolean isVoid = (type == V_TYPE); - Class btClass = type.btClass; - MethodType zeType = MethodType.methodType(btClass); - MethodType idType = isVoid ? zeType : zeType.appendParameterTypes(btClass); - // Look up some symbolic names. It might not be necessary to have these, - // but if we need to emit direct references to bytecodes, it helps. - // Zero is built from a call to an identity function with a constant zero input. - MemberName idMem = new MemberName(LambdaForm.class, "identity_"+btChar, idType, REF_invokeStatic); - MemberName zeMem = new MemberName(LambdaForm.class, "zero_"+btChar, zeType, REF_invokeStatic); - try { + private static final @Stable LambdaForm[] LF_identity = new LambdaForm[TYPE_LIMIT]; + private static final @Stable LambdaForm[] LF_zero = new LambdaForm[TYPE_LIMIT]; + private static final @Stable NamedFunction[] NF_identity = new NamedFunction[TYPE_LIMIT]; + private static final @Stable NamedFunction[] NF_zero = new NamedFunction[TYPE_LIMIT]; + + private static synchronized void createFormsFor(BasicType type) { + final int ord = type.ordinal(); + LambdaForm idForm = LF_identity[ord]; + if (idForm != null) { + return; + } + char btChar = type.basicTypeChar(); + boolean isVoid = (type == V_TYPE); + Class btClass = type.btClass; + MethodType zeType = MethodType.methodType(btClass); + MethodType idType = (isVoid) ? zeType : zeType.appendParameterTypes(btClass); + + // Look up symbolic names. It might not be necessary to have these, + // but if we need to emit direct references to bytecodes, it helps. + // Zero is built from a call to an identity function with a constant zero input. + MemberName idMem = new MemberName(LambdaForm.class, "identity_"+btChar, idType, REF_invokeStatic); + MemberName zeMem = null; + try { + idMem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, idMem, null, NoSuchMethodException.class); + if (!isVoid) { + zeMem = new MemberName(LambdaForm.class, "zero_"+btChar, zeType, REF_invokeStatic); zeMem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, zeMem, null, NoSuchMethodException.class); - idMem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, idMem, null, NoSuchMethodException.class); - } catch (IllegalAccessException|NoSuchMethodException ex) { - throw newInternalError(ex); } - - NamedFunction idFun = new NamedFunction(idMem); - LambdaForm idForm; - if (isVoid) { - Name[] idNames = new Name[] { argument(0, L_TYPE) }; - idForm = new LambdaForm(idMem.getName(), 1, idNames, VOID_RESULT); - } else { - Name[] idNames = new Name[] { argument(0, L_TYPE), argument(1, type) }; - idForm = new LambdaForm(idMem.getName(), 2, idNames, 1); - } - LF_identityForm[ord] = idForm; - NF_identity[ord] = idFun; - - NamedFunction zeFun = new NamedFunction(zeMem); - LambdaForm zeForm; - if (isVoid) { - zeForm = idForm; - } else { - Object zeValue = Wrapper.forBasicType(btChar).zero(); - Name[] zeNames = new Name[] { argument(0, L_TYPE), new Name(idFun, zeValue) }; - zeForm = new LambdaForm(zeMem.getName(), 1, zeNames, 1); - } - LF_zeroForm[ord] = zeForm; - NF_zero[ord] = zeFun; - - assert(idFun.isIdentity()); - assert(zeFun.isConstantZero()); - assert(new Name(zeFun).isConstantZero()); + } catch (IllegalAccessException|NoSuchMethodException ex) { + throw newInternalError(ex); } - // Do this in a separate pass, so that SimpleMethodHandle.make can see the tables. - for (BasicType type : BasicType.ALL_TYPES) { - int ord = type.ordinal(); - NamedFunction idFun = NF_identity[ord]; - LambdaForm idForm = LF_identityForm[ord]; - MemberName idMem = idFun.member; - idFun.resolvedHandle = SimpleMethodHandle.make(idMem.getInvocationType(), idForm); + NamedFunction idFun; + LambdaForm zeForm; + NamedFunction zeFun; - NamedFunction zeFun = NF_zero[ord]; - LambdaForm zeForm = LF_zeroForm[ord]; - MemberName zeMem = zeFun.member; - zeFun.resolvedHandle = SimpleMethodHandle.make(zeMem.getInvocationType(), zeForm); + // Create the LFs and NamedFunctions. Precompiling LFs to byte code is needed to break circular + // bootstrap dependency on this method in case we're interpreting LFs + if (isVoid) { + Name[] idNames = new Name[] { argument(0, L_TYPE) }; + idForm = new LambdaForm(idMem.getName(), 1, idNames, VOID_RESULT); + idForm.compileToBytecode(); + idFun = new NamedFunction(idMem, SimpleMethodHandle.make(idMem.getInvocationType(), idForm)); - assert(idFun.isIdentity()); - assert(zeFun.isConstantZero()); - assert(new Name(zeFun).isConstantZero()); + zeForm = idForm; + zeFun = idFun; + } else { + Name[] idNames = new Name[] { argument(0, L_TYPE), argument(1, type) }; + idForm = new LambdaForm(idMem.getName(), 2, idNames, 1); + idForm.compileToBytecode(); + idFun = new NamedFunction(idMem, SimpleMethodHandle.make(idMem.getInvocationType(), idForm)); + + Object zeValue = Wrapper.forBasicType(btChar).zero(); + Name[] zeNames = new Name[] { argument(0, L_TYPE), new Name(idFun, zeValue) }; + zeForm = new LambdaForm(zeMem.getName(), 1, zeNames, 1); + zeForm.compileToBytecode(); + zeFun = new NamedFunction(zeMem, SimpleMethodHandle.make(zeMem.getInvocationType(), zeForm)); } + + LF_zero[ord] = zeForm; + NF_zero[ord] = zeFun; + LF_identity[ord] = idForm; + NF_identity[ord] = idFun; + + assert(idFun.isIdentity()); + assert(zeFun.isConstantZero()); + assert(new Name(zeFun).isConstantZero()); } // Avoid appealing to ValueConversions at bootstrap time: @@ -1794,13 +1821,12 @@ class LambdaForm { private static float identity_F(float x) { return x; } private static double identity_D(double x) { return x; } private static Object identity_L(Object x) { return x; } - private static void identity_V() { return; } // same as zeroV, but that's OK + private static void identity_V() { return; } private static int zero_I() { return 0; } private static long zero_J() { return 0; } private static float zero_F() { return 0; } private static double zero_D() { return 0; } private static Object zero_L() { return null; } - private static void zero_V() { return; } /** * Internal marker for byte-compiled LambdaForms. @@ -1830,7 +1856,6 @@ class LambdaForm { // Put this last, so that previous static inits can run before. static { - createIdentityForms(); if (USE_PREDEFINED_INTERPRET_METHODS) computeInitialPreparedForms(); NamedFunction.initializeInvokers(); diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java index 7368108920d..753b1988ebc 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java @@ -541,7 +541,7 @@ class LambdaFormEditor { assert(pos > 0); // cannot spread the MH arg itself Name spreadParam = new Name(L_TYPE); - Name checkSpread = new Name(MethodHandleImpl.Lazy.NF_checkSpreadArgument, spreadParam, arrayLength); + Name checkSpread = new Name(MethodHandleImpl.NF_checkSpreadArgument, spreadParam, arrayLength); // insert the new expressions int exprPos = lambdaForm.arity(); diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java index 1f60038e121..5f9580e17f4 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java @@ -694,8 +694,11 @@ import java.util.Objects; @Override public int hashCode() { - return Objects.hash(clazz, getReferenceKind(), name, getType()); + // Avoid autoboxing getReferenceKind(), since this is used early and will force + // early initialization of Byte$ByteCache + return Objects.hash(clazz, new Byte(getReferenceKind()), name, getType()); } + @Override public boolean equals(Object that) { return (that instanceof MemberName && this.equals((MemberName)that)); diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java index 67017b6b02a..3be1b7ea7df 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java @@ -872,13 +872,54 @@ assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray * @see #asCollector */ public MethodHandle asSpreader(Class arrayType, int arrayLength) { - MethodType postSpreadType = asSpreaderChecks(arrayType, arrayLength); - int arity = type().parameterCount(); - int spreadArgPos = arity - arrayLength; + return asSpreader(type().parameterCount() - arrayLength, arrayType, arrayLength); + } + + /** + * Makes an array-spreading method handle, which accepts an array argument at a given position and spreads + * its elements as positional arguments in place of the array. The new method handle adapts, as its target, + * the current method handle. The type of the adapter will be the same as the type of the target, except that the + * {@code arrayLength} parameters of the target's type, starting at the zero-based position {@code spreadArgPos}, + * are replaced by a single array parameter of type {@code arrayType}. + *

+ * This method behaves very much like {@link #asSpreader(Class, int)}, but accepts an additional {@code spreadArgPos} + * argument to indicate at which position in the parameter list the spreading should take place. + *

+ * @apiNote Example: + *

{@code
+    MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class));
+    MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2);
+    Object[] ints = new Object[]{3, 9, 7, 7};
+    Comparator cmp = (a, b) -> a - b;
+    assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0);
+    assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0);
+    assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0);
+     * }
+ * @param spreadArgPos the position (zero-based index) in the argument list at which spreading should start. + * @param arrayType usually {@code Object[]}, the type of the array argument from which to extract the spread arguments + * @param arrayLength the number of arguments to spread from an incoming array argument + * @return a new method handle which spreads an array argument at a given position, + * before calling the original method handle + * @throws NullPointerException if {@code arrayType} is a null reference + * @throws IllegalArgumentException if {@code arrayType} is not an array type, + * or if target does not have at least + * {@code arrayLength} parameter types, + * or if {@code arrayLength} is negative, + * or if {@code spreadArgPos} has an illegal value (negative, or together with arrayLength exceeding the + * number of arguments), + * or if the resulting method handle's type would have + *
too many parameters + * @throws WrongMethodTypeException if the implied {@code asType} call fails + * + * @see #asSpreader(Class, int) + * @since 9 + */ + public MethodHandle asSpreader(int spreadArgPos, Class arrayType, int arrayLength) { + MethodType postSpreadType = asSpreaderChecks(arrayType, spreadArgPos, arrayLength); MethodHandle afterSpread = this.asType(postSpreadType); BoundMethodHandle mh = afterSpread.rebind(); LambdaForm lform = mh.editor().spreadArgumentsForm(1 + spreadArgPos, arrayType, arrayLength); - MethodType preSpreadType = postSpreadType.replaceParameterTypes(spreadArgPos, arity, arrayType); + MethodType preSpreadType = postSpreadType.replaceParameterTypes(spreadArgPos, spreadArgPos + arrayLength, arrayType); return mh.copyWith(preSpreadType, lform); } @@ -886,15 +927,18 @@ assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray * See if {@code asSpreader} can be validly called with the given arguments. * Return the type of the method handle call after spreading but before conversions. */ - private MethodType asSpreaderChecks(Class arrayType, int arrayLength) { + private MethodType asSpreaderChecks(Class arrayType, int pos, int arrayLength) { spreadArrayChecks(arrayType, arrayLength); int nargs = type().parameterCount(); if (nargs < arrayLength || arrayLength < 0) throw newIllegalArgumentException("bad spread array length"); + if (pos < 0 || pos + arrayLength > nargs) { + throw newIllegalArgumentException("bad spread position"); + } Class arrayElement = arrayType.getComponentType(); MethodType mtype = type(); boolean match = true, fail = false; - for (int i = nargs - arrayLength; i < nargs; i++) { + for (int i = pos; i < arrayLength; i++) { Class ptype = mtype.parameterType(i); if (ptype != arrayElement) { match = false; @@ -905,7 +949,7 @@ assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray } } if (match) return mtype; - MethodType needType = mtype.asSpreaderType(arrayType, arrayLength); + MethodType needType = mtype.asSpreaderType(arrayType, pos, arrayLength); if (!fail) return needType; // elicit an error: this.asType(needType); @@ -998,10 +1042,53 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123)); * @see #asVarargsCollector */ public MethodHandle asCollector(Class arrayType, int arrayLength) { - asCollectorChecks(arrayType, arrayLength); - int collectArgPos = type().parameterCount() - 1; + return asCollector(type().parameterCount() - 1, arrayType, arrayLength); + } + + /** + * Makes an array-collecting method handle, which accepts a given number of positional arguments starting + * at a given position, and collects them into an array argument. The new method handle adapts, as its + * target, the current method handle. The type of the adapter will be the same as the type of the target, + * except that the parameter at the position indicated by {@code collectArgPos} (usually of type {@code arrayType}) + * is replaced by {@code arrayLength} parameters whose type is element type of {@code arrayType}. + *

+ * This method behaves very much like {@link #asCollector(Class, int)}, but differs in that its {@code + * collectArgPos} argument indicates at which position in the parameter list arguments should be collected. This + * index is zero-based. + *

+ * @apiNote Examples: + *

{@code
+    StringWriter swr = new StringWriter();
+    MethodHandle swWrite = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, char[].class, int.class, int.class)).bindTo(swr);
+    MethodHandle swWrite4 = swWrite.asCollector(0, char[].class, 4);
+    swWrite4.invoke('A', 'B', 'C', 'D', 1, 2);
+    assertEquals("BC", swr.toString());
+    swWrite4.invoke('P', 'Q', 'R', 'S', 0, 4);
+    assertEquals("BCPQRS", swr.toString());
+    swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1);
+    assertEquals("BCPQRSZ", swr.toString());
+     * }
+ * @param collectArgPos the zero-based position in the parameter list at which to start collecting. + * @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments + * @param arrayLength the number of arguments to collect into a new array argument + * @return a new method handle which collects some arguments + * into an array, before calling the original method handle + * @throws NullPointerException if {@code arrayType} is a null reference + * @throws IllegalArgumentException if {@code arrayType} is not an array type + * or {@code arrayType} is not assignable to this method handle's array parameter type, + * or {@code arrayLength} is not a legal array size, + * or {@code collectArgPos} has an illegal value (negative, or greater than the number of arguments), + * or the resulting method handle's type would have + * too many parameters + * @throws WrongMethodTypeException if the implied {@code asType} call fails + * + * @see #asCollector(Class, int) + * @since 9 + */ + public MethodHandle asCollector(int collectArgPos, Class arrayType, int arrayLength) { + asCollectorChecks(arrayType, collectArgPos, arrayLength); BoundMethodHandle mh = rebind(); - MethodType resultType = type().asCollectorType(arrayType, arrayLength); + MethodType resultType = type().asCollectorType(arrayType, collectArgPos, arrayLength); MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength); LambdaForm lform = mh.editor().collectArgumentArrayForm(1 + collectArgPos, newArray); if (lform != null) { @@ -1015,15 +1102,18 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123)); * See if {@code asCollector} can be validly called with the given arguments. * Return false if the last parameter is not an exact match to arrayType. */ - /*non-public*/ boolean asCollectorChecks(Class arrayType, int arrayLength) { + /*non-public*/ boolean asCollectorChecks(Class arrayType, int pos, int arrayLength) { spreadArrayChecks(arrayType, arrayLength); int nargs = type().parameterCount(); - if (nargs != 0) { - Class lastParam = type().parameterType(nargs-1); - if (lastParam == arrayType) return true; - if (lastParam.isAssignableFrom(arrayType)) return false; + if (pos < 0 || pos >= nargs) { + throw newIllegalArgumentException("bad collect position"); } - throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType); + if (nargs != 0) { + Class param = type().parameterType(pos); + if (param == arrayType) return true; + if (param.isAssignableFrom(arrayType)) return false; + } + throw newIllegalArgumentException("array type not assignable to argument", this, arrayType); } /** @@ -1178,7 +1268,7 @@ assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0))); */ public MethodHandle asVarargsCollector(Class arrayType) { Objects.requireNonNull(arrayType); - boolean lastMatch = asCollectorChecks(arrayType, 0); + boolean lastMatch = asCollectorChecks(arrayType, type().parameterCount() - 1, 0); if (isVarargsCollector() && lastMatch) return this; return MethodHandleImpl.makeVarargsCollector(this, arrayType); @@ -1341,7 +1431,6 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString()); // cannot be cracked into MethodHandleInfo. assert viewAsTypeChecks(newType, strict); BoundMethodHandle mh = rebind(); - assert(!((MethodHandle)mh instanceof DirectMethodHandle)); return mh.copyWith(newType, mh.form); } diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java index 3fad3315ea1..7ad49cd38aa 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -27,16 +27,17 @@ package java.lang.invoke; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Iterator; +import java.util.List; import java.util.function.Function; +import java.util.stream.Collectors; import sun.invoke.empty.Empty; import sun.invoke.util.ValueConversions; import sun.invoke.util.VerifyType; import sun.invoke.util.Wrapper; -import jdk.internal.HotSpotIntrinsicCandidate; import sun.reflect.CallerSensitive; import sun.reflect.Reflection; import static java.lang.invoke.LambdaForm.*; @@ -219,7 +220,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; if (convSpec == null) continue; MethodHandle fn; if (convSpec instanceof Class) { - fn = Lazy.MH_cast.bindTo(convSpec); + fn = getConstantHandle(MH_cast).bindTo(convSpec); } else { fn = (MethodHandle) convSpec; } @@ -239,7 +240,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; if (convSpec == void.class) fn = null; else - fn = Lazy.MH_cast.bindTo(convSpec); + fn = getConstantHandle(MH_cast).bindTo(convSpec); } else { fn = (MethodHandle) convSpec; } @@ -302,7 +303,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; Name conv; if (convSpec instanceof Class) { Class convClass = (Class) convSpec; - conv = new Name(Lazy.MH_cast, convClass, names[INARG_BASE + i]); + conv = new Name(getConstantHandle(MH_cast), convClass, names[INARG_BASE + i]); } else { MethodHandle fn = (MethodHandle) convSpec; conv = new Name(fn, names[INARG_BASE + i]); @@ -326,7 +327,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; conv = new Name(LambdaForm.constantZero(BasicType.basicType(srcType.returnType()))); } else if (convSpec instanceof Class) { Class convClass = (Class) convSpec; - conv = new Name(Lazy.MH_cast, convClass, names[OUT_CALL]); + conv = new Name(getConstantHandle(MH_cast), convClass, names[OUT_CALL]); } else { MethodHandle fn = (MethodHandle) convSpec; if (fn.type().parameterCount() == 0) @@ -529,7 +530,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; // Spread the array. MethodHandle aload = MethodHandles.arrayElementGetter(spreadArgType); Name array = names[argIndex]; - names[nameCursor++] = new Name(Lazy.NF_checkSpreadArgument, array, spreadArgCount); + names[nameCursor++] = new Name(NF_checkSpreadArgument, array, spreadArgCount); for (int j = 0; j < spreadArgCount; i++, j++) { indexes[i] = nameCursor; names[nameCursor++] = new Name(aload, array, j); @@ -566,66 +567,6 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; throw newIllegalArgumentException("array is not of length "+n); } - /** - * Pre-initialized NamedFunctions for bootstrapping purposes. - * Factored in an inner class to delay initialization until first usage. - */ - static class Lazy { - private static final Class MHI = MethodHandleImpl.class; - private static final Class CLS = Class.class; - - private static final MethodHandle[] ARRAYS; - private static final MethodHandle[] FILL_ARRAYS; - - static final NamedFunction NF_checkSpreadArgument; - static final NamedFunction NF_guardWithCatch; - static final NamedFunction NF_throwException; - static final NamedFunction NF_profileBoolean; - - static final MethodHandle MH_cast; - static final MethodHandle MH_selectAlternative; - static final MethodHandle MH_copyAsPrimitiveArray; - static final MethodHandle MH_fillNewTypedArray; - static final MethodHandle MH_fillNewArray; - static final MethodHandle MH_arrayIdentity; - - static { - ARRAYS = makeArrays(); - FILL_ARRAYS = makeFillArrays(); - - try { - NF_checkSpreadArgument = new NamedFunction(MHI.getDeclaredMethod("checkSpreadArgument", Object.class, int.class)); - NF_guardWithCatch = new NamedFunction(MHI.getDeclaredMethod("guardWithCatch", MethodHandle.class, Class.class, - MethodHandle.class, Object[].class)); - NF_throwException = new NamedFunction(MHI.getDeclaredMethod("throwException", Throwable.class)); - NF_profileBoolean = new NamedFunction(MHI.getDeclaredMethod("profileBoolean", boolean.class, int[].class)); - - NF_checkSpreadArgument.resolve(); - NF_guardWithCatch.resolve(); - NF_throwException.resolve(); - NF_profileBoolean.resolve(); - - MH_cast = IMPL_LOOKUP.findVirtual(CLS, "cast", - MethodType.methodType(Object.class, Object.class)); - MH_copyAsPrimitiveArray = IMPL_LOOKUP.findStatic(MHI, "copyAsPrimitiveArray", - MethodType.methodType(Object.class, Wrapper.class, Object[].class)); - MH_arrayIdentity = IMPL_LOOKUP.findStatic(MHI, "identity", - MethodType.methodType(Object[].class, Object[].class)); - MH_fillNewArray = IMPL_LOOKUP.findStatic(MHI, "fillNewArray", - MethodType.methodType(Object[].class, Integer.class, Object[].class)); - MH_fillNewTypedArray = IMPL_LOOKUP.findStatic(MHI, "fillNewTypedArray", - MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class)); - - MH_selectAlternative = makeIntrinsic( - IMPL_LOOKUP.findStatic(MHI, "selectAlternative", - MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)), - Intrinsic.SELECT_ALTERNATIVE); - } catch (ReflectiveOperationException ex) { - throw newInternalError(ex); - } - } - } - /** Factory method: Collect or filter selected argument(s). */ static MethodHandle makeCollectArguments(MethodHandle target, MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) { @@ -911,10 +852,10 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; // profile branch if (PROFILE != -1) { - names[PROFILE] = new Name(Lazy.NF_profileBoolean, names[CALL_TEST], names[GET_COUNTERS]); + names[PROFILE] = new Name(NF_profileBoolean, names[CALL_TEST], names[GET_COUNTERS]); } // call selectAlternative - names[SELECT_ALT] = new Name(Lazy.MH_selectAlternative, names[TEST], names[GET_TARGET], names[GET_FALLBACK]); + names[SELECT_ALT] = new Name(getConstantHandle(MH_selectAlternative), names[TEST], names[GET_TARGET], names[GET_FALLBACK]); // call target or fallback invokeArgs[0] = names[SELECT_ALT]; @@ -989,7 +930,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; // t_{i+1}:L=MethodHandleImpl.guardWithCatch(target:L,exType:L,catcher:L,t_{i}:L); Object[] gwcArgs = new Object[] {names[GET_TARGET], names[GET_CLASS], names[GET_CATCHER], names[BOXED_ARGS]}; - names[TRY_CATCH] = new Name(Lazy.NF_guardWithCatch, gwcArgs); + names[TRY_CATCH] = new Name(NF_guardWithCatch, gwcArgs); // t_{i+2}:I=MethodHandle.invokeBasic(unbox:L,t_{i+1}:L); MethodHandle invokeBasicUnbox = MethodHandles.basicInvoker(MethodType.methodType(basicType.rtype(), Object.class)); @@ -1073,7 +1014,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; mh = MethodHandles.dropArguments(mh, 1, type.parameterList().subList(1, arity)); return mh; } - return makePairwiseConvert(Lazy.NF_throwException.resolvedHandle(), type, false, true); + return makePairwiseConvert(NF_throwException.resolvedHandle(), type, false, true); } static Empty throwException(T t) throws T { throw t; } @@ -1357,7 +1298,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; @Override public MethodHandle asCollector(Class arrayType, int arrayLength) { if (intrinsicName == Intrinsic.IDENTITY) { - MethodType resultType = type().asCollectorType(arrayType, arrayLength); + MethodType resultType = type().asCollectorType(arrayType, type().parameterCount() - 1, arrayLength); MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength); return newArray.asType(resultType); } @@ -1421,25 +1362,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } private static final int ARRAYS_COUNT = 11; - - private static MethodHandle[] makeArrays() { - MethodHandle[] mhs = new MethodHandle[MAX_ARITY + 1]; - for (int i = 0; i < ARRAYS_COUNT; i++) { - MethodHandle mh = findCollector("array", i, Object[].class); - mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY); - mhs[i] = mh; - } - assert(assertArrayMethodCount(mhs)); - return mhs; - } - - private static boolean assertArrayMethodCount(MethodHandle[] mhs) { - assert(findCollector("array", ARRAYS_COUNT, Object[].class) == null); - for (int i = 0; i < ARRAYS_COUNT; i++) { - assert(mhs[i] != null); - } - return true; - } + private static final @Stable MethodHandle[] ARRAYS = new MethodHandle[MAX_ARITY + 1]; // filling versions of the above: // using Integer len instead of int len and no varargs to avoid bootstrapping problems @@ -1488,24 +1411,17 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; } private static final int FILL_ARRAYS_COUNT = 11; // current number of fillArray methods + private static final @Stable MethodHandle[] FILL_ARRAYS = new MethodHandle[FILL_ARRAYS_COUNT]; - private static MethodHandle[] makeFillArrays() { - MethodHandle[] mhs = new MethodHandle[FILL_ARRAYS_COUNT]; - mhs[0] = null; // there is no empty fill; at least a0 is required - for (int i = 1; i < FILL_ARRAYS_COUNT; i++) { - MethodHandle mh = findCollector("fillArray", i, Object[].class, Integer.class, Object[].class); - mhs[i] = mh; + private static MethodHandle getFillArray(int count) { + assert (count > 0 && count < FILL_ARRAYS_COUNT); + MethodHandle mh = FILL_ARRAYS[count]; + if (mh != null) { + return mh; } - assert(assertFillArrayMethodCount(mhs)); - return mhs; - } - - private static boolean assertFillArrayMethodCount(MethodHandle[] mhs) { - assert(findCollector("fillArray", FILL_ARRAYS_COUNT, Object[].class, Integer.class, Object[].class) == null); - for (int i = 1; i < FILL_ARRAYS_COUNT; i++) { - assert(mhs[i] != null); - } - return true; + mh = findCollector("fillArray", count, Object[].class, Integer.class, Object[].class); + FILL_ARRAYS[count] = mh; + return mh; } private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) { @@ -1518,12 +1434,19 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; * arguments and returns an Object array of them, as if for varargs. */ static MethodHandle varargsArray(int nargs) { - MethodHandle mh = Lazy.ARRAYS[nargs]; - if (mh != null) return mh; - mh = buildVarargsArray(Lazy.MH_fillNewArray, Lazy.MH_arrayIdentity, nargs); + MethodHandle mh = ARRAYS[nargs]; + if (mh != null) { + return mh; + } + if (nargs < ARRAYS_COUNT) { + mh = findCollector("array", nargs, Object[].class); + } else { + mh = buildVarargsArray(getConstantHandle(MH_fillNewArray), + getConstantHandle(MH_arrayIdentity), nargs); + } assert(assertCorrectArity(mh, nargs)); mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY); - return Lazy.ARRAYS[nargs] = mh; + return ARRAYS[nargs] = mh; } private static boolean assertCorrectArity(MethodHandle mh, int arity) { @@ -1531,7 +1454,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; return true; } - // Array identity function (used as Lazy.MH_arrayIdentity). + // Array identity function (used as getConstantHandle(MH_arrayIdentity)). static T[] identity(T[] x) { return x; } @@ -1547,12 +1470,12 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; MethodHandle mh = finisher; if (rightLen > 0) { MethodHandle rightFiller = fillToRight(LEFT_ARGS + rightLen); - if (mh == Lazy.MH_arrayIdentity) + if (mh.equals(getConstantHandle(MH_arrayIdentity))) mh = rightFiller; else mh = MethodHandles.collectArguments(mh, 0, rightFiller); } - if (mh == Lazy.MH_arrayIdentity) + if (mh.equals(getConstantHandle(MH_arrayIdentity))) mh = leftCollector; else mh = MethodHandles.collectArguments(mh, 0, leftCollector); @@ -1560,7 +1483,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; } private static final int LEFT_ARGS = FILL_ARRAYS_COUNT - 1; - private static final MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1]; + private static final @Stable MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1]; /** fill_array_to_right(N).invoke(a, argL..arg[N-1]) * fills a[L]..a[N-1] with corresponding arguments, * and then returns a. The value L is a global constant (LEFT_ARGS). @@ -1574,7 +1497,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; } private static MethodHandle buildFiller(int nargs) { if (nargs <= LEFT_ARGS) - return Lazy.MH_arrayIdentity; // no args to fill; return the array unchanged + return getConstantHandle(MH_arrayIdentity); // no args to fill; return the array unchanged // we need room for both mh and a in mh.invoke(a, arg*[nargs]) final int CHUNK = LEFT_ARGS; int rightLen = nargs % CHUNK; @@ -1590,7 +1513,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; if (midLen < LEFT_ARGS) rightLen = nargs - (midLen = LEFT_ARGS); assert(rightLen > 0); MethodHandle midFill = fillToRight(midLen); // recursive fill - MethodHandle rightFill = Lazy.FILL_ARRAYS[rightLen].bindTo(midLen); // [midLen..nargs-1] + MethodHandle rightFill = getFillArray(rightLen).bindTo(midLen); // [midLen..nargs-1] assert(midFill.type().parameterCount() == 1 + midLen - LEFT_ARGS); assert(rightFill.type().parameterCount() == 1 + rightLen); @@ -1641,14 +1564,14 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; Object example = java.lang.reflect.Array.newInstance(arrayType.getComponentType(), 0); mh = MethodHandles.constant(arrayType, example); } else if (elemType.isPrimitive()) { - MethodHandle builder = Lazy.MH_fillNewArray; + MethodHandle builder = getConstantHandle(MH_fillNewArray); MethodHandle producer = buildArrayProducer(arrayType); mh = buildVarargsArray(builder, producer, nargs); } else { Class objArrayType = arrayType.asSubclass(Object[].class); Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType); - MethodHandle builder = Lazy.MH_fillNewTypedArray.bindTo(example); - MethodHandle producer = Lazy.MH_arrayIdentity; // must be weakly typed + MethodHandle builder = getConstantHandle(MH_fillNewTypedArray).bindTo(example); + MethodHandle producer = getConstantHandle(MH_arrayIdentity); // must be weakly typed mh = buildVarargsArray(builder, producer, nargs); } mh = mh.asType(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType))); @@ -1662,7 +1585,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; private static MethodHandle buildArrayProducer(Class arrayType) { Class elemType = arrayType.getComponentType(); assert(elemType.isPrimitive()); - return Lazy.MH_copyAsPrimitiveArray.bindTo(Wrapper.forPrimitiveType(elemType)); + return getConstantHandle(MH_copyAsPrimitiveArray).bindTo(Wrapper.forPrimitiveType(elemType)); } /*non-public*/ static void assertSame(Object mh1, Object mh2) { @@ -1673,4 +1596,346 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; throw newInternalError(msg); } } + + // Local constant functions: + /*non-public*/ static final NamedFunction + NF_checkSpreadArgument, + NF_guardWithCatch, + NF_throwException, + NF_profileBoolean; + + static { + try { + NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class + .getDeclaredMethod("checkSpreadArgument", Object.class, int.class)); + NF_guardWithCatch = new NamedFunction(MethodHandleImpl.class + .getDeclaredMethod("guardWithCatch", MethodHandle.class, Class.class, + MethodHandle.class, Object[].class)); + NF_throwException = new NamedFunction(MethodHandleImpl.class + .getDeclaredMethod("throwException", Throwable.class)); + NF_profileBoolean = new NamedFunction(MethodHandleImpl.class + .getDeclaredMethod("profileBoolean", boolean.class, int[].class)); + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + } + + /** + * Assembles a loop method handle from the given handles and type information. This works by binding and configuring + * the {@linkplain #looper(MethodHandle[], MethodHandle[], MethodHandle[], MethodHandle[], int, int, Object[]) "most + * generic loop"}. + * + * @param tloop the return type of the loop. + * @param targs types of the arguments to be passed to the loop. + * @param tvars types of loop-local variables. + * @param init sanitized array of initializers for loop-local variables. + * @param step sanitited array of loop bodies. + * @param pred sanitized array of predicates. + * @param fini sanitized array of loop finalizers. + * + * @return a handle that, when invoked, will execute the loop. + */ + static MethodHandle makeLoop(Class tloop, List> targs, List> tvars, List init, + List step, List pred, List fini) { + MethodHandle[] ainit = toArrayArgs(init); + MethodHandle[] astep = toArrayArgs(step); + MethodHandle[] apred = toArrayArgs(pred); + MethodHandle[] afini = toArrayArgs(fini); + + MethodHandle l = getConstantHandle(MH_looper); + + // Bind the statically known arguments. + l = MethodHandles.insertArguments(l, 0, ainit, astep, apred, afini, tvars.size(), targs.size()); + + // Turn the args array into an argument list. + l = l.asCollector(Object[].class, targs.size()); + + // Finally, make loop type. + MethodType loopType = MethodType.methodType(tloop, targs); + l = l.asType(loopType); + + return l; + } + + /** + * Converts all handles in the {@code hs} array to handles that accept an array of arguments. + * + * @param hs method handles to be converted. + * + * @return the {@code hs} array, with all method handles therein converted. + */ + static MethodHandle[] toArrayArgs(List hs) { + return hs.stream().map(h -> h.asSpreader(Object[].class, h.type().parameterCount())).toArray(MethodHandle[]::new); + } + + /** + * This method embodies the most generic loop for use by {@link MethodHandles#loop(MethodHandle[][])}. A handle on + * it will be transformed into a handle on a concrete loop instantiation by {@link #makeLoop}. + * + * @param init loop-local variable initializers. + * @param step bodies. + * @param pred predicates. + * @param fini finalizers. + * @param varSize number of loop-local variables. + * @param nArgs number of arguments passed to the loop. + * @param args arguments to the loop invocation. + * + * @return the result of executing the loop. + */ + static Object looper(MethodHandle[] init, MethodHandle[] step, MethodHandle[] pred, MethodHandle[] fini, + int varSize, int nArgs, Object[] args) throws Throwable { + Object[] varsAndArgs = new Object[varSize + nArgs]; + for (int i = 0, v = 0; i < init.length; ++i) { + if (init[i].type().returnType() == void.class) { + init[i].invoke(args); + } else { + varsAndArgs[v++] = init[i].invoke(args); + } + } + System.arraycopy(args, 0, varsAndArgs, varSize, nArgs); + final int nSteps = step.length; + for (; ; ) { + for (int i = 0, v = 0; i < nSteps; ++i) { + MethodHandle p = pred[i]; + MethodHandle s = step[i]; + MethodHandle f = fini[i]; + if (s.type().returnType() == void.class) { + s.invoke(varsAndArgs); + } else { + varsAndArgs[v++] = s.invoke(varsAndArgs); + } + if (!(boolean) p.invoke(varsAndArgs)) { + return f.invoke(varsAndArgs); + } + } + } + } + + /** + * This method is bound as the predicate in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle, + * MethodHandle) counting loops}. + * + * @param counter the counter parameter, passed in during loop execution. + * @param limit the upper bound of the parameter, statically bound at loop creation time. + * + * @return whether the counter has reached the limit. + */ + static boolean countedLoopPredicate(int counter, int limit) { + return counter <= limit; + } + + /** + * This method is bound as the step function in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle, + * MethodHandle) counting loops} to increment the counter. + * + * @param counter the loop counter. + * + * @return the loop counter incremented by 1. + */ + static int countedLoopStep(int counter, int limit) { + return counter + 1; + } + + /** + * This is bound to initialize the loop-local iterator in {@linkplain MethodHandles#iteratedLoop iterating loops}. + * + * @param it the {@link Iterable} over which the loop iterates. + * + * @return an {@link Iterator} over the argument's elements. + */ + static Iterator initIterator(Iterable it) { + return it.iterator(); + } + + /** + * This method is bound as the predicate in {@linkplain MethodHandles#iteratedLoop iterating loops}. + * + * @param it the iterator to be checked. + * + * @return {@code true} iff there are more elements to iterate over. + */ + static boolean iteratePredicate(Iterator it) { + return it.hasNext(); + } + + /** + * This method is bound as the step for retrieving the current value from the iterator in {@linkplain + * MethodHandles#iteratedLoop iterating loops}. + * + * @param it the iterator. + * + * @return the next element from the iterator. + */ + static Object iterateNext(Iterator it) { + return it.next(); + } + + /** + * Makes a {@code try-finally} handle that conforms to the type constraints. + * + * @param target the target to execute in a {@code try-finally} block. + * @param cleanup the cleanup to execute in the {@code finally} block. + * @param type the result type of the entire construct. + * @param argTypes the types of the arguments. + * + * @return a handle on the constructed {@code try-finally} block. + */ + static MethodHandle makeTryFinally(MethodHandle target, MethodHandle cleanup, Class type, List> argTypes) { + MethodHandle tf = getConstantHandle(type == void.class ? MH_tryFinallyVoidExec : MH_tryFinallyExec); + + // Bind the statically known arguments. + tf = MethodHandles.insertArguments(tf, 0, target, cleanup); + + // Turn the args array into an argument list. + tf = tf.asCollector(Object[].class, argTypes.size()); + + // Finally, make try-finally type. + MethodType tfType = MethodType.methodType(type, argTypes); + tf = tf.asType(tfType); + + return tf; + } + + /** + * A method that will be bound during construction of a {@code try-finally} handle with non-{@code void} return type + * by {@link MethodHandles#tryFinally(MethodHandle, MethodHandle)}. + * + * @param target the handle to wrap in a {@code try-finally} block. This will be bound. + * @param cleanup the handle to run in any case before returning. This will be bound. + * @param args the arguments to the call. These will remain as the argument list. + * + * @return whatever the execution of the {@code target} returned (it may have been modified by the execution of + * {@code cleanup}). + * @throws Throwable in case anything is thrown by the execution of {@code target}, the {@link Throwable} will be + * passed to the {@code cleanup} handle, which may decide to throw any exception it sees fit. + */ + static Object tryFinallyExecutor(MethodHandle target, MethodHandle cleanup, Object[] args) throws Throwable { + Throwable t = null; + Object r = null; + try { + r = target.invoke(args); + } catch (Throwable thrown) { + t = thrown; + throw t; + } finally { + r = cleanup.invoke(t, r, args); + } + return r; + } + + /** + * A method that will be bound during construction of a {@code try-finally} handle with {@code void} return type by + * {@link MethodHandles#tryFinally(MethodHandle, MethodHandle)}. + * + * @param target the handle to wrap in a {@code try-finally} block. This will be bound. + * @param cleanup the handle to run in any case before returning. This will be bound. + * @param args the arguments to the call. These will remain as the argument list. + * + * @throws Throwable in case anything is thrown by the execution of {@code target}, the {@link Throwable} will be + * passed to the {@code cleanup} handle, which may decide to throw any exception it sees fit. + */ + static void tryFinallyVoidExecutor(MethodHandle target, MethodHandle cleanup, Object[] args) throws Throwable { + Throwable t = null; + try { + target.invoke(args); + } catch (Throwable thrown) { + t = thrown; + throw t; + } finally { + cleanup.invoke(t, args); + } + } + + // Indexes into constant method handles: + static final int + MH_cast = 0, + MH_selectAlternative = 1, + MH_copyAsPrimitiveArray = 2, + MH_fillNewTypedArray = 3, + MH_fillNewArray = 4, + MH_arrayIdentity = 5, + MH_looper = 6, + MH_countedLoopPred = 7, + MH_countedLoopStep = 8, + MH_iteratePred = 9, + MH_initIterator = 10, + MH_iterateNext = 11, + MH_tryFinallyExec = 12, + MH_tryFinallyVoidExec = 13, + MH_LIMIT = 14; + + static MethodHandle getConstantHandle(int idx) { + MethodHandle handle = HANDLES[idx]; + if (handle != null) { + return handle; + } + return setCachedHandle(idx, makeConstantHandle(idx)); + } + + private static synchronized MethodHandle setCachedHandle(int idx, final MethodHandle method) { + // Simulate a CAS, to avoid racy duplication of results. + MethodHandle prev = HANDLES[idx]; + if (prev != null) { + return prev; + } + HANDLES[idx] = method; + return method; + } + + // Local constant method handles: + private static final @Stable MethodHandle[] HANDLES = new MethodHandle[MH_LIMIT]; + + private static MethodHandle makeConstantHandle(int idx) { + try { + switch (idx) { + case MH_cast: + return IMPL_LOOKUP.findVirtual(Class.class, "cast", + MethodType.methodType(Object.class, Object.class)); + case MH_copyAsPrimitiveArray: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "copyAsPrimitiveArray", + MethodType.methodType(Object.class, Wrapper.class, Object[].class)); + case MH_arrayIdentity: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "identity", + MethodType.methodType(Object[].class, Object[].class)); + case MH_fillNewArray: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "fillNewArray", + MethodType.methodType(Object[].class, Integer.class, Object[].class)); + case MH_fillNewTypedArray: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "fillNewTypedArray", + MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class)); + case MH_selectAlternative: + return makeIntrinsic(IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative", + MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)), + Intrinsic.SELECT_ALTERNATIVE); + case MH_looper: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "looper", MethodType.methodType(Object.class, + MethodHandle[].class, MethodHandle[].class, MethodHandle[].class, MethodHandle[].class, + int.class, int.class, Object[].class)); + case MH_countedLoopPred: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "countedLoopPredicate", + MethodType.methodType(boolean.class, int.class, int.class)); + case MH_countedLoopStep: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "countedLoopStep", + MethodType.methodType(int.class, int.class, int.class)); + case MH_iteratePred: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iteratePredicate", + MethodType.methodType(boolean.class, Iterator.class)); + case MH_initIterator: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "initIterator", + MethodType.methodType(Iterator.class, Iterable.class)); + case MH_iterateNext: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iterateNext", + MethodType.methodType(Object.class, Iterator.class)); + case MH_tryFinallyExec: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "tryFinallyExecutor", + MethodType.methodType(Object.class, MethodHandle.class, MethodHandle.class, Object[].class)); + case MH_tryFinallyVoidExec: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "tryFinallyVoidExecutor", + MethodType.methodType(void.class, MethodHandle.class, MethodHandle.class, Object[].class)); + } + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + throw newInternalError("Unknown function index: " + idx); + } } diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java index 29bd6dbf512..f983b2d792a 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java @@ -50,7 +50,7 @@ class MethodHandleNatives { static native int getMembers(Class defc, String matchName, String matchSig, int matchFlags, Class caller, int skip, MemberName[] results); - /// Field layout queries parallel to sun.misc.Unsafe: + /// Field layout queries parallel to jdk.internal.misc.Unsafe: static native long objectFieldOffset(MemberName self); // e.g., returns vmindex static native long staticFieldOffset(MemberName self); // e.g., returns vmindex static native Object staticFieldBase(MemberName self); // e.g., returns clazz diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java index aae6053acf2..c449f4cb591 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java @@ -27,7 +27,7 @@ package java.lang.invoke; import java.security.AccessController; import java.security.PrivilegedAction; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** * This class consists exclusively of static names internal to the diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java index 13520e45f1c..8378ab9e1c3 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -26,10 +26,7 @@ package java.lang.invoke; import java.lang.reflect.*; -import java.util.BitSet; -import java.util.List; -import java.util.Arrays; -import java.util.Objects; +import java.util.*; import sun.invoke.util.ValueConversions; import sun.invoke.util.VerifyAccess; @@ -39,11 +36,13 @@ import sun.reflect.Reflection; import sun.reflect.misc.ReflectUtil; import sun.security.util.SecurityConstants; import java.lang.invoke.LambdaForm.BasicType; -import static java.lang.invoke.LambdaForm.BasicType.*; + import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleImpl.Intrinsic; import static java.lang.invoke.MethodHandleNatives.Constants.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * This class consists exclusively of static methods that operate on or return @@ -176,7 +175,7 @@ public class MethodHandles { * equivalent of a particular bytecode behavior. * (Bytecode behaviors are described in section 5.4.3.5 of the Java Virtual Machine Specification.) * Here is a summary of the correspondence between these factory methods and - * the behavior the resulting method handles: + * the behavior of the resulting method handles: * * * @@ -235,6 +234,10 @@ public class MethodHandles { * * * + * + * + * + * *
lookup expression{@link java.lang.invoke.MethodHandles.Lookup#unreflect lookup.unreflect(aMethod)}({@code static})?
{@code T m(A*);}
{@code (T) aMethod.invoke(thisOrNull, arg*);}
{@link java.lang.invoke.MethodHandles.Lookup#findClass lookup.findClass("C")}{@code class C { ... }}{@code C.class;}
* * Here, the type {@code C} is the class or interface being searched for a member, @@ -255,6 +258,10 @@ public class MethodHandles { * The names {@code aMethod}, {@code aField}, and {@code aConstructor} stand * for reflective objects corresponding to the given members. *

+ * The bytecode behavior for a {@code findClass} operation is a load of a constant class, + * as if by {@code ldc CONSTANT_Class}. + * The behavior is represented, not as a method handle, but directly as a {@code Class} constant. + *

* In cases where the given member is of variable arity (i.e., a method or constructor) * the returned method handle will also be of {@linkplain MethodHandle#asVarargsCollector variable arity}. * In all other cases, the returned method handle will be of fixed arity. @@ -423,7 +430,7 @@ public class MethodHandles { * and the Core Reflection API * (as found on {@link java.lang.Class Class}). *

- * If a security manager is present, member lookups are subject to + * If a security manager is present, member and class lookups are subject to * additional checks. * From one to three calls are made to the security manager. * Any of these calls can refuse access by throwing a @@ -433,6 +440,8 @@ public class MethodHandles { * {@code refc} as the containing class in which the member * is being sought, and {@code defc} as the class in which the * member is actually defined. + * (If a class or other type is being accessed, + * the {@code refc} and {@code defc} values are the class itself.) * The value {@code lookc} is defined as not present * if the current lookup object does not have * private access. @@ -444,11 +453,16 @@ public class MethodHandles { * then {@link SecurityManager#checkPackageAccess * smgr.checkPackageAccess(refcPkg)} is called, * where {@code refcPkg} is the package of {@code refc}. - *

  • Step 2: + *
  • Step 2a: * If the retrieved member is not public and * {@code lookc} is not present, then * {@link SecurityManager#checkPermission smgr.checkPermission} * with {@code RuntimePermission("accessDeclaredMembers")} is called. + *
  • Step 2b: + * If the retrieved class has a {@code null} class loader, + * and {@code lookc} is not present, then + * {@link SecurityManager#checkPermission smgr.checkPermission} + * with {@code RuntimePermission("getClassLoader")} is called. *
  • Step 3: * If the retrieved member is not public, * and if {@code lookc} is not present, @@ -458,9 +472,9 @@ public class MethodHandles { * where {@code defcPkg} is the package of {@code defc}. * * Security checks are performed after other access checks have passed. - * Therefore, the above rules presuppose a member that is public, + * Therefore, the above rules presuppose a member or class that is public, * or else that is being accessed from a lookup class that has - * rights to access the member. + * rights to access the member or class. * *

    Caller sensitive methods

    * A small number of Java methods have a special property called caller sensitivity. @@ -921,6 +935,49 @@ assertEquals("[x, y, z]", pb.command().toString()); return getDirectConstructor(refc, ctor); } + /** + * Looks up a class by name from the lookup context defined by this {@code Lookup} object. The static + * initializer of the class is not run. + * + * @param targetName the fully qualified name of the class to be looked up. + * @return the requested class. + * @exception SecurityException if a security manager is present and it + * refuses access + * @throws LinkageError if the linkage fails + * @throws ClassNotFoundException if the class does not exist. + * @throws IllegalAccessException if the class is not accessible, using the allowed access + * modes. + * @exception SecurityException if a security manager is present and it + * refuses access + * @since 9 + */ + public Class findClass(String targetName) throws ClassNotFoundException, IllegalAccessException { + Class targetClass = Class.forName(targetName, false, lookupClass.getClassLoader()); + return accessClass(targetClass); + } + + /** + * Determines if a class can be accessed from the lookup context defined by this {@code Lookup} object. The + * static initializer of the class is not run. + * + * @param targetClass the class to be access-checked + * + * @return the class that has been access-checked + * + * @throws IllegalAccessException if the class is not accessible from the lookup class, using the allowed access + * modes. + * @exception SecurityException if a security manager is present and it + * refuses access + * @since 9 + */ + public Class accessClass(Class targetClass) throws IllegalAccessException { + if (!VerifyAccess.isClassAccessible(targetClass, lookupClass, allowedModes)) { + throw new MemberName(targetClass).makeAccessException("access violation", this); + } + checkSecurityManager(targetClass, null); + return targetClass; + } + /** * Produces an early-bound method handle for a virtual method. * It will bypass checks for overriding methods on the receiver, @@ -995,7 +1052,7 @@ assertEquals(""+l, (String) MH_this.invokeExact(subl)); // Listie method */ public MethodHandle findSpecial(Class refc, String name, MethodType type, Class specialCaller) throws NoSuchMethodException, IllegalAccessException { - checkSpecialCaller(specialCaller); + checkSpecialCaller(specialCaller, refc); Lookup specialLookup = this.in(specialCaller); MemberName method = specialLookup.resolveOrFail(REF_invokeSpecial, refc, name, type); return specialLookup.getDirectMethod(REF_invokeSpecial, refc, method, findBoundCallerClass(method)); @@ -1224,7 +1281,7 @@ return mh1; * @throws NullPointerException if any argument is null */ public MethodHandle unreflectSpecial(Method m, Class specialCaller) throws IllegalAccessException { - checkSpecialCaller(specialCaller); + checkSpecialCaller(specialCaller, null); Lookup specialLookup = this.in(specialCaller); MemberName method = new MemberName(m, true); assert(method.isMethod()); @@ -1444,7 +1501,15 @@ return mh1; ReflectUtil.checkPackageAccess(refc); } - // Step 2: + if (m == null) { // findClass or accessClass + // Step 2b: + if (!fullPowerLookup) { + smgr.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION); + } + return; + } + + // Step 2a: if (m.isPublic()) return; if (!fullPowerLookup) { smgr.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION); @@ -1557,11 +1622,13 @@ return mh1; private static final boolean ALLOW_NESTMATE_ACCESS = false; - private void checkSpecialCaller(Class specialCaller) throws IllegalAccessException { + private void checkSpecialCaller(Class specialCaller, Class refc) throws IllegalAccessException { int allowedModes = this.allowedModes; if (allowedModes == TRUSTED) return; if (!hasPrivateAccess() || (specialCaller != lookupClass() + // ensure non-abstract methods in superinterfaces can be special-invoked + && !(refc != null && refc.isInterface() && refc.isAssignableFrom(specialCaller)) && !(ALLOW_NESTMATE_ACCESS && VerifyAccess.isSamePackageMember(specialCaller, lookupClass())))) throw new MemberName(specialCaller). @@ -1888,7 +1955,7 @@ return invoker; MethodHandle spreadInvoker(MethodType type, int leadingArgCount) { if (leadingArgCount < 0 || leadingArgCount > type.parameterCount()) throw newIllegalArgumentException("bad argument count", leadingArgCount); - type = type.asSpreaderType(Object[].class, type.parameterCount() - leadingArgCount); + type = type.asSpreaderType(Object[].class, leadingArgCount, type.parameterCount() - leadingArgCount); return type.invokers().spreadInvoker(leadingArgCount); } @@ -2924,19 +2991,7 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); */ public static MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) { - int foldPos = 0; - MethodType targetType = target.type(); - MethodType combinerType = combiner.type(); - Class rtype = foldArgumentChecks(foldPos, targetType, combinerType); - BoundMethodHandle result = target.rebind(); - boolean dropResult = (rtype == void.class); - // Note: This may cache too many distinct LFs. Consider backing off to varargs code. - LambdaForm lform = result.editor().foldArgumentsForm(1 + foldPos, dropResult, combinerType.basicType()); - MethodType newType = targetType; - if (!dropResult) - newType = newType.dropParameterTypes(foldPos, foldPos + 1); - result = result.copyWithExtendL(newType, lform, combiner); - return result; + return foldArguments(target, 0, combiner); } private static Class foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) { @@ -2949,7 +3004,7 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); .equals(targetType.parameterList().subList(afterInsertPos, afterInsertPos + foldArgs)))) ok = false; - if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(0)) + if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(foldPos)) ok = false; if (!ok) throw misMatchedTypes("target and combiner types", targetType, combinerType); @@ -3011,7 +3066,7 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); return MethodHandleImpl.makeGuardWithTest(test, target, fallback); } - static RuntimeException misMatchedTypes(String what, MethodType t1, MethodType t2) { + static RuntimeException misMatchedTypes(String what, T t1, T t2) { return newIllegalArgumentException(what + " must match: " + t1 + " != " + t2); } @@ -3057,6 +3112,7 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); * the given exception type, or if the method handle types do * not match in their return types and their * corresponding parameters + * @see MethodHandles#tryFinally(MethodHandle, MethodHandle) */ public static MethodHandle catchException(MethodHandle target, @@ -3100,4 +3156,913 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); throw new ClassCastException(exType.getName()); return MethodHandleImpl.throwException(MethodType.methodType(returnType, exType)); } + + /** + * Constructs a method handle representing a loop with several loop variables that are updated and checked upon each + * iteration. Upon termination of the loop due to one of the predicates, a corresponding finalizer is run and + * delivers the loop's result, which is the return value of the resulting handle. + *

    + * Intuitively, every loop is formed by one or more "clauses", each specifying a local iteration value and/or a loop + * exit. Each iteration of the loop executes each clause in order. A clause can optionally update its iteration + * variable; it can also optionally perform a test and conditional loop exit. In order to express this logic in + * terms of method handles, each clause will determine four actions:

      + *
    • Before the loop executes, the initialization of an iteration variable or loop invariant local. + *
    • When a clause executes, an update step for the iteration variable. + *
    • When a clause executes, a predicate execution to test for loop exit. + *
    • If a clause causes a loop exit, a finalizer execution to compute the loop's return value. + *
    + *

    + * Some of these clause parts may be omitted according to certain rules, and useful default behavior is provided in + * this case. See below for a detailed description. + *

    + * Each clause function, with the exception of clause initializers, is able to observe the entire loop state, + * because it will be passed all current iteration variable values, as well as all incoming loop + * parameters. Most clause functions will not need all of this information, but they will be formally connected as + * if by {@link #dropArguments}. + *

    + * Given a set of clauses, there is a number of checks and adjustments performed to connect all the parts of the + * loop. They are spelled out in detail in the steps below. In these steps, every occurrence of the word "must" + * corresponds to a place where {@link IllegalArgumentException} may be thrown if the required constraint is not met + * by the inputs to the loop combinator. The term "effectively identical", applied to parameter type lists, means + * that they must be identical, or else one list must be a proper prefix of the other. + *

    + * Step 0: Determine clause structure.

      + *
    1. The clause array (of type {@code MethodHandle[][]} must be non-{@code null} and contain at least one element. + *
    2. The clause array may not contain {@code null}s or sub-arrays longer than four elements. + *
    3. Clauses shorter than four elements are treated as if they were padded by {@code null} elements to length + * four. Padding takes place by appending elements to the array. + *
    4. Clauses with all {@code null}s are disregarded. + *
    5. Each clause is treated as a four-tuple of functions, called "init", "step", "pred", and "fini". + *
    + *

    + * Step 1A: Determine iteration variables.

      + *
    1. Examine init and step function return types, pairwise, to determine each clause's iteration variable type. + *
    2. If both functions are omitted, use {@code void}; else if one is omitted, use the other's return type; else + * use the common return type (they must be identical). + *
    3. Form the list of return types (in clause order), omitting all occurrences of {@code void}. + *
    4. This list of types is called the "common prefix". + *
    + *

    + * Step 1B: Determine loop parameters.

      + *
    1. Examine init function parameter lists. + *
    2. Omitted init functions are deemed to have {@code null} parameter lists. + *
    3. All init function parameter lists must be effectively identical. + *
    4. The longest parameter list (which is necessarily unique) is called the "common suffix". + *
    + *

    + * Step 1C: Determine loop return type.

      + *
    1. Examine fini function return types, disregarding omitted fini functions. + *
    2. If there are no fini functions, use {@code void} as the loop return type. + *
    3. Otherwise, use the common return type of the fini functions; they must all be identical. + *
    + *

    + * Step 1D: Check other types.

      + *
    1. There must be at least one non-omitted pred function. + *
    2. Every non-omitted pred function must have a {@code boolean} return type. + *
    + *

    + * (Implementation Note: Steps 1A, 1B, 1C, 1D are logically independent of each other, and may be performed in any + * order.) + *

    + * Step 2: Determine parameter lists.

      + *
    1. The parameter list for the resulting loop handle will be the "common suffix". + *
    2. The parameter list for init functions will be adjusted to the "common suffix". (Note that their parameter + * lists are already effectively identical to the common suffix.) + *
    3. The parameter list for non-init (step, pred, and fini) functions will be adjusted to the common prefix + * followed by the common suffix, called the "common parameter sequence". + *
    4. Every non-init, non-omitted function parameter list must be effectively identical to the common parameter + * sequence. + *
    + *

    + * Step 3: Fill in omitted functions.

      + *
    1. If an init function is omitted, use a {@linkplain #constant constant function} of the appropriate + * {@code null}/zero/{@code false}/{@code void} type. (For this purpose, a constant {@code void} is simply a + * function which does nothing and returns {@code void}; it can be obtained from another constant function by + * {@linkplain MethodHandle#asType type conversion}.) + *
    2. If a step function is omitted, use an {@linkplain #identity identity function} of the clause's iteration + * variable type; insert dropped argument parameters before the identity function parameter for the non-{@code void} + * iteration variables of preceding clauses. (This will turn the loop variable into a local loop invariant.) + *
    3. If a pred function is omitted, the corresponding fini function must also be omitted. + *
    4. If a pred function is omitted, use a constant {@code true} function. (This will keep the loop going, as far + * as this clause is concerned.) + *
    5. If a fini function is omitted, use a constant {@code null}/zero/{@code false}/{@code void} function of the + * loop return type. + *
    + *

    + * Step 4: Fill in missing parameter types.

      + *
    1. At this point, every init function parameter list is effectively identical to the common suffix, but some + * lists may be shorter. For every init function with a short parameter list, pad out the end of the list by + * {@linkplain #dropArguments dropping arguments}. + *
    2. At this point, every non-init function parameter list is effectively identical to the common parameter + * sequence, but some lists may be shorter. For every non-init function with a short parameter list, pad out the end + * of the list by {@linkplain #dropArguments dropping arguments}. + *
    + *

    + * Final observations.

      + *
    1. After these steps, all clauses have been adjusted by supplying omitted functions and arguments. + *
    2. All init functions have a common parameter type list, which the final loop handle will also have. + *
    3. All fini functions have a common return type, which the final loop handle will also have. + *
    4. All non-init functions have a common parameter type list, which is the common parameter sequence, of + * (non-{@code void}) iteration variables followed by loop parameters. + *
    5. Each pair of init and step functions agrees in their return types. + *
    6. Each non-init function will be able to observe the current values of all iteration variables, by means of the + * common prefix. + *
    + *

    + * Loop execution.

      + *
    1. When the loop is called, the loop input values are saved in locals, to be passed (as the common suffix) to + * every clause function. These locals are loop invariant. + *
    2. Each init function is executed in clause order (passing the common suffix) and the non-{@code void} values + * are saved (as the common prefix) into locals. These locals are loop varying (unless their steps are identity + * functions, as noted above). + *
    3. All function executions (except init functions) will be passed the common parameter sequence, consisting of + * the non-{@code void} iteration values (in clause order) and then the loop inputs (in argument order). + *
    4. The step and pred functions are then executed, in clause order (step before pred), until a pred function + * returns {@code false}. + *
    5. The non-{@code void} result from a step function call is used to update the corresponding loop variable. The + * updated value is immediately visible to all subsequent function calls. + *
    6. If a pred function returns {@code false}, the corresponding fini function is called, and the resulting value + * is returned from the loop as a whole. + *
    + *

    + * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the types / values + * of loop variables; {@code A}/{@code a}, those of arguments passed to the resulting loop; and {@code R}, the + * result types of finalizers as well as of the resulting loop. + *

    {@code
    +     * V... init...(A...);
    +     * boolean pred...(V..., A...);
    +     * V... step...(V..., A...);
    +     * R fini...(V..., A...);
    +     * R loop(A... a) {
    +     *   V... v... = init...(a...);
    +     *   for (;;) {
    +     *     for ((v, p, s, f) in (v..., pred..., step..., fini...)) {
    +     *       v = s(v..., a...);
    +     *       if (!p(v..., a...)) {
    +     *         return f(v..., a...);
    +     *       }
    +     *     }
    +     *   }
    +     * }
    +     * }
    + *

    + * @apiNote Example: + *

    {@code
    +     * // iterative implementation of the factorial function as a loop handle
    +     * static int one(int k) { return 1; }
    +     * int inc(int i, int acc, int k) { return i + 1; }
    +     * int mult(int i, int acc, int k) { return i * acc; }
    +     * boolean pred(int i, int acc, int k) { return i < k; }
    +     * int fin(int i, int acc, int k) { return acc; }
    +     * // assume MH_one, MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods
    +     * // null initializer for counter, should initialize to 0
    +     * MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
    +     * MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
    +     * MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
    +     * assertEquals(120, loop.invoke(5));
    +     * }
    + * + * @param clauses an array of arrays (4-tuples) of {@link MethodHandle}s adhering to the rules described above. + * + * @return a method handle embodying the looping behavior as defined by the arguments. + * + * @throws IllegalArgumentException in case any of the constraints described above is violated. + * + * @see MethodHandles#whileLoop(MethodHandle, MethodHandle, MethodHandle) + * @see MethodHandles#doWhileLoop(MethodHandle, MethodHandle, MethodHandle) + * @see MethodHandles#countedLoop(MethodHandle, MethodHandle, MethodHandle) + * @see MethodHandles#iteratedLoop(MethodHandle, MethodHandle, MethodHandle) + * @since 9 + */ + public static MethodHandle loop(MethodHandle[]... clauses) { + // Step 0: determine clause structure. + checkLoop0(clauses); + + List init = new ArrayList<>(); + List step = new ArrayList<>(); + List pred = new ArrayList<>(); + List fini = new ArrayList<>(); + + Stream.of(clauses).filter(c -> Stream.of(c).anyMatch(Objects::nonNull)).forEach(clause -> { + init.add(clause[0]); // all clauses have at least length 1 + step.add(clause.length <= 1 ? null : clause[1]); + pred.add(clause.length <= 2 ? null : clause[2]); + fini.add(clause.length <= 3 ? null : clause[3]); + }); + + assert Stream.of(init, step, pred, fini).map(List::size).distinct().count() == 1; + final int nclauses = init.size(); + + // Step 1A: determine iteration variables. + final List> iterationVariableTypes = new ArrayList<>(); + for (int i = 0; i < nclauses; ++i) { + MethodHandle in = init.get(i); + MethodHandle st = step.get(i); + if (in == null && st == null) { + iterationVariableTypes.add(void.class); + } else if (in != null && st != null) { + checkLoop1a(i, in, st); + iterationVariableTypes.add(in.type().returnType()); + } else { + iterationVariableTypes.add(in == null ? st.type().returnType() : in.type().returnType()); + } + } + final List> commonPrefix = iterationVariableTypes.stream().filter(t -> t != void.class). + collect(Collectors.toList()); + + // Step 1B: determine loop parameters. + final List> empty = new ArrayList<>(); + final List> commonSuffix = init.stream().filter(Objects::nonNull).map(MethodHandle::type). + map(MethodType::parameterList).reduce((p, q) -> p.size() >= q.size() ? p : q).orElse(empty); + checkLoop1b(init, commonSuffix); + + // Step 1C: determine loop return type. + // Step 1D: check other types. + final Class loopReturnType = fini.stream().filter(Objects::nonNull).map(MethodHandle::type). + map(MethodType::returnType).findFirst().orElse(void.class); + checkLoop1cd(pred, fini, loopReturnType); + + // Step 2: determine parameter lists. + final List> commonParameterSequence = new ArrayList<>(commonPrefix); + commonParameterSequence.addAll(commonSuffix); + checkLoop2(step, pred, fini, commonParameterSequence); + + // Step 3: fill in omitted functions. + for (int i = 0; i < nclauses; ++i) { + Class t = iterationVariableTypes.get(i); + if (init.get(i) == null) { + init.set(i, zeroHandle(t)); + } + if (step.get(i) == null) { + step.set(i, dropArguments(t == void.class ? zeroHandle(t) : identity(t), 0, commonPrefix.subList(0, i))); + } + if (pred.get(i) == null) { + pred.set(i, constant(boolean.class, true)); + } + if (fini.get(i) == null) { + fini.set(i, zeroHandle(t)); + } + } + + // Step 4: fill in missing parameter types. + List finit = fillParameterTypes(init, commonSuffix); + List fstep = fillParameterTypes(step, commonParameterSequence); + List fpred = fillParameterTypes(pred, commonParameterSequence); + List ffini = fillParameterTypes(fini, commonParameterSequence); + + assert finit.stream().map(MethodHandle::type).map(MethodType::parameterList). + allMatch(pl -> pl.equals(commonSuffix)); + assert Stream.of(fstep, fpred, ffini).flatMap(List::stream).map(MethodHandle::type).map(MethodType::parameterList). + allMatch(pl -> pl.equals(commonParameterSequence)); + + return MethodHandleImpl.makeLoop(loopReturnType, commonSuffix, commonPrefix, finit, fstep, fpred, ffini); + } + + private static List fillParameterTypes(List hs, final List> targetParams) { + return hs.stream().map(h -> { + int pc = h.type().parameterCount(); + int tpsize = targetParams.size(); + return pc < tpsize ? dropArguments(h, pc, targetParams.subList(pc, tpsize)) : h; + }).collect(Collectors.toList()); + } + + /** + * Constructs a {@code while} loop from an initializer, a body, and a predicate. This is a convenience wrapper for + * the {@linkplain #loop(MethodHandle[][]) generic loop combinator}. + *

    + * The loop handle's result type is the same as the sole loop variable's, i.e., the result type of {@code init}. + * The parameter type list of {@code init} also determines that of the resulting handle. The {@code pred} handle + * must have an additional leading parameter of the same type as {@code init}'s result, and so must the {@code + * body}. These constraints follow directly from those described for the {@linkplain MethodHandles#loop(MethodHandle[][]) + * generic loop combinator}. + *

    + * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of + * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument + * passed to the loop. + *

    {@code
    +     * V init(A);
    +     * boolean pred(V, A);
    +     * V body(V, A);
    +     * V whileLoop(A a) {
    +     *   V v = init(a);
    +     *   while (pred(v, a)) {
    +     *     v = body(v, a);
    +     *   }
    +     *   return v;
    +     * }
    +     * }
    + *

    + * @apiNote Example: + *

    {@code
    +     * // implement the zip function for lists as a loop handle
    +     * List initZip(Iterator a, Iterator b) { return new ArrayList<>(); }
    +     * boolean zipPred(List zip, Iterator a, Iterator b) { return a.hasNext() && b.hasNext(); }
    +     * List zipStep(List zip, Iterator a, Iterator b) {
    +     *   zip.add(a.next());
    +     *   zip.add(b.next());
    +     *   return zip;
    +     * }
    +     * // assume MH_initZip, MH_zipPred, and MH_zipStep are handles to the above methods
    +     * MethodHandle loop = MethodHandles.doWhileLoop(MH_initZip, MH_zipPred, MH_zipStep);
    +     * List a = Arrays.asList("a", "b", "c", "d");
    +     * List b = Arrays.asList("e", "f", "g", "h");
    +     * List zipped = Arrays.asList("a", "e", "b", "f", "c", "g", "d", "h");
    +     * assertEquals(zipped, (List) loop.invoke(a.iterator(), b.iterator()));
    +     * }
    + * + *

    + * @implSpec The implementation of this method is equivalent to: + *

    {@code
    +     * MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
    +     *     MethodHandle[]
    +     *         checkExit = {null, null, pred, identity(init.type().returnType())},
    +     *         varBody = {init, body};
    +     *     return loop(checkExit, varBody);
    +     * }
    +     * }
    + * + * @param init initializer: it should provide the initial value of the loop variable. This controls the loop's + * result type. Passing {@code null} or a {@code void} init function will make the loop's result type + * {@code void}. + * @param pred condition for the loop, which may not be {@code null}. + * @param body body of the loop, which may not be {@code null}. + * + * @return the value of the loop variable as the loop terminates. + * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + * + * @see MethodHandles#loop(MethodHandle[][]) + * @since 9 + */ + public static MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) { + MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType()); + MethodHandle[] checkExit = {null, null, pred, fin}; + MethodHandle[] varBody = {init, body}; + return loop(checkExit, varBody); + } + + /** + * Constructs a {@code do-while} loop from an initializer, a body, and a predicate. This is a convenience wrapper + * for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}. + *

    + * The loop handle's result type is the same as the sole loop variable's, i.e., the result type of {@code init}. + * The parameter type list of {@code init} also determines that of the resulting handle. The {@code pred} handle + * must have an additional leading parameter of the same type as {@code init}'s result, and so must the {@code + * body}. These constraints follow directly from those described for the {@linkplain MethodHandles#loop(MethodHandle[][]) + * generic loop combinator}. + *

    + * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of + * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument + * passed to the loop. + *

    {@code
    +     * V init(A);
    +     * boolean pred(V, A);
    +     * V body(V, A);
    +     * V doWhileLoop(A a) {
    +     *   V v = init(a);
    +     *   do {
    +     *     v = body(v, a);
    +     *   } while (pred(v, a));
    +     *   return v;
    +     * }
    +     * }
    + *

    + * @apiNote Example: + *

    {@code
    +     * // int i = 0; while (i < limit) { ++i; } return i; => limit
    +     * int zero(int limit) { return 0; }
    +     * int step(int i, int limit) { return i + 1; }
    +     * boolean pred(int i, int limit) { return i < limit; }
    +     * // assume MH_zero, MH_step, and MH_pred are handles to the above methods
    +     * MethodHandle loop = MethodHandles.doWhileLoop(MH_zero, MH_step, MH_pred);
    +     * assertEquals(23, loop.invoke(23));
    +     * }
    + * + *

    + * @implSpec The implementation of this method is equivalent to: + *

    {@code
    +     * MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
    +     *     MethodHandle[] clause = { init, body, pred, identity(init.type().returnType()) };
    +     *     return loop(clause);
    +     * }
    +     * }
    + * + * + * @param init initializer: it should provide the initial value of the loop variable. This controls the loop's + * result type. Passing {@code null} or a {@code void} init function will make the loop's result type + * {@code void}. + * @param pred condition for the loop, which may not be {@code null}. + * @param body body of the loop, which may not be {@code null}. + * + * @return the value of the loop variable as the loop terminates. + * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + * + * @see MethodHandles#loop(MethodHandle[][]) + * @since 9 + */ + public static MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) { + MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType()); + MethodHandle[] clause = {init, body, pred, fin}; + return loop(clause); + } + + /** + * Constructs a loop that runs a given number of iterations. The loop counter is an {@code int} initialized from the + * {@code iterations} handle evaluation result. The counter is passed to the {@code body} function, so that must + * accept an initial {@code int} argument. The result of the loop execution is the final value of the additional + * local state. This is a convenience wrapper for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop + * combinator}. + *

    + * The result type and parameter type list of {@code init} determine those of the resulting handle. The {@code + * iterations} handle must accept the same parameter types as {@code init} but return an {@code int}. The {@code + * body} handle must accept the same parameter types as well, preceded by an {@code int} parameter for the counter, + * and a parameter of the same type as {@code init}'s result. These constraints follow directly from those described + * for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}. + *

    + * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of + * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument + * passed to the loop. + *

    {@code
    +     * int iterations(A);
    +     * V init(A);
    +     * V body(int, V, A);
    +     * V countedLoop(A a) {
    +     *   int end = iterations(a);
    +     *   V v = init(a);
    +     *   for (int i = 0; i < end; ++i) {
    +     *     v = body(i, v, a);
    +     *   }
    +     *   return v;
    +     * }
    +     * }
    + *

    + * @apiNote Example: + *

    {@code
    +     * // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
    +     * // => a variation on a well known theme
    +     * String start(String arg) { return arg; }
    +     * String step(int counter, String v, String arg) { return "na " + v; }
    +     * // assume MH_start and MH_step are handles to the two methods above
    +     * MethodHandle loop = MethodHandles.countedLoop(13, MH_start, MH_step);
    +     * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
    +     * }
    + * + *

    + * @implSpec The implementation of this method is equivalent to: + *

    {@code
    +     * MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) {
    +     *     return countedLoop(null, iterations, init, body);  // null => constant zero
    +     * }
    +     * }
    + * + * @param iterations a handle to return the number of iterations this loop should run. + * @param init initializer for additional loop state. This determines the loop's result type. + * Passing {@code null} or a {@code void} init function will make the loop's result type + * {@code void}. + * @param body the body of the loop, which must not be {@code null}. + * It must accept an initial {@code int} parameter (for the counter), and then any + * additional loop-local variable plus loop parameters. + * + * @return a method handle representing the loop. + * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + * + * @since 9 + */ + public static MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) { + return countedLoop(null, iterations, init, body); + } + + /** + * Constructs a loop that counts over a range of numbers. The loop counter is an {@code int} that will be + * initialized to the {@code int} value returned from the evaluation of the {@code start} handle and run to the + * value returned from {@code end} (exclusively) with a step width of 1. The counter value is passed to the {@code + * body} function in each iteration; it has to accept an initial {@code int} parameter + * for that. The result of the loop execution is the final value of the additional local state + * obtained by running {@code init}. + * This is a + * convenience wrapper for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}. + *

    + * The constraints for the {@code init} and {@code body} handles are the same as for {@link + * #countedLoop(MethodHandle, MethodHandle, MethodHandle)}. Additionally, the {@code start} and {@code end} handles + * must return an {@code int} and accept the same parameters as {@code init}. + *

    + * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of + * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument + * passed to the loop. + *

    {@code
    +     * int start(A);
    +     * int end(A);
    +     * V init(A);
    +     * V body(int, V, A);
    +     * V countedLoop(A a) {
    +     *   int s = start(a);
    +     *   int e = end(a);
    +     *   V v = init(a);
    +     *   for (int i = s; i < e; ++i) {
    +     *     v = body(i, v, a);
    +     *   }
    +     *   return v;
    +     * }
    +     * }
    + * + *

    + * @implSpec The implementation of this method is equivalent to: + *

    {@code
    +     * MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) {
    +     *     MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, int.class, int.class);
    +     *     // assume MH_increment and MH_lessThan are handles to x+1 and x
    + * + * @param start a handle to return the start value of the loop counter. + * If it is {@code null}, a constant zero is assumed. + * @param end a non-{@code null} handle to return the end value of the loop counter (the loop will run to {@code end-1}). + * @param init initializer for additional loop state. This determines the loop's result type. + * Passing {@code null} or a {@code void} init function will make the loop's result type + * {@code void}. + * @param body the body of the loop, which must not be {@code null}. + * It must accept an initial {@code int} parameter (for the counter), and then any + * additional loop-local variable plus loop parameters. + * + * @return a method handle representing the loop. + * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + * + * @since 9 + */ + public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) { + MethodHandle returnVar = dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()), + 0, int.class, int.class); + MethodHandle[] indexVar = {start, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopStep)}; + MethodHandle[] loopLimit = {end, null, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopPred), returnVar}; + MethodHandle[] bodyClause = {init, dropArguments(body, 1, int.class)}; + return loop(indexVar, loopLimit, bodyClause); + } + + /** + * Constructs a loop that ranges over the elements produced by an {@code Iterator}. + * The iterator will be produced by the evaluation of the {@code iterator} handle. + * If this handle is passed as {@code null} the method {@link Iterable#iterator} will be used instead, + * and will be applied to a leading argument of the loop handle. + * Each value produced by the iterator is passed to the {@code body}, which must accept an initial {@code T} parameter. + * The result of the loop execution is the final value of the additional local state + * obtained by running {@code init}. + *

    + * This is a convenience wrapper for the + * {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}, and the constraints imposed on the {@code body} + * handle follow directly from those described for the latter. + *

    + * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of + * the loop variable as well as the result type of the loop; {@code T}/{@code t}, that of the elements of the + * structure the loop iterates over, and {@code A}/{@code a}, that of the argument passed to the loop. + *

    {@code
    +     * Iterator iterator(A);  // defaults to Iterable::iterator
    +     * V init(A);
    +     * V body(T,V,A);
    +     * V iteratedLoop(A a) {
    +     *   Iterator it = iterator(a);
    +     *   V v = init(a);
    +     *   for (T t : it) {
    +     *     v = body(t, v, a);
    +     *   }
    +     *   return v;
    +     * }
    +     * }
    + *

    + * The type {@code T} may be either a primitive or reference. + * Since type {@code Iterator} is erased in the method handle representation to the raw type + * {@code Iterator}, the {@code iteratedLoop} combinator adjusts the leading argument type for {@code body} + * to {@code Object} as if by the {@link MethodHandle#asType asType} conversion method. + * Therefore, if an iterator of the wrong type appears as the loop is executed, + * runtime exceptions may occur as the result of dynamic conversions performed by {@code asType}. + *

    + * @apiNote Example: + *

    {@code
    +     * // reverse a list
    +     * List reverseStep(String e, List r) {
    +     *   r.add(0, e);
    +     *   return r;
    +     * }
    +     * List newArrayList() { return new ArrayList<>(); }
    +     * // assume MH_reverseStep, MH_newArrayList are handles to the above methods
    +     * MethodHandle loop = MethodHandles.iteratedLoop(null, MH_newArrayList, MH_reverseStep);
    +     * List list = Arrays.asList("a", "b", "c", "d", "e");
    +     * List reversedList = Arrays.asList("e", "d", "c", "b", "a");
    +     * assertEquals(reversedList, (List) loop.invoke(list));
    +     * }
    + *

    + * @implSpec The implementation of this method is equivalent to: + *

    {@code
    +     * MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) {
    +     *     // assume MH_next and MH_hasNext are handles to methods of Iterator
    +     *     Class itype = iterator.type().returnType();
    +     *     Class ttype = body.type().parameterType(0);
    +     *     MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, itype);
    +     *     MethodHandle nextVal = MH_next.asType(MH_next.type().changeReturnType(ttype));
    +     *     MethodHandle[]
    +     *         iterVar = {iterator, null, MH_hasNext, returnVar}, // it = iterator(); while (it.hasNext)
    +     *         bodyClause = {init, filterArgument(body, 0, nextVal)};  // v = body(t, v, a);
    +     *     return loop(iterVar, bodyClause);
    +     * }
    +     * }
    + * + * @param iterator a handle to return the iterator to start the loop. + * Passing {@code null} will make the loop call {@link Iterable#iterator()} on the first + * incoming value. + * @param init initializer for additional loop state. This determines the loop's result type. + * Passing {@code null} or a {@code void} init function will make the loop's result type + * {@code void}. + * @param body the body of the loop, which must not be {@code null}. + * It must accept an initial {@code T} parameter (for the iterated values), and then any + * additional loop-local variable plus loop parameters. + * + * @return a method handle embodying the iteration loop functionality. + * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure + * + * @since 9 + */ + public static MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) { + checkIteratedLoop(body); + + MethodHandle initit = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_initIterator); + MethodHandle initIterator = iterator == null ? + initit.asType(initit.type().changeParameterType(0, body.type().parameterType(init == null ? 1 : 2))) : + iterator; + Class itype = initIterator.type().returnType(); + Class ttype = body.type().parameterType(0); + + MethodHandle returnVar = + dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()), 0, itype); + MethodHandle initnx = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iterateNext); + MethodHandle nextVal = initnx.asType(initnx.type().changeReturnType(ttype)); + + MethodHandle[] iterVar = {initIterator, null, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iteratePred), returnVar}; + MethodHandle[] bodyClause = {init, filterArgument(body, 0, nextVal)}; + + return loop(iterVar, bodyClause); + } + + /** + * Makes a method handle that adapts a {@code target} method handle by wrapping it in a {@code try-finally} block. + * Another method handle, {@code cleanup}, represents the functionality of the {@code finally} block. Any exception + * thrown during the execution of the {@code target} handle will be passed to the {@code cleanup} handle. The + * exception will be rethrown, unless {@code cleanup} handle throws an exception first. The + * value returned from the {@code cleanup} handle's execution will be the result of the execution of the + * {@code try-finally} handle. + *

    + * The {@code cleanup} handle will be passed one or two additional leading arguments. + * The first is the exception thrown during the + * execution of the {@code target} handle, or {@code null} if no exception was thrown. + * The second is the result of the execution of the {@code target} handle, or, if it throws an exception, + * a {@code null}, zero, or {@code false} value of the required type is supplied as a placeholder. + * The second argument is not present if the {@code target} handle has a {@code void} return type. + * (Note that, except for argument type conversions, combinators represent {@code void} values in parameter lists + * by omitting the corresponding paradoxical arguments, not by inserting {@code null} or zero values.) + *

    + * The {@code target} and {@code cleanup} handles' return types must be the same. Their parameter type lists also + * must be the same, but the {@code cleanup} handle must accept one or two more leading parameters:

      + *
    • a {@code Throwable}, which will carry the exception thrown by the {@code target} handle (if any); and + *
    • a parameter of the same type as the return type of both {@code target} and {@code cleanup}, which will carry + * the result from the execution of the {@code target} handle. + * This parameter is not present if the {@code target} returns {@code void}. + *
    + *

    + * The pseudocode for the resulting adapter looks as follows. In the code, {@code V} represents the result type of + * the {@code try/finally} construct; {@code A}/{@code a}, the types and values of arguments to the resulting + * handle consumed by the cleanup; and {@code B}/{@code b}, those of arguments to the resulting handle discarded by + * the cleanup. + *

    {@code
    +     * V target(A..., B...);
    +     * V cleanup(Throwable, V, A...);
    +     * V adapter(A... a, B... b) {
    +     *   V result = (zero value for V);
    +     *   Throwable throwable = null;
    +     *   try {
    +     *     result = target(a..., b...);
    +     *   } catch (Throwable t) {
    +     *     throwable = t;
    +     *     throw t;
    +     *   } finally {
    +     *     result = cleanup(throwable, result, a...);
    +     *   }
    +     *   return result;
    +     * }
    +     * }
    + *

    + * Note that the saved arguments ({@code a...} in the pseudocode) cannot + * be modified by execution of the target, and so are passed unchanged + * from the caller to the cleanup, if it is invoked. + *

    + * The target and cleanup must return the same type, even if the cleanup + * always throws. + * To create such a throwing cleanup, compose the cleanup logic + * with {@link #throwException throwException}, + * in order to create a method handle of the correct return type. + *

    + * Note that {@code tryFinally} never converts exceptions into normal returns. + * In rare cases where exceptions must be converted in that way, first wrap + * the target with {@link #catchException(MethodHandle, Class, MethodHandle)} + * to capture an outgoing exception, and then wrap with {@code tryFinally}. + * + * @param target the handle whose execution is to be wrapped in a {@code try} block. + * @param cleanup the handle that is invoked in the finally block. + * + * @return a method handle embodying the {@code try-finally} block composed of the two arguments. + * @throws NullPointerException if any argument is null + * @throws IllegalArgumentException if {@code cleanup} does not accept + * the required leading arguments, or if the method handle types do + * not match in their return types and their + * corresponding trailing parameters + * + * @see MethodHandles#catchException(MethodHandle, Class, MethodHandle) + * @since 9 + */ + public static MethodHandle tryFinally(MethodHandle target, MethodHandle cleanup) { + List> targetParamTypes = target.type().parameterList(); + List> cleanupParamTypes = cleanup.type().parameterList(); + Class rtype = target.type().returnType(); + + checkTryFinally(target, cleanup); + + // Match parameter lists: if the cleanup has a shorter parameter list than the target, add ignored arguments. + int tpSize = targetParamTypes.size(); + int cpPrefixLength = rtype == void.class ? 1 : 2; + int cpSize = cleanupParamTypes.size(); + MethodHandle aCleanup = cpSize - cpPrefixLength < tpSize ? + dropArguments(cleanup, cpSize, targetParamTypes.subList(tpSize - (cpSize - cpPrefixLength), tpSize)) : + cleanup; + + MethodHandle aTarget = target.asSpreader(Object[].class, target.type().parameterCount()); + aCleanup = aCleanup.asSpreader(Object[].class, tpSize); + + return MethodHandleImpl.makeTryFinally(aTarget, aCleanup, rtype, targetParamTypes); + } + + /** + * Adapts a target method handle by pre-processing some of its arguments, starting at a given position, and then + * calling the target with the result of the pre-processing, inserted into the original sequence of arguments just + * before the folded arguments. + *

    + * This method is closely related to {@link #foldArguments(MethodHandle, MethodHandle)}, but allows to control the + * position in the parameter list at which folding takes place. The argument controlling this, {@code pos}, is a + * zero-based index. The aforementioned method {@link #foldArguments(MethodHandle, MethodHandle)} assumes position + * 0. + *

    + * @apiNote Example: + *

    {@code
    +    import static java.lang.invoke.MethodHandles.*;
    +    import static java.lang.invoke.MethodType.*;
    +    ...
    +    MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
    +    "println", methodType(void.class, String.class))
    +    .bindTo(System.out);
    +    MethodHandle cat = lookup().findVirtual(String.class,
    +    "concat", methodType(String.class, String.class));
    +    assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
    +    MethodHandle catTrace = foldArguments(cat, 1, trace);
    +    // also prints "jum":
    +    assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
    +     * }
    + *

    Here is pseudocode for the resulting adapter: + *

    {@code
    +     * // there are N arguments in A...
    +     * T target(Z..., V, A[N]..., B...);
    +     * V combiner(A...);
    +     * T adapter(Z... z, A... a, B... b) {
    +     *   V v = combiner(a...);
    +     *   return target(z..., v, a..., b...);
    +     * }
    +     * // and if the combiner has a void return:
    +     * T target2(Z..., A[N]..., B...);
    +     * void combiner2(A...);
    +     * T adapter2(Z... z, A... a, B... b) {
    +     *   combiner2(a...);
    +     *   return target2(z..., a..., b...);
    +     * }
    +     * }
    + * + * @param target the method handle to invoke after arguments are combined + * @param pos the position at which to start folding and at which to insert the folding result; if this is {@code + * 0}, the effect is the same as for {@link #foldArguments(MethodHandle, MethodHandle)}. + * @param combiner method handle to call initially on the incoming arguments + * @return method handle which incorporates the specified argument folding logic + * @throws NullPointerException if either argument is null + * @throws IllegalArgumentException if {@code combiner}'s return type + * is non-void and not the same as the argument type at position {@code pos} of + * the target signature, or if the {@code N} argument types at position {@code pos} + * of the target signature + * (skipping one matching the {@code combiner}'s return type) + * are not identical with the argument types of {@code combiner} + * + * @see #foldArguments(MethodHandle, MethodHandle) + * @since 9 + */ + public static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner) { + MethodType targetType = target.type(); + MethodType combinerType = combiner.type(); + Class rtype = foldArgumentChecks(pos, targetType, combinerType); + BoundMethodHandle result = target.rebind(); + boolean dropResult = rtype == void.class; + LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType()); + MethodType newType = targetType; + if (!dropResult) { + newType = newType.dropParameterTypes(pos, pos + 1); + } + result = result.copyWithExtendL(newType, lform, combiner); + return result; + } + + /** + * Wrap creation of a proper zero handle for a given type. + * + * @param type the type. + * + * @return a zero value for the given type. + */ + static MethodHandle zeroHandle(Class type) { + return type.isPrimitive() ? zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type); + } + + private static void checkLoop0(MethodHandle[][] clauses) { + if (clauses == null || clauses.length == 0) { + throw newIllegalArgumentException("null or no clauses passed"); + } + if (Stream.of(clauses).anyMatch(Objects::isNull)) { + throw newIllegalArgumentException("null clauses are not allowed"); + } + if (Stream.of(clauses).anyMatch(c -> c.length > 4)) { + throw newIllegalArgumentException("All loop clauses must be represented as MethodHandle arrays with at most 4 elements."); + } + } + + private static void checkLoop1a(int i, MethodHandle in, MethodHandle st) { + if (in.type().returnType() != st.type().returnType()) { + throw misMatchedTypes("clause " + i + ": init and step return types", in.type().returnType(), + st.type().returnType()); + } + } + + private static void checkLoop1b(List init, List> commonSuffix) { + if (init.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::parameterList). + anyMatch(pl -> !pl.equals(commonSuffix.subList(0, pl.size())))) { + throw newIllegalArgumentException("found non-effectively identical init parameter type lists: " + init + + " (common suffix: " + commonSuffix + ")"); + } + } + + private static void checkLoop1cd(List pred, List fini, Class loopReturnType) { + if (fini.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType). + anyMatch(t -> t != loopReturnType)) { + throw newIllegalArgumentException("found non-identical finalizer return types: " + fini + " (return type: " + + loopReturnType + ")"); + } + + if (!pred.stream().filter(Objects::nonNull).findFirst().isPresent()) { + throw newIllegalArgumentException("no predicate found", pred); + } + if (pred.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType). + anyMatch(t -> t != boolean.class)) { + throw newIllegalArgumentException("predicates must have boolean return type", pred); + } + } + + private static void checkLoop2(List step, List pred, List fini, List> commonParameterSequence) { + if (Stream.of(step, pred, fini).flatMap(List::stream).filter(Objects::nonNull).map(MethodHandle::type). + map(MethodType::parameterList).anyMatch(pl -> !pl.equals(commonParameterSequence.subList(0, pl.size())))) { + throw newIllegalArgumentException("found non-effectively identical parameter type lists:\nstep: " + step + + "\npred: " + pred + "\nfini: " + fini + " (common parameter sequence: " + commonParameterSequence + ")"); + } + } + + private static void checkIteratedLoop(MethodHandle body) { + if (null == body) { + throw newIllegalArgumentException("iterated loop body must not be null"); + } + } + + private static void checkTryFinally(MethodHandle target, MethodHandle cleanup) { + Class rtype = target.type().returnType(); + if (rtype != cleanup.type().returnType()) { + throw misMatchedTypes("target and return types", cleanup.type().returnType(), rtype); + } + List> cleanupParamTypes = cleanup.type().parameterList(); + if (!Throwable.class.isAssignableFrom(cleanupParamTypes.get(0))) { + throw misMatchedTypes("cleanup first argument and Throwable", cleanup.type(), Throwable.class); + } + if (rtype != void.class && cleanupParamTypes.get(1) != rtype) { + throw misMatchedTypes("cleanup second argument and target return type", cleanup.type(), rtype); + } + // The cleanup parameter list (minus the leading Throwable and result parameters) must be a sublist of the + // target parameter list. + int cleanupArgIndex = rtype == void.class ? 1 : 2; + if (!cleanupParamTypes.subList(cleanupArgIndex, cleanupParamTypes.size()). + equals(target.type().parameterList().subList(0, cleanupParamTypes.size() - cleanupArgIndex))) { + throw misMatchedTypes("cleanup parameters after (Throwable,result) and target parameter list prefix", + cleanup.type(), target.type()); + } + } + } diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java index 7f77c5e84ad..be3090a4451 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java @@ -469,12 +469,13 @@ class MethodType implements java.io.Serializable { /** Replace the last arrayLength parameter types with the component type of arrayType. * @param arrayType any array type + * @param pos position at which to spread * @param arrayLength the number of parameter types to change * @return the resulting type */ - /*non-public*/ MethodType asSpreaderType(Class arrayType, int arrayLength) { + /*non-public*/ MethodType asSpreaderType(Class arrayType, int pos, int arrayLength) { assert(parameterCount() >= arrayLength); - int spreadPos = ptypes.length - arrayLength; + int spreadPos = pos; if (arrayLength == 0) return this; // nothing to change if (arrayType == Object[].class) { if (isGeneric()) return this; // nothing to change @@ -489,10 +490,10 @@ class MethodType implements java.io.Serializable { } Class elemType = arrayType.getComponentType(); assert(elemType != null); - for (int i = spreadPos; i < ptypes.length; i++) { + for (int i = spreadPos; i < spreadPos + arrayLength; i++) { if (ptypes[i] != elemType) { Class[] fixedPtypes = ptypes.clone(); - Arrays.fill(fixedPtypes, i, ptypes.length, elemType); + Arrays.fill(fixedPtypes, i, spreadPos + arrayLength, elemType); return methodType(rtype, fixedPtypes); } } @@ -512,12 +513,14 @@ class MethodType implements java.io.Serializable { /** Delete the last parameter type and replace it with arrayLength copies of the component type of arrayType. * @param arrayType any array type + * @param pos position at which to insert parameters * @param arrayLength the number of parameter types to insert * @return the resulting type */ - /*non-public*/ MethodType asCollectorType(Class arrayType, int arrayLength) { + /*non-public*/ MethodType asCollectorType(Class arrayType, int pos, int arrayLength) { assert(parameterCount() >= 1); - assert(lastParameterType().isAssignableFrom(arrayType)); + assert(pos < ptypes.length); + assert(ptypes[pos].isAssignableFrom(arrayType)); MethodType res; if (arrayType == Object[].class) { res = genericMethodType(arrayLength); @@ -532,7 +535,11 @@ class MethodType implements java.io.Serializable { if (ptypes.length == 1) { return res; } else { - return res.insertParameterTypes(0, parameterList().subList(0, ptypes.length-1)); + // insert after (if need be), then before + if (pos < parameterList().size() - 1) { + res = res.insertParameterTypes(arrayLength, parameterList().subList(pos + 1, parameterList().size())); + } + return res.insertParameterTypes(0, parameterList().subList(0, pos)); } } diff --git a/jdk/src/java.base/share/classes/java/math/BigDecimal.java b/jdk/src/java.base/share/classes/java/math/BigDecimal.java index 7214950c489..a126e9339f1 100644 --- a/jdk/src/java.base/share/classes/java/math/BigDecimal.java +++ b/jdk/src/java.base/share/classes/java/math/BigDecimal.java @@ -3726,12 +3726,12 @@ public class BigDecimal extends Number implements Comparable { } private static class UnsafeHolder { - private static final sun.misc.Unsafe unsafe; + private static final jdk.internal.misc.Unsafe unsafe; private static final long intCompactOffset; private static final long intValOffset; static { try { - unsafe = sun.misc.Unsafe.getUnsafe(); + unsafe = jdk.internal.misc.Unsafe.getUnsafe(); intCompactOffset = unsafe.objectFieldOffset (BigDecimal.class.getDeclaredField("intCompact")); intValOffset = unsafe.objectFieldOffset diff --git a/jdk/src/java.base/share/classes/java/math/BigInteger.java b/jdk/src/java.base/share/classes/java/math/BigInteger.java index 80330c8c4e4..4471de71198 100644 --- a/jdk/src/java.base/share/classes/java/math/BigInteger.java +++ b/jdk/src/java.base/share/classes/java/math/BigInteger.java @@ -4526,12 +4526,12 @@ public class BigInteger extends Number implements Comparable { // Support for resetting final fields while deserializing private static class UnsafeHolder { - private static final sun.misc.Unsafe unsafe; + private static final jdk.internal.misc.Unsafe unsafe; private static final long signumOffset; private static final long magOffset; static { try { - unsafe = sun.misc.Unsafe.getUnsafe(); + unsafe = jdk.internal.misc.Unsafe.getUnsafe(); signumOffset = unsafe.objectFieldOffset (BigInteger.class.getDeclaredField("signum")); magOffset = unsafe.objectFieldOffset diff --git a/jdk/src/java.base/share/classes/java/net/Inet6Address.java b/jdk/src/java.base/share/classes/java/net/Inet6Address.java index e51064fbd31..0daac2f2b03 100644 --- a/jdk/src/java.base/share/classes/java/net/Inet6Address.java +++ b/jdk/src/java.base/share/classes/java/net/Inet6Address.java @@ -576,11 +576,11 @@ class Inet6Address extends InetAddress { }; private static final long FIELDS_OFFSET; - private static final sun.misc.Unsafe UNSAFE; + private static final jdk.internal.misc.Unsafe UNSAFE; static { try { - sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); + jdk.internal.misc.Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe(); FIELDS_OFFSET = unsafe.objectFieldOffset( Inet6Address.class.getDeclaredField("holder6")); UNSAFE = unsafe; diff --git a/jdk/src/java.base/share/classes/java/net/InetAddress.java b/jdk/src/java.base/share/classes/java/net/InetAddress.java index a2d89b25d31..0878c57848b 100644 --- a/jdk/src/java.base/share/classes/java/net/InetAddress.java +++ b/jdk/src/java.base/share/classes/java/net/InetAddress.java @@ -29,6 +29,7 @@ import java.util.NavigableSet; import java.util.Iterator; import java.util.List; import java.util.ArrayList; +import java.util.Objects; import java.util.ServiceLoader; import java.security.AccessController; import java.io.ObjectStreamException; @@ -733,7 +734,7 @@ class InetAddress implements java.io.Serializable { */ public String toString() { String hostName = holder().getHostName(); - return ((hostName != null) ? hostName : "") + return Objects.toString(hostName, "") + "/" + getHostAddress(); } @@ -1493,11 +1494,11 @@ class InetAddress implements java.io.Serializable { } private static final long FIELDS_OFFSET; - private static final sun.misc.Unsafe UNSAFE; + private static final jdk.internal.misc.Unsafe UNSAFE; static { try { - sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); + jdk.internal.misc.Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe(); FIELDS_OFFSET = unsafe.objectFieldOffset( InetAddress.class.getDeclaredField("holder") ); diff --git a/jdk/src/java.base/share/classes/java/net/InetSocketAddress.java b/jdk/src/java.base/share/classes/java/net/InetSocketAddress.java index 2bdfdc25102..8e2a2fc925c 100644 --- a/jdk/src/java.base/share/classes/java/net/InetSocketAddress.java +++ b/jdk/src/java.base/share/classes/java/net/InetSocketAddress.java @@ -303,10 +303,10 @@ public class InetSocketAddress } private static final long FIELDS_OFFSET; - private static final sun.misc.Unsafe UNSAFE; + private static final jdk.internal.misc.Unsafe UNSAFE; static { try { - sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); + jdk.internal.misc.Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe(); FIELDS_OFFSET = unsafe.objectFieldOffset( InetSocketAddress.class.getDeclaredField("holder")); UNSAFE = unsafe; diff --git a/jdk/src/java.base/share/classes/java/net/SocketOptions.java b/jdk/src/java.base/share/classes/java/net/SocketOptions.java index 82e98e99064..c846bbb9b12 100644 --- a/jdk/src/java.base/share/classes/java/net/SocketOptions.java +++ b/jdk/src/java.base/share/classes/java/net/SocketOptions.java @@ -61,21 +61,21 @@ public interface SocketOptions { * If the requested option is binary, it can be set using this method by * a java.lang.Boolean: *
    -     * s.setOption(TCP_NODELAY, new Boolean(true));
    +     * s.setOption(TCP_NODELAY, Boolean.TRUE);
          *    // OK - enables TCP_NODELAY, a binary option
          * 
    *
    - * Any option can be disabled using this method with a Boolean(false): + * Any option can be disabled using this method with a Boolean.FALSE: *
    -     * s.setOption(TCP_NODELAY, new Boolean(false));
    +     * s.setOption(TCP_NODELAY, Boolean.FALSE);
          *    // OK - disables TCP_NODELAY
    -     * s.setOption(SO_LINGER, new Boolean(false));
    +     * s.setOption(SO_LINGER, Boolean.FALSE);
          *    // OK - disables SO_LINGER
          * 
    *
    * For an option that has a notion of on and off, and requires * a non-boolean parameter, setting its value to anything other than - * Boolean(false) implicitly enables it. + * Boolean.FALSE implicitly enables it. *
    * Throws SocketException if the option is unrecognized, * the socket is closed, or some low-level error occurred @@ -91,8 +91,8 @@ public interface SocketOptions { /** * Fetch the value of an option. - * Binary options will return java.lang.Boolean(true) - * if enabled, java.lang.Boolean(false) if disabled, e.g.: + * Binary options will return java.lang.Boolean.TRUE + * if enabled, java.lang.Boolean.FALSE if disabled, e.g.: *
          * SocketImpl s;
          * ...
    @@ -105,13 +105,13 @@ public interface SocketOptions {
          * 

    * For options that take a particular type as a parameter, * getOption(int) will return the parameter's value, else - * it will return java.lang.Boolean(false): + * it will return java.lang.Boolean.FALSE: *

          * Object o = s.getOption(SO_LINGER);
          * if (o instanceof Integer) {
          *     System.out.print("Linger time is " + ((Integer)o).intValue());
          * } else {
    -     *   // the true type of o is java.lang.Boolean(false);
    +     *   // the true type of o is java.lang.Boolean.FALSE;
          * }
          * 
    * diff --git a/jdk/src/java.base/share/classes/java/net/URLConnection.java b/jdk/src/java.base/share/classes/java/net/URLConnection.java index bdc37963d6c..544107828b9 100644 --- a/jdk/src/java.base/share/classes/java/net/URLConnection.java +++ b/jdk/src/java.base/share/classes/java/net/URLConnection.java @@ -32,6 +32,7 @@ import java.security.PrivilegedAction; import java.util.Hashtable; import java.util.Date; import java.util.Iterator; +import java.util.Objects; import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import java.util.StringTokenizer; @@ -1250,7 +1251,7 @@ public abstract class URLConnection { if (handler != null) { ContentHandler h = handlers.putIfAbsent(contentType, handler); - return h != null ? h : handler; + return Objects.requireNonNullElse(h, handler); } try { @@ -1263,7 +1264,7 @@ public abstract class URLConnection { assert handler != null; ContentHandler h = handlers.putIfAbsent(contentType, handler); - return h != null ? h : handler; + return Objects.requireNonNullElse(h, handler); } /* diff --git a/jdk/src/java.base/share/classes/java/nio/Bits.java b/jdk/src/java.base/share/classes/java/nio/Bits.java index 803360ea132..526c71d3f8b 100644 --- a/jdk/src/java.base/share/classes/java/nio/Bits.java +++ b/jdk/src/java.base/share/classes/java/nio/Bits.java @@ -32,7 +32,7 @@ import java.util.concurrent.atomic.LongAdder; import jdk.internal.misc.JavaNioAccess; import jdk.internal.misc.JavaLangRefAccess; import jdk.internal.misc.SharedSecrets; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import sun.misc.VM; /** diff --git a/jdk/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template b/jdk/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template index cf2cc29c864..c2d8d8ea12e 100644 --- a/jdk/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template +++ b/jdk/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template @@ -29,7 +29,7 @@ package java.nio; import java.io.FileDescriptor; import sun.misc.Cleaner; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import sun.misc.VM; import sun.nio.ch.DirectBuffer; diff --git a/jdk/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template b/jdk/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template index c37901431fb..9ca784b8a77 100644 --- a/jdk/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template +++ b/jdk/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template @@ -27,7 +27,7 @@ package java.nio; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** #if[rw] @@ -477,7 +477,7 @@ class Heap$Type$Buffer$RW$ #if[rw] public float getFloat() { - int x = unsafe.getIntUnaligned(hb, byteOffset(nextPutIndex(4)), bigEndian); + int x = unsafe.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian); return Float.intBitsToFloat(x); } diff --git a/jdk/src/java.base/share/classes/java/nio/MappedByteBuffer.java b/jdk/src/java.base/share/classes/java/nio/MappedByteBuffer.java index 953a78b8206..a9c6005d28c 100644 --- a/jdk/src/java.base/share/classes/java/nio/MappedByteBuffer.java +++ b/jdk/src/java.base/share/classes/java/nio/MappedByteBuffer.java @@ -26,7 +26,7 @@ package java.nio; import java.io.FileDescriptor; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** diff --git a/jdk/src/java.base/share/classes/java/nio/charset/Charset.java b/jdk/src/java.base/share/classes/java/nio/charset/Charset.java index 6766cb53c3d..498ed365213 100644 --- a/jdk/src/java.base/share/classes/java/nio/charset/Charset.java +++ b/jdk/src/java.base/share/classes/java/nio/charset/Charset.java @@ -37,6 +37,7 @@ import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Set; import java.util.ServiceLoader; import java.util.ServiceConfigurationError; @@ -625,6 +626,7 @@ public abstract class Charset private final String name; // tickles a bug in oldjavac private final String[] aliases; // tickles a bug in oldjavac + private final String[] zeroAliases = new String[0]; private Set aliasSet = null; /** @@ -642,7 +644,7 @@ public abstract class Charset */ protected Charset(String canonicalName, String[] aliases) { checkName(canonicalName); - String[] as = (aliases == null) ? new String[0] : aliases; + String[] as = Objects.requireNonNullElse(aliases, zeroAliases); for (int i = 0; i < as.length; i++) checkName(as[i]); this.name = canonicalName; diff --git a/jdk/src/java.base/share/classes/java/security/SecureRandom.java b/jdk/src/java.base/share/classes/java/security/SecureRandom.java index 2f8d734ac00..a2a246f4a14 100644 --- a/jdk/src/java.base/share/classes/java/security/SecureRandom.java +++ b/jdk/src/java.base/share/classes/java/security/SecureRandom.java @@ -419,7 +419,7 @@ public class SecureRandom extends java.util.Random { * @since 1.5 */ public String getAlgorithm() { - return (algorithm != null) ? algorithm : "unknown"; + return Objects.toString(algorithm, "unknown"); } /** diff --git a/jdk/src/java.base/share/classes/java/time/Clock.java b/jdk/src/java.base/share/classes/java/time/Clock.java index 6402806070d..0798b563eaa 100644 --- a/jdk/src/java.base/share/classes/java/time/Clock.java +++ b/jdk/src/java.base/share/classes/java/time/Clock.java @@ -65,7 +65,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import static java.time.LocalTime.NANOS_PER_MINUTE; import static java.time.LocalTime.NANOS_PER_SECOND; - +import static java.time.LocalTime.NANOS_PER_MILLI; import java.io.Serializable; import java.util.Objects; import java.util.TimeZone; @@ -182,7 +182,7 @@ public abstract class Clock { } /** - * Obtains a clock that returns the current instant using best available + * Obtains a clock that returns the current instant using the best available * system clock. *

    * This clock is based on the best available system clock. @@ -204,10 +204,34 @@ public abstract class Clock { return new SystemClock(zone); } + //------------------------------------------------------------------------- + /** + * Obtains a clock that returns the current instant ticking in whole milliseconds + * using the best available system clock. + *

    + * This clock will always have the nano-of-second field truncated to milliseconds. + * This ensures that the visible time ticks in whole milliseconds. + * The underlying clock is the best available system clock, equivalent to + * using {@link #system(ZoneId)}. + *

    + * Implementations may use a caching strategy for performance reasons. + * As such, it is possible that the start of the millisecond observed via this + * clock will be later than that observed directly via the underlying clock. + *

    + * The returned implementation is immutable, thread-safe and {@code Serializable}. + * It is equivalent to {@code tick(system(zone), Duration.ofMillis(1))}. + * + * @param zone the time-zone to use to convert the instant to date-time, not null + * @return a clock that ticks in whole milliseconds using the specified zone, not null + */ + public static Clock tickMillis(ZoneId zone) { + return new TickClock(system(zone), NANOS_PER_MILLI); + } + //------------------------------------------------------------------------- /** * Obtains a clock that returns the current instant ticking in whole seconds - * using best available system clock. + * using the best available system clock. *

    * This clock will always have the nano-of-second field set to zero. * This ensures that the visible time ticks in whole seconds. @@ -230,7 +254,7 @@ public abstract class Clock { /** * Obtains a clock that returns the current instant ticking in whole minutes - * using best available system clock. + * using the best available system clock. *

    * This clock will always have the nano-of-second and second-of-minute fields set to zero. * This ensures that the visible time ticks in whole minutes. diff --git a/jdk/src/java.base/share/classes/java/time/Duration.java b/jdk/src/java.base/share/classes/java/time/Duration.java index d5a49956e90..9bf25ba8c27 100644 --- a/jdk/src/java.base/share/classes/java/time/Duration.java +++ b/jdk/src/java.base/share/classes/java/time/Duration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, 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 @@ -359,8 +359,8 @@ public final class Duration * there must be at least one section after the "T". * The number part of each section must consist of one or more ASCII digits. * The number may be prefixed by the ASCII negative or positive symbol. - * The number of days, hours and minutes must parse to an {@code long}. - * The number of seconds must parse to an {@code long} with optional fraction. + * The number of days, hours and minutes must parse to a {@code long}. + * The number of seconds must parse to a {@code long} with optional fraction. * The decimal point may be either a dot or a comma. * The fractional part may have from zero to 9 digits. *

    @@ -374,9 +374,9 @@ public final class Duration * "PT10H" -- parses as "10 hours" (where an hour is 3600 seconds) * "P2D" -- parses as "2 days" (where a day is 24 hours or 86400 seconds) * "P2DT3H4M" -- parses as "2 days, 3 hours and 4 minutes" - * "P-6H3M" -- parses as "-6 hours and +3 minutes" - * "-P6H3M" -- parses as "-6 hours and -3 minutes" - * "-P-6H+3M" -- parses as "+6 hours and -3 minutes" + * "PT-6H3M" -- parses as "-6 hours and +3 minutes" + * "-PT6H3M" -- parses as "-6 hours and -3 minutes" + * "-PT-6H+3M" -- parses as "+6 hours and -3 minutes" *

    * * @param text the text to parse, not null @@ -402,7 +402,8 @@ public final class Duration long hoursAsSecs = parseNumber(text, hourStart, hourEnd, SECONDS_PER_HOUR, "hours"); long minsAsSecs = parseNumber(text, minuteStart, minuteEnd, SECONDS_PER_MINUTE, "minutes"); long seconds = parseNumber(text, secondStart, secondEnd, 1, "seconds"); - int nanos = parseFraction(text, fractionStart, fractionEnd, seconds < 0 ? -1 : 1); + boolean negativeSecs = secondStart >= 0 && text.charAt(secondStart) == '-'; + int nanos = parseFraction(text, fractionStart, fractionEnd, negativeSecs ? -1 : 1); try { return create(negate, daysAsSecs, hoursAsSecs, minsAsSecs, seconds, nanos); } catch (ArithmeticException ex) { diff --git a/jdk/src/java.base/share/classes/java/time/LocalDate.java b/jdk/src/java.base/share/classes/java/time/LocalDate.java index 794676a2410..ba0fdbf3c10 100644 --- a/jdk/src/java.base/share/classes/java/time/LocalDate.java +++ b/jdk/src/java.base/share/classes/java/time/LocalDate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, 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 @@ -81,7 +81,7 @@ import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; import java.time.chrono.ChronoLocalDate; -import java.time.chrono.Era; +import java.time.chrono.IsoEra; import java.time.chrono.IsoChronology; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @@ -220,12 +220,8 @@ public final class LocalDate */ public static LocalDate now(Clock clock) { Objects.requireNonNull(clock, "clock"); - // inline to avoid creating object and Instant checks final Instant now = clock.instant(); // called once - ZoneOffset offset = clock.getZone().getRules().getOffset(now); - long epochSec = now.getEpochSecond() + offset.getTotalSeconds(); // overflow caught later - long epochDay = Math.floorDiv(epochSec, SECONDS_PER_DAY); - return LocalDate.ofEpochDay(epochDay); + return ofInstant(now, clock.getZone()); } //----------------------------------------------------------------------- @@ -298,6 +294,30 @@ public final class LocalDate return new LocalDate(year, moy.getValue(), dom); } + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code LocalDate} from an {@code Instant} and zone ID. + *

    + * This creates a local date based on the specified instant. + * First, the offset from UTC/Greenwich is obtained using the zone ID and instant, + * which is simple as there is only one valid offset for each instant. + * Then, the instant and offset are used to calculate the local date. + * + * @param instant the instant to create the date from, not null + * @param zone the time-zone, which may be an offset, not null + * @return the local date, not null + * @throws DateTimeException if the result exceeds the supported range + */ + public static LocalDate ofInstant(Instant instant, ZoneId zone) { + Objects.requireNonNull(instant, "instant"); + Objects.requireNonNull(zone, "zone"); + ZoneRules rules = zone.getRules(); + ZoneOffset offset = rules.getOffset(instant); + long localSecond = instant.getEpochSecond() + offset.getTotalSeconds(); + long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY); + return ofEpochDay(localEpochDay); + } + //----------------------------------------------------------------------- /** * Obtains an instance of {@code LocalDate} from the epoch day count. @@ -712,15 +732,12 @@ public final class LocalDate * Users of this class should typically ignore this method as it exists primarily * to fulfill the {@link ChronoLocalDate} contract where it is necessary to support * the Japanese calendar system. - *

    - * The returned era will be a singleton capable of being compared with the constants - * in {@link IsoChronology} using the {@code ==} operator. * - * @return the {@code IsoChronology} era constant applicable at this date, not null + * @return the IsoEra applicable at this date, not null */ @Override // override for Javadoc - public Era getEra() { - return ChronoLocalDate.super.getEra(); + public IsoEra getEra() { + return (getYear() >= 1 ? IsoEra.CE : IsoEra.BCE); } /** diff --git a/jdk/src/java.base/share/classes/java/time/LocalTime.java b/jdk/src/java.base/share/classes/java/time/LocalTime.java index efb5265ce00..85cd7a1bf81 100644 --- a/jdk/src/java.base/share/classes/java/time/LocalTime.java +++ b/jdk/src/java.base/share/classes/java/time/LocalTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, 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 @@ -189,10 +189,14 @@ public final class LocalTime * Microseconds per day. */ static final long MICROS_PER_DAY = SECONDS_PER_DAY * 1000_000L; + /** + * Nanos per millisecond. + */ + static final long NANOS_PER_MILLI = 1000_000L; /** * Nanos per second. */ - static final long NANOS_PER_SECOND = 1000_000_000L; + static final long NANOS_PER_SECOND = 1000_000_000L; /** * Nanos per minute. */ @@ -272,12 +276,8 @@ public final class LocalTime */ public static LocalTime now(Clock clock) { Objects.requireNonNull(clock, "clock"); - // inline OffsetTime factory to avoid creating object and InstantProvider checks final Instant now = clock.instant(); // called once - ZoneOffset offset = clock.getZone().getRules().getOffset(now); - long localSecond = now.getEpochSecond() + offset.getTotalSeconds(); // overflow caught later - int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY); - return ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + now.getNano()); + return ofInstant(now, clock.getZone()); } //----------------------------------------------------------------------- @@ -343,6 +343,27 @@ public final class LocalTime return create(hour, minute, second, nanoOfSecond); } + /** + * Obtains an instance of {@code LocalTime} from an {@code Instant} and zone ID. + *

    + * This creates a local time based on the specified instant. + * First, the offset from UTC/Greenwich is obtained using the zone ID and instant, + * which is simple as there is only one valid offset for each instant. + * Then, the instant and offset are used to calculate the local time. + * + * @param instant the instant to create the time from, not null + * @param zone the time-zone, which may be an offset, not null + * @return the local time, not null + */ + public static LocalTime ofInstant(Instant instant, ZoneId zone) { + Objects.requireNonNull(instant, "instant"); + Objects.requireNonNull(zone, "zone"); + ZoneOffset offset = zone.getRules().getOffset(instant); + long localSecond = instant.getEpochSecond() + offset.getTotalSeconds(); + int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY); + return ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + instant.getNano()); + } + //----------------------------------------------------------------------- /** * Obtains an instance of {@code LocalTime} from a second-of-day value. diff --git a/jdk/src/java.base/share/classes/java/time/ZoneId.java b/jdk/src/java.base/share/classes/java/time/ZoneId.java index dd282d4a4c4..bae99f601cc 100644 --- a/jdk/src/java.base/share/classes/java/time/ZoneId.java +++ b/jdk/src/java.base/share/classes/java/time/ZoneId.java @@ -310,8 +310,7 @@ public abstract class ZoneId implements Serializable { public static ZoneId of(String zoneId, Map aliasMap) { Objects.requireNonNull(zoneId, "zoneId"); Objects.requireNonNull(aliasMap, "aliasMap"); - String id = aliasMap.get(zoneId); - id = (id != null ? id : zoneId); + String id = Objects.requireNonNullElse(aliasMap.get(zoneId), zoneId); return of(id); } diff --git a/jdk/src/java.base/share/classes/java/time/chrono/Chronology.java b/jdk/src/java.base/share/classes/java/time/chrono/Chronology.java index fbcdcec3472..5783f93ddf1 100644 --- a/jdk/src/java.base/share/classes/java/time/chrono/Chronology.java +++ b/jdk/src/java.base/share/classes/java/time/chrono/Chronology.java @@ -177,7 +177,7 @@ public interface Chronology extends Comparable { static Chronology from(TemporalAccessor temporal) { Objects.requireNonNull(temporal, "temporal"); Chronology obj = temporal.query(TemporalQueries.chronology()); - return (obj != null ? obj : IsoChronology.INSTANCE); + return Objects.requireNonNullElse(obj, IsoChronology.INSTANCE); } //----------------------------------------------------------------------- diff --git a/jdk/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/jdk/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index 90c6108c244..441ac711110 100644 --- a/jdk/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/jdk/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -2892,7 +2892,8 @@ public final class DateTimeFormatterBuilder { @Override public String toString() { - return "ReducedValue(" + field + "," + minWidth + "," + maxWidth + "," + (baseDate != null ? baseDate : baseValue) + ")"; + return "ReducedValue(" + field + "," + minWidth + "," + maxWidth + + "," + Objects.requireNonNullElse(baseDate, baseValue) + ")"; } } @@ -3851,6 +3852,10 @@ public final class DateTimeFormatterBuilder { return parseOffsetBased(context, text, position, position + 2, OffsetIdPrinterParser.INSTANCE_ID_ZERO); } else if (context.charEquals(nextChar, 'G') && length >= position + 3 && context.charEquals(nextNextChar, 'M') && context.charEquals(text.charAt(position + 2), 'T')) { + if (length >= position + 4 && context.charEquals(text.charAt(position + 3), '0')) { + context.setParsed(ZoneId.of("GMT0")); + return position + 4; + } return parseOffsetBased(context, text, position, position + 3, OffsetIdPrinterParser.INSTANCE_ID_ZERO); } } @@ -4328,7 +4333,7 @@ public final class DateTimeFormatterBuilder { private String getChronologyName(Chronology chrono, Locale locale) { String key = "calendarname." + chrono.getCalendarType(); String name = DateTimeTextProvider.getLocalizedResource(key, locale); - return name != null ? name : chrono.getId(); + return Objects.requireNonNullElseGet(name, () -> chrono.getId()); } } diff --git a/jdk/src/java.base/share/classes/java/time/format/DateTimePrintContext.java b/jdk/src/java.base/share/classes/java/time/format/DateTimePrintContext.java index 543a317e92e..310f6598fc9 100644 --- a/jdk/src/java.base/share/classes/java/time/format/DateTimePrintContext.java +++ b/jdk/src/java.base/share/classes/java/time/format/DateTimePrintContext.java @@ -146,7 +146,7 @@ final class DateTimePrintContext { if (overrideZone != null) { // if have zone and instant, calculation is simple, defaulting chrono if necessary if (temporal.isSupported(INSTANT_SECONDS)) { - Chronology chrono = (effectiveChrono != null ? effectiveChrono : IsoChronology.INSTANCE); + Chronology chrono = Objects.requireNonNullElse(effectiveChrono, IsoChronology.INSTANCE); return chrono.zonedDateTime(Instant.from(temporal), overrideZone); } // block changing zone on OffsetTime, and similar problem cases diff --git a/jdk/src/java.base/share/classes/java/time/temporal/IsoFields.java b/jdk/src/java.base/share/classes/java/time/temporal/IsoFields.java index 76528c4e7dd..73ebdf40abf 100644 --- a/jdk/src/java.base/share/classes/java/time/temporal/IsoFields.java +++ b/jdk/src/java.base/share/classes/java/time/temporal/IsoFields.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, 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 @@ -102,7 +102,7 @@ import sun.util.locale.provider.LocaleResources; * The complete date is expressed using three fields: *

      *
    • {@link #DAY_OF_QUARTER DAY_OF_QUARTER} - the day within the quarter, from 1 to 90, 91 or 92 - *
    • {@link #QUARTER_OF_YEAR QUARTER_OF_YEAR} - the week within the week-based-year + *
    • {@link #QUARTER_OF_YEAR QUARTER_OF_YEAR} - the quarter within the year, from 1 to 4 *
    • {@link ChronoField#YEAR YEAR} - the standard ISO year *
    * @@ -571,9 +571,6 @@ public final class IsoFields { //------------------------------------------------------------------------- private static final int[] QUARTER_DAYS = {0, 90, 181, 273, 0, 91, 182, 274}; - private static boolean isIso(TemporalAccessor temporal) { - return Chronology.from(temporal).equals(IsoChronology.INSTANCE); - } private static void ensureIso(TemporalAccessor temporal) { if (isIso(temporal) == false) { @@ -681,7 +678,7 @@ public final class IsoFields { @Override public boolean isSupportedBy(Temporal temporal) { - return temporal.isSupported(EPOCH_DAY); + return temporal.isSupported(EPOCH_DAY) && isIso(temporal); } @SuppressWarnings("unchecked") @@ -721,4 +718,8 @@ public final class IsoFields { return name; } } + + static boolean isIso(TemporalAccessor temporal) { + return Chronology.from(temporal).equals(IsoChronology.INSTANCE); + } } diff --git a/jdk/src/java.base/share/classes/java/util/Arrays.java b/jdk/src/java.base/share/classes/java/util/Arrays.java index f18180ac84d..af64264b3f3 100644 --- a/jdk/src/java.base/share/classes/java/util/Arrays.java +++ b/jdk/src/java.base/share/classes/java/util/Arrays.java @@ -2882,6 +2882,7 @@ public class Arrays { * @param a2 the other array to be tested for equality * @return {@code true} if the two arrays are equal */ + @HotSpotIntrinsicCandidate public static boolean equals(byte[] a, byte[] a2) { if (a==a2) return true; @@ -3304,6 +3305,103 @@ public class Arrays { return true; } + /** + * Returns {@code true} if the two specified arrays of Objects are + * equal to one another. + * + *

    Two arrays are considered equal if both arrays contain the same number + * of elements, and all corresponding pairs of elements in the two arrays + * are equal. In other words, the two arrays are equal if they contain the + * same elements in the same order. Also, two array references are + * considered equal if both are {@code null}. + * + *

    Two objects {@code e1} and {@code e2} are considered equal if, + * given the specified comparator, {@code cmp.compare(e1, e2) == 0}. + * + * @param a one array to be tested for equality + * @param a2 the other array to be tested for equality + * @param cmp the comparator to compare array elements + * @param the type of array elements + * @return {@code true} if the two arrays are equal + * @throws NullPointerException if the comparator is {@code null} + * @since 9 + */ + public static boolean equals(T[] a, T[] a2, Comparator cmp) { + Objects.requireNonNull(cmp); + if (a==a2) + return true; + if (a==null || a2==null) + return false; + + int length = a.length; + if (a2.length != length) + return false; + + for (int i=0; iequal to one another. + * + *

    Two arrays are considered equal if the number of elements covered by + * each range is the same, and all corresponding pairs of elements over the + * specified ranges in the two arrays are equal. In other words, two arrays + * are equal if they contain, over the specified ranges, the same elements + * in the same order. + * + *

    Two objects {@code e1} and {@code e2} are considered equal if, + * given the specified comparator, {@code cmp.compare(e1, e2) == 0}. + * + * @param a the first array to be tested for equality + * @param aFromIndex the index (inclusive) of the first element in the + * first array to be tested + * @param aToIndex the index (exclusive) of the last element in the + * first array to be tested + * @param b the second array to be tested fro equality + * @param bFromIndex the index (inclusive) of the first element in the + * second array to be tested + * @param bToIndex the index (exclusive) of the last element in the + * second array to be tested + * @param cmp the comparator to compare array elements + * @param the type of array elements + * @return {@code true} if the two arrays, over the specified ranges, are + * equal + * @throws IllegalArgumentException + * if {@code aFromIndex > aToIndex} or + * if {@code bFromIndex > bToIndex} + * @throws ArrayIndexOutOfBoundsException + * if {@code aFromIndex < 0 or aToIndex > a.length} or + * if {@code bFromIndex < 0 or bToIndex > b.length} + * @throws NullPointerException + * if either array or the comparator is {@code null} + * @since 9 + */ + public static boolean equals(T[] a, int aFromIndex, int aToIndex, + T[] b, int bFromIndex, int bToIndex, + Comparator cmp) { + Objects.requireNonNull(cmp); + rangeCheck(a.length, aFromIndex, aToIndex); + rangeCheck(b.length, bFromIndex, bToIndex); + + int aLength = aToIndex - aFromIndex; + int bLength = bToIndex - bFromIndex; + if (aLength != bLength) + return false; + + for (int i = 0; i < aLength; i++) { + if (cmp.compare(a[aFromIndex++], b[bFromIndex++]) != 0) + return false; + } + + return true; + } + // Filling /** @@ -8743,9 +8841,7 @@ public class Arrays { *

    {@code
          *     pl >= 0 &&
          *     pl < Math.min(a.length, b.length) &&
    -     *     IntStream.range(0, pl).
    -     *         map(i -> cmp.compare(a[i], b[i])).
    -     *         allMatch(c -> c == 0) &&
    +     *     Arrays.equals(a, 0, pl, b, 0, pl, cmp)
          *     cmp.compare(a[pl], b[pl]) != 0
          * }
    * Note that a common prefix length of {@code 0} indicates that the first @@ -8755,9 +8851,9 @@ public class Arrays { * prefix if the following expression is true: *
    {@code
          *     a.length != b.length &&
    -     *     IntStream.range(0, Math.min(a.length, b.length)).
    -     *         map(i -> cmp.compare(a[i], b[i])).
    -     *         allMatch(c -> c == 0) &&
    +     *     Arrays.equals(a, 0, Math.min(a.length, b.length),
    +     *                   b, 0, Math.min(a.length, b.length),
    +     *                   cmp)
          * }
    * * @param a the first array to be tested for a mismatch @@ -8814,9 +8910,7 @@ public class Arrays { *
    {@code
          *     pl >= 0 &&
          *     pl < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex) &&
    -     *     IntStream.range(0, pl).
    -     *         map(i -> cmp.compare(a[aFromIndex + i], b[bFromIndex + i])).
    -     *         allMatch(c -> c == 0) &&
    +     *     Arrays.equals(a, aFromIndex, aFromIndex + pl, b, bFromIndex, bFromIndex + pl, cmp) &&
          *     cmp.compare(a[aFromIndex + pl], b[bFromIndex + pl]) != 0
          * }
    * Note that a common prefix length of {@code 0} indicates that the first @@ -8828,9 +8922,9 @@ public class Arrays { * if the following expression is true: *
    {@code
          *     (aToIndex - aFromIndex) != (bToIndex - bFromIndex) &&
    -     *     IntStream.range(0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex)).
    -     *         map(i -> cmp.compare(a[aFromIndex + i], b[bFromIndex + i])).
    -     *         allMatch(c -> c == 0)
    +     *     Arrays.equals(a, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex),
    +     *                   b, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex),
    +     *                   cmp)
          * }
    * * @param a the first array to be tested for a mismatch diff --git a/jdk/src/java.base/share/classes/java/util/Collections.java b/jdk/src/java.base/share/classes/java/util/Collections.java index fec21c29079..5dcc7e8c6b0 100644 --- a/jdk/src/java.base/share/classes/java/util/Collections.java +++ b/jdk/src/java.base/share/classes/java/util/Collections.java @@ -2090,7 +2090,8 @@ public class Collections { * through the returned set.

    * * It is imperative that the user manually synchronize on the returned - * set when iterating over it: + * collection when traversing it via {@link Iterator}, {@link Spliterator} + * or {@link Stream}: *

          *  Set s = Collections.synchronizedSet(new HashSet());
          *      ...
    @@ -2149,8 +2150,9 @@ public class Collections {
          * through the returned sorted set (or its views).

    * * It is imperative that the user manually synchronize on the returned - * sorted set when iterating over it or any of its {@code subSet}, - * {@code headSet}, or {@code tailSet} views. + * sorted set when traversing it or any of its {@code subSet}, + * {@code headSet}, or {@code tailSet} views via {@link Iterator}, + * {@link Spliterator} or {@link Stream}: *

          *  SortedSet s = Collections.synchronizedSortedSet(new TreeSet());
          *      ...
    @@ -2240,8 +2242,9 @@ public class Collections {
          * accomplished through the returned navigable set (or its views).

    * * It is imperative that the user manually synchronize on the returned - * navigable set when iterating over it or any of its {@code subSet}, - * {@code headSet}, or {@code tailSet} views. + * navigable set when traversing it, or any of its {@code subSet}, + * {@code headSet}, or {@code tailSet} views, via {@link Iterator}, + * {@link Spliterator} or {@link Stream}: *

          *  NavigableSet s = Collections.synchronizedNavigableSet(new TreeSet());
          *      ...
    @@ -2355,7 +2358,8 @@ public class Collections {
          * through the returned list.

    * * It is imperative that the user manually synchronize on the returned - * list when iterating over it: + * list when traversing it via {@link Iterator}, {@link Spliterator} + * or {@link Stream}: *

          *  List list = Collections.synchronizedList(new ArrayList());
          *      ...
    @@ -2523,7 +2527,8 @@ public class Collections {
          * through the returned map.

    * * It is imperative that the user manually synchronize on the returned - * map when iterating over any of its collection views: + * map when traversing any of its collection views via {@link Iterator}, + * {@link Spliterator} or {@link Stream}: *

          *  Map m = Collections.synchronizedMap(new HashMap());
          *      ...
    @@ -2700,9 +2705,10 @@ public class Collections {
          * through the returned sorted map (or its views).

    * * It is imperative that the user manually synchronize on the returned - * sorted map when iterating over any of its collection views, or the + * sorted map when traversing any of its collection views, or the * collections views of any of its {@code subMap}, {@code headMap} or - * {@code tailMap} views. + * {@code tailMap} views, via {@link Iterator}, {@link Spliterator} or + * {@link Stream}: *

          *  SortedMap m = Collections.synchronizedSortedMap(new TreeMap());
          *      ...
    @@ -2797,9 +2803,10 @@ public class Collections {
          * accomplished through the returned navigable map (or its views).

    * * It is imperative that the user manually synchronize on the returned - * navigable map when iterating over any of its collection views, or the + * navigable map when traversing any of its collection views, or the * collections views of any of its {@code subMap}, {@code headMap} or - * {@code tailMap} views. + * {@code tailMap} views, via {@link Iterator}, {@link Spliterator} or + * {@link Stream}: *

          *  NavigableMap m = Collections.synchronizedNavigableMap(new TreeMap());
          *      ...
    diff --git a/jdk/src/java.base/share/classes/java/util/Formatter.java b/jdk/src/java.base/share/classes/java/util/Formatter.java
    index d8fc7e60ecd..0a3b50b6a13 100644
    --- a/jdk/src/java.base/share/classes/java/util/Formatter.java
    +++ b/jdk/src/java.base/share/classes/java/util/Formatter.java
    @@ -49,6 +49,7 @@ import java.text.DecimalFormatSymbols;
     import java.text.NumberFormat;
     import java.util.regex.Matcher;
     import java.util.regex.Pattern;
    +import java.util.Objects;
     
     import java.time.DateTimeException;
     import java.time.Instant;
    @@ -3860,7 +3861,7 @@ public final class Formatter implements Closeable, Flushable {
                         ampm = dfs.getAmPmStrings();
                     }
                     String s = ampm[t.get(Calendar.AM_PM)];
    -                sb.append(s.toLowerCase(l != null ? l : Locale.US));
    +                sb.append(s.toLowerCase(Objects.requireNonNullElse(l, Locale.US)));
                     break;
                 }
                 case DateTime.SECONDS_SINCE_EPOCH: { // 's' (0 - 99...?)
    @@ -3893,7 +3894,7 @@ public final class Formatter implements Closeable, Flushable {
                     TimeZone tz = t.getTimeZone();
                     sb.append(tz.getDisplayName((t.get(Calendar.DST_OFFSET) != 0),
                                                TimeZone.SHORT,
    -                                            (l == null) ? Locale.US : l));
    +                                           Objects.requireNonNullElse(l, Locale.US)));
                     break;
                 }
     
    @@ -3901,7 +3902,7 @@ public final class Formatter implements Closeable, Flushable {
                 case DateTime.NAME_OF_DAY_ABBREV:     // 'a'
                 case DateTime.NAME_OF_DAY:          { // 'A'
                     int i = t.get(Calendar.DAY_OF_WEEK);
    -                Locale lt = ((l == null) ? Locale.US : l);
    +                Locale lt = Objects.requireNonNullElse(l, Locale.US);
                     DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
                     if (c == DateTime.NAME_OF_DAY)
                         sb.append(dfs.getWeekdays()[i]);
    @@ -3913,7 +3914,7 @@ public final class Formatter implements Closeable, Flushable {
                 case DateTime.NAME_OF_MONTH_ABBREV_X: // 'h' -- same b
                 case DateTime.NAME_OF_MONTH:        { // 'B'
                     int i = t.get(Calendar.MONTH);
    -                Locale lt = ((l == null) ? Locale.US : l);
    +                Locale lt = Objects.requireNonNullElse(l, Locale.US);
                     DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
                     if (c == DateTime.NAME_OF_MONTH)
                         sb.append(dfs.getMonths()[i]);
    @@ -3984,7 +3985,7 @@ public final class Formatter implements Closeable, Flushable {
                     StringBuilder tsb = new StringBuilder();
                     print(tsb, t, DateTime.AM_PM, l);
     
    -                sb.append(tsb.toString().toUpperCase(l != null ? l : Locale.US));
    +                sb.append(tsb.toString().toUpperCase(Objects.requireNonNullElse(l, Locale.US)));
                     break;
                 }
                 case DateTime.DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
    @@ -4092,7 +4093,7 @@ public final class Formatter implements Closeable, Flushable {
                             ampm = dfs.getAmPmStrings();
                         }
                         String s = ampm[t.get(ChronoField.AMPM_OF_DAY)];
    -                    sb.append(s.toLowerCase(l != null ? l : Locale.US));
    +                    sb.append(s.toLowerCase(Objects.requireNonNullElse(l, Locale.US)));
                         break;
                     }
                     case DateTime.SECONDS_SINCE_EPOCH: { // 's' (0 - 99...?)
    @@ -4131,7 +4132,7 @@ public final class Formatter implements Closeable, Flushable {
                             sb.append(TimeZone.getTimeZone(zid.getId())
                                               .getDisplayName(zid.getRules().isDaylightSavings(instant),
                                                               TimeZone.SHORT,
    -                                                          (l == null) ? Locale.US : l));
    +                                                          Objects.requireNonNullElse(l, Locale.US)));
                             break;
                         }
                         sb.append(zid.getId());
    @@ -4141,7 +4142,7 @@ public final class Formatter implements Closeable, Flushable {
                     case DateTime.NAME_OF_DAY_ABBREV:     // 'a'
                     case DateTime.NAME_OF_DAY:          { // 'A'
                         int i = t.get(ChronoField.DAY_OF_WEEK) % 7 + 1;
    -                    Locale lt = ((l == null) ? Locale.US : l);
    +                    Locale lt = Objects.requireNonNullElse(l, Locale.US);
                         DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
                         if (c == DateTime.NAME_OF_DAY)
                             sb.append(dfs.getWeekdays()[i]);
    @@ -4153,7 +4154,7 @@ public final class Formatter implements Closeable, Flushable {
                     case DateTime.NAME_OF_MONTH_ABBREV_X: // 'h' -- same b
                     case DateTime.NAME_OF_MONTH:        { // 'B'
                         int i = t.get(ChronoField.MONTH_OF_YEAR) - 1;
    -                    Locale lt = ((l == null) ? Locale.US : l);
    +                    Locale lt = Objects.requireNonNullElse(l, Locale.US);
                         DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
                         if (c == DateTime.NAME_OF_MONTH)
                             sb.append(dfs.getMonths()[i]);
    @@ -4223,7 +4224,7 @@ public final class Formatter implements Closeable, Flushable {
                         // this may be in wrong place for some locales
                         StringBuilder tsb = new StringBuilder();
                         print(tsb, t, DateTime.AM_PM, l);
    -                    sb.append(tsb.toString().toUpperCase(l != null ? l : Locale.US));
    +                    sb.append(tsb.toString().toUpperCase(Objects.requireNonNullElse(l, Locale.US)));
                         break;
                     }
                     case DateTime.DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
    diff --git a/jdk/src/java.base/share/classes/java/util/Objects.java b/jdk/src/java.base/share/classes/java/util/Objects.java
    index 65089553b87..769ca73a739 100644
    --- a/jdk/src/java.base/share/classes/java/util/Objects.java
    +++ b/jdk/src/java.base/share/classes/java/util/Objects.java
    @@ -295,7 +295,7 @@ public final class Objects {
          *        {@code defaultObj} is {@code null}
          * @since 9
          */
    -    public static  T nonNullElse(T obj, T defaultObj) {
    +    public static  T requireNonNullElse(T obj, T defaultObj) {
             return (obj != null) ? obj : requireNonNull(defaultObj, "defaultObj");
         }
     
    @@ -314,8 +314,9 @@ public final class Objects {
          *        the {@code supplier.get()} value is {@code null}
          * @since 9
          */
    -    public static  T nonNullElseGet(T obj, Supplier supplier) {
    -        return (obj != null) ? obj : requireNonNull(requireNonNull(supplier, "supplier").get(), "supplier.get()");
    +    public static  T requireNonNullElseGet(T obj, Supplier supplier) {
    +        return (obj != null) ? obj
    +                : requireNonNull(requireNonNull(supplier, "supplier").get(), "supplier.get()");
         }
     
         /**
    @@ -351,15 +352,16 @@ public final class Objects {
          * @param b the second out of bound value
          * @param oobe the exception mapping function that when applied with out of
          *        bounds arguments returns a runtime exception.  If {@code null}
    -     *        then, it's as if an exception mapping function was supplied that
    +     *        then, it is as if an exception mapping function was supplied that
          *        returns {@link IndexOutOfBoundsException} for any given arguments.
          * @return the runtime exception
          */
         private static RuntimeException outOfBounds(
                 int a, int b, BiFunction oobe) {
    -        return oobe == null
    -               ? new IndexOutOfBoundsException(a, b)
    -               : oobe.apply(a, b);
    +        RuntimeException e = oobe == null
    +                             ? null : oobe.apply(a, b);
    +        return e == null
    +               ? new IndexOutOfBoundsException(a, b) : e;
         }
     
         /**
    @@ -407,8 +409,10 @@ public final class Objects {
          * @param length the upper-bound (exclusive) of the range
          * @param oobe the exception mapping function that when applied with out
          *        of bounds arguments returns a runtime exception.  If {@code null}
    -     *        then, it's as if an exception mapping function was supplied that
    -     *        returns {@link IndexOutOfBoundsException} for any given arguments.
    +     *        or returns {@code null} then, it is as if an exception mapping
    +     *        function was supplied that returns
    +     *        {@link IndexOutOfBoundsException} for any given arguments.
    +     *        Exceptions thrown by the function are relayed to the caller.
          * @return {@code index} if it is within bounds of the range
          * @throws T if the {@code index} is out of bounds, then a runtime exception
          *         is thrown that is the result of applying the out of bounds
    @@ -483,8 +487,10 @@ public final class Objects {
          * @param length the upper-bound (exclusive) the range
          * @param oobe the exception mapping function that when applied with out
          *        of bounds arguments returns a runtime exception.  If {@code null}
    -     *        then, it's as if an exception mapping function was supplied that
    -     *        returns {@link IndexOutOfBoundsException} for any given arguments.
    +     *        or returns {@code null} then, it is as if an exception mapping
    +     *        function was supplied that returns
    +     *        {@link IndexOutOfBoundsException} for any given arguments.
    +     *        Exceptions thrown by the function are relayed to the caller.
          * @return {@code fromIndex} if the sub-range within bounds of the range
          * @throws T if the sub-range is out of bounds, then a runtime exception is
          *         thrown that is the result of applying the out of bounds arguments
    @@ -552,8 +558,10 @@ public final class Objects {
          * @param length the upper-bound (exclusive) of the range
          * @param oobe the exception mapping function that when applied with out
          *        of bounds arguments returns a runtime exception.  If {@code null}
    -     *        then, it's as if an exception mapping function was supplied that
    -     *        returns {@link IndexOutOfBoundsException} for any given arguments.
    +     *        or returns {@code null} then, it is as if an exception mapping
    +     *        function was supplied that returns
    +     *        {@link IndexOutOfBoundsException} for any given arguments.
    +     *        Exceptions thrown by the function are relayed to the caller.
          * @return {@code fromIndex} if the sub-range within bounds of the range
          * @throws T if the sub-range is out of bounds, then a runtime exception is
          *         thrown that is the result of applying the out of bounds arguments
    diff --git a/jdk/src/java.base/share/classes/java/util/Random.java b/jdk/src/java.base/share/classes/java/util/Random.java
    index f22dca758f7..78e4953bfe8 100644
    --- a/jdk/src/java.base/share/classes/java/util/Random.java
    +++ b/jdk/src/java.base/share/classes/java/util/Random.java
    @@ -34,7 +34,7 @@ import java.util.stream.IntStream;
     import java.util.stream.LongStream;
     import java.util.stream.StreamSupport;
     
    -import sun.misc.Unsafe;
    +import jdk.internal.misc.Unsafe;
     
     /**
      * An instance of this class is used to generate a stream of
    diff --git a/jdk/src/java.base/share/classes/java/util/TreeMap.java b/jdk/src/java.base/share/classes/java/util/TreeMap.java
    index a5ed0f5e91c..248ef934369 100644
    --- a/jdk/src/java.base/share/classes/java/util/TreeMap.java
    +++ b/jdk/src/java.base/share/classes/java/util/TreeMap.java
    @@ -2581,19 +2581,17 @@ public class TreeMap
         }
     
         /**
    -     * Find the level down to which to assign all nodes BLACK.  This is the
    -     * last `full' level of the complete binary tree produced by
    -     * buildTree. The remaining nodes are colored RED. (This makes a `nice'
    -     * set of color assignments wrt future insertions.) This level number is
    +     * Finds the level down to which to assign all nodes BLACK.  This is the
    +     * last `full' level of the complete binary tree produced by buildTree.
    +     * The remaining nodes are colored RED. (This makes a `nice' set of
    +     * color assignments wrt future insertions.) This level number is
          * computed by finding the number of splits needed to reach the zeroeth
    -     * node.  (The answer is ~lg(N), but in any case must be computed by same
    -     * quick O(lg(N)) loop.)
    +     * node.
    +     *
    +     * @param size the (non-negative) number of keys in the tree to be built
          */
    -    private static int computeRedLevel(int sz) {
    -        int level = 0;
    -        for (int m = sz - 1; m >= 0; m = m / 2 - 1)
    -            level++;
    -        return level;
    +    private static int computeRedLevel(int size) {
    +        return 31 - Integer.numberOfLeadingZeros(size + 1);
         }
     
         /**
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java b/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java
    index ee553e8f261..45389ac9109 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java
    @@ -2775,7 +2775,7 @@ public class CompletableFuture implements Future, CompletionStage {
         }
     
         // Unsafe mechanics
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long RESULT;
         private static final long STACK;
         private static final long NEXT;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java
    index 4c1180dc11b..b2c2d2aa701 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java
    @@ -297,7 +297,7 @@ public class ConcurrentHashMap extends AbstractMap
          * Table accesses require volatile/atomic reads, writes, and
          * CASes.  Because there is no other way to arrange this without
          * adding further indirections, we use intrinsics
    -     * (sun.misc.Unsafe) operations.
    +     * (jdk.internal.misc.Unsafe) operations.
          *
          * We use the top (sign) bit of Node hash fields for control
          * purposes -- it is available anyway because of addressing
    @@ -3287,7 +3287,7 @@ public class ConcurrentHashMap extends AbstractMap
                 return true;
             }
     
    -        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
             private static final long LOCKSTATE;
             static {
                 try {
    @@ -6330,7 +6330,7 @@ public class ConcurrentHashMap extends AbstractMap
         }
     
         // Unsafe mechanics
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long SIZECTL;
         private static final long TRANSFERINDEX;
         private static final long BASECOUNT;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java
    index 0991efc9e28..4f8fdd92c49 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java
    @@ -326,7 +326,7 @@ public class ConcurrentLinkedDeque
     
             // Unsafe mechanics
     
    -        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
             private static final long PREV;
             private static final long ITEM;
             private static final long NEXT;
    @@ -1608,7 +1608,7 @@ public class ConcurrentLinkedDeque
     
         // Unsafe mechanics
     
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long HEAD;
         private static final long TAIL;
         static {
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java
    index 6e68ed0d333..d302b6f55b8 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java
    @@ -929,7 +929,7 @@ public class ConcurrentLinkedQueue extends AbstractQueue
     
         // Unsafe mechanics
     
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long HEAD;
         private static final long TAIL;
         private static final long ITEM;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java
    index 60d3c42e8bb..745de306bc1 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java
    @@ -534,7 +534,7 @@ public class ConcurrentSkipListMap extends AbstractMap
     
             // Unsafe mechanics
     
    -        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
             private static final long VALUE;
             private static final long NEXT;
     
    @@ -614,7 +614,7 @@ public class ConcurrentSkipListMap extends AbstractMap
             }
     
             // Unsafe mechanics
    -        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
             private static final long RIGHT;
             static {
                 try {
    @@ -3596,7 +3596,7 @@ public class ConcurrentSkipListMap extends AbstractMap
         }
     
         // Unsafe mechanics
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long HEAD;
         static {
             try {
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java
    index 96dff4f8989..2ecacf8e185 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java
    @@ -510,7 +510,7 @@ public class ConcurrentSkipListSet
             U.putObjectVolatile(this, MAP, map);
         }
     
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long MAP;
         static {
             try {
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java b/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java
    index 771d8b44fa9..65baf7c87c8 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java
    @@ -1545,7 +1545,7 @@ public class CopyOnWriteArrayList
         private void resetLock() {
             U.putObjectVolatile(this, LOCK, new Object());
         }
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long LOCK;
         static {
             try {
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java b/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java
    index eebc700f932..24c154e2f05 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java
    @@ -754,7 +754,7 @@ public abstract class CountedCompleter extends ForkJoinTask {
         protected void setRawResult(T t) { }
     
         // Unsafe mechanics
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long PENDING;
         static {
             try {
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java b/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java
    index 4c03c25d03d..503d2500578 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java
    @@ -625,7 +625,7 @@ public class Exchanger {
         }
     
         // Unsafe mechanics
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long BOUND;
         private static final long SLOT;
         private static final long MATCH;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java
    index d2490854abf..e4e91a1a187 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java
    @@ -1337,7 +1337,7 @@ public class ForkJoinPool extends AbstractExecutorService {
             }
     
             // Unsafe mechanics. Note that some are (and must be) the same as in FJP
    -        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
             private static final long QLOCK;
             private static final int ABASE;
             private static final int ASHIFT;
    @@ -3452,7 +3452,7 @@ public class ForkJoinPool extends AbstractExecutorService {
         }
     
         // Unsafe mechanics
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long CTL;
         private static final long RUNSTATE;
         private static final int ABASE;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java
    index 6a373367d0e..2b714dff9b4 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java
    @@ -1517,7 +1517,7 @@ public abstract class ForkJoinTask implements Future, Serializable {
         }
     
         // Unsafe mechanics
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long STATUS;
     
         static {
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java
    index e98ba993c30..54d8931de4f 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java
    @@ -185,7 +185,7 @@ public class ForkJoinWorkerThread extends Thread {
         }
     
         // Set up to allow setting thread fields in constructor
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long THREADLOCALS;
         private static final long INHERITABLETHREADLOCALS;
         private static final long INHERITEDACCESSCONTROLCONTEXT;
    @@ -248,7 +248,7 @@ public class ForkJoinWorkerThread extends Thread {
              */
             private static ThreadGroup createThreadGroup() {
                 try {
    -                sun.misc.Unsafe u = sun.misc.Unsafe.getUnsafe();
    +                jdk.internal.misc.Unsafe u = jdk.internal.misc.Unsafe.getUnsafe();
                     long tg = u.objectFieldOffset
                         (Thread.class.getDeclaredField("group"));
                     long gp = u.objectFieldOffset
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java b/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java
    index 62c2bfcb291..27943fd7cb6 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java
    @@ -484,7 +484,7 @@ public class FutureTask implements RunnableFuture {
         }
     
         // Unsafe mechanics
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long STATE;
         private static final long RUNNER;
         private static final long WAITERS;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java b/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java
    index 8127fe3a34f..02ffd871b7a 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java
    @@ -538,7 +538,7 @@ public class LinkedTransferQueue extends AbstractQueue
             private static final long serialVersionUID = -3375979862319811754L;
     
             // Unsafe mechanics
    -        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
             private static final long ITEM;
             private static final long NEXT;
             private static final long WAITER;
    @@ -1564,7 +1564,7 @@ public class LinkedTransferQueue extends AbstractQueue
     
         // Unsafe mechanics
     
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long HEAD;
         private static final long TAIL;
         private static final long SWEEPVOTES;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java b/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java
    index 9ef99361f45..e8d91e557c0 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java
    @@ -1137,7 +1137,7 @@ public class Phaser {
     
         // Unsafe mechanics
     
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long STATE;
         static {
             try {
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java b/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java
    index 7432529305e..652fd6958cf 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java
    @@ -1010,7 +1010,7 @@ public class PriorityBlockingQueue extends AbstractQueue
         }
     
         // Unsafe mechanics
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long ALLOCATIONSPINLOCK;
         static {
             try {
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java b/jdk/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java
    index 0d6e66bf516..03462ac1ef4 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java
    @@ -1596,7 +1596,7 @@ public class SubmissionPublisher implements Flow.Publisher,
             }
     
             // Unsafe mechanics
    -        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
             private static final long CTL;
             private static final long TAIL;
             private static final long HEAD;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java b/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java
    index 029aa9c895c..069d0a6b851 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java
    @@ -283,7 +283,7 @@ public class SynchronousQueue extends AbstractQueue
                 }
     
                 // Unsafe mechanics
    -            private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +            private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
                 private static final long MATCH;
                 private static final long NEXT;
     
    @@ -509,7 +509,7 @@ public class SynchronousQueue extends AbstractQueue
             }
     
             // Unsafe mechanics
    -        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
             private static final long HEAD;
             static {
                 try {
    @@ -575,7 +575,7 @@ public class SynchronousQueue extends AbstractQueue
                 }
     
                 // Unsafe mechanics
    -            private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +            private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
                 private static final long ITEM;
                 private static final long NEXT;
     
    @@ -817,7 +817,7 @@ public class SynchronousQueue extends AbstractQueue
                 }
             }
     
    -        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
             private static final long HEAD;
             private static final long TAIL;
             private static final long CLEANME;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java b/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java
    index f9542d504d7..f4bf5a4fef2 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java
    @@ -1050,7 +1050,7 @@ public class ThreadLocalRandom extends Random {
         }
     
         // Unsafe mechanics
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long SEED;
         private static final long PROBE;
         private static final long SECONDARY;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java
    index b2a23903c22..01fc1c2c0d7 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java
    @@ -49,7 +49,7 @@ package java.util.concurrent.atomic;
     public class AtomicBoolean implements java.io.Serializable {
         private static final long serialVersionUID = 4654671469794556979L;
     
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long VALUE;
     
         static {
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java
    index 19b341819b9..96e13f60ed8 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java
    @@ -54,7 +54,7 @@ import java.util.function.IntUnaryOperator;
     public class AtomicInteger extends Number implements java.io.Serializable {
         private static final long serialVersionUID = 6214790243416807050L;
     
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long VALUE;
     
         static {
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java
    index 2ea9a272008..f3a262b65b4 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java
    @@ -49,7 +49,7 @@ import java.util.function.IntUnaryOperator;
     public class AtomicIntegerArray implements java.io.Serializable {
         private static final long serialVersionUID = 2862133569453604235L;
     
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final int ABASE;
         private static final int ASHIFT;
         private final int[] array;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
    index 06e517d1a2b..1ec99ccc42f 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
    @@ -367,7 +367,7 @@ public abstract class AtomicIntegerFieldUpdater {
          */
         private static class AtomicIntegerFieldUpdaterImpl
                 extends AtomicIntegerFieldUpdater {
    -        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
             private final long offset;
             private final Class tclass;
             private final Class cclass;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java
    index b919f1e3c7a..2af5d1dd07f 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java
    @@ -54,7 +54,7 @@ import java.util.function.LongUnaryOperator;
     public class AtomicLong extends Number implements java.io.Serializable {
         private static final long serialVersionUID = 1927816293512124184L;
     
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long VALUE;
     
         /**
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java
    index d1af039aa61..58aa1875b19 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java
    @@ -48,7 +48,7 @@ import java.util.function.LongUnaryOperator;
     public class AtomicLongArray implements java.io.Serializable {
         private static final long serialVersionUID = -2308431214976778248L;
     
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final int ABASE;
         private static final int ASHIFT;
         private final long[] array;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
    index 5cb20197126..870a311aebf 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
    @@ -366,7 +366,7 @@ public abstract class AtomicLongFieldUpdater {
         }
     
         private static class CASUpdater extends AtomicLongFieldUpdater {
    -        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
             private final long offset;
             private final Class tclass;
             private final Class cclass;
    @@ -490,7 +490,7 @@ public abstract class AtomicLongFieldUpdater {
     
     
         private static class LockedUpdater extends AtomicLongFieldUpdater {
    -        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
             private final long offset;
             private final Class tclass;
             private final Class cclass;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java
    index b49118b1c60..8204c3fcc61 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java
    @@ -190,7 +190,7 @@ public class AtomicMarkableReference {
     
         // Unsafe mechanics
     
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long PAIR;
         static {
             try {
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java
    index dbc668582f7..9d4d2a385c7 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java
    @@ -49,7 +49,7 @@ import java.util.function.UnaryOperator;
     public class AtomicReference implements java.io.Serializable {
         private static final long serialVersionUID = -1848883965231344442L;
     
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long VALUE;
     
         static {
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java
    index cce148e6f0e..03f950d43e0 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java
    @@ -52,7 +52,7 @@ import java.util.function.UnaryOperator;
     public class AtomicReferenceArray implements java.io.Serializable {
         private static final long serialVersionUID = -6209656149925076980L;
     
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long ARRAY;
         private static final int ABASE;
         private static final int ASHIFT;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
    index f33498c8f9f..aa89aead91b 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
    @@ -284,7 +284,7 @@ public abstract class AtomicReferenceFieldUpdater {
     
         private static final class AtomicReferenceFieldUpdaterImpl
             extends AtomicReferenceFieldUpdater {
    -        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
             private final long offset;
             private final Class tclass;
             private final Class vclass;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java
    index 40ceeb2650f..fd520d7d889 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java
    @@ -190,7 +190,7 @@ public class AtomicStampedReference {
     
         // Unsafe mechanics
     
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long PAIR;
         static {
             try {
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java
    index 18d720fa391..153a3adc378 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java
    @@ -133,7 +133,7 @@ abstract class Striped64 extends Number {
             }
     
             // Unsafe mechanics
    -        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
             private static final long VALUE;
             static {
                 try {
    @@ -372,7 +372,7 @@ abstract class Striped64 extends Number {
         }
     
         // Unsafe mechanics
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long BASE;
         private static final long CELLSBUSY;
         private static final long PROBE;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
    index 7f6de8971ce..06f50846234 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
    @@ -1821,7 +1821,7 @@ public abstract class AbstractQueuedLongSynchronizer
          * are at it, we do the same for other CASable fields (which could
          * otherwise be done with atomic field updaters).
          */
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long STATE;
         private static final long HEAD;
         private static final long TAIL;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
    index 1f36435bb4c..3d604a63214 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
    @@ -524,7 +524,7 @@ public abstract class AbstractQueuedSynchronizer
                 return U.compareAndSwapObject(this, NEXT, expect, update);
             }
     
    -        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +        private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
             private static final long NEXT;
             static final long PREV;
             private static final long THREAD;
    @@ -2285,7 +2285,7 @@ public abstract class AbstractQueuedSynchronizer
          * are at it, we do the same for other CASable fields (which could
          * otherwise be done with atomic field updaters).
          */
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long STATE;
         private static final long HEAD;
         private static final long TAIL;
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
    index 297de583436..a82d2d9f736 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
    @@ -394,7 +394,7 @@ public class LockSupport {
         }
     
         // Hotspot implementation via intrinsics API
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long PARKBLOCKER;
         private static final long SECONDARY;
         static {
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java
    index c41b4285a67..a2c627aed3b 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java
    @@ -1501,7 +1501,7 @@ public class ReentrantReadWriteLock
         }
     
         // Unsafe mechanics
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long TID;
         static {
             try {
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java
    index d6895996d91..0e8252587b5 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java
    @@ -1398,7 +1398,7 @@ public class StampedLock implements java.io.Serializable {
         }
     
         // Unsafe mechanics
    -    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    +    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
         private static final long STATE;
         private static final long WHEAD;
         private static final long WTAIL;
    diff --git a/jdk/src/java.base/share/classes/java/util/zip/CRC32C.java b/jdk/src/java.base/share/classes/java/util/zip/CRC32C.java
    index c40886fafa6..2d82625780e 100644
    --- a/jdk/src/java.base/share/classes/java/util/zip/CRC32C.java
    +++ b/jdk/src/java.base/share/classes/java/util/zip/CRC32C.java
    @@ -28,7 +28,7 @@ import java.nio.ByteBuffer;
     import java.nio.ByteOrder;
     
     import jdk.internal.HotSpotIntrinsicCandidate;
    -import sun.misc.Unsafe;
    +import jdk.internal.misc.Unsafe;
     import sun.nio.ch.DirectBuffer;
     
     /**
    diff --git a/jdk/src/java.base/share/classes/jdk/internal/logger/AbstractLoggerWrapper.java b/jdk/src/java.base/share/classes/jdk/internal/logger/AbstractLoggerWrapper.java
    new file mode 100644
    index 00000000000..95d35ed4fa2
    --- /dev/null
    +++ b/jdk/src/java.base/share/classes/jdk/internal/logger/AbstractLoggerWrapper.java
    @@ -0,0 +1,380 @@
    +/*
    + * Copyright (c) 2015, 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.internal.logger;
    +
    +import java.util.ResourceBundle;
    +import java.util.function.Supplier;
    +import java.lang.System.Logger;
    +import java.lang.System.Logger.Level;
    +import sun.util.logging.PlatformLogger;
    +
    +/**
    + * An implementation of {@link System.Logger System.Logger}
    + * that redirects all calls to a wrapped instance of {@link
    + * System.Logger System.Logger}
    + *
    + * @param  Type of the wrapped Logger: {@code Logger} or
    + *        an extension of that interface.
    + *
    + */
    +abstract class AbstractLoggerWrapper
    +    implements Logger, PlatformLogger.Bridge, PlatformLogger.ConfigurableBridge {
    +
    +    AbstractLoggerWrapper() { }
    +
    +    abstract L wrapped();
    +
    +    abstract PlatformLogger.Bridge platformProxy();
    +
    +    L getWrapped() {
    +        return wrapped();
    +    }
    +
    +    @Override
    +    public final String getName() {
    +        return wrapped().getName();
    +    }
    +
    +    // -----------------------------------------------------------------
    +    // Generic methods taking a Level as parameter
    +    // -----------------------------------------------------------------
    +
    +
    +    @Override
    +    public boolean isLoggable(Level level) {
    +        return wrapped().isLoggable(level);
    +    }
    +
    +    @Override
    +    public void log(Level level, String msg) {
    +        wrapped().log(level, msg);
    +    }
    +
    +    @Override
    +    public void log(Level level,
    +                    Supplier msgSupplier) {
    +        wrapped().log(level, msgSupplier);
    +    }
    +
    +    @Override
    +    public void log(Level level, Object obj) {
    +        wrapped().log(level, obj);
    +    }
    +
    +    @Override
    +    public void log(Level level,
    +                   String msg, Throwable thrown) {
    +        wrapped().log(level, msg, thrown);
    +    }
    +
    +    @Override
    +    public void log(Level level, Supplier msgSupplier, Throwable thrown) {
    +        wrapped().log(level, msgSupplier, thrown);
    +    }
    +
    +    @Override
    +    public void log(Level level,
    +                    String format, Object... params) {
    +        wrapped().log(level, format, params);
    +    }
    +
    +    @Override
    +    public void log(Level level, ResourceBundle bundle,
    +                    String key, Throwable thrown) {
    +        wrapped().log(level, bundle, key, thrown);
    +    }
    +
    +    @Override
    +    public void log(Level level, ResourceBundle bundle,
    +                    String format, Object... params) {
    +        wrapped().log(level, bundle, format, params);
    +    }
    +
    +    // ---------------------------------------------------------
    +    // Methods from PlatformLogger.Bridge
    +    // ---------------------------------------------------------
    +
    +    @Override
    +    public boolean isLoggable(PlatformLogger.Level level) {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        if (platformProxy == null) return isLoggable(level.systemLevel());
    +        else return platformProxy.isLoggable(level);
    +    }
    +
    +    @Override
    +    public boolean isEnabled() {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        return platformProxy == null || platformProxy.isEnabled();
    +    }
    +
    +    @Override
    +    public void log(PlatformLogger.Level level, String msg) {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        if (platformProxy == null)  {
    +            wrapped().log(level.systemLevel(), msg);
    +        } else {
    +            platformProxy.log(level, msg);
    +        }
    +    }
    +
    +    @Override
    +    public void log(PlatformLogger.Level level, String msg, Throwable thrown) {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        if (platformProxy == null)  {
    +            wrapped().log(level.systemLevel(), msg, thrown);
    +        } else {
    +            platformProxy.log(level, msg, thrown);
    +        }
    +    }
    +
    +    @Override
    +    public void log(PlatformLogger.Level level, String msg, Object... params) {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        if (platformProxy == null)  {
    +            wrapped().log(level.systemLevel(), msg, params);
    +        } else {
    +            platformProxy.log(level, msg, params);
    +        }
    +    }
    +
    +    @Override
    +    public void log(PlatformLogger.Level level, Supplier msgSupplier) {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        if (platformProxy == null)  {
    +            wrapped().log(level.systemLevel(),msgSupplier);
    +        } else {
    +            platformProxy.log(level,msgSupplier);
    +        }
    +    }
    +
    +    @Override
    +    public void log(PlatformLogger.Level level, Throwable thrown,
    +                    Supplier msgSupplier) {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        if (platformProxy == null)  {
    +            wrapped().log(level.systemLevel(), msgSupplier, thrown);
    +        } else {
    +            platformProxy.log(level, thrown, msgSupplier);
    +        }
    +    }
    +
    +    @Override
    +    public void logp(PlatformLogger.Level level, String sourceClass,
    +                     String sourceMethod, String msg) {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        if (platformProxy == null)  {
    +            if (sourceClass == null && sourceMethod == null) { // best effort
    +                wrapped().log(level.systemLevel(), msg);
    +            } else {
    +                Level systemLevel = level.systemLevel();
    +                Logger wrapped = wrapped();
    +                if (wrapped.isLoggable(systemLevel)) {
    +                    sourceClass  = sourceClass  == null ? "" : sourceClass;
    +                    sourceMethod = sourceMethod == null ? "" : sourceMethod;
    +                    msg = msg == null ? "" : msg;
    +                    wrapped.log(systemLevel, String.format("[%s %s] %s",
    +                            sourceClass, sourceMethod, msg));
    +                }
    +            }
    +        } else {
    +            platformProxy.logp(level, sourceClass, sourceMethod, msg);
    +        }
    +    }
    +
    +    @Override
    +    public void logp(PlatformLogger.Level level, String sourceClass,
    +                     String sourceMethod, Supplier msgSupplier) {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        if (platformProxy == null) { // best effort
    +            if (sourceClass == null && sourceMethod == null) {
    +                wrapped().log(level.systemLevel(), msgSupplier);
    +            } else {
    +                Level systemLevel = level.systemLevel();
    +                Logger wrapped = wrapped();
    +                if (wrapped.isLoggable(systemLevel)) {
    +                    final String sClass  = sourceClass  == null ? "" : sourceClass;
    +                    final String sMethod = sourceMethod == null ? "" : sourceMethod;
    +                    wrapped.log(systemLevel, () -> String.format("[%s %s] %s",
    +                            sClass, sMethod, msgSupplier.get()));
    +                }
    +            }
    +        } else {
    +            platformProxy.logp(level, sourceClass, sourceMethod, msgSupplier);
    +        }
    +    }
    +
    +    @Override
    +    public void logp(PlatformLogger.Level level, String sourceClass,
    +                     String sourceMethod, String msg, Object... params) {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        if (platformProxy == null) { // best effort
    +            if (sourceClass == null && sourceMethod == null) {
    +                wrapped().log(level.systemLevel(), msg, params);
    +            } else {
    +                Level systemLevel = level.systemLevel();
    +                Logger wrapped = wrapped();
    +                if (wrapped.isLoggable(systemLevel)) {
    +                    sourceClass  = sourceClass  == null ? "" : sourceClass;
    +                    sourceMethod = sourceMethod == null ? "" : sourceMethod;
    +                    msg = msg == null ? "" : msg;
    +                    wrapped.log(systemLevel, String.format("[%s %s] %s",
    +                            sourceClass, sourceMethod, msg), params);
    +                }
    +            }
    +        } else {
    +            platformProxy.logp(level, sourceClass, sourceMethod, msg, params);
    +        }
    +    }
    +
    +    @Override
    +    public void logp(PlatformLogger.Level level, String sourceClass,
    +                     String sourceMethod, String msg, Throwable thrown) {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        if (platformProxy == null) { // best effort
    +            if (sourceClass == null && sourceMethod == null) {
    +                wrapped().log(level.systemLevel(), msg, thrown);
    +            } else {
    +                Level systemLevel = level.systemLevel();
    +                Logger wrapped = wrapped();
    +                if (wrapped.isLoggable(systemLevel)) {
    +                    sourceClass  = sourceClass  == null ? "" : sourceClass;
    +                    sourceMethod = sourceMethod == null ? "" : sourceMethod;
    +                    msg = msg == null ? "" : msg;
    +                    wrapped.log(systemLevel, String.format("[%s %s] %s",
    +                            sourceClass, sourceMethod, msg), thrown);
    +                }
    +            }
    +        } else {
    +            platformProxy.logp(level, sourceClass, sourceMethod, msg, thrown);
    +        }
    +    }
    +
    +    @Override
    +    public void logp(PlatformLogger.Level level, String sourceClass,
    +                     String sourceMethod, Throwable thrown,
    +                     Supplier msgSupplier) {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        if (platformProxy == null)  { // best effort
    +            if (sourceClass == null && sourceMethod == null) {
    +                wrapped().log(level.systemLevel(), msgSupplier, thrown);
    +            } else {
    +                Level systemLevel = level.systemLevel();
    +                Logger wrapped = wrapped();
    +                if (wrapped.isLoggable(systemLevel)) {
    +                    final String sClass  = sourceClass  == null ? "" : sourceClass;
    +                    final String sMethod = sourceMethod == null ? "" : sourceMethod;
    +                    wrapped.log(systemLevel,  () -> String.format("[%s %s] %s",
    +                            sClass, sMethod, msgSupplier.get()), thrown);
    +                }
    +            }
    +        } else {
    +            platformProxy.logp(level, sourceClass, sourceMethod,
    +                               thrown, msgSupplier);
    +        }
    +    }
    +
    +    @Override
    +    public void logrb(PlatformLogger.Level level, String sourceClass,
    +                      String sourceMethod, ResourceBundle bundle,
    +                      String msg, Object... params) {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        if (platformProxy == null)  { // best effort
    +            if (bundle != null || sourceClass == null && sourceMethod == null) {
    +                wrapped().log(level.systemLevel(), bundle, msg, params);
    +            } else {
    +                Level systemLevel = level.systemLevel();
    +                Logger wrapped = wrapped();
    +                if (wrapped.isLoggable(systemLevel)) {
    +                    sourceClass  = sourceClass  == null ? "" : sourceClass;
    +                    sourceMethod = sourceMethod == null ? "" : sourceMethod;
    +                    msg = msg == null ? "" : msg;
    +                    wrapped.log(systemLevel, bundle, String.format("[%s %s] %s",
    +                            sourceClass, sourceMethod, msg), params);
    +                }
    +            }
    +        } else {
    +            platformProxy.logrb(level, sourceClass, sourceMethod,
    +                    bundle, msg, params);
    +        }
    +    }
    +
    +    @Override
    +    public void logrb(PlatformLogger.Level level, String sourceClass,
    +                      String sourceMethod, ResourceBundle bundle, String msg,
    +                      Throwable thrown) {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        if (platformProxy == null)  { // best effort
    +            if (bundle != null || sourceClass == null && sourceMethod == null) {
    +                wrapped().log(level.systemLevel(), bundle, msg, thrown);
    +            } else {
    +                Level systemLevel = level.systemLevel();
    +                Logger wrapped = wrapped();
    +                if (wrapped.isLoggable(systemLevel)) {
    +                    sourceClass  = sourceClass  == null ? "" : sourceClass;
    +                    sourceMethod = sourceMethod == null ? "" : sourceMethod;
    +                    msg = msg == null ? "" : msg;
    +                    wrapped.log(systemLevel, bundle, String.format("[%s %s] %s",
    +                            sourceClass, sourceMethod, msg), thrown);
    +                }
    +            }
    +        } else {
    +            platformProxy.logrb(level, sourceClass, sourceMethod, bundle,
    +                                msg, thrown);
    +        }
    +    }
    +
    +    @Override
    +    public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
    +                      String msg, Throwable thrown) {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        if (platformProxy == null)  {
    +            wrapped().log(level.systemLevel(), bundle, msg, thrown);
    +        } else {
    +            platformProxy.logrb(level, bundle, msg, thrown);
    +        }
    +    }
    +
    +    @Override
    +    public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
    +                      String msg, Object... params) {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        if (platformProxy == null)  {
    +            wrapped().log(level.systemLevel(), bundle, msg, params);
    +        } else {
    +            platformProxy.logrb(level, bundle, msg, params);
    +        }
    +    }
    +
    +
    +    @Override
    +    public LoggerConfiguration getLoggerConfiguration() {
    +        final PlatformLogger.Bridge platformProxy = platformProxy();
    +        return platformProxy == null ? null
    +               : PlatformLogger.ConfigurableBridge
    +                       .getLoggerConfiguration(platformProxy);
    +    }
    +
    +}
    diff --git a/jdk/src/java.base/share/classes/jdk/internal/logger/BootstrapLogger.java b/jdk/src/java.base/share/classes/jdk/internal/logger/BootstrapLogger.java
    new file mode 100644
    index 00000000000..6338f871644
    --- /dev/null
    +++ b/jdk/src/java.base/share/classes/jdk/internal/logger/BootstrapLogger.java
    @@ -0,0 +1,1074 @@
    +/*
    + * Copyright (c) 2015, 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.internal.logger;
    +
    +import java.security.AccessControlContext;
    +import java.security.AccessController;
    +import java.security.PrivilegedAction;
    +import java.util.HashMap;
    +import java.util.Iterator;
    +import java.util.Map;
    +import java.util.ResourceBundle;
    +import java.util.ServiceLoader;
    +import java.util.function.BooleanSupplier;
    +import java.util.function.Function;
    +import java.util.function.Supplier;
    +import java.lang.System.LoggerFinder;
    +import java.lang.System.Logger;
    +import java.lang.System.Logger.Level;
    +import java.lang.ref.WeakReference;
    +import java.util.Objects;
    +import java.util.concurrent.ExecutionException;
    +import java.util.concurrent.ExecutorService;
    +import java.util.concurrent.LinkedBlockingQueue;
    +import java.util.concurrent.ThreadFactory;
    +import java.util.concurrent.ThreadPoolExecutor;
    +import java.util.concurrent.TimeUnit;
    +import sun.misc.InnocuousThread;
    +import sun.misc.VM;
    +import sun.util.logging.PlatformLogger;
    +import jdk.internal.logger.LazyLoggers.LazyLoggerAccessor;
    +
    +/**
    + * The BootstrapLogger class handles all the logic needed by Lazy Loggers
    + * to delay the creation of System.Logger instances until the VM is booted.
    + * By extension - it also contains the logic that will delay the creation
    + * of JUL Loggers until the LogManager is initialized by the application, in
    + * the common case where JUL is the default and there is no custom JUL
    + * configuration.
    + *
    + * A BootstrapLogger instance is both a Logger and a
    + * PlatformLogger.Bridge instance, which will put all Log messages in a queue
    + * until the VM is booted.
    + * Once the VM is booted, it obtain the real System.Logger instance from the
    + * LoggerFinder and flushes the message to the queue.
    + *
    + * There are a few caveat:
    + *  - the queue may not be flush until the next message is logged after
    + *    the VM is booted
    + *  - while the BootstrapLogger is active, the default implementation
    + *    for all convenience methods is used
    + *  - PlatformLogger.setLevel calls are ignored
    + *
    + *
    + */
    +public final class BootstrapLogger implements Logger, PlatformLogger.Bridge,
    +        PlatformLogger.ConfigurableBridge {
    +
    +    // We use the BootstrapExecutors class to submit delayed messages
    +    // to an independent InnocuousThread which will ensure that
    +    // delayed log events will be clearly identified as messages that have
    +    // been delayed during the boot sequence.
    +    private static class BootstrapExecutors implements ThreadFactory {
    +
    +        // Maybe that should be made configurable with system properties.
    +        static final long KEEP_EXECUTOR_ALIVE_SECONDS = 30;
    +
    +        // The BootstrapMessageLoggerTask is a Runnable which keeps
    +        // a hard ref to the ExecutorService that owns it.
    +        // This ensure that the ExecutorService is not gc'ed until the thread
    +        // has stopped running.
    +        private static class BootstrapMessageLoggerTask implements Runnable {
    +            ExecutorService owner;
    +            Runnable run;
    +            public BootstrapMessageLoggerTask(ExecutorService owner, Runnable r) {
    +                this.owner = owner;
    +                this.run = r;
    +            }
    +            @Override
    +            public void run() {
    +                try {
    +                    run.run();
    +                } finally {
    +                    owner = null; // allow the ExecutorService to be gced.
    +                }
    +            }
    +        }
    +
    +        private static volatile WeakReference executorRef;
    +        private static ExecutorService getExecutor() {
    +            WeakReference ref = executorRef;
    +            ExecutorService executor = ref == null ? null : ref.get();
    +            if (executor != null) return executor;
    +            synchronized (BootstrapExecutors.class) {
    +                ref = executorRef;
    +                executor = ref == null ? null : ref.get();
    +                if (executor == null) {
    +                    executor = new ThreadPoolExecutor(0, 1,
    +                            KEEP_EXECUTOR_ALIVE_SECONDS, TimeUnit.SECONDS,
    +                            new LinkedBlockingQueue<>(), new BootstrapExecutors());
    +                }
    +                // The executor service will be elligible for gc
    +                // KEEP_EXECUTOR_ALIVE_SECONDS seconds (30s)
    +                // after the execution of its last pending task.
    +                executorRef = new WeakReference<>(executor);
    +                return executorRef.get();
    +            }
    +        }
    +
    +        @Override
    +        public Thread newThread(Runnable r) {
    +            ExecutorService owner = getExecutor();
    +            Thread thread = AccessController.doPrivileged(new PrivilegedAction() {
    +                @Override
    +                public Thread run() {
    +                    Thread t = new InnocuousThread(new BootstrapMessageLoggerTask(owner, r));
    +                    t.setName("BootstrapMessageLoggerTask-"+t.getName());
    +                    return t;
    +                }
    +            }, null, new RuntimePermission("enableContextClassLoaderOverride"));
    +            thread.setDaemon(true);
    +            return thread;
    +        }
    +
    +        static void submit(Runnable r) {
    +            getExecutor().execute(r);
    +        }
    +
    +        // This is used by tests.
    +        static void join(Runnable r) {
    +            try {
    +                getExecutor().submit(r).get();
    +            } catch (InterruptedException | ExecutionException ex) {
    +                // should not happen
    +                throw new RuntimeException(ex);
    +            }
    +        }
    +
    +        // This is used by tests.
    +        static void awaitPendingTasks() {
    +            WeakReference ref = executorRef;
    +            ExecutorService executor = ref == null ? null : ref.get();
    +            if (ref == null) {
    +                synchronized(BootstrapExecutors.class) {
    +                    ref = executorRef;
    +                    executor = ref == null ? null : ref.get();
    +                }
    +            }
    +            if (executor != null) {
    +                // since our executor uses a FIFO and has a single thread
    +                // then awaiting the execution of its pending tasks can be done
    +                // simply by registering a new task and waiting until it
    +                // completes. This of course would not work if we were using
    +                // several threads, but we don't.
    +                join(()->{});
    +            }
    +        }
    +
    +        // This is used by tests.
    +        static boolean isAlive() {
    +            WeakReference ref = executorRef;
    +            ExecutorService executor = ref == null ? null : ref.get();
    +            if (executor != null) return true;
    +            synchronized (BootstrapExecutors.class) {
    +                ref = executorRef;
    +                executor = ref == null ? null : ref.get();
    +                return executor != null;
    +            }
    +        }
    +
    +        // The pending log event queue. The first event is the head, and
    +        // new events are added at the tail
    +        static LogEvent head, tail;
    +
    +        static void enqueue(LogEvent event) {
    +            if (event.next != null) return;
    +            synchronized (BootstrapExecutors.class) {
    +                if (event.next != null) return;
    +                event.next = event;
    +                if (tail == null) {
    +                    head = tail = event;
    +                } else {
    +                    tail.next = event;
    +                    tail = event;
    +                }
    +            }
    +        }
    +
    +        static void flush() {
    +            LogEvent event;
    +            // drain the whole queue
    +            synchronized(BootstrapExecutors.class) {
    +                event = head;
    +                head = tail = null;
    +            }
    +            while(event != null) {
    +                LogEvent.log(event);
    +                synchronized(BootstrapExecutors.class) {
    +                    LogEvent prev = event;
    +                    event = (event.next == event ? null : event.next);
    +                    prev.next = null;
    +                }
    +            }
    +        }
    +    }
    +
    +    // The accessor in which this logger is temporarily set.
    +    final LazyLoggerAccessor holder;
    +
    +    BootstrapLogger(LazyLoggerAccessor holder) {
    +        this.holder = holder;
    +    }
    +
    +    // Temporary data object storing log events
    +    // It would be nice to use a Consumer instead of a LogEvent.
    +    // This way we could simply do things like:
    +    //    push((logger) -> logger.log(level, msg));
    +    // Unfortunately, if we come to here it means we are in the bootsraping
    +    // phase where using lambdas is not safe yet - so we have to use a
    +    // a data object instead...
    +    //
    +    static final class LogEvent {
    +        // only one of these two levels should be non null
    +        final Level level;
    +        final PlatformLogger.Level platformLevel;
    +        final BootstrapLogger bootstrap;
    +
    +        final ResourceBundle bundle;
    +        final String msg;
    +        final Throwable thrown;
    +        final Object[] params;
    +        final Supplier msgSupplier;
    +        final String sourceClass;
    +        final String sourceMethod;
    +        final long timeMillis;
    +        final long nanoAdjustment;
    +
    +        // because logging a message may entail calling toString() on
    +        // the parameters etc... we need to store the context of the
    +        // caller who logged the message - so that we can reuse it when
    +        // we finally log the message.
    +        final AccessControlContext acc;
    +
    +        // The next event in the queue
    +        LogEvent next;
    +
    +        private LogEvent(BootstrapLogger bootstrap, Level level,
    +                ResourceBundle bundle, String msg,
    +                Throwable thrown, Object[] params) {
    +            this.acc = AccessController.getContext();
    +            this.timeMillis = System.currentTimeMillis();
    +            this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
    +            this.level = level;
    +            this.platformLevel = null;
    +            this.bundle = bundle;
    +            this.msg = msg;
    +            this.msgSupplier = null;
    +            this.thrown = thrown;
    +            this.params = params;
    +            this.sourceClass = null;
    +            this.sourceMethod = null;
    +            this.bootstrap = bootstrap;
    +        }
    +
    +        private LogEvent(BootstrapLogger bootstrap, Level level,
    +                Supplier msgSupplier,
    +                Throwable thrown, Object[] params) {
    +            this.acc = AccessController.getContext();
    +            this.timeMillis = System.currentTimeMillis();
    +            this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
    +            this.level = level;
    +            this.platformLevel = null;
    +            this.bundle = null;
    +            this.msg = null;
    +            this.msgSupplier = msgSupplier;
    +            this.thrown = thrown;
    +            this.params = params;
    +            this.sourceClass = null;
    +            this.sourceMethod = null;
    +            this.bootstrap = bootstrap;
    +        }
    +
    +        private LogEvent(BootstrapLogger bootstrap,
    +                PlatformLogger.Level platformLevel,
    +                String sourceClass, String sourceMethod,
    +                ResourceBundle bundle, String msg,
    +                Throwable thrown, Object[] params) {
    +            this.acc = AccessController.getContext();
    +            this.timeMillis = System.currentTimeMillis();
    +            this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
    +            this.level = null;
    +            this.platformLevel = platformLevel;
    +            this.bundle = bundle;
    +            this.msg = msg;
    +            this.msgSupplier = null;
    +            this.thrown = thrown;
    +            this.params = params;
    +            this.sourceClass = sourceClass;
    +            this.sourceMethod = sourceMethod;
    +            this.bootstrap = bootstrap;
    +        }
    +
    +        private LogEvent(BootstrapLogger bootstrap,
    +                PlatformLogger.Level platformLevel,
    +                String sourceClass, String sourceMethod,
    +                Supplier msgSupplier,
    +                Throwable thrown, Object[] params) {
    +            this.acc = AccessController.getContext();
    +            this.timeMillis = System.currentTimeMillis();
    +            this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
    +            this.level = null;
    +            this.platformLevel = platformLevel;
    +            this.bundle = null;
    +            this.msg = null;
    +            this.msgSupplier = msgSupplier;
    +            this.thrown = thrown;
    +            this.params = params;
    +            this.sourceClass = sourceClass;
    +            this.sourceMethod = sourceMethod;
    +            this.bootstrap = bootstrap;
    +        }
    +
    +        // Log this message in the given logger. Do not call directly.
    +        // Use LogEvent.log(LogEvent, logger) instead.
    +        private void log(Logger logger) {
    +            assert platformLevel == null && level != null;
    +            //new Exception("logging delayed message").printStackTrace();
    +            if (msgSupplier != null) {
    +                if (thrown != null) {
    +                    logger.log(level, msgSupplier, thrown);
    +                } else {
    +                    logger.log(level, msgSupplier);
    +                }
    +            } else {
    +                // BootstrapLoggers are never localized so we can safely
    +                // use the method that takes a ResourceBundle parameter
    +                // even when that resource bundle is null.
    +                if (thrown != null) {
    +                    logger.log(level, bundle, msg, thrown);
    +                } else {
    +                    logger.log(level, bundle, msg, params);
    +                }
    +            }
    +        }
    +
    +        // Log this message in the given logger.  Do not call directly.
    +        // Use LogEvent.doLog(LogEvent, logger) instead.
    +        private void log(PlatformLogger.Bridge logger) {
    +            assert platformLevel != null && level == null;
    +            if (sourceClass == null) {
    +                if (msgSupplier != null) {
    +                    if (thrown != null) {
    +                        logger.log(platformLevel, thrown, msgSupplier);
    +                    } else {
    +                        logger.log(platformLevel, msgSupplier);
    +                    }
    +                } else {
    +                    // BootstrapLoggers are never localized so we can safely
    +                    // use the method that takes a ResourceBundle parameter
    +                    // even when that resource bundle is null.
    +                    if (thrown != null) {
    +                        logger.logrb(platformLevel, bundle, msg, thrown);
    +                    } else {
    +                        logger.logrb(platformLevel, bundle, msg, params);
    +                    }
    +                }
    +            } else {
    +                if (msgSupplier != null) {
    +                    if (thrown != null) {
    +                        logger.log(platformLevel, sourceClass, sourceMethod, thrown, msgSupplier);
    +                    } else {
    +                        logger.log(platformLevel,sourceClass, sourceMethod,  msgSupplier);
    +                    }
    +                } else {
    +                    // BootstrapLoggers are never localized so we can safely
    +                    // use the method that takes a ResourceBundle parameter
    +                    // even when that resource bundle is null.
    +                    if (thrown != null) {
    +                        logger.logrb(platformLevel, sourceClass, sourceMethod, bundle, msg, thrown);
    +                    } else {
    +                        logger.logrb(platformLevel, sourceClass, sourceMethod, bundle, msg, params);
    +                    }
    +                }
    +            }
    +        }
    +
    +        // non default methods from Logger interface
    +        static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
    +                ResourceBundle bundle, String key, Throwable thrown) {
    +            return new LogEvent(Objects.requireNonNull(bootstrap),
    +                                Objects.requireNonNull(level), bundle, key,
    +                                thrown, null);
    +        }
    +        static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
    +                ResourceBundle bundle, String format, Object[] params) {
    +            return new LogEvent(Objects.requireNonNull(bootstrap),
    +                                Objects.requireNonNull(level), bundle, format,
    +                                null, params);
    +        }
    +        static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
    +                                Supplier msgSupplier, Throwable thrown) {
    +            return new LogEvent(Objects.requireNonNull(bootstrap),
    +                    Objects.requireNonNull(level),
    +                    Objects.requireNonNull(msgSupplier), thrown, null);
    +        }
    +        static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
    +                                Supplier msgSupplier) {
    +            return new LogEvent(Objects.requireNonNull(bootstrap),
    +                                Objects.requireNonNull(level),
    +                                Objects.requireNonNull(msgSupplier), null, null);
    +        }
    +        static void log(LogEvent log, Logger logger) {
    +            final SecurityManager sm = System.getSecurityManager();
    +            // not sure we can actually use lambda here. We may need to create
    +            // an anonymous class. Although if we reach here, then it means
    +            // the VM is booted.
    +            if (sm == null || log.acc == null) {
    +                BootstrapExecutors.submit(() -> log.log(logger));
    +            } else {
    +                BootstrapExecutors.submit(() ->
    +                    AccessController.doPrivileged((PrivilegedAction) () -> {
    +                        log.log(logger); return null;
    +                    }, log.acc));
    +            }
    +        }
    +
    +        // non default methods from PlatformLogger.Bridge interface
    +        static LogEvent valueOf(BootstrapLogger bootstrap,
    +                                PlatformLogger.Level level, String msg) {
    +            return new LogEvent(Objects.requireNonNull(bootstrap),
    +                                Objects.requireNonNull(level), null, null, null,
    +                                msg, null, null);
    +        }
    +        static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
    +                                String msg, Throwable thrown) {
    +            return new LogEvent(Objects.requireNonNull(bootstrap),
    +                    Objects.requireNonNull(level), null, null, null, msg, thrown, null);
    +        }
    +        static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
    +                                String msg, Object[] params) {
    +            return new LogEvent(Objects.requireNonNull(bootstrap),
    +                    Objects.requireNonNull(level), null, null, null, msg, null, params);
    +        }
    +        static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
    +                                Supplier msgSupplier) {
    +            return new LogEvent(Objects.requireNonNull(bootstrap),
    +                    Objects.requireNonNull(level), null, null, msgSupplier, null, null);
    +        }
    +        static LogEvent vaueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
    +                               Supplier msgSupplier,
    +                               Throwable thrown) {
    +            return new LogEvent(Objects.requireNonNull(bootstrap),
    +                                Objects.requireNonNull(level), null, null,
    +                                msgSupplier, thrown, null);
    +        }
    +        static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
    +                                String sourceClass, String sourceMethod,
    +                                ResourceBundle bundle, String msg, Object[] params) {
    +            return new LogEvent(Objects.requireNonNull(bootstrap),
    +                                Objects.requireNonNull(level), sourceClass,
    +                                sourceMethod, bundle, msg, null, params);
    +        }
    +        static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
    +                                String sourceClass, String sourceMethod,
    +                                ResourceBundle bundle, String msg, Throwable thrown) {
    +            return new LogEvent(Objects.requireNonNull(bootstrap),
    +                                Objects.requireNonNull(level), sourceClass,
    +                                sourceMethod, bundle, msg, thrown, null);
    +        }
    +        static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
    +                                String sourceClass, String sourceMethod,
    +                                Supplier msgSupplier, Throwable thrown) {
    +            return new LogEvent(Objects.requireNonNull(bootstrap),
    +                                Objects.requireNonNull(level), sourceClass,
    +                                sourceMethod, msgSupplier, thrown, null);
    +        }
    +        static void log(LogEvent log, PlatformLogger.Bridge logger) {
    +            final SecurityManager sm = System.getSecurityManager();
    +            if (sm == null || log.acc == null) {
    +                log.log(logger);
    +            } else {
    +                // not sure we can actually use lambda here. We may need to create
    +                // an anonymous class. Although if we reach here, then it means
    +                // the VM is booted.
    +                AccessController.doPrivileged((PrivilegedAction) () -> {
    +                    log.log(logger); return null;
    +                }, log.acc);
    +            }
    +        }
    +
    +        static void log(LogEvent event) {
    +            event.bootstrap.flush(event);
    +        }
    +
    +    }
    +
    +    // Push a log event at the end of the pending LogEvent queue.
    +    void push(LogEvent log) {
    +        BootstrapExecutors.enqueue(log);
    +        // if the queue has been flushed just before we entered
    +        // the synchronized block we need to flush it again.
    +        checkBootstrapping();
    +    }
    +
    +    // Flushes the queue of pending LogEvents to the logger.
    +    void flush(LogEvent event) {
    +        assert event.bootstrap == this;
    +        if (event.platformLevel != null) {
    +            PlatformLogger.Bridge concrete = holder.getConcretePlatformLogger(this);
    +            LogEvent.log(event, concrete);
    +        } else {
    +            Logger concrete = holder.getConcreteLogger(this);
    +            LogEvent.log(event, concrete);
    +        }
    +    }
    +
    +    /**
    +     * The name of this logger. This is the name of the actual logger for which
    +     * this logger acts as a temporary proxy.
    +     * @return The logger name.
    +     */
    +    @Override
    +    public String getName() {
    +        return holder.name;
    +    }
    +
    +    /**
    +     * Check whether the VM is still bootstrapping, and if not, arranges
    +     * for this logger's holder to create the real logger and flush the
    +     * pending event queue.
    +     * @return true if the VM is still bootstrapping.
    +     */
    +    boolean checkBootstrapping() {
    +        if (isBooted()) {
    +            BootstrapExecutors.flush();
    +            return false;
    +        }
    +        return true;
    +    }
    +
    +    // ----------------------------------
    +    // Methods from Logger
    +    // ----------------------------------
    +
    +    @Override
    +    public boolean isLoggable(Level level) {
    +        if (checkBootstrapping()) {
    +            return level.getSeverity() >= Level.INFO.getSeverity();
    +        } else {
    +            final Logger spi = holder.wrapped();
    +            return spi.isLoggable(level);
    +        }
    +    }
    +
    +    @Override
    +    public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, bundle, key, thrown));
    +        } else {
    +            final Logger spi = holder.wrapped();
    +            spi.log(level, bundle, key, thrown);
    +        }
    +    }
    +
    +    @Override
    +    public void log(Level level, ResourceBundle bundle, String format, Object... params) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, bundle, format, params));
    +        } else {
    +            final Logger spi = holder.wrapped();
    +            spi.log(level, bundle, format, params);
    +        }
    +    }
    +
    +    @Override
    +    public void log(Level level, String msg, Throwable thrown) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, null, msg, thrown));
    +        } else {
    +            final Logger spi = holder.wrapped();
    +            spi.log(level, msg, thrown);
    +        }
    +    }
    +
    +    @Override
    +    public void log(Level level, String format, Object... params) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, null, format, params));
    +        } else {
    +            final Logger spi = holder.wrapped();
    +            spi.log(level, format, params);
    +        }
    +    }
    +
    +    @Override
    +    public void log(Level level, Supplier msgSupplier) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, msgSupplier));
    +        } else {
    +            final Logger spi = holder.wrapped();
    +            spi.log(level, msgSupplier);
    +        }
    +    }
    +
    +    @Override
    +    public void log(Level level, Object obj) {
    +        if (checkBootstrapping()) {
    +            Logger.super.log(level, obj);
    +        } else {
    +            final Logger spi = holder.wrapped();
    +            spi.log(level, obj);
    +        }
    +    }
    +
    +    @Override
    +    public void log(Level level, String msg) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, null, msg, (Object[])null));
    +        } else {
    +            final Logger spi = holder.wrapped();
    +            spi.log(level, msg);
    +        }
    +    }
    +
    +    @Override
    +    public void log(Level level, Supplier msgSupplier, Throwable thrown) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, msgSupplier, thrown));
    +        } else {
    +            final Logger spi = holder.wrapped();
    +            spi.log(level, msgSupplier, thrown);
    +        }
    +    }
    +
    +    // ----------------------------------
    +    // Methods from PlatformLogger.Bridge
    +    // ----------------------------------
    +
    +    @Override
    +    public boolean isLoggable(PlatformLogger.Level level) {
    +        if (checkBootstrapping()) {
    +            return level.intValue() >= PlatformLogger.Level.INFO.intValue();
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            return spi.isLoggable(level);
    +        }
    +    }
    +
    +    @Override
    +    public boolean isEnabled() {
    +        if (checkBootstrapping()) {
    +            return true;
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            return spi.isEnabled();
    +        }
    +    }
    +
    +    @Override
    +    public void log(PlatformLogger.Level level, String msg) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, msg));
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            spi.log(level, msg);
    +        }
    +    }
    +
    +    @Override
    +    public void log(PlatformLogger.Level level, String msg, Throwable thrown) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, msg, thrown));
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            spi.log(level, msg, thrown);
    +        }
    +    }
    +
    +    @Override
    +    public void log(PlatformLogger.Level level, String msg, Object... params) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, msg, params));
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            spi.log(level, msg, params);
    +        }
    +    }
    +
    +    @Override
    +    public void log(PlatformLogger.Level level, Supplier msgSupplier) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, msgSupplier));
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            spi.log(level, msgSupplier);
    +        }
    +    }
    +
    +    @Override
    +    public void log(PlatformLogger.Level level, Throwable thrown,
    +            Supplier msgSupplier) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.vaueOf(this, level, msgSupplier, thrown));
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            spi.log(level, thrown, msgSupplier);
    +        }
    +    }
    +
    +    @Override
    +    public void logp(PlatformLogger.Level level, String sourceClass,
    +            String sourceMethod, String msg) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, null,
    +                    msg, (Object[])null));
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            spi.logp(level, sourceClass, sourceMethod, msg);
    +        }
    +    }
    +
    +    @Override
    +    public void logp(PlatformLogger.Level level, String sourceClass,
    +            String sourceMethod, Supplier msgSupplier) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, msgSupplier, null));
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            spi.logp(level, sourceClass, sourceMethod, msgSupplier);
    +        }
    +    }
    +
    +    @Override
    +    public void logp(PlatformLogger.Level level, String sourceClass,
    +            String sourceMethod, String msg, Object... params) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, null, msg, params));
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            spi.logp(level, sourceClass, sourceMethod, msg, params);
    +        }
    +    }
    +
    +    @Override
    +    public void logp(PlatformLogger.Level level, String sourceClass,
    +            String sourceMethod, String msg, Throwable thrown) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, null, msg, thrown));
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            spi.logp(level, sourceClass, sourceMethod, msg, thrown);
    +        }
    +    }
    +
    +    @Override
    +    public void logp(PlatformLogger.Level level, String sourceClass,
    +            String sourceMethod, Throwable thrown, Supplier msgSupplier) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, msgSupplier, thrown));
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            spi.logp(level, sourceClass, sourceMethod, thrown, msgSupplier);
    +        }
    +    }
    +
    +    @Override
    +    public void logrb(PlatformLogger.Level level, String sourceClass,
    +            String sourceMethod, ResourceBundle bundle, String msg, Object... params) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, bundle, msg, params));
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            spi.logrb(level, sourceClass, sourceMethod, bundle, msg, params);
    +        }
    +    }
    +
    +    @Override
    +    public void logrb(PlatformLogger.Level level, String sourceClass,
    +            String sourceMethod, ResourceBundle bundle, String msg, Throwable thrown) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, bundle, msg, thrown));
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            spi.logrb(level, sourceClass, sourceMethod, bundle, msg, thrown);
    +        }
    +    }
    +
    +    @Override
    +    public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
    +            String msg, Object... params) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, null, null, bundle, msg, params));
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            spi.logrb(level, bundle, msg, params);
    +        }
    +    }
    +
    +    @Override
    +    public void logrb(PlatformLogger.Level level, ResourceBundle bundle, String msg, Throwable thrown) {
    +        if (checkBootstrapping()) {
    +            push(LogEvent.valueOf(this, level, null, null, bundle, msg, thrown));
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            spi.logrb(level, bundle, msg, thrown);
    +        }
    +    }
    +
    +    @Override
    +    public LoggerConfiguration getLoggerConfiguration() {
    +        if (checkBootstrapping()) {
    +            // This practically means that PlatformLogger.setLevel()
    +            // calls will be ignored if the VM is still bootstrapping. We could
    +            // attempt to fix that but is it worth it?
    +            return PlatformLogger.ConfigurableBridge.super.getLoggerConfiguration();
    +        } else {
    +            final PlatformLogger.Bridge spi = holder.platform();
    +            return PlatformLogger.ConfigurableBridge.getLoggerConfiguration(spi);
    +        }
    +    }
    +
    +    // This BooleanSupplier is a hook for tests - so that we can simulate
    +    // what would happen before the VM is booted.
    +    private static volatile BooleanSupplier isBooted;
    +    public static boolean isBooted() {
    +        if (isBooted != null) return isBooted.getAsBoolean();
    +        else return VM.isBooted();
    +    }
    +
    +    // A bit of black magic. We try to find out the nature of the logging
    +    // backend without actually loading it.
    +    private static enum LoggingBackend {
    +        // There is no LoggerFinder and JUL is not present
    +        NONE(true),
    +
    +        // There is no LoggerFinder, but we have found a
    +        // JdkLoggerFinder installed (which means JUL is present),
    +        // and we haven't found any custom configuration for JUL.
    +        // Until LogManager is initialized we can use a simple console
    +        // logger.
    +        JUL_DEFAULT(false),
    +
    +        // Same as above, except that we have found a custom configuration
    +        // for JUL. We cannot use the simple console logger in this case.
    +        JUL_WITH_CONFIG(true),
    +
    +        // We have found a custom LoggerFinder.
    +        CUSTOM(true);
    +
    +        final boolean useLoggerFinder;
    +        private LoggingBackend(boolean useLoggerFinder) {
    +            this.useLoggerFinder = useLoggerFinder;
    +        }
    +    };
    +
    +    // The purpose of this class is to delay the initialization of
    +    // the detectedBackend field until it is actually read.
    +    // We do not want this field to get initialized if VM.isBooted() is false.
    +    private static final class DetectBackend {
    +        static final LoggingBackend detectedBackend;
    +        static {
    +            detectedBackend = AccessController.doPrivileged(new PrivilegedAction() {
    +                    @Override
    +                    public LoggingBackend run() {
    +                        final Iterator iterator =
    +                            ServiceLoader.load(LoggerFinder.class, ClassLoader.getSystemClassLoader())
    +                            .iterator();
    +                        if (iterator.hasNext()) {
    +                            return LoggingBackend.CUSTOM; // Custom Logger Provider is registered
    +                        }
    +                        // No custom logger provider: we will be using the default
    +                        // backend.
    +                        final Iterator iterator2 =
    +                            ServiceLoader.loadInstalled(DefaultLoggerFinder.class)
    +                            .iterator();
    +                        if (iterator2.hasNext()) {
    +                            // LoggingProviderImpl is registered. The default
    +                            // implementation is java.util.logging
    +                            String cname = System.getProperty("java.util.logging.config.class");
    +                            String fname = System.getProperty("java.util.logging.config.file");
    +                            return (cname != null || fname != null)
    +                                ? LoggingBackend.JUL_WITH_CONFIG
    +                                : LoggingBackend.JUL_DEFAULT;
    +                        } else {
    +                            // SimpleLogger is used
    +                            return LoggingBackend.NONE;
    +                        }
    +                    }
    +                });
    +
    +        }
    +    }
    +
    +    // We will use temporary SimpleConsoleLoggers if
    +    // the logging backend is JUL, there is no custom config,
    +    // and the LogManager has not been initialized yet.
    +    private static  boolean useTemporaryLoggers() {
    +        // being paranoid: this should already have been checked
    +        if (!isBooted()) return true;
    +        return DetectBackend.detectedBackend == LoggingBackend.JUL_DEFAULT
    +                && !logManagerConfigured;
    +    }
    +
    +    // We will use lazy loggers if:
    +    //    - the VM is not yet booted
    +    //    - the logging backend is a custom backend
    +    //    - the logging backend is JUL, there is no custom config,
    +    //      and the LogManager has not been initialized yet.
    +    public static synchronized boolean useLazyLoggers() {
    +        return !BootstrapLogger.isBooted()
    +                || DetectBackend.detectedBackend == LoggingBackend.CUSTOM
    +                || useTemporaryLoggers();
    +    }
    +
    +    // Called by LazyLoggerAccessor. This method will determine whether
    +    // to create a BootstrapLogger (if the VM is not yet booted),
    +    // a SimpleConsoleLogger (if JUL is the default backend and there
    +    // is no custom JUL configuration and LogManager is not yet initialized),
    +    // or a logger returned by the loaded LoggerFinder (all other cases).
    +    static Logger getLogger(LazyLoggerAccessor accessor) {
    +        if (!BootstrapLogger.isBooted()) {
    +            return new BootstrapLogger(accessor);
    +        } else {
    +            boolean temporary = useTemporaryLoggers();
    +            if (temporary) {
    +                // JUL is the default backend, there is no custom configuration,
    +                // LogManager has not been used.
    +                synchronized(BootstrapLogger.class) {
    +                    if (useTemporaryLoggers()) {
    +                        return makeTemporaryLogger(accessor);
    +                    }
    +                }
    +            }
    +            // Already booted. Return the real logger.
    +            return accessor.createLogger();
    +        }
    +    }
    +
    +
    +    // If the backend is JUL, and there is no custom configuration, and
    +    // nobody has attempted to call LogManager.getLogManager() yet, then
    +    // we can temporarily substitute JUL Logger with SimpleConsoleLoggers,
    +    // which avoids the cost of actually loading up the LogManager...
    +    // The TemporaryLoggers class has the logic to create such temporary
    +    // loggers, and to possibly replace them with real JUL loggers if
    +    // someone calls LogManager.getLogManager().
    +    static final class TemporaryLoggers implements
    +            Function {
    +
    +        // all accesses must be synchronized on the outer BootstrapLogger.class
    +        final Map temporaryLoggers =
    +                new HashMap<>();
    +
    +        // all accesses must be synchronized on the outer BootstrapLogger.class
    +        // The temporaryLoggers map will be cleared when LogManager is initialized.
    +        boolean cleared;
    +
    +        @Override
    +        // all accesses must be synchronized on the outer BootstrapLogger.class
    +        public SimpleConsoleLogger apply(LazyLoggerAccessor t) {
    +            if (cleared) throw new IllegalStateException("LoggerFinder already initialized");
    +            return SimpleConsoleLogger.makeSimpleLogger(t.getLoggerName(), true);
    +        }
    +
    +        // all accesses must be synchronized on the outer BootstrapLogger.class
    +        SimpleConsoleLogger get(LazyLoggerAccessor a) {
    +            if (cleared) throw new IllegalStateException("LoggerFinder already initialized");
    +            return temporaryLoggers.computeIfAbsent(a, this);
    +        }
    +
    +        // all accesses must be synchronized on the outer BootstrapLogger.class
    +        Map drainTemporaryLoggers() {
    +            if (temporaryLoggers.isEmpty()) return null;
    +            if (cleared) throw new IllegalStateException("LoggerFinder already initialized");
    +            final Map accessors = new HashMap<>(temporaryLoggers);
    +            temporaryLoggers.clear();
    +            cleared = true;
    +            return accessors;
    +        }
    +
    +        static void resetTemporaryLoggers(Map accessors) {
    +            // When the backend is JUL we want to force the creation of
    +            // JUL loggers here: some tests are expecting that the
    +            // PlatformLogger will create JUL loggers as soon as the
    +            // LogManager is initialized.
    +            //
    +            // If the backend is not JUL then we can delay the re-creation
    +            // of the wrapped logger until they are next accessed.
    +            //
    +            final LoggingBackend detectedBackend = DetectBackend.detectedBackend;
    +            final boolean lazy = detectedBackend != LoggingBackend.JUL_DEFAULT
    +                    && detectedBackend != LoggingBackend.JUL_WITH_CONFIG;
    +            for (Map.Entry a : accessors.entrySet()) {
    +                a.getKey().release(a.getValue(), !lazy);
    +            }
    +        }
    +
    +        // all accesses must be synchronized on the outer BootstrapLogger.class
    +        static final TemporaryLoggers INSTANCE = new TemporaryLoggers();
    +    }
    +
    +    static synchronized Logger makeTemporaryLogger(LazyLoggerAccessor a) {
    +        // accesses to TemporaryLoggers is synchronized on BootstrapLogger.class
    +        return TemporaryLoggers.INSTANCE.get(a);
    +    }
    +
    +    private static volatile boolean logManagerConfigured;
    +
    +    private static synchronized Map
    +         releaseTemporaryLoggers() {
    +        // first check whether there's a chance that we have used
    +        // temporary loggers; Will be false if logManagerConfigured is already
    +        // true.
    +        final boolean clearTemporaryLoggers = useTemporaryLoggers();
    +
    +        // then sets the flag that tells that the log manager is configured
    +        logManagerConfigured = true;
    +
    +        // finally replace all temporary loggers by real JUL loggers
    +        if (clearTemporaryLoggers) {
    +            // accesses to TemporaryLoggers is synchronized on BootstrapLogger.class
    +            return TemporaryLoggers.INSTANCE.drainTemporaryLoggers();
    +        } else {
    +            return null;
    +        }
    +    }
    +
    +    public static void redirectTemporaryLoggers() {
    +        // This call is synchronized on BootstrapLogger.class.
    +        final Map accessors =
    +                releaseTemporaryLoggers();
    +
    +        // We will now reset the logger accessors, triggering the
    +        // (possibly lazy) replacement of any temporary logger by the
    +        // real logger returned from the loaded LoggerFinder.
    +        if (accessors != null) {
    +            TemporaryLoggers.resetTemporaryLoggers(accessors);
    +        }
    +
    +        BootstrapExecutors.flush();
    +    }
    +
    +    // Hook for tests which need to wait until pending messages
    +    // are processed.
    +    static void awaitPendingTasks() {
    +        BootstrapExecutors.awaitPendingTasks();
    +    }
    +    static boolean isAlive() {
    +        return BootstrapExecutors.isAlive();
    +    }
    +
    +}
    diff --git a/jdk/src/java.base/share/classes/jdk/internal/logger/DefaultLoggerFinder.java b/jdk/src/java.base/share/classes/jdk/internal/logger/DefaultLoggerFinder.java
    new file mode 100644
    index 00000000000..da255a3e2b8
    --- /dev/null
    +++ b/jdk/src/java.base/share/classes/jdk/internal/logger/DefaultLoggerFinder.java
    @@ -0,0 +1,173 @@
    +/*
    + * Copyright (c) 2015, 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.internal.logger;
    +
    +import java.lang.ref.Reference;
    +import java.lang.ref.WeakReference;
    +import java.util.HashMap;
    +import java.util.Map;
    +import java.util.function.Function;
    +import java.lang.System.LoggerFinder;
    +import java.lang.System.Logger;
    +import java.lang.ref.ReferenceQueue;
    +import java.util.Collection;
    +import java.util.ResourceBundle;
    +
    +/**
    + * Internal Service Provider Interface (SPI) that makes it possible to use
    + * {@code java.util.logging} as backend when the {@link
    + * sun.util.logging.internal.LoggingProviderImpl
    + * sun.util.logging.internal.LoggingProviderImpl} is present.
    + * 

    + * The JDK default implementation of the {@link LoggerFinder} will + * attempt to locate and load an {@linkplain + * java.util.ServiceLoader#loadInstalled(java.lang.Class) installed} + * implementation of the {@code DefaultLoggerFinder}. If {@code java.util.logging} + * is present, this will usually resolve to an instance of {@link + * sun.util.logging.internal.LoggingProviderImpl sun.util.logging.internal.LoggingProviderImpl}. + * Otherwise, if no concrete service provider is declared for + * {@code DefaultLoggerFinder}, the default implementation provided by this class + * will be used. + *

    + * When the {@link sun.util.logging.internal.LoggingProviderImpl + * sun.util.logging.internal.LoggingProviderImpl} is not present then the + * default implementation provided by this class is to use a simple logger + * that will log messages whose level is INFO and above to the console. + * These simple loggers are not configurable. + *

    + * When configuration is needed, an application should either link with + * {@code java.util.logging} - and use the {@code java.util.logging} for + * configuration, or link with {@link LoggerFinder another implementation} + * of the {@link LoggerFinder} + * that provides the necessary configuration. + * + * @apiNote Programmers are not expected to call this class directly. + * Instead they should rely on the static methods defined by {@link + * java.lang.System java.lang.System} or {@link sun.util.logging.PlatformLogger + * sun.util.logging.PlatformLogger}. + * + * @see java.lang.System.LoggerFinder + * @see jdk.internal.logger + * @see sun.util.logging.internal + * + */ +public class DefaultLoggerFinder extends LoggerFinder { + + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + + /** + * Creates a new instance of DefaultLoggerFinder. + * @throws SecurityException if the calling code does not have the + * {@code RuntimePermission("loggerFinder")} + */ + protected DefaultLoggerFinder() { + this(checkPermission()); + } + + private DefaultLoggerFinder(Void unused) { + // nothing to do. + } + + private static Void checkPermission() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(LOGGERFINDER_PERMISSION); + } + return null; + } + + // SharedLoggers is a default cache of loggers used when JUL is not + // present - in that case we use instances of SimpleConsoleLogger which + // cannot be directly configure through public APIs. + // + // We can therefore afford to simply maintain two domains - one for the + // system, and one for the application. + // + static final class SharedLoggers { + private final Map> loggers = + new HashMap<>(); + private final ReferenceQueue queue = new ReferenceQueue<>(); + + synchronized Logger get(Function loggerSupplier, final String name) { + Reference ref = loggers.get(name); + Logger w = ref == null ? null : ref.get(); + if (w == null) { + w = loggerSupplier.apply(name); + loggers.put(name, new WeakReference<>(w, queue)); + } + + // Remove stale mapping... + Collection> values = null; + while ((ref = queue.poll()) != null) { + if (values == null) values = loggers.values(); + values.remove(ref); + } + return w; + } + + + final static SharedLoggers system = new SharedLoggers(); + final static SharedLoggers application = new SharedLoggers(); + } + + @Override + public final Logger getLogger(String name, /* Module */ Class caller) { + checkPermission(); + return demandLoggerFor(name, caller); + } + + @Override + public final Logger getLocalizedLogger(String name, ResourceBundle bundle, + /* Module */ Class caller) { + return super.getLocalizedLogger(name, bundle, caller); + } + + + + /** + * Returns a {@link Logger logger} suitable for the caller usage. + * + * @implSpec The default implementation for this method is to return a + * simple logger that will print all messages of INFO level and above + * to the console. That simple logger is not configurable. + * + * @param name The name of the logger. + * @param caller The class on behalf of which the logger is created. + * @return A {@link Logger logger} suitable for the application usage. + * @throws SecurityException if the calling code does not have the + * {@code RuntimePermission("loggerFinder")}. + */ + protected Logger demandLoggerFor(String name, /* Module */ Class caller) { + checkPermission(); + if (caller.getClassLoader() == null) { + return SharedLoggers.system.get(SimpleConsoleLogger::makeSimpleLogger, name); + } else { + return SharedLoggers.application.get(SimpleConsoleLogger::makeSimpleLogger, name); + } + } + +} diff --git a/jdk/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java b/jdk/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java new file mode 100644 index 00000000000..c59ff195cc4 --- /dev/null +++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2015, 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.internal.logger; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.function.BiFunction; +import java.lang.System.LoggerFinder; +import java.lang.System.Logger; +import java.lang.ref.WeakReference; +import java.util.Objects; +import sun.misc.VM; +import sun.util.logging.PlatformLogger; + +/** + * This class is a factory for Lazy Loggers; only system loggers can be + * Lazy Loggers. + */ +public final class LazyLoggers { + + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + + private LazyLoggers() { + throw new InternalError(); + } + + /** + * This class is used to hold the factories that a Lazy Logger will use + * to create (or map) its wrapped logger. + * @param {@link Logger} or a subclass of {@link Logger}. + */ + private static final class LazyLoggerFactories { + + /** + * A factory method to create an SPI logger. + * Usually, this will be something like LazyLoggers::getSystemLogger. + */ + final BiFunction, L> loggerSupplier; + + + public LazyLoggerFactories(BiFunction, L> loggerSupplier) { + this(Objects.requireNonNull(loggerSupplier), + (Void)null); + } + + private LazyLoggerFactories(BiFunction, L> loggerSupplier, + Void unused) { + this.loggerSupplier = loggerSupplier; + } + + } + + static interface LoggerAccessor { + /** + * The logger name. + * @return The name of the logger that is / will be lazily created. + */ + public String getLoggerName(); + + /** + * Returns the wrapped logger object. + * @return the wrapped logger object. + */ + public Logger wrapped(); + + /** + * A PlatformLogger.Bridge view of the wrapped logger object. + * @return A PlatformLogger.Bridge view of the wrapped logger object. + */ + public PlatformLogger.Bridge platform(); + } + + /** + * The LazyLoggerAccessor class holds all the logic that delays the creation + * of the SPI logger until such a time that the VM is booted and the logger + * is actually used for logging. + * + * This class uses the services of the BootstrapLogger class to instantiate + * temporary loggers if appropriate. + */ + static final class LazyLoggerAccessor implements LoggerAccessor { + + // The factories that will be used to create the logger lazyly + final LazyLoggerFactories factories; + + // We need to pass the actual caller when creating the logger. + private final WeakReference> callerRef; + + // The name of the logger that will be created lazyly + final String name; + // The plain logger SPI object - null until it is accessed for the + // first time. + private volatile Logger w; + // A PlatformLogger.Bridge view of w. + private volatile PlatformLogger.Bridge p; + + + private LazyLoggerAccessor(String name, + LazyLoggerFactories factories, + Class caller) { + this(Objects.requireNonNull(name), Objects.requireNonNull(factories), + Objects.requireNonNull(caller), null); + } + + private LazyLoggerAccessor(String name, + LazyLoggerFactories factories, + Class caller, Void unused) { + this.name = name; + this.factories = factories; + this.callerRef = new WeakReference>(caller); + } + + /** + * The logger name. + * @return The name of the logger that is / will be lazily created. + */ + @Override + public String getLoggerName() { + return name; + } + + // must be called in synchronized block + // set wrapped logger if not set + private void setWrappedIfNotSet(Logger wrapped) { + if (w == null) { + w = wrapped; + } + } + + /** + * Returns the logger SPI object, creating it if 'w' is still null. + * @return the logger SPI object. + */ + public Logger wrapped() { + Logger wrapped = w; + if (wrapped != null) return wrapped; + // Wrapped logger not created yet: create it. + // BootstrapLogger has the logic to decide whether to invoke the + // SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger) + // logger. + wrapped = BootstrapLogger.getLogger(this); + synchronized(this) { + // if w has already been in between, simply drop 'wrapped'. + setWrappedIfNotSet(wrapped); + return w; + } + } + + /** + * A PlatformLogger.Bridge view of the wrapped logger. + * @return A PlatformLogger.Bridge view of the wrapped logger. + */ + public PlatformLogger.Bridge platform() { + // We can afford to return the platform view of the previous + // logger - if that view is not null. + // Because that view will either be the BootstrapLogger, which + // will redirect to the new wrapper properly, or the temporary + // logger - which in effect is equivalent to logging something + // just before the application initialized LogManager. + PlatformLogger.Bridge platform = p; + if (platform != null) return platform; + synchronized (this) { + if (w != null) { + if (p == null) p = PlatformLogger.Bridge.convert(w); + return p; + } + } + // If we reach here it means that the wrapped logger may not + // have been created yet: attempt to create it. + // BootstrapLogger has the logic to decide whether to invoke the + // SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger) + // logger. + final Logger wrapped = BootstrapLogger.getLogger(this); + synchronized(this) { + // if w has already been set, simply drop 'wrapped'. + setWrappedIfNotSet(wrapped); + if (p == null) p = PlatformLogger.Bridge.convert(w); + return p; + } + } + + /** + * Makes this accessor release a temporary logger. + * This method is called + * by BootstrapLogger when JUL is the default backend and LogManager + * is initialized, in order to replace temporary SimpleConsoleLoggers by + * real JUL loggers. See BootstrapLogger for more details. + * If {@code replace} is {@code true}, then this method will force + * the accessor to eagerly recreate its wrapped logger. + * Note: passing {@code replace=false} is no guarantee that the + * method will not actually replace the released logger. + * @param temporary The temporary logger too be released. + * @param replace Whether the released logger should be eagerly + * replaced. + */ + void release(SimpleConsoleLogger temporary, boolean replace) { + PlatformLogger.ConfigurableBridge.LoggerConfiguration conf = + PlatformLogger.ConfigurableBridge.getLoggerConfiguration(temporary); + PlatformLogger.Level level = conf != null + ? conf.getPlatformLevel() + : null; + synchronized (this) { + if (this.w == temporary) { + this.w = null; this.p = null; + } + } + PlatformLogger.Bridge platform = replace || level != null + ? this.platform() : null; + + if (level != null) { + conf = (platform != null && platform != temporary) + ? PlatformLogger.ConfigurableBridge.getLoggerConfiguration(platform) + : null; + if (conf != null) conf.setPlatformLevel(level); + } + } + + /** + * Replace 'w' by the real SPI logger and flush the log messages pending + * in the temporary 'bootstrap' Logger. Called by BootstrapLogger when + * this accessor's bootstrap logger is accessed and BootstrapLogger + * notices that the VM is no longer booting. + * @param bootstrap This accessor's bootstrap logger (usually this is 'w'). + */ + Logger getConcreteLogger(BootstrapLogger bootstrap) { + assert VM.isBooted(); + synchronized(this) { + // another thread may have already invoked flush() + if (this.w == bootstrap) { + this.w = null; this.p = null; + } + } + return this.wrapped(); + } + + PlatformLogger.Bridge getConcretePlatformLogger(BootstrapLogger bootstrap) { + assert VM.isBooted(); + synchronized(this) { + // another thread may have already invoked flush() + if (this.w == bootstrap) { + this.w = null; this.p = null; + } + } + return this.platform(); + } + + // Creates the wrapped logger by invoking the SPI. + Logger createLogger() { + final Class caller = callerRef.get(); + if (caller == null) { + throw new IllegalStateException("The class for which this logger" + + " was created has been garbage collected"); + } + return this.factories.loggerSupplier.apply(name, caller); + } + + /** + * Creates a new lazy logger accessor for the named logger. The given + * factories will be use when it becomes necessary to actually create + * the logger. + * @param An interface that extends {@link Logger}. + * @param name The logger name. + * @param factories The factories that should be used to create the + * wrapped logger. + * @return A new LazyLoggerAccessor. + */ + public static LazyLoggerAccessor makeAccessor(String name, + LazyLoggerFactories factories, Class caller) { + return new LazyLoggerAccessor(name, factories, caller); + } + + } + + /** + * An implementation of {@link Logger} that redirects all calls to a wrapped + * instance of {@code Logger}. + */ + private static class LazyLoggerWrapper + extends AbstractLoggerWrapper { + + final LoggerAccessor loggerAccessor; + + public LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier) { + this(Objects.requireNonNull(loggerSinkSupplier), (Void)null); + } + + private LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier, + Void unused) { + this.loggerAccessor = loggerSinkSupplier; + } + + @Override + final Logger wrapped() { + return loggerAccessor.wrapped(); + } + + @Override + PlatformLogger.Bridge platformProxy() { + return loggerAccessor.platform(); + } + + } + + // Do not expose this outside of this package. + private static volatile LoggerFinder provider = null; + private static LoggerFinder accessLoggerFinder() { + if (provider == null) { + // no need to lock: it doesn't matter if we call + // getLoggerFinder() twice - since LoggerFinder already caches + // the result. + // This is just an optimization to avoid the cost of calling + // doPrivileged every time. + final SecurityManager sm = System.getSecurityManager(); + provider = sm == null ? LoggerFinder.getLoggerFinder() : + AccessController.doPrivileged( + (PrivilegedAction)LoggerFinder::getLoggerFinder); + } + return provider; + } + + // Avoid using lambda here as lazy loggers could be created early + // in the bootstrap sequence... + private static final BiFunction, Logger> loggerSupplier = + new BiFunction<>() { + @Override + public Logger apply(String name, Class caller) { + return LazyLoggers.getLoggerFromFinder(name, caller); + } + }; + + private static final LazyLoggerFactories factories = + new LazyLoggerFactories<>(loggerSupplier); + + + + // A concrete implementation of Logger that delegates to a System.Logger, + // but only creates the System.Logger instance lazily when it's used for + // the first time. + // The JdkLazyLogger uses a LazyLoggerAccessor objects, which relies + // on the logic embedded in BootstrapLogger to avoid loading the concrete + // logger provider until the VM has finished booting. + // + private static final class JdkLazyLogger extends LazyLoggerWrapper { + JdkLazyLogger(String name, Class caller) { + this(LazyLoggerAccessor.makeAccessor(name, factories, caller), + (Void)null); + } + private JdkLazyLogger(LazyLoggerAccessor holder, Void unused) { + super(holder); + } + } + + /** + * Gets a logger from the LoggerFinder. Creates the actual concrete + * logger. + * @param name name of the logger + * @param caller class on behalf of which the logger is created + * @return The logger returned by the LoggerFinder. + */ + static Logger getLoggerFromFinder(String name, Class caller) { + final SecurityManager sm = System.getSecurityManager(); + if (sm == null) { + return accessLoggerFinder().getLogger(name, caller); + } else { + return AccessController.doPrivileged((PrivilegedAction) + () -> {return accessLoggerFinder().getLogger(name, caller);}, + null, LOGGERFINDER_PERMISSION); + } + } + + /** + * Returns a (possibly lazy) Logger for the caller. + * + * @param name the logger name + * @param caller The class on behalf of which the logger is created. + * If the caller is not loaded from the Boot ClassLoader, + * the LoggerFinder is accessed and the logger returned + * by {@link LoggerFinder#getLogger(java.lang.String, java.lang.Class)} + * is returned to the caller directly. + * Otherwise, the logger returned by + * {@link #getLazyLogger(java.lang.String, java.lang.Class)} + * is returned to the caller. + * + * @return a (possibly lazy) Logger instance. + */ + public static final Logger getLogger(String name, Class caller) { + if (caller.getClassLoader() == null) { + return getLazyLogger(name, caller); + } else { + return getLoggerFromFinder(name, caller); + } + } + + /** + * Returns a (possibly lazy) Logger suitable for system classes. + * Whether the returned logger is lazy or not depend on the result + * returned by {@link BootstrapLogger#useLazyLoggers()}. + * + * @param name the logger name + * @param caller the class on behalf of which the logger is created. + * @return a (possibly lazy) Logger instance. + */ + public static final Logger getLazyLogger(String name, Class caller) { + + // BootstrapLogger has the logic to determine whether a LazyLogger + // should be used. Usually, it is worth it only if: + // - the VM is not yet booted + // - or, the backend is JUL and there is no configuration + // - or, the backend is a custom backend, as we don't know what + // that is going to load... + // So if for instance the VM is booted and we use JUL with a custom + // configuration, we're not going to delay the creation of loggers... + final boolean useLazyLogger = BootstrapLogger.useLazyLoggers(); + if (useLazyLogger) { + return new JdkLazyLogger(name, caller); + } else { + // Directly invoke the LoggerFinder. + return getLoggerFromFinder(name, caller); + } + } + +} diff --git a/jdk/src/java.base/share/classes/jdk/internal/logger/LocalizedLoggerWrapper.java b/jdk/src/java.base/share/classes/jdk/internal/logger/LocalizedLoggerWrapper.java new file mode 100644 index 00000000000..361ebc30e7b --- /dev/null +++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LocalizedLoggerWrapper.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2015, 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.internal.logger; + +import java.util.ResourceBundle; +import java.util.function.Supplier; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; + +/** + * This implementation of {@link Logger} redirects all logging method + * calls to calls to {@code log(Level, String, ResourceBundle, ...)} + * methods, passing the Logger's ResourceBundle as parameter. + * So for instance a call to {@link Logger#log(Level, String) + * log(Level.INFO, msg)} will be redirected + * to a call to {@link #log(java.lang.System.Logger.Level, + * java.util.ResourceBundle, java.lang.String, java.lang.Object...) + * this.log(Level.INFO, this.bundle, msg, (Object[]) null)}. + *

    + * Note that methods that take a {@link Supplier Supplier<String>} + * or an Object are not redirected. It is assumed that a string returned + * by a {@code Supplier} is already localized, or cannot be localized. + * + * @param Type of the wrapped Logger: {@code Logger} or an + * extension of the {@code Logger} interface. + */ +public class LocalizedLoggerWrapper extends LoggerWrapper { + + private final ResourceBundle bundle; + + public LocalizedLoggerWrapper(L wrapped, ResourceBundle bundle) { + super(wrapped); + this.bundle = bundle; + } + + public final ResourceBundle getBundle() { + return bundle; + } + + // We assume that messages returned by Supplier and Object are + // either already localized or not localizable. To be evaluated. + + // ----------------------------------------------------------------- + // Generic methods taking a Level as parameter + // ----------------------------------------------------------------- + + @Override + public final void log(Level level, String msg) { + log(level, bundle, msg, (Object[]) null); + } + + @Override + public final void log(Level level, + String msg, Throwable thrown) { + log(level, bundle, msg, thrown); + } + + @Override + public final void log(Level level, + String format, Object... params) { + log(level, bundle, format, params); + } + + @Override + public final void log(Level level, Object obj) { + wrapped.log(level, obj); + } + + @Override + public final void log(Level level, Supplier msgSupplier) { + wrapped.log(level, msgSupplier); + } + + @Override + public final void log(Level level, Supplier msgSupplier, Throwable thrown) { + wrapped.log(level, msgSupplier, thrown); + } + + @Override + public final void log(Level level, ResourceBundle bundle, String format, Object... params) { + wrapped.log(level, bundle, format, params); + } + + @Override + public final void log(Level level, ResourceBundle bundle, String key, Throwable thrown) { + wrapped.log(level, bundle, key, thrown); + } + + @Override + public final boolean isLoggable(Level level) { + return wrapped.isLoggable(level); + } + + // Override methods from PlatformLogger.Bridge that don't take a + // resource bundle... + + @Override + public final void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod, + String key) { + logrb(level, sourceClass, sourceMethod, bundle, key, (Object[]) null); + } + + @Override + public final void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod, + String key, Throwable thrown) { + logrb(level, sourceClass, sourceMethod, bundle, key, thrown); + } + + @Override + public final void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod, + String key, Object... params) { + logrb(level, sourceClass, sourceMethod, bundle, key, params); + } + + @Override + public final void log(sun.util.logging.PlatformLogger.Level level, String msg, Throwable thrown) { + logrb(level, bundle, msg, thrown); + } + + @Override + public final void log(sun.util.logging.PlatformLogger.Level level, String msg) { + logrb(level, bundle, msg, (Object[]) null); + } + + @Override + public final void log(sun.util.logging.PlatformLogger.Level level, String format, Object... params) { + logrb(level, bundle, format, params); + } + + +} diff --git a/jdk/src/java.base/share/classes/jdk/internal/logger/LoggerFinderLoader.java b/jdk/src/java.base/share/classes/jdk/internal/logger/LoggerFinderLoader.java new file mode 100644 index 00000000000..7d315ba7057 --- /dev/null +++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LoggerFinderLoader.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2015, 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.internal.logger; + +import java.io.FilePermission; +import java.security.AccessController; +import java.security.Permission; +import java.security.PrivilegedAction; +import java.util.Iterator; +import java.util.Locale; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import sun.security.util.SecurityConstants; + +/** + * Helper class used to load the {@link java.lang.System.LoggerFinder}. + */ +public final class LoggerFinderLoader { + private static volatile System.LoggerFinder service; + private static final Object lock = new int[0]; + static final Permission CLASSLOADER_PERMISSION = + SecurityConstants.GET_CLASSLOADER_PERMISSION; + static final Permission READ_PERMISSION = + new FilePermission("<>", + SecurityConstants.FILE_READ_ACTION); + public static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + + // This is used to control how the LoggerFinderLoader handles + // errors when instantiating the LoggerFinder provider. + // ERROR => throws ServiceConfigurationError + // WARNING => Do not fail, use plain default (simple logger) implementation, + // prints warning on console. (this is the default) + // DEBUG => Do not fail, use plain default (simple logger) implementation, + // prints warning and exception stack trace on console. + // QUIET => Do not fail and stay silent. + private static enum ErrorPolicy { ERROR, WARNING, DEBUG, QUIET }; + + // This class is static and cannot be instantiated. + private LoggerFinderLoader() { + throw new InternalError("LoggerFinderLoader cannot be instantiated"); + } + + + // Return the loaded LoggerFinder, or load it if not already loaded. + private static System.LoggerFinder service() { + if (service != null) return service; + synchronized(lock) { + if (service != null) return service; + service = loadLoggerFinder(); + } + // Since the LoggerFinder is already loaded - we can stop using + // temporary loggers. + BootstrapLogger.redirectTemporaryLoggers(); + return service; + } + + // Get configuration error policy + private static ErrorPolicy configurationErrorPolicy() { + final PrivilegedAction getConfigurationErrorPolicy = + () -> System.getProperty("jdk.logger.finder.error"); + String errorPolicy = AccessController.doPrivileged(getConfigurationErrorPolicy); + if (errorPolicy == null || errorPolicy.isEmpty()) { + return ErrorPolicy.WARNING; + } + try { + return ErrorPolicy.valueOf(errorPolicy.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException x) { + return ErrorPolicy.WARNING; + } + } + + // Whether multiple provider should be considered as an error. + // This is further submitted to the configuration error policy. + private static boolean ensureSingletonProvider() { + final PrivilegedAction ensureSingletonProvider = + () -> Boolean.getBoolean("jdk.logger.finder.singleton"); + return AccessController.doPrivileged(ensureSingletonProvider); + } + + private static Iterator findLoggerFinderProviders() { + final Iterator iterator; + if (System.getSecurityManager() == null) { + iterator = ServiceLoader.load(System.LoggerFinder.class, + ClassLoader.getSystemClassLoader()).iterator(); + } else { + final PrivilegedAction> pa = + () -> ServiceLoader.load(System.LoggerFinder.class, + ClassLoader.getSystemClassLoader()).iterator(); + iterator = AccessController.doPrivileged(pa, null, + LOGGERFINDER_PERMISSION, CLASSLOADER_PERMISSION, + READ_PERMISSION); + } + return iterator; + } + + // Loads the LoggerFinder using ServiceLoader. If no LoggerFinder + // is found returns the default (possibly JUL based) implementation + private static System.LoggerFinder loadLoggerFinder() { + System.LoggerFinder result; + try { + // Iterator iterates with the access control context stored + // at ServiceLoader creation time. + final Iterator iterator = + findLoggerFinderProviders(); + if (iterator.hasNext()) { + result = iterator.next(); + if (iterator.hasNext() && ensureSingletonProvider()) { + throw new ServiceConfigurationError( + "More than on LoggerFinder implementation"); + } + } else { + result = loadDefaultImplementation(); + } + } catch (Error | RuntimeException x) { + // next caller will get the plain default impl (not linked + // to java.util.logging) + service = result = new DefaultLoggerFinder(); + ErrorPolicy errorPolicy = configurationErrorPolicy(); + if (errorPolicy == ErrorPolicy.ERROR) { + // rethrow any exception as a ServiceConfigurationError. + if (x instanceof Error) { + throw x; + } else { + throw new ServiceConfigurationError( + "Failed to instantiate LoggerFinder provider; Using default.", x); + } + } else if (errorPolicy != ErrorPolicy.QUIET) { + // if QUIET just silently use the plain default impl + // otherwise, log a warning, possibly adding the exception + // stack trace (if DEBUG is specified). + SimpleConsoleLogger logger = + new SimpleConsoleLogger("jdk.internal.logger", false); + logger.log(System.Logger.Level.WARNING, + "Failed to instantiate LoggerFinder provider; Using default."); + if (errorPolicy == ErrorPolicy.DEBUG) { + logger.log(System.Logger.Level.WARNING, + "Exception raised trying to instantiate LoggerFinder", x); + } + } + } + return result; + } + + private static System.LoggerFinder loadDefaultImplementation() { + final SecurityManager sm = System.getSecurityManager(); + final Iterator iterator; + if (sm == null) { + iterator = ServiceLoader.loadInstalled(DefaultLoggerFinder.class).iterator(); + } else { + // We use limited do privileged here - the minimum set of + // permissions required to 'see' the META-INF/services resources + // seems to be CLASSLOADER_PERMISSION and READ_PERMISSION. + // Note that do privileged is required because + // otherwise the SecurityManager will prevent the ServiceLoader + // from seeing the installed provider. + PrivilegedAction> pa = () -> + ServiceLoader.loadInstalled(DefaultLoggerFinder.class).iterator(); + iterator = AccessController.doPrivileged(pa, null, + LOGGERFINDER_PERMISSION, CLASSLOADER_PERMISSION, + READ_PERMISSION); + } + DefaultLoggerFinder result = null; + try { + // Iterator iterates with the access control context stored + // at ServiceLoader creation time. + if (iterator.hasNext()) { + result = iterator.next(); + } + } catch (RuntimeException x) { + throw new ServiceConfigurationError( + "Failed to instantiate default LoggerFinder", x); + } + if (result == null) { + result = new DefaultLoggerFinder(); + } + return result; + } + + public static System.LoggerFinder getLoggerFinder() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(LOGGERFINDER_PERMISSION); + } + return service(); + } + +} diff --git a/jdk/src/java.base/share/classes/jdk/internal/logger/LoggerWrapper.java b/jdk/src/java.base/share/classes/jdk/internal/logger/LoggerWrapper.java new file mode 100644 index 00000000000..8f214239d3e --- /dev/null +++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LoggerWrapper.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015, 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.internal.logger; + +import java.util.Objects; +import java.lang.System.Logger; +import sun.util.logging.PlatformLogger; + +/** + * An implementation of {@link Logger} that redirects all calls to a wrapped + instance of Logger. + * + * @param Type of the wrapped Logger: {@code Logger} or an + * extension of that interface. + */ +public class LoggerWrapper extends AbstractLoggerWrapper { + + final L wrapped; + final PlatformLogger.Bridge platformProxy; + + public LoggerWrapper(L wrapped) { + this(Objects.requireNonNull(wrapped), (Void)null); + } + + LoggerWrapper(L wrapped, Void unused) { + this.wrapped = wrapped; + this.platformProxy = (wrapped instanceof PlatformLogger.Bridge) ? + (PlatformLogger.Bridge) wrapped : null; + } + + @Override + public final L wrapped() { + return wrapped; + } + + @Override + public final PlatformLogger.Bridge platformProxy() { + return platformProxy; + } + +} diff --git a/jdk/src/java.base/share/classes/jdk/internal/logger/SimpleConsoleLogger.java b/jdk/src/java.base/share/classes/jdk/internal/logger/SimpleConsoleLogger.java new file mode 100644 index 00000000000..8f6ddf7764d --- /dev/null +++ b/jdk/src/java.base/share/classes/jdk/internal/logger/SimpleConsoleLogger.java @@ -0,0 +1,486 @@ +/* + * Copyright (c) 2015, 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.internal.logger; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.time.ZonedDateTime; +import java.util.ResourceBundle; +import java.util.function.Function; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.util.function.Supplier; +import jdk.internal.misc.JavaLangAccess; +import jdk.internal.misc.SharedSecrets; +import sun.util.logging.PlatformLogger; +import sun.util.logging.PlatformLogger.ConfigurableBridge.LoggerConfiguration; + +/** + * A simple console logger to emulate the behavior of JUL loggers when + * in the default configuration. SimpleConsoleLoggers are also used when + * JUL is not present and no DefaultLoggerFinder is installed. + */ +public class SimpleConsoleLogger extends LoggerConfiguration + implements Logger, PlatformLogger.Bridge, PlatformLogger.ConfigurableBridge { + + static final PlatformLogger.Level DEFAULT_LEVEL = PlatformLogger.Level.INFO; + + final String name; + volatile PlatformLogger.Level level; + final boolean usePlatformLevel; + SimpleConsoleLogger(String name, boolean usePlatformLevel) { + this.name = name; + this.usePlatformLevel = usePlatformLevel; + } + + @Override + public String getName() { + return name; + } + + private Enum logLevel(PlatformLogger.Level level) { + return usePlatformLevel ? level : level.systemLevel(); + } + + private Enum logLevel(Level level) { + return usePlatformLevel ? PlatformLogger.toPlatformLevel(level) : level; + } + + // --------------------------------------------------- + // From Logger + // --------------------------------------------------- + + @Override + public boolean isLoggable(Level level) { + return isLoggable(PlatformLogger.toPlatformLevel(level)); + } + + @Override + public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) { + if (isLoggable(level)) { + if (bundle != null) { + key = bundle.getString(key); + } + publish(getCallerInfo(), logLevel(level), key, thrown); + } + } + + @Override + public void log(Level level, ResourceBundle bundle, String format, Object... params) { + if (isLoggable(level)) { + if (bundle != null) { + format = bundle.getString(format); + } + publish(getCallerInfo(), logLevel(level), format, params); + } + } + + // --------------------------------------------------- + // From PlatformLogger.Bridge + // --------------------------------------------------- + + @Override + public boolean isLoggable(PlatformLogger.Level level) { + final PlatformLogger.Level effectiveLevel = effectiveLevel(); + return level != PlatformLogger.Level.OFF + && level.ordinal() >= effectiveLevel.ordinal(); + } + + @Override + public boolean isEnabled() { + return level != PlatformLogger.Level.OFF; + } + + @Override + public void log(PlatformLogger.Level level, String msg) { + if (isLoggable(level)) { + publish(getCallerInfo(), logLevel(level), msg); + } + } + + @Override + public void log(PlatformLogger.Level level, String msg, Throwable thrown) { + if (isLoggable(level)) { + publish(getCallerInfo(), logLevel(level), msg, thrown); + } + } + + @Override + public void log(PlatformLogger.Level level, String msg, Object... params) { + if (isLoggable(level)) { + publish(getCallerInfo(), logLevel(level), msg, params); + } + } + + private PlatformLogger.Level effectiveLevel() { + if (level == null) return DEFAULT_LEVEL; + return level; + } + + @Override + public PlatformLogger.Level getPlatformLevel() { + return level; + } + + @Override + public void setPlatformLevel(PlatformLogger.Level newLevel) { + level = newLevel; + } + + @Override + public LoggerConfiguration getLoggerConfiguration() { + return this; + } + + /** + * Default platform logging support - output messages to System.err - + * equivalent to ConsoleHandler with SimpleFormatter. + */ + static PrintStream outputStream() { + return System.err; + } + + // Returns the caller's class and method's name; best effort + // if cannot infer, return the logger's name. + private String getCallerInfo() { + String sourceClassName = null; + String sourceMethodName = null; + + JavaLangAccess access = SharedSecrets.getJavaLangAccess(); + Throwable throwable = new Throwable(); + int depth = access.getStackTraceDepth(throwable); + + String logClassName = "sun.util.logging.PlatformLogger"; + String simpleLoggerClassName = "jdk.internal.logger.SimpleConsoleLogger"; + boolean lookingForLogger = true; + for (int ix = 0; ix < depth; ix++) { + // Calling getStackTraceElement directly prevents the VM + // from paying the cost of building the entire stack frame. + final StackTraceElement frame = + access.getStackTraceElement(throwable, ix); + final String cname = frame.getClassName(); + if (lookingForLogger) { + // Skip all frames until we have found the first logger frame. + if (cname.equals(logClassName) || cname.equals(simpleLoggerClassName)) { + lookingForLogger = false; + } + } else { + if (skipLoggingFrame(cname)) continue; + if (!cname.equals(logClassName) && !cname.equals(simpleLoggerClassName)) { + // We've found the relevant frame. + sourceClassName = cname; + sourceMethodName = frame.getMethodName(); + break; + } + } + } + + if (sourceClassName != null) { + return sourceClassName + " " + sourceMethodName; + } else { + return name; + } + } + + private String getCallerInfo(String sourceClassName, String sourceMethodName) { + if (sourceClassName == null) return name; + if (sourceMethodName == null) return sourceClassName; + return sourceClassName + " " + sourceMethodName; + } + + private String toString(Throwable thrown) { + String throwable = ""; + if (thrown != null) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + pw.println(); + thrown.printStackTrace(pw); + pw.close(); + throwable = sw.toString(); + } + return throwable; + } + + private synchronized String format(Enum level, + String msg, Throwable thrown, String callerInfo) { + + ZonedDateTime zdt = ZonedDateTime.now(); + String throwable = toString(thrown); + + return String.format(Formatting.formatString, + zdt, + callerInfo, + name, + level.name(), + msg, + throwable); + } + + // publish accepts both PlatformLogger Levels and LoggerFinder Levels. + private void publish(String callerInfo, Enum level, String msg) { + outputStream().print(format(level, msg, null, callerInfo)); + } + // publish accepts both PlatformLogger Levels and LoggerFinder Levels. + private void publish(String callerInfo, Enum level, String msg, Throwable thrown) { + outputStream().print(format(level, msg, thrown, callerInfo)); + } + // publish accepts both PlatformLogger Levels and LoggerFinder Levels. + private void publish(String callerInfo, Enum level, String msg, Object... params) { + msg = params == null || params.length == 0 ? msg + : Formatting.formatMessage(msg, params); + outputStream().print(format(level, msg, null, callerInfo)); + } + + public static SimpleConsoleLogger makeSimpleLogger(String name, boolean usePlatformLevel) { + return new SimpleConsoleLogger(name, usePlatformLevel); + } + + public static SimpleConsoleLogger makeSimpleLogger(String name) { + return new SimpleConsoleLogger(name, false); + } + + public static String getSimpleFormat(Function defaultPropertyGetter) { + return Formatting.getSimpleFormat(defaultPropertyGetter); + } + + public static boolean skipLoggingFrame(String cname) { + return Formatting.skipLoggingFrame(cname); + } + + @Override + public void log(PlatformLogger.Level level, Supplier msgSupplier) { + if (isLoggable(level)) { + publish(getCallerInfo(), logLevel(level), msgSupplier.get()); + } + } + + @Override + public void log(PlatformLogger.Level level, Throwable thrown, + Supplier msgSupplier) { + if (isLoggable(level)) { + publish(getCallerInfo(), logLevel(level), msgSupplier.get(), thrown); + } + } + + @Override + public void logp(PlatformLogger.Level level, String sourceClass, + String sourceMethod, String msg) { + if (isLoggable(level)) { + publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg); + } + } + + @Override + public void logp(PlatformLogger.Level level, String sourceClass, + String sourceMethod, Supplier msgSupplier) { + if (isLoggable(level)) { + publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msgSupplier.get()); + } + } + + @Override + public void logp(PlatformLogger.Level level, String sourceClass, String sourceMethod, + String msg, Object... params) { + if (isLoggable(level)) { + publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, params); + } + } + + @Override + public void logp(PlatformLogger.Level level, String sourceClass, + String sourceMethod, String msg, Throwable thrown) { + if (isLoggable(level)) { + publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, thrown); + } + } + + @Override + public void logp(PlatformLogger.Level level, String sourceClass, + String sourceMethod, Throwable thrown, Supplier msgSupplier) { + if (isLoggable(level)) { + publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msgSupplier.get(), thrown); + } + } + + @Override + public void logrb(PlatformLogger.Level level, String sourceClass, + String sourceMethod, ResourceBundle bundle, String key, Object... params) { + if (isLoggable(level)) { + String msg = bundle == null ? key : bundle.getString(key); + publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, params); + } + } + + @Override + public void logrb(PlatformLogger.Level level, String sourceClass, + String sourceMethod, ResourceBundle bundle, String key, Throwable thrown) { + if (isLoggable(level)) { + String msg = bundle == null ? key : bundle.getString(key); + publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, thrown); + } + } + + @Override + public void logrb(PlatformLogger.Level level, ResourceBundle bundle, + String key, Object... params) { + if (isLoggable(level)) { + String msg = bundle == null ? key : bundle.getString(key); + publish(getCallerInfo(), logLevel(level), msg, params); + } + } + + @Override + public void logrb(PlatformLogger.Level level, ResourceBundle bundle, + String key, Throwable thrown) { + if (isLoggable(level)) { + String msg = bundle == null ? key : bundle.getString(key); + publish(getCallerInfo(), logLevel(level), msg, thrown); + } + } + + private static final class Formatting { + static final String DEFAULT_FORMAT = + "%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%n%4$s: %5$s%6$s%n"; + static final String FORMAT_PROP_KEY = + "java.util.logging.SimpleFormatter.format"; + static final String formatString = getSimpleFormat(null); + + // Make it easier to wrap Logger... + static private final String[] skips; + static { + String additionalPkgs = AccessController.doPrivileged( + (PrivilegedAction) + () -> System.getProperty("jdk.logger.packages")); + skips = additionalPkgs == null ? new String[0] : additionalPkgs.split(","); + + } + + static boolean skipLoggingFrame(String cname) { + // skip logging/logger infrastructure + + // fast escape path: all the prefixes below start with 's' or 'j' and + // have more than 12 characters. + char c = cname.length() < 12 ? 0 : cname.charAt(0); + if (c == 's') { + // skip internal machinery classes + if (cname.startsWith("sun.util.logging.")) return true; + if (cname.startsWith("sun.reflect.")) return true; + if (cname.startsWith("sun.rmi.runtime.Log")) return true; + } else if (c == 'j') { + // Message delayed at Bootstrap: no need to go further up. + if (cname.startsWith("jdk.internal.logger.BootstrapLogger$LogEvent")) return false; + // skip public machinery classes + if (cname.startsWith("jdk.internal.logger.")) return true; + if (cname.startsWith("java.util.logging.")) return true; + if (cname.startsWith("java.lang.System$Logger")) return true; + if (cname.startsWith("java.lang.reflect.")) return true; + if (cname.startsWith("java.lang.invoke.MethodHandle")) return true; + if (cname.startsWith("java.lang.invoke.LambdaForm")) return true; + if (cname.startsWith("java.security.AccessController")) return true; + } + + // check additional prefixes if any are specified. + if (skips.length > 0) { + for (int i=0; i defaultPropertyGetter) { + // Using a lambda here causes + // jdk/test/java/lang/invoke/lambda/LogGeneratedClassesTest.java + // to fail - because that test has a testcase which somehow references + // PlatformLogger and counts the number of generated lambda classes + // So we explicitely use new PrivilegedAction here. + String format = + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public String run() { + return System.getProperty(FORMAT_PROP_KEY); + } + }); + if (format == null && defaultPropertyGetter != null) { + format = defaultPropertyGetter.apply(FORMAT_PROP_KEY); + } + if (format != null) { + try { + // validate the user-defined format string + String.format(format, ZonedDateTime.now(), "", "", "", "", ""); + } catch (IllegalArgumentException e) { + // illegal syntax; fall back to the default format + format = DEFAULT_FORMAT; + } + } else { + format = DEFAULT_FORMAT; + } + return format; + } + + + // Copied from java.util.logging.Formatter.formatMessage + static String formatMessage(String format, Object... parameters) { + // Do the formatting. + try { + if (parameters == null || parameters.length == 0) { + // No parameters. Just return format string. + return format; + } + // Is it a java.text style format? + // Ideally we could match with + // Pattern.compile("\\{\\d").matcher(format).find()) + // However the cost is 14% higher, so we cheaply check for + // + boolean isJavaTestFormat = false; + final int len = format.length(); + for (int i=0; i= '0' && d <= '9') { + isJavaTestFormat = true; + break; + } + } + } + if (isJavaTestFormat) { + return java.text.MessageFormat.format(format, parameters); + } + return format; + } catch (Exception ex) { + // Formatting failed: use format string. + return format; + } + } + } +} diff --git a/jdk/src/java.base/share/classes/jdk/internal/logger/package-info.java b/jdk/src/java.base/share/classes/jdk/internal/logger/package-info.java new file mode 100644 index 00000000000..28c622d4cde --- /dev/null +++ b/jdk/src/java.base/share/classes/jdk/internal/logger/package-info.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015, 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. + */ + +/** + * [JDK INTERNAL] + * The {@code jdk.internal.logger} package defines an internal provider + * whose default naive implementation is replaced by the {@code java.logging} + * module when the {@code java.logging} module is present. + *

    + * Default Implementation + *

    + * The JDK default implementation of the System.LoggerFinder will attempt to + * load an installed instance of the {@link jdk.internal.logger.DefaultLoggerFinder} + * defined in this package. + * When the {@code java.util.logging} package is present, this will usually + * resolve to an instance of {@link sun.util.logging.internal.LoggingProviderImpl} - + * which provides an implementation of the Logger whose backend is a + * {@link java.util.logging.Logger java.util.logging.Logger}. + * Configuration can thus be performed by direct access to the regular + * {@code java.util.logging} APIs, + * using {@link java.util.logging.Logger java.util.logging.Logger} and + * {@link java.util.logging.LogManager} to access and configure the backend + * Loggers. + *
    + * If however {@code java.util.logging} is not linked with the application, then + * the default implementation will return a simple logger that will print out + * all log messages of INFO level and above to the console ({@code System.err}), + * as implemented by the base {@link jdk.internal.logger.DefaultLoggerFinder} class. + *

    + * Message Levels and Mapping to java.util.logging + *

    + * The {@link java.lang.System.LoggerFinder} class documentation describe how + * {@linkplain java.lang.System.Logger.Level System.Logger levels} are mapped + * to {@linkplain java.util.logging.Level JUL levels} when {@code + * java.util.logging} is the backend. + * + * @see jdk.internal.logger.DefaultLoggerFinder + * @see sun.util.logging.internal.LoggingProviderImpl + * @see java.lang.System.LoggerFinder + * @see java.lang.System.Logger + * @see sun.util.logging.PlatformLogger.Bridge + * @see sun.util.logging.internal + * + * @since 1.9 + */ +package jdk.internal.logger; diff --git a/jdk/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java b/jdk/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java index 39bbbc625a1..1d74a0da741 100644 --- a/jdk/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java +++ b/jdk/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java @@ -30,7 +30,7 @@ import java.io.Console; import java.io.FileDescriptor; import java.security.ProtectionDomain; import java.security.AccessController; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** A repository of "shared secrets", which are a mechanism for calling implementation-private methods in another package without diff --git a/jdk/src/java.base/share/classes/jdk/internal/misc/Unsafe.java b/jdk/src/java.base/share/classes/jdk/internal/misc/Unsafe.java new file mode 100644 index 00000000000..83bb4f34ff1 --- /dev/null +++ b/jdk/src/java.base/share/classes/jdk/internal/misc/Unsafe.java @@ -0,0 +1,1391 @@ +/* + * Copyright (c) 2000, 2015, 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.internal.misc; + +import java.lang.reflect.Field; +import java.security.ProtectionDomain; + +import sun.reflect.CallerSensitive; +import sun.reflect.Reflection; +import sun.misc.VM; + +import jdk.internal.HotSpotIntrinsicCandidate; + + +/** + * A collection of methods for performing low-level, unsafe operations. + * Although the class and all methods are public, use of this class is + * limited because only trusted code can obtain instances of it. + * + * @author John R. Rose + * @see #getUnsafe + */ + +public final class Unsafe { + + private static native void registerNatives(); + static { + registerNatives(); + sun.reflect.Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe"); + } + + private Unsafe() {} + + private static final Unsafe theUnsafe = new Unsafe(); + + /** + * Provides the caller with the capability of performing unsafe + * operations. + * + *

    The returned {@code Unsafe} object should be carefully guarded + * by the caller, since it can be used to read and write data at arbitrary + * memory addresses. It must never be passed to untrusted code. + * + *

    Most methods in this class are very low-level, and correspond to a + * small number of hardware instructions (on typical machines). Compilers + * are encouraged to optimize these methods accordingly. + * + *

    Here is a suggested idiom for using unsafe operations: + * + *

     {@code
    +     * class MyTrustedClass {
    +     *   private static final Unsafe unsafe = Unsafe.getUnsafe();
    +     *   ...
    +     *   private long myCountAddress = ...;
    +     *   public int getCount() { return unsafe.getByte(myCountAddress); }
    +     * }}
    + * + * (It may assist compilers to make the local variable {@code final}.) + * + * @throws SecurityException if a security manager exists and its + * {@code checkPropertiesAccess} method doesn't allow + * access to the system properties. + */ + @CallerSensitive + public static Unsafe getUnsafe() { + Class caller = Reflection.getCallerClass(); + if (!VM.isSystemDomainLoader(caller.getClassLoader())) + throw new SecurityException("Unsafe"); + return theUnsafe; + } + + /// peek and poke operations + /// (compilers should optimize these to memory ops) + + // These work on object fields in the Java heap. + // They will not work on elements of packed arrays. + + /** + * Fetches a value from a given Java variable. + * More specifically, fetches a field or array element within the given + * object {@code o} at the given offset, or (if {@code o} is null) + * from the memory address whose numerical value is the given offset. + *

    + * The results are undefined unless one of the following cases is true: + *

      + *
    • The offset was obtained from {@link #objectFieldOffset} on + * the {@link java.lang.reflect.Field} of some Java field and the object + * referred to by {@code o} is of a class compatible with that + * field's class. + * + *
    • The offset and object reference {@code o} (either null or + * non-null) were both obtained via {@link #staticFieldOffset} + * and {@link #staticFieldBase} (respectively) from the + * reflective {@link Field} representation of some Java field. + * + *
    • The object referred to by {@code o} is an array, and the offset + * is an integer of the form {@code B+N*S}, where {@code N} is + * a valid index into the array, and {@code B} and {@code S} are + * the values obtained by {@link #arrayBaseOffset} and {@link + * #arrayIndexScale} (respectively) from the array's class. The value + * referred to is the {@code N}th element of the array. + * + *
    + *

    + * If one of the above cases is true, the call references a specific Java + * variable (field or array element). However, the results are undefined + * if that variable is not in fact of the type returned by this method. + *

    + * This method refers to a variable by means of two parameters, and so + * it provides (in effect) a double-register addressing mode + * for Java variables. When the object reference is null, this method + * uses its offset as an absolute address. This is similar in operation + * to methods such as {@link #getInt(long)}, which provide (in effect) a + * single-register addressing mode for non-Java variables. + * However, because Java variables may have a different layout in memory + * from non-Java variables, programmers should not assume that these + * two addressing modes are ever equivalent. Also, programmers should + * remember that offsets from the double-register addressing mode cannot + * be portably confused with longs used in the single-register addressing + * mode. + * + * @param o Java heap object in which the variable resides, if any, else + * null + * @param offset indication of where the variable resides in a Java heap + * object, if any, else a memory address locating the variable + * statically + * @return the value fetched from the indicated Java variable + * @throws RuntimeException No defined exceptions are thrown, not even + * {@link NullPointerException} + */ + @HotSpotIntrinsicCandidate + public native int getInt(Object o, long offset); + + /** + * Stores a value into a given Java variable. + *

    + * The first two parameters are interpreted exactly as with + * {@link #getInt(Object, long)} to refer to a specific + * Java variable (field or array element). The given value + * is stored into that variable. + *

    + * The variable must be of the same type as the method + * parameter {@code x}. + * + * @param o Java heap object in which the variable resides, if any, else + * null + * @param offset indication of where the variable resides in a Java heap + * object, if any, else a memory address locating the variable + * statically + * @param x the value to store into the indicated Java variable + * @throws RuntimeException No defined exceptions are thrown, not even + * {@link NullPointerException} + */ + @HotSpotIntrinsicCandidate + public native void putInt(Object o, long offset, int x); + + /** + * Fetches a reference value from a given Java variable. + * @see #getInt(Object, long) + */ + @HotSpotIntrinsicCandidate + public native Object getObject(Object o, long offset); + + /** + * Stores a reference value into a given Java variable. + *

    + * Unless the reference {@code x} being stored is either null + * or matches the field type, the results are undefined. + * If the reference {@code o} is non-null, card marks or + * other store barriers for that object (if the VM requires them) + * are updated. + * @see #putInt(Object, long, int) + */ + @HotSpotIntrinsicCandidate + public native void putObject(Object o, long offset, Object x); + + /** @see #getInt(Object, long) */ + @HotSpotIntrinsicCandidate + public native boolean getBoolean(Object o, long offset); + /** @see #putInt(Object, long, int) */ + @HotSpotIntrinsicCandidate + public native void putBoolean(Object o, long offset, boolean x); + /** @see #getInt(Object, long) */ + @HotSpotIntrinsicCandidate + public native byte getByte(Object o, long offset); + /** @see #putInt(Object, long, int) */ + @HotSpotIntrinsicCandidate + public native void putByte(Object o, long offset, byte x); + /** @see #getInt(Object, long) */ + @HotSpotIntrinsicCandidate + public native short getShort(Object o, long offset); + /** @see #putInt(Object, long, int) */ + @HotSpotIntrinsicCandidate + public native void putShort(Object o, long offset, short x); + /** @see #getInt(Object, long) */ + @HotSpotIntrinsicCandidate + public native char getChar(Object o, long offset); + /** @see #putInt(Object, long, int) */ + @HotSpotIntrinsicCandidate + public native void putChar(Object o, long offset, char x); + /** @see #getInt(Object, long) */ + @HotSpotIntrinsicCandidate + public native long getLong(Object o, long offset); + /** @see #putInt(Object, long, int) */ + @HotSpotIntrinsicCandidate + public native void putLong(Object o, long offset, long x); + /** @see #getInt(Object, long) */ + @HotSpotIntrinsicCandidate + public native float getFloat(Object o, long offset); + /** @see #putInt(Object, long, int) */ + @HotSpotIntrinsicCandidate + public native void putFloat(Object o, long offset, float x); + /** @see #getInt(Object, long) */ + @HotSpotIntrinsicCandidate + public native double getDouble(Object o, long offset); + /** @see #putInt(Object, long, int) */ + @HotSpotIntrinsicCandidate + public native void putDouble(Object o, long offset, double x); + + // These read VM internal data. + + /** + * Fetches an uncompressed reference value from a given native variable + * ignoring the VM's compressed references mode. + * + * @param address a memory address locating the variable + * @return the value fetched from the indicated native variable + */ + public native Object getUncompressedObject(long address); + + /** + * Fetches the {@link java.lang.Class} Java mirror for the given native + * metaspace {@code Klass} pointer. + * + * @param metaspaceKlass a native metaspace {@code Klass} pointer + * @return the {@link java.lang.Class} Java mirror + */ + public native Class getJavaMirror(long metaspaceKlass); + + /** + * Fetches a native metaspace {@code Klass} pointer for the given Java + * object. + * + * @param o Java heap object for which to fetch the class pointer + * @return a native metaspace {@code Klass} pointer + */ + public native long getKlassPointer(Object o); + + // These work on values in the C heap. + + /** + * Fetches a value from a given memory address. If the address is zero, or + * does not point into a block obtained from {@link #allocateMemory}, the + * results are undefined. + * + * @see #allocateMemory + */ + @HotSpotIntrinsicCandidate + public native byte getByte(long address); + + /** + * Stores a value into a given memory address. If the address is zero, or + * does not point into a block obtained from {@link #allocateMemory}, the + * results are undefined. + * + * @see #getByte(long) + */ + @HotSpotIntrinsicCandidate + public native void putByte(long address, byte x); + + /** @see #getByte(long) */ + @HotSpotIntrinsicCandidate + public native short getShort(long address); + /** @see #putByte(long, byte) */ + @HotSpotIntrinsicCandidate + public native void putShort(long address, short x); + /** @see #getByte(long) */ + @HotSpotIntrinsicCandidate + public native char getChar(long address); + /** @see #putByte(long, byte) */ + @HotSpotIntrinsicCandidate + public native void putChar(long address, char x); + /** @see #getByte(long) */ + @HotSpotIntrinsicCandidate + public native int getInt(long address); + /** @see #putByte(long, byte) */ + @HotSpotIntrinsicCandidate + public native void putInt(long address, int x); + /** @see #getByte(long) */ + @HotSpotIntrinsicCandidate + public native long getLong(long address); + /** @see #putByte(long, byte) */ + @HotSpotIntrinsicCandidate + public native void putLong(long address, long x); + /** @see #getByte(long) */ + @HotSpotIntrinsicCandidate + public native float getFloat(long address); + /** @see #putByte(long, byte) */ + @HotSpotIntrinsicCandidate + public native void putFloat(long address, float x); + /** @see #getByte(long) */ + @HotSpotIntrinsicCandidate + public native double getDouble(long address); + /** @see #putByte(long, byte) */ + @HotSpotIntrinsicCandidate + public native void putDouble(long address, double x); + + /** + * Fetches a native pointer from a given memory address. If the address is + * zero, or does not point into a block obtained from {@link + * #allocateMemory}, the results are undefined. + * + *

    If the native pointer is less than 64 bits wide, it is extended as + * an unsigned number to a Java long. The pointer may be indexed by any + * given byte offset, simply by adding that offset (as a simple integer) to + * the long representing the pointer. The number of bytes actually read + * from the target address may be determined by consulting {@link + * #addressSize}. + * + * @see #allocateMemory + */ + @HotSpotIntrinsicCandidate + public native long getAddress(long address); + + /** + * Stores a native pointer into a given memory address. If the address is + * zero, or does not point into a block obtained from {@link + * #allocateMemory}, the results are undefined. + * + *

    The number of bytes actually written at the target address may be + * determined by consulting {@link #addressSize}. + * + * @see #getAddress(long) + */ + @HotSpotIntrinsicCandidate + public native void putAddress(long address, long x); + + /// wrappers for malloc, realloc, free: + + /** + * Allocates a new block of native memory, of the given size in bytes. The + * contents of the memory are uninitialized; they will generally be + * garbage. The resulting native pointer will never be zero, and will be + * aligned for all value types. Dispose of this memory by calling {@link + * #freeMemory}, or resize it with {@link #reallocateMemory}. + * + * @throws IllegalArgumentException if the size is negative or too large + * for the native size_t type + * + * @throws OutOfMemoryError if the allocation is refused by the system + * + * @see #getByte(long) + * @see #putByte(long, byte) + */ + public native long allocateMemory(long bytes); + + /** + * Resizes a new block of native memory, to the given size in bytes. The + * contents of the new block past the size of the old block are + * uninitialized; they will generally be garbage. The resulting native + * pointer will be zero if and only if the requested size is zero. The + * resulting native pointer will be aligned for all value types. Dispose + * of this memory by calling {@link #freeMemory}, or resize it with {@link + * #reallocateMemory}. The address passed to this method may be null, in + * which case an allocation will be performed. + * + * @throws IllegalArgumentException if the size is negative or too large + * for the native size_t type + * + * @throws OutOfMemoryError if the allocation is refused by the system + * + * @see #allocateMemory + */ + public native long reallocateMemory(long address, long bytes); + + /** + * Sets all bytes in a given block of memory to a fixed value + * (usually zero). + * + *

    This method determines a block's base address by means of two parameters, + * and so it provides (in effect) a double-register addressing mode, + * as discussed in {@link #getInt(Object,long)}. When the object reference is null, + * the offset supplies an absolute base address. + * + *

    The stores are in coherent (atomic) units of a size determined + * by the address and length parameters. If the effective address and + * length are all even modulo 8, the stores take place in 'long' units. + * If the effective address and length are (resp.) even modulo 4 or 2, + * the stores take place in units of 'int' or 'short'. + * + * @since 1.7 + */ + public native void setMemory(Object o, long offset, long bytes, byte value); + + /** + * Sets all bytes in a given block of memory to a fixed value + * (usually zero). This provides a single-register addressing mode, + * as discussed in {@link #getInt(Object,long)}. + * + *

    Equivalent to {@code setMemory(null, address, bytes, value)}. + */ + public void setMemory(long address, long bytes, byte value) { + setMemory(null, address, bytes, value); + } + + /** + * Sets all bytes in a given block of memory to a copy of another + * block. + * + *

    This method determines each block's base address by means of two parameters, + * and so it provides (in effect) a double-register addressing mode, + * as discussed in {@link #getInt(Object,long)}. When the object reference is null, + * the offset supplies an absolute base address. + * + *

    The transfers are in coherent (atomic) units of a size determined + * by the address and length parameters. If the effective addresses and + * length are all even modulo 8, the transfer takes place in 'long' units. + * If the effective addresses and length are (resp.) even modulo 4 or 2, + * the transfer takes place in units of 'int' or 'short'. + * + * @since 1.7 + */ + @HotSpotIntrinsicCandidate + public native void copyMemory(Object srcBase, long srcOffset, + Object destBase, long destOffset, + long bytes); + /** + * Sets all bytes in a given block of memory to a copy of another + * block. This provides a single-register addressing mode, + * as discussed in {@link #getInt(Object,long)}. + * + * Equivalent to {@code copyMemory(null, srcAddress, null, destAddress, bytes)}. + */ + public void copyMemory(long srcAddress, long destAddress, long bytes) { + copyMemory(null, srcAddress, null, destAddress, bytes); + } + + /** + * Disposes of a block of native memory, as obtained from {@link + * #allocateMemory} or {@link #reallocateMemory}. The address passed to + * this method may be null, in which case no action is taken. + * + * @see #allocateMemory + */ + public native void freeMemory(long address); + + /// random queries + + /** + * This constant differs from all results that will ever be returned from + * {@link #staticFieldOffset}, {@link #objectFieldOffset}, + * or {@link #arrayBaseOffset}. + */ + public static final int INVALID_FIELD_OFFSET = -1; + + /** + * Reports the location of a given field in the storage allocation of its + * class. Do not expect to perform any sort of arithmetic on this offset; + * it is just a cookie which is passed to the unsafe heap memory accessors. + * + *

    Any given field will always have the same offset and base, and no + * two distinct fields of the same class will ever have the same offset + * and base. + * + *

    As of 1.4.1, offsets for fields are represented as long values, + * although the Sun JVM does not use the most significant 32 bits. + * However, JVM implementations which store static fields at absolute + * addresses can use long offsets and null base pointers to express + * the field locations in a form usable by {@link #getInt(Object,long)}. + * Therefore, code which will be ported to such JVMs on 64-bit platforms + * must preserve all bits of static field offsets. + * @see #getInt(Object, long) + */ + public native long objectFieldOffset(Field f); + + /** + * Reports the location of a given static field, in conjunction with {@link + * #staticFieldBase}. + *

    Do not expect to perform any sort of arithmetic on this offset; + * it is just a cookie which is passed to the unsafe heap memory accessors. + * + *

    Any given field will always have the same offset, and no two distinct + * fields of the same class will ever have the same offset. + * + *

    As of 1.4.1, offsets for fields are represented as long values, + * although the Sun JVM does not use the most significant 32 bits. + * It is hard to imagine a JVM technology which needs more than + * a few bits to encode an offset within a non-array object, + * However, for consistency with other methods in this class, + * this method reports its result as a long value. + * @see #getInt(Object, long) + */ + public native long staticFieldOffset(Field f); + + /** + * Reports the location of a given static field, in conjunction with {@link + * #staticFieldOffset}. + *

    Fetch the base "Object", if any, with which static fields of the + * given class can be accessed via methods like {@link #getInt(Object, + * long)}. This value may be null. This value may refer to an object + * which is a "cookie", not guaranteed to be a real Object, and it should + * not be used in any way except as argument to the get and put routines in + * this class. + */ + public native Object staticFieldBase(Field f); + + /** + * Detects if the given class may need to be initialized. This is often + * needed in conjunction with obtaining the static field base of a + * class. + * @return false only if a call to {@code ensureClassInitialized} would have no effect + */ + public native boolean shouldBeInitialized(Class c); + + /** + * Ensures the given class has been initialized. This is often + * needed in conjunction with obtaining the static field base of a + * class. + */ + public native void ensureClassInitialized(Class c); + + /** + * Reports the offset of the first element in the storage allocation of a + * given array class. If {@link #arrayIndexScale} returns a non-zero value + * for the same class, you may use that scale factor, together with this + * base offset, to form new offsets to access elements of arrays of the + * given class. + * + * @see #getInt(Object, long) + * @see #putInt(Object, long, int) + */ + public native int arrayBaseOffset(Class arrayClass); + + /** The value of {@code arrayBaseOffset(boolean[].class)} */ + public static final int ARRAY_BOOLEAN_BASE_OFFSET + = theUnsafe.arrayBaseOffset(boolean[].class); + + /** The value of {@code arrayBaseOffset(byte[].class)} */ + public static final int ARRAY_BYTE_BASE_OFFSET + = theUnsafe.arrayBaseOffset(byte[].class); + + /** The value of {@code arrayBaseOffset(short[].class)} */ + public static final int ARRAY_SHORT_BASE_OFFSET + = theUnsafe.arrayBaseOffset(short[].class); + + /** The value of {@code arrayBaseOffset(char[].class)} */ + public static final int ARRAY_CHAR_BASE_OFFSET + = theUnsafe.arrayBaseOffset(char[].class); + + /** The value of {@code arrayBaseOffset(int[].class)} */ + public static final int ARRAY_INT_BASE_OFFSET + = theUnsafe.arrayBaseOffset(int[].class); + + /** The value of {@code arrayBaseOffset(long[].class)} */ + public static final int ARRAY_LONG_BASE_OFFSET + = theUnsafe.arrayBaseOffset(long[].class); + + /** The value of {@code arrayBaseOffset(float[].class)} */ + public static final int ARRAY_FLOAT_BASE_OFFSET + = theUnsafe.arrayBaseOffset(float[].class); + + /** The value of {@code arrayBaseOffset(double[].class)} */ + public static final int ARRAY_DOUBLE_BASE_OFFSET + = theUnsafe.arrayBaseOffset(double[].class); + + /** The value of {@code arrayBaseOffset(Object[].class)} */ + public static final int ARRAY_OBJECT_BASE_OFFSET + = theUnsafe.arrayBaseOffset(Object[].class); + + /** + * Reports the scale factor for addressing elements in the storage + * allocation of a given array class. However, arrays of "narrow" types + * will generally not work properly with accessors like {@link + * #getByte(Object, long)}, so the scale factor for such classes is reported + * as zero. + * + * @see #arrayBaseOffset + * @see #getInt(Object, long) + * @see #putInt(Object, long, int) + */ + public native int arrayIndexScale(Class arrayClass); + + /** The value of {@code arrayIndexScale(boolean[].class)} */ + public static final int ARRAY_BOOLEAN_INDEX_SCALE + = theUnsafe.arrayIndexScale(boolean[].class); + + /** The value of {@code arrayIndexScale(byte[].class)} */ + public static final int ARRAY_BYTE_INDEX_SCALE + = theUnsafe.arrayIndexScale(byte[].class); + + /** The value of {@code arrayIndexScale(short[].class)} */ + public static final int ARRAY_SHORT_INDEX_SCALE + = theUnsafe.arrayIndexScale(short[].class); + + /** The value of {@code arrayIndexScale(char[].class)} */ + public static final int ARRAY_CHAR_INDEX_SCALE + = theUnsafe.arrayIndexScale(char[].class); + + /** The value of {@code arrayIndexScale(int[].class)} */ + public static final int ARRAY_INT_INDEX_SCALE + = theUnsafe.arrayIndexScale(int[].class); + + /** The value of {@code arrayIndexScale(long[].class)} */ + public static final int ARRAY_LONG_INDEX_SCALE + = theUnsafe.arrayIndexScale(long[].class); + + /** The value of {@code arrayIndexScale(float[].class)} */ + public static final int ARRAY_FLOAT_INDEX_SCALE + = theUnsafe.arrayIndexScale(float[].class); + + /** The value of {@code arrayIndexScale(double[].class)} */ + public static final int ARRAY_DOUBLE_INDEX_SCALE + = theUnsafe.arrayIndexScale(double[].class); + + /** The value of {@code arrayIndexScale(Object[].class)} */ + public static final int ARRAY_OBJECT_INDEX_SCALE + = theUnsafe.arrayIndexScale(Object[].class); + + /** + * Reports the size in bytes of a native pointer, as stored via {@link + * #putAddress}. This value will be either 4 or 8. Note that the sizes of + * other primitive types (as stored in native memory blocks) is determined + * fully by their information content. + */ + public native int addressSize(); + + /** The value of {@code addressSize()} */ + public static final int ADDRESS_SIZE = theUnsafe.addressSize(); + + /** + * Reports the size in bytes of a native memory page (whatever that is). + * This value will always be a power of two. + */ + public native int pageSize(); + + + /// random trusted operations from JNI: + + /** + * Tells the VM to define a class, without security checks. By default, the + * class loader and protection domain come from the caller's class. + */ + public native Class defineClass(String name, byte[] b, int off, int len, + ClassLoader loader, + ProtectionDomain protectionDomain); + + /** + * Defines a class but does not make it known to the class loader or system dictionary. + *

    + * For each CP entry, the corresponding CP patch must either be null or have + * the a format that matches its tag: + *

      + *
    • Integer, Long, Float, Double: the corresponding wrapper object type from java.lang + *
    • Utf8: a string (must have suitable syntax if used as signature or name) + *
    • Class: any java.lang.Class object + *
    • String: any object (not just a java.lang.String) + *
    • InterfaceMethodRef: (NYI) a method handle to invoke on that call site's arguments + *
    + * @param hostClass context for linkage, access control, protection domain, and class loader + * @param data bytes of a class file + * @param cpPatches where non-null entries exist, they replace corresponding CP entries in data + */ + public native Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches); + + /** + * Allocates an instance but does not run any constructor. + * Initializes the class if it has not yet been. + */ + @HotSpotIntrinsicCandidate + public native Object allocateInstance(Class cls) + throws InstantiationException; + + /** Throws the exception without telling the verifier. */ + public native void throwException(Throwable ee); + + /** + * Atomically updates Java variable to {@code x} if it is currently + * holding {@code expected}. + * + *

    This operation has memory semantics of a {@code volatile} read + * and write. Corresponds to C11 atomic_compare_exchange_strong. + * + * @return {@code true} if successful + */ + @HotSpotIntrinsicCandidate + public final native boolean compareAndSwapObject(Object o, long offset, + Object expected, + Object x); + + /** + * Atomically updates Java variable to {@code x} if it is currently + * holding {@code expected}. + * + *

    This operation has memory semantics of a {@code volatile} read + * and write. Corresponds to C11 atomic_compare_exchange_strong. + * + * @return {@code true} if successful + */ + @HotSpotIntrinsicCandidate + public final native boolean compareAndSwapInt(Object o, long offset, + int expected, + int x); + + /** + * Atomically updates Java variable to {@code x} if it is currently + * holding {@code expected}. + * + *

    This operation has memory semantics of a {@code volatile} read + * and write. Corresponds to C11 atomic_compare_exchange_strong. + * + * @return {@code true} if successful + */ + @HotSpotIntrinsicCandidate + public final native boolean compareAndSwapLong(Object o, long offset, + long expected, + long x); + + /** + * Fetches a reference value from a given Java variable, with volatile + * load semantics. Otherwise identical to {@link #getObject(Object, long)} + */ + @HotSpotIntrinsicCandidate + public native Object getObjectVolatile(Object o, long offset); + + /** + * Stores a reference value into a given Java variable, with + * volatile store semantics. Otherwise identical to {@link #putObject(Object, long, Object)} + */ + @HotSpotIntrinsicCandidate + public native void putObjectVolatile(Object o, long offset, Object x); + + /** Volatile version of {@link #getInt(Object, long)} */ + @HotSpotIntrinsicCandidate + public native int getIntVolatile(Object o, long offset); + + /** Volatile version of {@link #putInt(Object, long, int)} */ + @HotSpotIntrinsicCandidate + public native void putIntVolatile(Object o, long offset, int x); + + /** Volatile version of {@link #getBoolean(Object, long)} */ + @HotSpotIntrinsicCandidate + public native boolean getBooleanVolatile(Object o, long offset); + + /** Volatile version of {@link #putBoolean(Object, long, boolean)} */ + @HotSpotIntrinsicCandidate + public native void putBooleanVolatile(Object o, long offset, boolean x); + + /** Volatile version of {@link #getByte(Object, long)} */ + @HotSpotIntrinsicCandidate + public native byte getByteVolatile(Object o, long offset); + + /** Volatile version of {@link #putByte(Object, long, byte)} */ + @HotSpotIntrinsicCandidate + public native void putByteVolatile(Object o, long offset, byte x); + + /** Volatile version of {@link #getShort(Object, long)} */ + @HotSpotIntrinsicCandidate + public native short getShortVolatile(Object o, long offset); + + /** Volatile version of {@link #putShort(Object, long, short)} */ + @HotSpotIntrinsicCandidate + public native void putShortVolatile(Object o, long offset, short x); + + /** Volatile version of {@link #getChar(Object, long)} */ + @HotSpotIntrinsicCandidate + public native char getCharVolatile(Object o, long offset); + + /** Volatile version of {@link #putChar(Object, long, char)} */ + @HotSpotIntrinsicCandidate + public native void putCharVolatile(Object o, long offset, char x); + + /** Volatile version of {@link #getLong(Object, long)} */ + @HotSpotIntrinsicCandidate + public native long getLongVolatile(Object o, long offset); + + /** Volatile version of {@link #putLong(Object, long, long)} */ + @HotSpotIntrinsicCandidate + public native void putLongVolatile(Object o, long offset, long x); + + /** Volatile version of {@link #getFloat(Object, long)} */ + @HotSpotIntrinsicCandidate + public native float getFloatVolatile(Object o, long offset); + + /** Volatile version of {@link #putFloat(Object, long, float)} */ + @HotSpotIntrinsicCandidate + public native void putFloatVolatile(Object o, long offset, float x); + + /** Volatile version of {@link #getDouble(Object, long)} */ + @HotSpotIntrinsicCandidate + public native double getDoubleVolatile(Object o, long offset); + + /** Volatile version of {@link #putDouble(Object, long, double)} */ + @HotSpotIntrinsicCandidate + public native void putDoubleVolatile(Object o, long offset, double x); + + /** + * Version of {@link #putObjectVolatile(Object, long, Object)} + * that does not guarantee immediate visibility of the store to + * other threads. This method is generally only useful if the + * underlying field is a Java volatile (or if an array cell, one + * that is otherwise only accessed using volatile accesses). + * + * Corresponds to C11 atomic_store_explicit(..., memory_order_release). + */ + @HotSpotIntrinsicCandidate + public native void putOrderedObject(Object o, long offset, Object x); + + /** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)} */ + @HotSpotIntrinsicCandidate + public native void putOrderedInt(Object o, long offset, int x); + + /** Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)} */ + @HotSpotIntrinsicCandidate + public native void putOrderedLong(Object o, long offset, long x); + + /** + * Unblocks the given thread blocked on {@code park}, or, if it is + * not blocked, causes the subsequent call to {@code park} not to + * block. Note: this operation is "unsafe" solely because the + * caller must somehow ensure that the thread has not been + * destroyed. Nothing special is usually required to ensure this + * when called from Java (in which there will ordinarily be a live + * reference to the thread) but this is not nearly-automatically + * so when calling from native code. + * + * @param thread the thread to unpark. + */ + @HotSpotIntrinsicCandidate + public native void unpark(Object thread); + + /** + * Blocks current thread, returning when a balancing + * {@code unpark} occurs, or a balancing {@code unpark} has + * already occurred, or the thread is interrupted, or, if not + * absolute and time is not zero, the given time nanoseconds have + * elapsed, or if absolute, the given deadline in milliseconds + * since Epoch has passed, or spuriously (i.e., returning for no + * "reason"). Note: This operation is in the Unsafe class only + * because {@code unpark} is, so it would be strange to place it + * elsewhere. + */ + @HotSpotIntrinsicCandidate + public native void park(boolean isAbsolute, long time); + + /** + * Gets the load average in the system run queue assigned + * to the available processors averaged over various periods of time. + * This method retrieves the given {@code nelem} samples and + * assigns to the elements of the given {@code loadavg} array. + * The system imposes a maximum of 3 samples, representing + * averages over the last 1, 5, and 15 minutes, respectively. + * + * @param loadavg an array of double of size nelems + * @param nelems the number of samples to be retrieved and + * must be 1 to 3. + * + * @return the number of samples actually retrieved; or -1 + * if the load average is unobtainable. + */ + public native int getLoadAverage(double[] loadavg, int nelems); + + // The following contain CAS-based Java implementations used on + // platforms not supporting native instructions + + /** + * Atomically adds the given value to the current value of a field + * or array element within the given object {@code o} + * at the given {@code offset}. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param delta the value to add + * @return the previous value + * @since 1.8 + */ + @HotSpotIntrinsicCandidate + public final int getAndAddInt(Object o, long offset, int delta) { + int v; + do { + v = getIntVolatile(o, offset); + } while (!compareAndSwapInt(o, offset, v, v + delta)); + return v; + } + + /** + * Atomically adds the given value to the current value of a field + * or array element within the given object {@code o} + * at the given {@code offset}. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param delta the value to add + * @return the previous value + * @since 1.8 + */ + @HotSpotIntrinsicCandidate + public final long getAndAddLong(Object o, long offset, long delta) { + long v; + do { + v = getLongVolatile(o, offset); + } while (!compareAndSwapLong(o, offset, v, v + delta)); + return v; + } + + /** + * Atomically exchanges the given value with the current value of + * a field or array element within the given object {@code o} + * at the given {@code offset}. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param newValue new value + * @return the previous value + * @since 1.8 + */ + @HotSpotIntrinsicCandidate + public final int getAndSetInt(Object o, long offset, int newValue) { + int v; + do { + v = getIntVolatile(o, offset); + } while (!compareAndSwapInt(o, offset, v, newValue)); + return v; + } + + /** + * Atomically exchanges the given value with the current value of + * a field or array element within the given object {@code o} + * at the given {@code offset}. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param newValue new value + * @return the previous value + * @since 1.8 + */ + @HotSpotIntrinsicCandidate + public final long getAndSetLong(Object o, long offset, long newValue) { + long v; + do { + v = getLongVolatile(o, offset); + } while (!compareAndSwapLong(o, offset, v, newValue)); + return v; + } + + /** + * Atomically exchanges the given reference value with the current + * reference value of a field or array element within the given + * object {@code o} at the given {@code offset}. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param newValue new value + * @return the previous value + * @since 1.8 + */ + @HotSpotIntrinsicCandidate + public final Object getAndSetObject(Object o, long offset, Object newValue) { + Object v; + do { + v = getObjectVolatile(o, offset); + } while (!compareAndSwapObject(o, offset, v, newValue)); + return v; + } + + + /** + * Ensures that loads before the fence will not be reordered with loads and + * stores after the fence; a "LoadLoad plus LoadStore barrier". + * + * Corresponds to C11 atomic_thread_fence(memory_order_acquire) + * (an "acquire fence"). + * + * A pure LoadLoad fence is not provided, since the addition of LoadStore + * is almost always desired, and most current hardware instructions that + * provide a LoadLoad barrier also provide a LoadStore barrier for free. + * @since 1.8 + */ + @HotSpotIntrinsicCandidate + public native void loadFence(); + + /** + * Ensures that loads and stores before the fence will not be reordered with + * stores after the fence; a "StoreStore plus LoadStore barrier". + * + * Corresponds to C11 atomic_thread_fence(memory_order_release) + * (a "release fence"). + * + * A pure StoreStore fence is not provided, since the addition of LoadStore + * is almost always desired, and most current hardware instructions that + * provide a StoreStore barrier also provide a LoadStore barrier for free. + * @since 1.8 + */ + @HotSpotIntrinsicCandidate + public native void storeFence(); + + /** + * Ensures that loads and stores before the fence will not be reordered + * with loads and stores after the fence. Implies the effects of both + * loadFence() and storeFence(), and in addition, the effect of a StoreLoad + * barrier. + * + * Corresponds to C11 atomic_thread_fence(memory_order_seq_cst). + * @since 1.8 + */ + @HotSpotIntrinsicCandidate + public native void fullFence(); + + /** + * Throws IllegalAccessError; for use by the VM for access control + * error support. + * @since 1.8 + */ + private static void throwIllegalAccessError() { + throw new IllegalAccessError(); + } + + /** + * @return Returns true if the native byte ordering of this + * platform is big-endian, false if it is little-endian. + */ + public final boolean isBigEndian() { return BE; } + + /** + * @return Returns true if this platform is capable of performing + * accesses at addresses which are not aligned for the type of the + * primitive type being accessed, false otherwise. + */ + public final boolean unalignedAccess() { return unalignedAccess; } + + /** + * Fetches a value at some byte offset into a given Java object. + * More specifically, fetches a value within the given object + * o at the given offset, or (if o is + * null) from the memory address whose numerical value is the + * given offset.

    + * + * The specification of this method is the same as {@link + * #getLong(Object, long)} except that the offset does not need to + * have been obtained from {@link #objectFieldOffset} on the + * {@link java.lang.reflect.Field} of some Java field. The value + * in memory is raw data, and need not correspond to any Java + * variable. Unless o is null, the value accessed + * must be entirely within the allocated object. The endianness + * of the value in memory is the endianness of the native platform. + * + *

    The read will be atomic with respect to the largest power + * of two that divides the GCD of the offset and the storage size. + * For example, getLongUnaligned will make atomic reads of 2-, 4-, + * or 8-byte storage units if the offset is zero mod 2, 4, or 8, + * respectively. There are no other guarantees of atomicity. + *

    + * 8-byte atomicity is only guaranteed on platforms on which + * support atomic accesses to longs. + * + * @param o Java heap object in which the value resides, if any, else + * null + * @param offset The offset in bytes from the start of the object + * @return the value fetched from the indicated object + * @throws RuntimeException No defined exceptions are thrown, not even + * {@link NullPointerException} + * @since 1.9 + */ + @HotSpotIntrinsicCandidate + public final long getLongUnaligned(Object o, long offset) { + if ((offset & 7) == 0) { + return getLong(o, offset); + } else if ((offset & 3) == 0) { + return makeLong(getInt(o, offset), + getInt(o, offset + 4)); + } else if ((offset & 1) == 0) { + return makeLong(getShort(o, offset), + getShort(o, offset + 2), + getShort(o, offset + 4), + getShort(o, offset + 6)); + } else { + return makeLong(getByte(o, offset), + getByte(o, offset + 1), + getByte(o, offset + 2), + getByte(o, offset + 3), + getByte(o, offset + 4), + getByte(o, offset + 5), + getByte(o, offset + 6), + getByte(o, offset + 7)); + } + } + /** + * As {@link #getLongUnaligned(Object, long)} but with an + * additional argument which specifies the endianness of the value + * as stored in memory. + * + * @param o Java heap object in which the variable resides + * @param offset The offset in bytes from the start of the object + * @param bigEndian The endianness of the value + * @return the value fetched from the indicated object + * @since 1.9 + */ + public final long getLongUnaligned(Object o, long offset, boolean bigEndian) { + return convEndian(bigEndian, getLongUnaligned(o, offset)); + } + + /** @see #getLongUnaligned(Object, long) */ + @HotSpotIntrinsicCandidate + public final int getIntUnaligned(Object o, long offset) { + if ((offset & 3) == 0) { + return getInt(o, offset); + } else if ((offset & 1) == 0) { + return makeInt(getShort(o, offset), + getShort(o, offset + 2)); + } else { + return makeInt(getByte(o, offset), + getByte(o, offset + 1), + getByte(o, offset + 2), + getByte(o, offset + 3)); + } + } + /** @see #getLongUnaligned(Object, long, boolean) */ + public final int getIntUnaligned(Object o, long offset, boolean bigEndian) { + return convEndian(bigEndian, getIntUnaligned(o, offset)); + } + + /** @see #getLongUnaligned(Object, long) */ + @HotSpotIntrinsicCandidate + public final short getShortUnaligned(Object o, long offset) { + if ((offset & 1) == 0) { + return getShort(o, offset); + } else { + return makeShort(getByte(o, offset), + getByte(o, offset + 1)); + } + } + /** @see #getLongUnaligned(Object, long, boolean) */ + public final short getShortUnaligned(Object o, long offset, boolean bigEndian) { + return convEndian(bigEndian, getShortUnaligned(o, offset)); + } + + /** @see #getLongUnaligned(Object, long) */ + @HotSpotIntrinsicCandidate + public final char getCharUnaligned(Object o, long offset) { + return (char)getShortUnaligned(o, offset); + } + + /** @see #getLongUnaligned(Object, long, boolean) */ + public final char getCharUnaligned(Object o, long offset, boolean bigEndian) { + return convEndian(bigEndian, getCharUnaligned(o, offset)); + } + + /** + * Stores a value at some byte offset into a given Java object. + *

    + * The specification of this method is the same as {@link + * #getLong(Object, long)} except that the offset does not need to + * have been obtained from {@link #objectFieldOffset} on the + * {@link java.lang.reflect.Field} of some Java field. The value + * in memory is raw data, and need not correspond to any Java + * variable. The endianness of the value in memory is the + * endianness of the native platform. + *

    + * The write will be atomic with respect to the largest power of + * two that divides the GCD of the offset and the storage size. + * For example, putLongUnaligned will make atomic writes of 2-, 4-, + * or 8-byte storage units if the offset is zero mod 2, 4, or 8, + * respectively. There are no other guarantees of atomicity. + *

    + * 8-byte atomicity is only guaranteed on platforms on which + * support atomic accesses to longs. + * + * @param o Java heap object in which the value resides, if any, else + * null + * @param offset The offset in bytes from the start of the object + * @param x the value to store + * @throws RuntimeException No defined exceptions are thrown, not even + * {@link NullPointerException} + * @since 1.9 + */ + @HotSpotIntrinsicCandidate + public final void putLongUnaligned(Object o, long offset, long x) { + if ((offset & 7) == 0) { + putLong(o, offset, x); + } else if ((offset & 3) == 0) { + putLongParts(o, offset, + (int)(x >> 0), + (int)(x >>> 32)); + } else if ((offset & 1) == 0) { + putLongParts(o, offset, + (short)(x >>> 0), + (short)(x >>> 16), + (short)(x >>> 32), + (short)(x >>> 48)); + } else { + putLongParts(o, offset, + (byte)(x >>> 0), + (byte)(x >>> 8), + (byte)(x >>> 16), + (byte)(x >>> 24), + (byte)(x >>> 32), + (byte)(x >>> 40), + (byte)(x >>> 48), + (byte)(x >>> 56)); + } + } + + /** + * As {@link #putLongUnaligned(Object, long, long)} but with an additional + * argument which specifies the endianness of the value as stored in memory. + * @param o Java heap object in which the value resides + * @param offset The offset in bytes from the start of the object + * @param x the value to store + * @param bigEndian The endianness of the value + * @throws RuntimeException No defined exceptions are thrown, not even + * {@link NullPointerException} + * @since 1.9 + */ + public final void putLongUnaligned(Object o, long offset, long x, boolean bigEndian) { + putLongUnaligned(o, offset, convEndian(bigEndian, x)); + } + + /** @see #putLongUnaligned(Object, long, long) */ + @HotSpotIntrinsicCandidate + public final void putIntUnaligned(Object o, long offset, int x) { + if ((offset & 3) == 0) { + putInt(o, offset, x); + } else if ((offset & 1) == 0) { + putIntParts(o, offset, + (short)(x >> 0), + (short)(x >>> 16)); + } else { + putIntParts(o, offset, + (byte)(x >>> 0), + (byte)(x >>> 8), + (byte)(x >>> 16), + (byte)(x >>> 24)); + } + } + /** @see #putLongUnaligned(Object, long, long, boolean) */ + public final void putIntUnaligned(Object o, long offset, int x, boolean bigEndian) { + putIntUnaligned(o, offset, convEndian(bigEndian, x)); + } + + /** @see #putLongUnaligned(Object, long, long) */ + @HotSpotIntrinsicCandidate + public final void putShortUnaligned(Object o, long offset, short x) { + if ((offset & 1) == 0) { + putShort(o, offset, x); + } else { + putShortParts(o, offset, + (byte)(x >>> 0), + (byte)(x >>> 8)); + } + } + /** @see #putLongUnaligned(Object, long, long, boolean) */ + public final void putShortUnaligned(Object o, long offset, short x, boolean bigEndian) { + putShortUnaligned(o, offset, convEndian(bigEndian, x)); + } + + /** @see #putLongUnaligned(Object, long, long) */ + @HotSpotIntrinsicCandidate + public final void putCharUnaligned(Object o, long offset, char x) { + putShortUnaligned(o, offset, (short)x); + } + /** @see #putLongUnaligned(Object, long, long, boolean) */ + public final void putCharUnaligned(Object o, long offset, char x, boolean bigEndian) { + putCharUnaligned(o, offset, convEndian(bigEndian, x)); + } + + // JVM interface methods + private native boolean unalignedAccess0(); + private native boolean isBigEndian0(); + + // BE is true iff the native endianness of this platform is big. + private static final boolean BE = theUnsafe.isBigEndian0(); + + // unalignedAccess is true iff this platform can perform unaligned accesses. + private static final boolean unalignedAccess = theUnsafe.unalignedAccess0(); + + private static int pickPos(int top, int pos) { return BE ? top - pos : pos; } + + // These methods construct integers from bytes. The byte ordering + // is the native endianness of this platform. + private static long makeLong(byte i0, byte i1, byte i2, byte i3, byte i4, byte i5, byte i6, byte i7) { + return ((toUnsignedLong(i0) << pickPos(56, 0)) + | (toUnsignedLong(i1) << pickPos(56, 8)) + | (toUnsignedLong(i2) << pickPos(56, 16)) + | (toUnsignedLong(i3) << pickPos(56, 24)) + | (toUnsignedLong(i4) << pickPos(56, 32)) + | (toUnsignedLong(i5) << pickPos(56, 40)) + | (toUnsignedLong(i6) << pickPos(56, 48)) + | (toUnsignedLong(i7) << pickPos(56, 56))); + } + private static long makeLong(short i0, short i1, short i2, short i3) { + return ((toUnsignedLong(i0) << pickPos(48, 0)) + | (toUnsignedLong(i1) << pickPos(48, 16)) + | (toUnsignedLong(i2) << pickPos(48, 32)) + | (toUnsignedLong(i3) << pickPos(48, 48))); + } + private static long makeLong(int i0, int i1) { + return (toUnsignedLong(i0) << pickPos(32, 0)) + | (toUnsignedLong(i1) << pickPos(32, 32)); + } + private static int makeInt(short i0, short i1) { + return (toUnsignedInt(i0) << pickPos(16, 0)) + | (toUnsignedInt(i1) << pickPos(16, 16)); + } + private static int makeInt(byte i0, byte i1, byte i2, byte i3) { + return ((toUnsignedInt(i0) << pickPos(24, 0)) + | (toUnsignedInt(i1) << pickPos(24, 8)) + | (toUnsignedInt(i2) << pickPos(24, 16)) + | (toUnsignedInt(i3) << pickPos(24, 24))); + } + private static short makeShort(byte i0, byte i1) { + return (short)((toUnsignedInt(i0) << pickPos(8, 0)) + | (toUnsignedInt(i1) << pickPos(8, 8))); + } + + private static byte pick(byte le, byte be) { return BE ? be : le; } + private static short pick(short le, short be) { return BE ? be : le; } + private static int pick(int le, int be) { return BE ? be : le; } + + // These methods write integers to memory from smaller parts + // provided by their caller. The ordering in which these parts + // are written is the native endianness of this platform. + private void putLongParts(Object o, long offset, byte i0, byte i1, byte i2, byte i3, byte i4, byte i5, byte i6, byte i7) { + putByte(o, offset + 0, pick(i0, i7)); + putByte(o, offset + 1, pick(i1, i6)); + putByte(o, offset + 2, pick(i2, i5)); + putByte(o, offset + 3, pick(i3, i4)); + putByte(o, offset + 4, pick(i4, i3)); + putByte(o, offset + 5, pick(i5, i2)); + putByte(o, offset + 6, pick(i6, i1)); + putByte(o, offset + 7, pick(i7, i0)); + } + private void putLongParts(Object o, long offset, short i0, short i1, short i2, short i3) { + putShort(o, offset + 0, pick(i0, i3)); + putShort(o, offset + 2, pick(i1, i2)); + putShort(o, offset + 4, pick(i2, i1)); + putShort(o, offset + 6, pick(i3, i0)); + } + private void putLongParts(Object o, long offset, int i0, int i1) { + putInt(o, offset + 0, pick(i0, i1)); + putInt(o, offset + 4, pick(i1, i0)); + } + private void putIntParts(Object o, long offset, short i0, short i1) { + putShort(o, offset + 0, pick(i0, i1)); + putShort(o, offset + 2, pick(i1, i0)); + } + private void putIntParts(Object o, long offset, byte i0, byte i1, byte i2, byte i3) { + putByte(o, offset + 0, pick(i0, i3)); + putByte(o, offset + 1, pick(i1, i2)); + putByte(o, offset + 2, pick(i2, i1)); + putByte(o, offset + 3, pick(i3, i0)); + } + private void putShortParts(Object o, long offset, byte i0, byte i1) { + putByte(o, offset + 0, pick(i0, i1)); + putByte(o, offset + 1, pick(i1, i0)); + } + + // Zero-extend an integer + private static int toUnsignedInt(byte n) { return n & 0xff; } + private static int toUnsignedInt(short n) { return n & 0xffff; } + private static long toUnsignedLong(byte n) { return n & 0xffl; } + private static long toUnsignedLong(short n) { return n & 0xffffl; } + private static long toUnsignedLong(int n) { return n & 0xffffffffl; } + + // Maybe byte-reverse an integer + private static char convEndian(boolean big, char n) { return big == BE ? n : Character.reverseBytes(n); } + private static short convEndian(boolean big, short n) { return big == BE ? n : Short.reverseBytes(n) ; } + private static int convEndian(boolean big, int n) { return big == BE ? n : Integer.reverseBytes(n) ; } + private static long convEndian(boolean big, long n) { return big == BE ? n : Long.reverseBytes(n) ; } +} diff --git a/jdk/src/java.base/share/classes/sun/invoke/anon/AnonymousClassLoader.java b/jdk/src/java.base/share/classes/sun/invoke/anon/AnonymousClassLoader.java index 60544e25f38..6c5cb3f5563 100644 --- a/jdk/src/java.base/share/classes/sun/invoke/anon/AnonymousClassLoader.java +++ b/jdk/src/java.base/share/classes/sun/invoke/anon/AnonymousClassLoader.java @@ -78,7 +78,7 @@ public class AnonymousClassLoader { this.hostClass = hostClass; } - public static AnonymousClassLoader make(sun.misc.Unsafe unsafe, Class hostClass) { + public static AnonymousClassLoader make(jdk.internal.misc.Unsafe unsafe, Class hostClass) { if (unsafe == null) throw new NullPointerException(); return new AnonymousClassLoader(hostClass); } @@ -189,13 +189,13 @@ public class AnonymousClassLoader { private static int fakeNameCounter = 99999; // ignore two warnings on this line: - private static sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); + private static jdk.internal.misc.Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe(); // preceding line requires that this class be on the boot class path private static final Method defineAnonymousClass; static { Method dac = null; - Class unsafeClass = unsafe.getClass(); + Class unsafeClass = unsafe.getClass(); try { dac = unsafeClass.getMethod("defineAnonymousClass", Class.class, diff --git a/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java b/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java index e69330b6846..a6054924ff5 100644 --- a/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java +++ b/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java @@ -26,35 +26,34 @@ package sun.invoke.util; public enum Wrapper { - // wrapperType primitiveType char zero emptyArray format - BOOLEAN( Boolean.class, boolean.class, 'Z', (Boolean)false, new boolean[0], Format.unsigned( 1)), + // wrapperType primitiveType char emptyArray format + BOOLEAN( Boolean.class, boolean.class, 'Z', new boolean[0], Format.unsigned( 1)), // These must be in the order defined for widening primitive conversions in JLS 5.1.2 - BYTE ( Byte.class, byte.class, 'B', (Byte)(byte)0, new byte[0], Format.signed( 8)), - SHORT ( Short.class, short.class, 'S', (Short)(short)0, new short[0], Format.signed( 16)), - CHAR (Character.class, char.class, 'C', (Character)(char)0, new char[0], Format.unsigned(16)), - INT ( Integer.class, int.class, 'I', (Integer)/*(int)*/0, new int[0], Format.signed( 32)), - LONG ( Long.class, long.class, 'J', (Long)(long)0, new long[0], Format.signed( 64)), - FLOAT ( Float.class, float.class, 'F', (Float)(float)0, new float[0], Format.floating(32)), - DOUBLE ( Double.class, double.class, 'D', (Double)(double)0, new double[0], Format.floating(64)), - OBJECT ( Object.class, Object.class, 'L', null, new Object[0], Format.other( 1)), + // Avoid boxing integral types here to defer initialization of internal caches + BYTE ( Byte.class, byte.class, 'B', new byte[0], Format.signed( 8)), + SHORT ( Short.class, short.class, 'S', new short[0], Format.signed( 16)), + CHAR (Character.class, char.class, 'C', new char[0], Format.unsigned(16)), + INT ( Integer.class, int.class, 'I', new int[0], Format.signed( 32)), + LONG ( Long.class, long.class, 'J', new long[0], Format.signed( 64)), + FLOAT ( Float.class, float.class, 'F', new float[0], Format.floating(32)), + DOUBLE ( Double.class, double.class, 'D', new double[0], Format.floating(64)), + OBJECT ( Object.class, Object.class, 'L', new Object[0], Format.other( 1)), // VOID must be the last type, since it is "assignable" from any other type: - VOID ( Void.class, void.class, 'V', null, null, Format.other( 0)), + VOID ( Void.class, void.class, 'V', null, Format.other( 0)), ; private final Class wrapperType; private final Class primitiveType; private final char basicTypeChar; - private final Object zero; private final Object emptyArray; private final int format; private final String wrapperSimpleName; private final String primitiveSimpleName; - private Wrapper(Class wtype, Class ptype, char tchar, Object zero, Object emptyArray, int format) { + private Wrapper(Class wtype, Class ptype, char tchar, Object emptyArray, int format) { this.wrapperType = wtype; this.primitiveType = ptype; this.basicTypeChar = tchar; - this.zero = zero; this.emptyArray = emptyArray; this.format = format; this.wrapperSimpleName = wtype.getSimpleName(); @@ -65,7 +64,7 @@ public enum Wrapper { public String detailString() { return wrapperSimpleName+ java.util.Arrays.asList(wrapperType, primitiveType, - basicTypeChar, zero, + basicTypeChar, zero(), "0x"+Integer.toHexString(format)); } @@ -222,13 +221,39 @@ public enum Wrapper { * type. (For void, it is what a reflective method returns * instead of no value at all.) */ - public Object zero() { return zero; } + public Object zero() { + switch (this) { + case BOOLEAN: + return Boolean.FALSE; + case INT: + return (Integer)0; + case BYTE: + return (Byte)(byte)0; + case CHAR: + return (Character)(char)0; + case SHORT: + return (Short)(short)0; + case LONG: + return (Long)(long)0; + case FLOAT: + return FLOAT_ZERO; + case DOUBLE: + return DOUBLE_ZERO; + case VOID: + case OBJECT: + default: + return null; + } + } + + private static final Object DOUBLE_ZERO = (Double)(double)0; + private static final Object FLOAT_ZERO = (Float)(float)0; /** Produce a zero value for the given wrapper type T. * The optional argument must a type compatible with this wrapper. * Equivalent to {@code this.cast(this.zero(), type)}. */ - public T zero(Class type) { return convert(zero, type); } + public T zero(Class type) { return convert(zero(), type); } /** Return the wrapper that wraps values of the given type. * The type may be {@code Object}, meaning the {@code OBJECT} wrapper. @@ -473,7 +498,7 @@ public enum Wrapper { } } else if (x == null) { @SuppressWarnings("unchecked") - T z = (T) zero; + T z = (T) zero(); return z; } @SuppressWarnings("unchecked") diff --git a/jdk/src/java.base/share/classes/sun/misc/InnocuousThread.java b/jdk/src/java.base/share/classes/sun/misc/InnocuousThread.java index 78a0a185f0a..a9c129cd79e 100644 --- a/jdk/src/java.base/share/classes/sun/misc/InnocuousThread.java +++ b/jdk/src/java.base/share/classes/sun/misc/InnocuousThread.java @@ -36,7 +36,7 @@ import java.util.concurrent.atomic.AtomicInteger; * ThreadGroup and supports the ability to erase ThreadLocals. */ public final class InnocuousThread extends ManagedLocalsThread { - private static final Unsafe UNSAFE; + private static final jdk.internal.misc.Unsafe UNSAFE; private static final ThreadGroup INNOCUOUSTHREADGROUP; private static final AccessControlContext ACC; private static final long INHERITEDACCESSCONTROLCONTEXT; @@ -92,7 +92,7 @@ public final class InnocuousThread extends ManagedLocalsThread { }); // Find and use topmost ThreadGroup as parent of new group - UNSAFE = Unsafe.getUnsafe(); + UNSAFE = jdk.internal.misc.Unsafe.getUnsafe(); Class tk = Thread.class; Class gk = ThreadGroup.class; diff --git a/jdk/src/java.base/share/classes/sun/misc/ManagedLocalsThread.java b/jdk/src/java.base/share/classes/sun/misc/ManagedLocalsThread.java index f520ca384b6..58d9013c261 100644 --- a/jdk/src/java.base/share/classes/sun/misc/ManagedLocalsThread.java +++ b/jdk/src/java.base/share/classes/sun/misc/ManagedLocalsThread.java @@ -30,7 +30,7 @@ package sun.misc; * locals erased on construction. */ public class ManagedLocalsThread extends Thread { - private static final Unsafe UNSAFE; + private static final jdk.internal.misc.Unsafe UNSAFE; private static final long THREAD_LOCALS; private static final long INHERITABLE_THREAD_LOCALS; @@ -77,7 +77,7 @@ public class ManagedLocalsThread extends Thread { } static { - UNSAFE = Unsafe.getUnsafe(); + UNSAFE = jdk.internal.misc.Unsafe.getUnsafe(); Class t = Thread.class; try { THREAD_LOCALS = UNSAFE.objectFieldOffset diff --git a/jdk/src/java.base/share/classes/sun/misc/Unsafe.java b/jdk/src/java.base/share/classes/sun/misc/Unsafe.java index b6cca9d8f14..6a4775a4c3a 100644 --- a/jdk/src/java.base/share/classes/sun/misc/Unsafe.java +++ b/jdk/src/java.base/share/classes/sun/misc/Unsafe.java @@ -1036,355 +1036,8 @@ public final class Unsafe { throw new IllegalAccessError(); } - /** - * @return Returns true if the native byte ordering of this - * platform is big-endian, false if it is little-endian. - */ - public final boolean isBigEndian() { return BE; } - - /** - * @return Returns true if this platform is capable of performing - * accesses at addresses which are not aligned for the type of the - * primitive type being accessed, false otherwise. - */ - public final boolean unalignedAccess() { return unalignedAccess; } - - /** - * Fetches a value at some byte offset into a given Java object. - * More specifically, fetches a value within the given object - * o at the given offset, or (if o is - * null) from the memory address whose numerical value is the - * given offset.

    - * - * The specification of this method is the same as {@link - * #getLong(Object, long)} except that the offset does not need to - * have been obtained from {@link #objectFieldOffset} on the - * {@link java.lang.reflect.Field} of some Java field. The value - * in memory is raw data, and need not correspond to any Java - * variable. Unless o is null, the value accessed - * must be entirely within the allocated object. The endianness - * of the value in memory is the endianness of the native platform. - * - *

    The read will be atomic with respect to the largest power - * of two that divides the GCD of the offset and the storage size. - * For example, getLongUnaligned will make atomic reads of 2-, 4-, - * or 8-byte storage units if the offset is zero mod 2, 4, or 8, - * respectively. There are no other guarantees of atomicity. - *

    - * 8-byte atomicity is only guaranteed on platforms on which - * support atomic accesses to longs. - * - * @param o Java heap object in which the value resides, if any, else - * null - * @param offset The offset in bytes from the start of the object - * @return the value fetched from the indicated object - * @throws RuntimeException No defined exceptions are thrown, not even - * {@link NullPointerException} - * @since 1.9 - */ - @HotSpotIntrinsicCandidate - public final long getLongUnaligned(Object o, long offset) { - if ((offset & 7) == 0) { - return getLong(o, offset); - } else if ((offset & 3) == 0) { - return makeLong(getInt(o, offset), - getInt(o, offset + 4)); - } else if ((offset & 1) == 0) { - return makeLong(getShort(o, offset), - getShort(o, offset + 2), - getShort(o, offset + 4), - getShort(o, offset + 6)); - } else { - return makeLong(getByte(o, offset), - getByte(o, offset + 1), - getByte(o, offset + 2), - getByte(o, offset + 3), - getByte(o, offset + 4), - getByte(o, offset + 5), - getByte(o, offset + 6), - getByte(o, offset + 7)); - } - } - /** - * As {@link #getLongUnaligned(Object, long)} but with an - * additional argument which specifies the endianness of the value - * as stored in memory. - * - * @param o Java heap object in which the variable resides - * @param offset The offset in bytes from the start of the object - * @param bigEndian The endianness of the value - * @return the value fetched from the indicated object - * @since 1.9 - */ - public final long getLongUnaligned(Object o, long offset, boolean bigEndian) { - return convEndian(bigEndian, getLongUnaligned(o, offset)); - } - - /** @see #getLongUnaligned(Object, long) */ - @HotSpotIntrinsicCandidate - public final int getIntUnaligned(Object o, long offset) { - if ((offset & 3) == 0) { - return getInt(o, offset); - } else if ((offset & 1) == 0) { - return makeInt(getShort(o, offset), - getShort(o, offset + 2)); - } else { - return makeInt(getByte(o, offset), - getByte(o, offset + 1), - getByte(o, offset + 2), - getByte(o, offset + 3)); - } - } - /** @see #getLongUnaligned(Object, long, boolean) */ - public final int getIntUnaligned(Object o, long offset, boolean bigEndian) { - return convEndian(bigEndian, getIntUnaligned(o, offset)); - } - - /** @see #getLongUnaligned(Object, long) */ - @HotSpotIntrinsicCandidate - public final short getShortUnaligned(Object o, long offset) { - if ((offset & 1) == 0) { - return getShort(o, offset); - } else { - return makeShort(getByte(o, offset), - getByte(o, offset + 1)); - } - } - /** @see #getLongUnaligned(Object, long, boolean) */ - public final short getShortUnaligned(Object o, long offset, boolean bigEndian) { - return convEndian(bigEndian, getShortUnaligned(o, offset)); - } - - /** @see #getLongUnaligned(Object, long) */ - @HotSpotIntrinsicCandidate - public final char getCharUnaligned(Object o, long offset) { - return (char)getShortUnaligned(o, offset); - } - - /** @see #getLongUnaligned(Object, long, boolean) */ - public final char getCharUnaligned(Object o, long offset, boolean bigEndian) { - return convEndian(bigEndian, getCharUnaligned(o, offset)); - } - - /** - * Stores a value at some byte offset into a given Java object. - *

    - * The specification of this method is the same as {@link - * #getLong(Object, long)} except that the offset does not need to - * have been obtained from {@link #objectFieldOffset} on the - * {@link java.lang.reflect.Field} of some Java field. The value - * in memory is raw data, and need not correspond to any Java - * variable. The endianness of the value in memory is the - * endianness of the native platform. - *

    - * The write will be atomic with respect to the largest power of - * two that divides the GCD of the offset and the storage size. - * For example, putLongUnaligned will make atomic writes of 2-, 4-, - * or 8-byte storage units if the offset is zero mod 2, 4, or 8, - * respectively. There are no other guarantees of atomicity. - *

    - * 8-byte atomicity is only guaranteed on platforms on which - * support atomic accesses to longs. - * - * @param o Java heap object in which the value resides, if any, else - * null - * @param offset The offset in bytes from the start of the object - * @param x the value to store - * @throws RuntimeException No defined exceptions are thrown, not even - * {@link NullPointerException} - * @since 1.9 - */ - @HotSpotIntrinsicCandidate - public final void putLongUnaligned(Object o, long offset, long x) { - if ((offset & 7) == 0) { - putLong(o, offset, x); - } else if ((offset & 3) == 0) { - putLongParts(o, offset, - (int)(x >> 0), - (int)(x >>> 32)); - } else if ((offset & 1) == 0) { - putLongParts(o, offset, - (short)(x >>> 0), - (short)(x >>> 16), - (short)(x >>> 32), - (short)(x >>> 48)); - } else { - putLongParts(o, offset, - (byte)(x >>> 0), - (byte)(x >>> 8), - (byte)(x >>> 16), - (byte)(x >>> 24), - (byte)(x >>> 32), - (byte)(x >>> 40), - (byte)(x >>> 48), - (byte)(x >>> 56)); - } - } - - /** - * As {@link #putLongUnaligned(Object, long, long)} but with an additional - * argument which specifies the endianness of the value as stored in memory. - * @param o Java heap object in which the value resides - * @param offset The offset in bytes from the start of the object - * @param x the value to store - * @param bigEndian The endianness of the value - * @throws RuntimeException No defined exceptions are thrown, not even - * {@link NullPointerException} - * @since 1.9 - */ - public final void putLongUnaligned(Object o, long offset, long x, boolean bigEndian) { - putLongUnaligned(o, offset, convEndian(bigEndian, x)); - } - - /** @see #putLongUnaligned(Object, long, long) */ - @HotSpotIntrinsicCandidate - public final void putIntUnaligned(Object o, long offset, int x) { - if ((offset & 3) == 0) { - putInt(o, offset, x); - } else if ((offset & 1) == 0) { - putIntParts(o, offset, - (short)(x >> 0), - (short)(x >>> 16)); - } else { - putIntParts(o, offset, - (byte)(x >>> 0), - (byte)(x >>> 8), - (byte)(x >>> 16), - (byte)(x >>> 24)); - } - } - /** @see #putLongUnaligned(Object, long, long, boolean) */ - public final void putIntUnaligned(Object o, long offset, int x, boolean bigEndian) { - putIntUnaligned(o, offset, convEndian(bigEndian, x)); - } - - /** @see #putLongUnaligned(Object, long, long) */ - @HotSpotIntrinsicCandidate - public final void putShortUnaligned(Object o, long offset, short x) { - if ((offset & 1) == 0) { - putShort(o, offset, x); - } else { - putShortParts(o, offset, - (byte)(x >>> 0), - (byte)(x >>> 8)); - } - } - /** @see #putLongUnaligned(Object, long, long, boolean) */ - public final void putShortUnaligned(Object o, long offset, short x, boolean bigEndian) { - putShortUnaligned(o, offset, convEndian(bigEndian, x)); - } - - /** @see #putLongUnaligned(Object, long, long) */ - @HotSpotIntrinsicCandidate - public final void putCharUnaligned(Object o, long offset, char x) { - putShortUnaligned(o, offset, (short)x); - } - /** @see #putLongUnaligned(Object, long, long, boolean) */ - public final void putCharUnaligned(Object o, long offset, char x, boolean bigEndian) { - putCharUnaligned(o, offset, convEndian(bigEndian, x)); - } - // JVM interface methods private native boolean unalignedAccess0(); private native boolean isBigEndian0(); - // BE is true iff the native endianness of this platform is big. - private static final boolean BE = theUnsafe.isBigEndian0(); - - // unalignedAccess is true iff this platform can perform unaligned accesses. - private static final boolean unalignedAccess = theUnsafe.unalignedAccess0(); - - private static int pickPos(int top, int pos) { return BE ? top - pos : pos; } - - // These methods construct integers from bytes. The byte ordering - // is the native endianness of this platform. - private static long makeLong(byte i0, byte i1, byte i2, byte i3, byte i4, byte i5, byte i6, byte i7) { - return ((toUnsignedLong(i0) << pickPos(56, 0)) - | (toUnsignedLong(i1) << pickPos(56, 8)) - | (toUnsignedLong(i2) << pickPos(56, 16)) - | (toUnsignedLong(i3) << pickPos(56, 24)) - | (toUnsignedLong(i4) << pickPos(56, 32)) - | (toUnsignedLong(i5) << pickPos(56, 40)) - | (toUnsignedLong(i6) << pickPos(56, 48)) - | (toUnsignedLong(i7) << pickPos(56, 56))); - } - private static long makeLong(short i0, short i1, short i2, short i3) { - return ((toUnsignedLong(i0) << pickPos(48, 0)) - | (toUnsignedLong(i1) << pickPos(48, 16)) - | (toUnsignedLong(i2) << pickPos(48, 32)) - | (toUnsignedLong(i3) << pickPos(48, 48))); - } - private static long makeLong(int i0, int i1) { - return (toUnsignedLong(i0) << pickPos(32, 0)) - | (toUnsignedLong(i1) << pickPos(32, 32)); - } - private static int makeInt(short i0, short i1) { - return (toUnsignedInt(i0) << pickPos(16, 0)) - | (toUnsignedInt(i1) << pickPos(16, 16)); - } - private static int makeInt(byte i0, byte i1, byte i2, byte i3) { - return ((toUnsignedInt(i0) << pickPos(24, 0)) - | (toUnsignedInt(i1) << pickPos(24, 8)) - | (toUnsignedInt(i2) << pickPos(24, 16)) - | (toUnsignedInt(i3) << pickPos(24, 24))); - } - private static short makeShort(byte i0, byte i1) { - return (short)((toUnsignedInt(i0) << pickPos(8, 0)) - | (toUnsignedInt(i1) << pickPos(8, 8))); - } - - private static byte pick(byte le, byte be) { return BE ? be : le; } - private static short pick(short le, short be) { return BE ? be : le; } - private static int pick(int le, int be) { return BE ? be : le; } - - // These methods write integers to memory from smaller parts - // provided by their caller. The ordering in which these parts - // are written is the native endianness of this platform. - private void putLongParts(Object o, long offset, byte i0, byte i1, byte i2, byte i3, byte i4, byte i5, byte i6, byte i7) { - putByte(o, offset + 0, pick(i0, i7)); - putByte(o, offset + 1, pick(i1, i6)); - putByte(o, offset + 2, pick(i2, i5)); - putByte(o, offset + 3, pick(i3, i4)); - putByte(o, offset + 4, pick(i4, i3)); - putByte(o, offset + 5, pick(i5, i2)); - putByte(o, offset + 6, pick(i6, i1)); - putByte(o, offset + 7, pick(i7, i0)); - } - private void putLongParts(Object o, long offset, short i0, short i1, short i2, short i3) { - putShort(o, offset + 0, pick(i0, i3)); - putShort(o, offset + 2, pick(i1, i2)); - putShort(o, offset + 4, pick(i2, i1)); - putShort(o, offset + 6, pick(i3, i0)); - } - private void putLongParts(Object o, long offset, int i0, int i1) { - putInt(o, offset + 0, pick(i0, i1)); - putInt(o, offset + 4, pick(i1, i0)); - } - private void putIntParts(Object o, long offset, short i0, short i1) { - putShort(o, offset + 0, pick(i0, i1)); - putShort(o, offset + 2, pick(i1, i0)); - } - private void putIntParts(Object o, long offset, byte i0, byte i1, byte i2, byte i3) { - putByte(o, offset + 0, pick(i0, i3)); - putByte(o, offset + 1, pick(i1, i2)); - putByte(o, offset + 2, pick(i2, i1)); - putByte(o, offset + 3, pick(i3, i0)); - } - private void putShortParts(Object o, long offset, byte i0, byte i1) { - putByte(o, offset + 0, pick(i0, i1)); - putByte(o, offset + 1, pick(i1, i0)); - } - - // Zero-extend an integer - private static int toUnsignedInt(byte n) { return n & 0xff; } - private static int toUnsignedInt(short n) { return n & 0xffff; } - private static long toUnsignedLong(byte n) { return n & 0xffl; } - private static long toUnsignedLong(short n) { return n & 0xffffl; } - private static long toUnsignedLong(int n) { return n & 0xffffffffl; } - - // Maybe byte-reverse an integer - private static char convEndian(boolean big, char n) { return big == BE ? n : Character.reverseBytes(n); } - private static short convEndian(boolean big, short n) { return big == BE ? n : Short.reverseBytes(n) ; } - private static int convEndian(boolean big, int n) { return big == BE ? n : Integer.reverseBytes(n) ; } - private static long convEndian(boolean big, long n) { return big == BE ? n : Long.reverseBytes(n) ; } } diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/NativeObject.java b/jdk/src/java.base/share/classes/sun/nio/ch/NativeObject.java index e4e4366289a..839b64df8ad 100644 --- a/jdk/src/java.base/share/classes/sun/nio/ch/NativeObject.java +++ b/jdk/src/java.base/share/classes/sun/nio/ch/NativeObject.java @@ -29,7 +29,7 @@ package sun.nio.ch; // Formerly in sun.misc import java.nio.ByteOrder; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; // ## In the fullness of time, this class will be eliminated diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java index 614850577e0..fd428149999 100644 --- a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java +++ b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java @@ -35,7 +35,7 @@ import java.nio.channels.*; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.*; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import sun.misc.Cleaner; import sun.security.action.GetPropertyAction; diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java b/jdk/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java index 6b0abe98d4b..0ba73309524 100644 --- a/jdk/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java +++ b/jdk/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java @@ -32,4 +32,8 @@ package sun.nio.cs; public interface ArrayDecoder { int decode(byte[] src, int off, int len, char[] dst); + + default boolean isASCIICompatible() { + return false; + } } diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java b/jdk/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java index 2ef46e7f3bf..b4ced428b33 100644 --- a/jdk/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java +++ b/jdk/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java @@ -26,10 +26,24 @@ package sun.nio.cs; /* - * FastPath char[]->byte[] encoder, REPLACE on malformed input or + * FastPath char[]/byte[] -> byte[] encoder, REPLACE on malformed input or * unmappable input. */ public interface ArrayEncoder { + + // is only used by j.u.zip.ZipCoder for utf8 int encode(char[] src, int off, int len, byte[] dst); + + default int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) { + return -1; + } + + default int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) { + return -1; + } + + default boolean isASCIICompatible() { + return false; + } } diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/DoubleByte.java b/jdk/src/java.base/share/classes/sun/nio/cs/DoubleByte.java index 6121ab282d8..9f8d97fdb39 100644 --- a/jdk/src/java.base/share/classes/sun/nio/cs/DoubleByte.java +++ b/jdk/src/java.base/share/classes/sun/nio/cs/DoubleByte.java @@ -115,6 +115,7 @@ public class DoubleByte { final char[] b2cSB; final int b2Min; final int b2Max; + final boolean isASCIICompatible; // for SimpleEUC override protected CoderResult crMalformedOrUnderFlow(int b) { @@ -132,16 +133,23 @@ public class DoubleByte { public Decoder(Charset cs, float avgcpb, float maxcpb, char[][] b2c, char[] b2cSB, - int b2Min, int b2Max) { + int b2Min, int b2Max, + boolean isASCIICompatible) { super(cs, avgcpb, maxcpb); this.b2c = b2c; this.b2cSB = b2cSB; this.b2Min = b2Min; this.b2Max = b2Max; + this.isASCIICompatible = isASCIICompatible; + } + + public Decoder(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max, + boolean isASCIICompatible) { + this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max, isASCIICompatible); } public Decoder(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { - this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max); + this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max, false); } protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) { @@ -215,6 +223,7 @@ public class DoubleByte { return decodeBufferLoop(src, dst); } + @Override public int decode(byte[] src, int sp, int len, char[] dst) { int dp = 0; int sl = sp + len; @@ -230,12 +239,12 @@ public class DoubleByte { if (b2c[b1] == B2C_UNMAPPABLE || // isNotLeadingByte b2c[b2] != B2C_UNMAPPABLE || // isLeadingByte decodeSingle(b2) != UNMAPPABLE_DECODING) { - sp--; + sp--; } } } if (c == UNMAPPABLE_DECODING) { - c = repl; + c = repl; } } dst[dp++] = c; @@ -243,6 +252,11 @@ public class DoubleByte { return dp; } + @Override + public boolean isASCIICompatible() { + return isASCIICompatible; + } + public void implReset() { super.implReset(); } @@ -274,8 +288,14 @@ public class DoubleByte { private int currentState; public Decoder_EBCDIC(Charset cs, - char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { - super(cs, b2c, b2cSB, b2Min, b2Max); + char[][] b2c, char[] b2cSB, int b2Min, int b2Max, + boolean isASCIICompatible) { + super(cs, b2c, b2cSB, b2Min, b2Max, isASCIICompatible); + } + + public Decoder_EBCDIC(Charset cs, + char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { + super(cs, b2c, b2cSB, b2Min, b2Max, false); } public void implReset() { @@ -403,6 +423,7 @@ public class DoubleByte { } } + @Override public int decode(byte[] src, int sp, int len, char[] dst) { int dp = 0; int sl = sp + len; @@ -451,8 +472,13 @@ public class DoubleByte { b2cSB_UNMAPPABLE = new char[0x100]; Arrays.fill(b2cSB_UNMAPPABLE, UNMAPPABLE_DECODING); } + public Decoder_DBCSONLY(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max, + boolean isASCIICompatible) { + super(cs, 0.5f, 1.0f, b2c, b2cSB_UNMAPPABLE, b2Min, b2Max, isASCIICompatible); + } + public Decoder_DBCSONLY(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { - super(cs, 0.5f, 1.0f, b2c, b2cSB_UNMAPPABLE, b2Min, b2Max); + super(cs, 0.5f, 1.0f, b2c, b2cSB_UNMAPPABLE, b2Min, b2Max, false); } } @@ -464,8 +490,9 @@ public class DoubleByte { private final int SS3 = 0x8F; public Decoder_EUC_SIM(Charset cs, - char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { - super(cs, b2c, b2cSB, b2Min, b2Max); + char[][] b2c, char[] b2cSB, int b2Min, int b2Max, + boolean isASCIICompatible) { + super(cs, b2c, b2cSB, b2Min, b2Max, isASCIICompatible); } // No support provided for G2/G3 for SimpleEUC @@ -481,6 +508,7 @@ public class DoubleByte { return CoderResult.unmappableForLength(2); } + @Override public int decode(byte[] src, int sp, int len, char[] dst) { int dp = 0; int sl = sp + len; @@ -515,17 +543,25 @@ public class DoubleByte { private final char[] c2b; private final char[] c2bIndex; protected Surrogate.Parser sgp; + final boolean isASCIICompatible; public Encoder(Charset cs, char[] c2b, char[] c2bIndex) { + this(cs, c2b, c2bIndex, false); + } + + public Encoder(Charset cs, char[] c2b, char[] c2bIndex, boolean isASCIICompatible) { super(cs, 2.0f, 2.0f); this.c2b = c2b; this.c2bIndex = c2bIndex; + this.isASCIICompatible = isASCIICompatible; } - public Encoder(Charset cs, float avg, float max, byte[] repl, char[] c2b, char[] c2bIndex) { + public Encoder(Charset cs, float avg, float max, byte[] repl, char[] c2b, char[] c2bIndex, + boolean isASCIICompatible) { super(cs, avg, max, repl); this.c2b = c2b; this.c2bIndex = c2bIndex; + this.isASCIICompatible = isASCIICompatible; } public boolean canEncode(char c) { @@ -624,6 +660,7 @@ public class DoubleByte { repl = newReplacement; } + @Override public int encode(char[] src, int sp, int len, byte[] dst) { int dp = 0; int sl = sp + len; @@ -647,11 +684,69 @@ public class DoubleByte { } else { // SingleByte dst[dp++] = (byte)bb; } + } + return dp; + } + + @Override + public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + len; + while (sp < sl) { + char c = (char)(src[sp++] & 0xff); + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + // no surrogate pair in latin1 string + dst[dp++] = repl[0]; + if (repl.length > 1) { + dst[dp++] = repl[1]; + } + continue; + } //else + if (bb > MAX_SINGLEBYTE) { // DoubleByte + dst[dp++] = (byte)(bb >> 8); + dst[dp++] = (byte)bb; + } else { // SingleByte + dst[dp++] = (byte)bb; + } } return dp; } + @Override + public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + len; + while (sp < sl) { + char c = StringUTF16.getChar(src, sp++); + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(StringUTF16.getChar(src, sp))) { + sp++; + } + dst[dp++] = repl[0]; + if (repl.length > 1) { + dst[dp++] = repl[1]; + } + continue; + } //else + if (bb > MAX_SINGLEBYTE) { // DoubleByte + dst[dp++] = (byte)(bb >> 8); + dst[dp++] = (byte)bb; + } else { // SingleByte + dst[dp++] = (byte)bb; + } + } + return dp; + } + + @Override + public boolean isASCIICompatible() { + return isASCIICompatible; + } + public int encodeChar(char ch) { return c2b[c2bIndex[ch >> 8] + (ch & 0xff)]; } @@ -741,9 +836,11 @@ public class DoubleByte { } public static class Encoder_DBCSONLY extends Encoder { + public Encoder_DBCSONLY(Charset cs, byte[] repl, - char[] c2b, char[] c2bIndex) { - super(cs, 2.0f, 2.0f, repl, c2b, c2bIndex); + char[] c2b, char[] c2bIndex, + boolean isASCIICompatible) { + super(cs, 2.0f, 2.0f, repl, c2b, c2bIndex, isASCIICompatible); } public int encodeChar(char ch) { @@ -754,8 +851,6 @@ public class DoubleByte { } } - - public static class Encoder_EBCDIC extends Encoder { static final int SBCS = 0; static final int DBCS = 1; @@ -764,8 +859,9 @@ public class DoubleByte { protected int currentState = SBCS; - public Encoder_EBCDIC(Charset cs, char[] c2b, char[] c2bIndex) { - super(cs, 4.0f, 5.0f, new byte[] {(byte)0x6f}, c2b, c2bIndex); + public Encoder_EBCDIC(Charset cs, char[] c2b, char[] c2bIndex, + boolean isASCIICompatible) { + super(cs, 4.0f, 5.0f, new byte[] {(byte)0x6f}, c2b, c2bIndex, isASCIICompatible); } protected void implReset() { @@ -878,6 +974,7 @@ public class DoubleByte { } } + @Override public int encode(char[] src, int sp, int len, byte[] dst) { int dp = 0; int sl = sp + len; @@ -917,12 +1014,88 @@ public class DoubleByte { } return dp; } + + @Override + public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + len; + while (sp < sl) { + char c = (char)(src[sp++] & 0xff); + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + // no surrogate pair in latin1 string + dst[dp++] = repl[0]; + if (repl.length > 1) + dst[dp++] = repl[1]; + continue; + } //else + if (bb > MAX_SINGLEBYTE) { // DoubleByte + if (currentState == SBCS) { + currentState = DBCS; + dst[dp++] = SO; + } + dst[dp++] = (byte)(bb >> 8); + dst[dp++] = (byte)bb; + } else { // SingleByte + if (currentState == DBCS) { + currentState = SBCS; + dst[dp++] = SI; + } + dst[dp++] = (byte)bb; + } + } + if (currentState == DBCS) { + currentState = SBCS; + dst[dp++] = SI; + } + return dp; + } + + @Override + public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + len; + while (sp < sl) { + char c = StringUTF16.getChar(src, sp++); + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(StringUTF16.getChar(src, sp))) { + sp++; + } + dst[dp++] = repl[0]; + if (repl.length > 1) + dst[dp++] = repl[1]; + continue; + } //else + if (bb > MAX_SINGLEBYTE) { // DoubleByte + if (currentState == SBCS) { + currentState = DBCS; + dst[dp++] = SO; + } + dst[dp++] = (byte)(bb >> 8); + dst[dp++] = (byte)bb; + } else { // SingleByte + if (currentState == DBCS) { + currentState = SBCS; + dst[dp++] = SI; + } + dst[dp++] = (byte)bb; + } + } + if (currentState == DBCS) { + currentState = SBCS; + dst[dp++] = SI; + } + return dp; + } } // EUC_SIMPLE public static class Encoder_EUC_SIM extends Encoder { - public Encoder_EUC_SIM(Charset cs, char[] c2b, char[] c2bIndex) { - super(cs, c2b, c2bIndex); + public Encoder_EUC_SIM(Charset cs, char[] c2b, char[] c2bIndex, + boolean isASCIICompatible) { + super(cs, c2b, c2bIndex, isASCIICompatible); } } diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/HKSCS.java b/jdk/src/java.base/share/classes/sun/nio/cs/HKSCS.java index 773c7a08a36..ed920e74ade 100644 --- a/jdk/src/java.base/share/classes/sun/nio/cs/HKSCS.java +++ b/jdk/src/java.base/share/classes/sun/nio/cs/HKSCS.java @@ -53,7 +53,7 @@ public class HKSCS { // super(cs, 0.5f, 1.0f); // need to extends DoubleByte.Decoder so the // sun.io can use it. this implementation - super(cs, 0.5f, 1.0f, null, null, 0, 0); + super(cs, 0.5f, 1.0f, null, null, 0, 0, true); this.big5Dec = big5Dec; this.b2cBmp = b2cBmp; this.b2cSupp = b2cSupp; @@ -239,7 +239,7 @@ public class HKSCS { char[][] c2bBmp, char[][] c2bSupp) { - super(cs, null, null); + super(cs, null, null, true); this.big5Enc = big5Enc; this.c2bBmp = c2bBmp; this.c2bSupp = c2bSupp; @@ -389,6 +389,33 @@ public class HKSCS { return dp; } + public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + len; + int dl = dst.length; + while (sp < sl) { + char c = StringUTF16.getChar(src, sp++); + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + if (!Character.isHighSurrogate(c) || sp == sl || + !Character.isLowSurrogate(StringUTF16.getChar(src,sp)) || + (bb = encodeSupp(Character.toCodePoint(c, StringUTF16.getChar(src, sp++)))) + == UNMAPPABLE_ENCODING) { + dst[dp++] = repl[0]; + if (repl.length > 1) + dst[dp++] = repl[1]; + continue; + } + } + if (bb > MAX_SINGLEBYTE) { // DoubleByte + dst[dp++] = (byte)(bb >> 8); + dst[dp++] = (byte)bb; + } else { // SingleByte + dst[dp++] = (byte)bb; + } + } + return dp; + } static char[] C2B_UNMAPPABLE = new char[0x100]; static { diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java b/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java index 30181337173..b5d93a8f677 100644 --- a/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java +++ b/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java @@ -132,6 +132,10 @@ class ISO_8859_1 dst[dp++] = (char)(src[sp++] & 0xff); return dp; } + + public boolean isASCIICompatible() { + return true; + } } private static class Encoder extends CharsetEncoder @@ -297,5 +301,9 @@ class ISO_8859_1 } return dp; } + + public boolean isASCIICompatible() { + return true; + } } } diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/SingleByte.java b/jdk/src/java.base/share/classes/sun/nio/cs/SingleByte.java index 6fd6faf331f..29a4246804d 100644 --- a/jdk/src/java.base/share/classes/sun/nio/cs/SingleByte.java +++ b/jdk/src/java.base/share/classes/sun/nio/cs/SingleByte.java @@ -49,10 +49,18 @@ public class SingleByte public static final class Decoder extends CharsetDecoder implements ArrayDecoder { private final char[] b2c; + private final boolean isASCIICompatible; public Decoder(Charset cs, char[] b2c) { super(cs, 1.0f, 1.0f); this.b2c = b2c; + this.isASCIICompatible = false; + } + + public Decoder(Charset cs, char[] b2c, boolean isASCIICompatible) { + super(cs, 1.0f, 1.0f); + this.b2c = b2c; + this.isASCIICompatible = isASCIICompatible; } private CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) { @@ -116,6 +124,7 @@ public class SingleByte repl = newReplacement.charAt(0); } + @Override public int decode(byte[] src, int sp, int len, char[] dst) { if (len > dst.length) len = dst.length; @@ -129,6 +138,11 @@ public class SingleByte } return dp; } + + @Override + public boolean isASCIICompatible() { + return isASCIICompatible; + } } public static final class Encoder extends CharsetEncoder @@ -136,11 +150,13 @@ public class SingleByte private Surrogate.Parser sgp; private final char[] c2b; private final char[] c2bIndex; + private final boolean isASCIICompatible; - public Encoder(Charset cs, char[] c2b, char[] c2bIndex) { + public Encoder(Charset cs, char[] c2b, char[] c2bIndex, boolean isASCIICompatible) { super(cs, 1.0f, 1.0f); this.c2b = c2b; this.c2bIndex = c2bIndex; + this.isASCIICompatible = isASCIICompatible; } public boolean canEncode(char c) { @@ -252,6 +268,51 @@ public class SingleByte } return dp; } + + @Override + public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + Math.min(len, dst.length); + while (sp < sl) { + char c = (char)(src[sp++] & 0xff); + int b = encode(c); + if (b == UNMAPPABLE_ENCODING) { + dst[dp++] = repl; + } else { + dst[dp++] = (byte)b; + } + } + return dp; + } + + @Override + public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + Math.min(len, dst.length); + while (sp < sl) { + char c = StringUTF16.getChar(src, sp++); + int b = encode(c); + if (b != UNMAPPABLE_ENCODING) { + dst[dp++] = (byte)b; + continue; + } + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(StringUTF16.getChar(src, sp))) { + if (len > dst.length) { + sl++; + len--; + } + sp++; + } + dst[dp++] = repl; + } + return dp; + } + + @Override + public boolean isASCIICompatible() { + return isASCIICompatible; + } } // init the c2b and c2bIndex tables from b2c. diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java b/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java new file mode 100644 index 00000000000..f1608cab5d5 --- /dev/null +++ b/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, 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 sun.nio.cs; + +import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; +import static sun.misc.Unsafe.ARRAY_BYTE_INDEX_SCALE; + +class StringUTF16 { + + public static char getChar(byte[] val, int index) { + return unsafe.getChar(val, + ARRAY_BYTE_BASE_OFFSET + ARRAY_BYTE_INDEX_SCALE * index * 2L); + } + + private static final sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); +} diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/US_ASCII.java b/jdk/src/java.base/share/classes/sun/nio/cs/US_ASCII.java index 816a1ac439f..39b7df07d85 100644 --- a/jdk/src/java.base/share/classes/sun/nio/cs/US_ASCII.java +++ b/jdk/src/java.base/share/classes/sun/nio/cs/US_ASCII.java @@ -146,6 +146,10 @@ public class US_ASCII } return dp; } + + public boolean isASCIICompatible() { + return true; + } } private static class Encoder extends CharsetEncoder @@ -259,6 +263,10 @@ public class US_ASCII } return dp; } + + public boolean isASCIICompatible() { + return true; + } } } diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_8.java b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_8.java index 3ee2341f000..06a43400745 100644 --- a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_8.java +++ b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_8.java @@ -549,6 +549,10 @@ class UTF_8 extends Unicode } return dp; } + + public boolean isASCIICompatible() { + return true; + } } private static final class Encoder extends CharsetEncoder @@ -742,5 +746,9 @@ class UTF_8 extends Unicode } return dp; } + + public boolean isASCIICompatible() { + return true; + } } } diff --git a/jdk/src/java.base/share/classes/sun/nio/fs/Cancellable.java b/jdk/src/java.base/share/classes/sun/nio/fs/Cancellable.java index 648d4089ce8..4e283a5f157 100644 --- a/jdk/src/java.base/share/classes/sun/nio/fs/Cancellable.java +++ b/jdk/src/java.base/share/classes/sun/nio/fs/Cancellable.java @@ -26,7 +26,7 @@ package sun.nio.fs; import sun.misc.ManagedLocalsThread; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import java.util.concurrent.ExecutionException; /** diff --git a/jdk/src/java.base/share/classes/sun/nio/fs/NativeBuffer.java b/jdk/src/java.base/share/classes/sun/nio/fs/NativeBuffer.java index 1009bda5de9..8acc0c198a1 100644 --- a/jdk/src/java.base/share/classes/sun/nio/fs/NativeBuffer.java +++ b/jdk/src/java.base/share/classes/sun/nio/fs/NativeBuffer.java @@ -25,7 +25,7 @@ package sun.nio.fs; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import sun.misc.Cleaner; /** diff --git a/jdk/src/java.base/share/classes/sun/nio/fs/NativeBuffers.java b/jdk/src/java.base/share/classes/sun/nio/fs/NativeBuffers.java index 41ae7ea8835..728d96eb2b3 100644 --- a/jdk/src/java.base/share/classes/sun/nio/fs/NativeBuffers.java +++ b/jdk/src/java.base/share/classes/sun/nio/fs/NativeBuffers.java @@ -25,7 +25,7 @@ package sun.nio.fs; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** * Factory for native buffers. diff --git a/jdk/src/java.base/share/classes/sun/reflect/AccessorGenerator.java b/jdk/src/java.base/share/classes/sun/reflect/AccessorGenerator.java index 96f64ec1847..3b06f550f0f 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/AccessorGenerator.java +++ b/jdk/src/java.base/share/classes/sun/reflect/AccessorGenerator.java @@ -26,7 +26,7 @@ package sun.reflect; import java.lang.reflect.*; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** Shared functionality for all accessor generators */ diff --git a/jdk/src/java.base/share/classes/sun/reflect/ClassDefiner.java b/jdk/src/java.base/share/classes/sun/reflect/ClassDefiner.java index 6b1b43c841b..b4d50095939 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/ClassDefiner.java +++ b/jdk/src/java.base/share/classes/sun/reflect/ClassDefiner.java @@ -27,7 +27,7 @@ package sun.reflect; import java.security.AccessController; import java.security.PrivilegedAction; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** Utility class which assists in calling Unsafe.defineClass() by creating a new class loader which delegates to the one needed in diff --git a/jdk/src/java.base/share/classes/sun/reflect/FieldInfo.java b/jdk/src/java.base/share/classes/sun/reflect/FieldInfo.java index 6972155fb10..72abd07b861 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/FieldInfo.java +++ b/jdk/src/java.base/share/classes/sun/reflect/FieldInfo.java @@ -38,7 +38,7 @@ public class FieldInfo { private String signature; private int modifiers; // This is compatible with the old reflection implementation's - // "slot" value to allow sun.misc.Unsafe to work + // "slot" value to allow jdk.internal.misc.Unsafe to work private int slot; // Not really necessary to provide a constructor since the VM diff --git a/jdk/src/java.base/share/classes/sun/reflect/MagicAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/MagicAccessorImpl.java index 59624366141..af1382f6a5e 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/MagicAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/MagicAccessorImpl.java @@ -32,7 +32,7 @@ package sun.reflect; fields and methods of other classes. It is used to hold the code for dynamically-generated FieldAccessorImpl and MethodAccessorImpl subclasses. (Use of the word "unsafe" was avoided in this class's - name to avoid confusion with {@link sun.misc.Unsafe}.)

    + name to avoid confusion with {@link jdk.internal.misc.Unsafe}.)

    The bug fix for 4486457 also necessitated disabling verification for this class and all subclasses, as opposed to just diff --git a/jdk/src/java.base/share/classes/sun/reflect/ReflectionFactory.java b/jdk/src/java.base/share/classes/sun/reflect/ReflectionFactory.java index 35445331168..24aa225b89e 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/ReflectionFactory.java +++ b/jdk/src/java.base/share/classes/sun/reflect/ReflectionFactory.java @@ -44,7 +44,7 @@ import sun.reflect.misc.ReflectUtil; subversion of both the language and the verifier. For this reason, they are all instance methods, and access to the constructor of this factory is guarded by a security check, in similar style to - {@link sun.misc.Unsafe}.

    + {@link jdk.internal.misc.Unsafe}.

    */ public class ReflectionFactory { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeFieldAccessorImpl.java index 8e830999f13..d54c7f8a6f6 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeFieldAccessorImpl.java @@ -27,9 +27,9 @@ package sun.reflect; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; -/** Base class for sun.misc.Unsafe-based FieldAccessors. The +/** Base class for jdk.internal.misc.Unsafe-based FieldAccessors. The observation is that there are only nine types of fields from the standpoint of reflection code: the eight primitive types and Object. Using class Unsafe instead of generated bytecodes saves diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedFieldAccessorImpl.java index a40ca950881..5cf6c5ee5c7 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedFieldAccessorImpl.java @@ -27,10 +27,10 @@ package sun.reflect; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** - * Base class for sun.misc.Unsafe-based FieldAccessors for fields with + * Base class for jdk.internal.misc.Unsafe-based FieldAccessors for fields with * final or volatile qualifiers. These differ from unqualified * versions in that (1) they check for read-only status (2) they use * the volatile forms of Unsafe get/put methods. (When accessed via diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticFieldAccessorImpl.java index 04219f1f49c..74b40571eab 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticFieldAccessorImpl.java @@ -28,9 +28,9 @@ package sun.reflect; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.security.AccessController; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; -/** Base class for sun.misc.Unsafe-based FieldAccessors for final or +/** Base class for jdk.internal.misc.Unsafe-based FieldAccessors for final or static volatile fields. */ abstract class UnsafeQualifiedStaticFieldAccessorImpl diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticFieldAccessorImpl.java index f07a17cdd5b..e87ba31b543 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticFieldAccessorImpl.java @@ -28,9 +28,9 @@ package sun.reflect; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.security.AccessController; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; -/** Base class for sun.misc.Unsafe-based FieldAccessors for static +/** Base class for jdk.internal.misc.Unsafe-based FieldAccessors for static fields. The observation is that there are only nine types of fields from the standpoint of reflection code: the eight primitive types and Object. Using class Unsafe instead of generated diff --git a/jdk/src/java.base/share/classes/sun/reflect/misc/ReflectUtil.java b/jdk/src/java.base/share/classes/sun/reflect/misc/ReflectUtil.java index cee12a029ea..c90a630e2fa 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/misc/ReflectUtil.java +++ b/jdk/src/java.base/share/classes/sun/reflect/misc/ReflectUtil.java @@ -337,7 +337,7 @@ public final class ReflectUtil { /** * Checks if {@code Class cls} is a VM-anonymous class - * as defined by {@link sun.misc.Unsafe#defineAnonymousClass} + * as defined by {@link jdk.internal.misc.Unsafe#defineAnonymousClass} * (not to be confused with a Java Language anonymous inner class). */ public static boolean isVMAnonymousClass(Class cls) { diff --git a/jdk/src/java.base/share/classes/sun/security/provider/ByteArrayAccess.java b/jdk/src/java.base/share/classes/sun/security/provider/ByteArrayAccess.java index e9e0b8f74ac..64f1c9da9fe 100644 --- a/jdk/src/java.base/share/classes/sun/security/provider/ByteArrayAccess.java +++ b/jdk/src/java.base/share/classes/sun/security/provider/ByteArrayAccess.java @@ -30,7 +30,7 @@ import static java.lang.Long.reverseBytes; import java.nio.ByteOrder; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** * Optimized methods for converting between byte[] and int[]/long[], both for diff --git a/jdk/src/java.base/share/classes/sun/security/provider/certpath/AdaptableX509CertSelector.java b/jdk/src/java.base/share/classes/sun/security/provider/certpath/AdaptableX509CertSelector.java index db36c0e4a44..0ad5387b324 100644 --- a/jdk/src/java.base/share/classes/sun/security/provider/certpath/AdaptableX509CertSelector.java +++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/AdaptableX509CertSelector.java @@ -36,9 +36,7 @@ import java.util.Date; import sun.security.util.Debug; import sun.security.util.DerInputStream; -import sun.security.util.DerOutputStream; import sun.security.x509.SerialNumber; -import sun.security.x509.KeyIdentifier; import sun.security.x509.AuthorityKeyIdentifierExtension; /** @@ -131,13 +129,7 @@ class AdaptableX509CertSelector extends X509CertSelector { serial = null; if (ext != null) { - KeyIdentifier akid = (KeyIdentifier)ext.get( - AuthorityKeyIdentifierExtension.KEY_ID); - if (akid != null) { - DerOutputStream derout = new DerOutputStream(); - derout.putOctetString(akid.getIdentifier()); - ski = derout.toByteArray(); - } + ski = ext.getEncodedKeyIdentifier(); SerialNumber asn = (SerialNumber)ext.get( AuthorityKeyIdentifierExtension.SERIAL_NUMBER); if (asn != null) { diff --git a/jdk/src/java.base/share/classes/sun/security/provider/certpath/DistributionPointFetcher.java b/jdk/src/java.base/share/classes/sun/security/provider/certpath/DistributionPointFetcher.java index 0285cc02e4e..95ef350a9d3 100644 --- a/jdk/src/java.base/share/classes/sun/security/provider/certpath/DistributionPointFetcher.java +++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/DistributionPointFetcher.java @@ -33,7 +33,6 @@ import javax.security.auth.x500.X500Principal; import java.util.*; import sun.security.util.Debug; -import sun.security.util.DerOutputStream; import static sun.security.x509.PKIXExtensions.*; import sun.security.x509.*; @@ -607,12 +606,9 @@ public class DistributionPointFetcher { AuthorityKeyIdentifierExtension akidext = crlImpl.getAuthKeyIdExtension(); if (akidext != null) { - KeyIdentifier akid = (KeyIdentifier)akidext.get( - AuthorityKeyIdentifierExtension.KEY_ID); - if (akid != null) { - DerOutputStream derout = new DerOutputStream(); - derout.putOctetString(akid.getIdentifier()); - certSel.setSubjectKeyIdentifier(derout.toByteArray()); + byte[] kid = akidext.getEncodedKeyIdentifier(); + if (kid != null) { + certSel.setSubjectKeyIdentifier(kid); } SerialNumber asn = (SerialNumber)akidext.get( diff --git a/jdk/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java b/jdk/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java index 12c32cae1bc..3c86d6206ff 100644 --- a/jdk/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java +++ b/jdk/src/java.base/share/classes/sun/security/provider/certpath/ForwardBuilder.java @@ -46,9 +46,10 @@ import sun.security.provider.certpath.PKIX.BuilderParams; import sun.security.util.Debug; import sun.security.x509.AccessDescription; import sun.security.x509.AuthorityInfoAccessExtension; +import sun.security.x509.AuthorityKeyIdentifierExtension; import static sun.security.x509.PKIXExtensions.*; import sun.security.x509.X500Name; -import sun.security.x509.AuthorityKeyIdentifierExtension; +import sun.security.x509.X509CertImpl; /** * This class represents a forward builder, which is able to retrieve @@ -69,7 +70,6 @@ class ForwardBuilder extends Builder { private AdaptableX509CertSelector caSelector; private X509CertSelector caTargetSelector; TrustAnchor trustAnchor; - private Comparator comparator; private boolean searchAllCertStores = true; /** @@ -93,7 +93,6 @@ class ForwardBuilder extends Builder { trustedSubjectDNs.add(anchor.getCA()); } } - comparator = new PKIXCertComparator(trustedSubjectDNs); this.searchAllCertStores = searchAllCertStores; } @@ -122,6 +121,8 @@ class ForwardBuilder extends Builder { * As each cert is added, it is sorted based on the PKIXCertComparator * algorithm. */ + Comparator comparator = + new PKIXCertComparator(trustedSubjectDNs, currState.cert); Set certs = new TreeSet<>(comparator); /* @@ -264,14 +265,6 @@ class ForwardBuilder extends Builder { CertPathHelper.setPathToNames (caSelector, currentState.subjectNamesTraversed); - /* - * Facilitate certification path construction with authority - * key identifier and subject key identifier. - */ - AuthorityKeyIdentifierExtension akidext = - currentState.cert.getAuthorityKeyIdentifierExtension(); - caSelector.setSkiAndSerialNumber(akidext); - /* * check the validity period */ @@ -404,41 +397,68 @@ class ForwardBuilder extends Builder { * * Preference order for current cert: * - * 1) Issuer matches a trusted subject + * 1) The key identifier of an AKID extension (if present) in the + * previous certificate matches the key identifier in the SKID extension + * + * 2) Issuer matches a trusted subject * Issuer: ou=D,ou=C,o=B,c=A * - * 2) Issuer is a descendant of a trusted subject (in order of + * 3) Issuer is a descendant of a trusted subject (in order of * number of links to the trusted subject) * a) Issuer: ou=E,ou=D,ou=C,o=B,c=A [links=1] * b) Issuer: ou=F,ou=E,ou=D,ou=C,ou=B,c=A [links=2] * - * 3) Issuer is an ancestor of a trusted subject (in order of number of + * 4) Issuer is an ancestor of a trusted subject (in order of number of * links to the trusted subject) * a) Issuer: ou=C,o=B,c=A [links=1] * b) Issuer: o=B,c=A [links=2] * - * 4) Issuer is in the same namespace as a trusted subject (in order of + * 5) Issuer is in the same namespace as a trusted subject (in order of * number of links to the trusted subject) * a) Issuer: ou=G,ou=C,o=B,c=A [links=2] * b) Issuer: ou=H,o=B,c=A [links=3] * - * 5) Issuer is an ancestor of certificate subject (in order of number + * 6) Issuer is an ancestor of certificate subject (in order of number * of links to the certificate subject) * a) Issuer: ou=K,o=J,c=A * Subject: ou=L,ou=K,o=J,c=A * b) Issuer: o=J,c=A * Subject: ou=L,ou=K,0=J,c=A * - * 6) Any other certificates + * 7) Any other certificates */ static class PKIXCertComparator implements Comparator { static final String METHOD_NME = "PKIXCertComparator.compare()"; private final Set trustedSubjectDNs; + private final X509CertSelector certSkidSelector; - PKIXCertComparator(Set trustedSubjectDNs) { + PKIXCertComparator(Set trustedSubjectDNs, + X509CertImpl previousCert) throws IOException { this.trustedSubjectDNs = trustedSubjectDNs; + this.certSkidSelector = getSelector(previousCert); + } + + /** + * Returns an X509CertSelector for matching on the authority key + * identifier, or null if not applicable. + */ + private X509CertSelector getSelector(X509CertImpl previousCert) + throws IOException { + if (previousCert != null) { + AuthorityKeyIdentifierExtension akidExt = + previousCert.getAuthorityKeyIdentifierExtension(); + if (akidExt != null) { + byte[] skid = akidExt.getEncodedKeyIdentifier(); + if (skid != null) { + X509CertSelector selector = new X509CertSelector(); + selector.setSubjectKeyIdentifier(skid); + return selector; + } + } + } + return null; } /** @@ -462,6 +482,16 @@ class ForwardBuilder extends Builder { // if certs are the same, return 0 if (oCert1.equals(oCert2)) return 0; + // If akid/skid match then it is preferable + if (certSkidSelector != null) { + if (certSkidSelector.match(oCert1)) { + return -1; + } + if (certSkidSelector.match(oCert2)) { + return 1; + } + } + X500Principal cIssuer1 = oCert1.getIssuerX500Principal(); X500Principal cIssuer2 = oCert2.getIssuerX500Principal(); X500Name cIssuer1Name = X500Name.asX500Name(cIssuer1); diff --git a/jdk/src/java.base/share/classes/sun/security/x509/AlgorithmId.java b/jdk/src/java.base/share/classes/sun/security/x509/AlgorithmId.java index 530dc167c00..f84371da80e 100644 --- a/jdk/src/java.base/share/classes/sun/security/x509/AlgorithmId.java +++ b/jdk/src/java.base/share/classes/sun/security/x509/AlgorithmId.java @@ -977,4 +977,69 @@ public class AlgorithmId implements Serializable, DerEncoder { } return null; } + + /** + * Checks if a signature algorithm matches a key algorithm, i.e. a + * signature can be initialized with a key. + * + * @param kAlg must not be null + * @param sAlg must not be null + * @throws IllegalArgumentException if they do not match + */ + public static void checkKeyAndSigAlgMatch(String kAlg, String sAlg) { + String sAlgUp = sAlg.toUpperCase(Locale.US); + if ((sAlgUp.endsWith("WITHRSA") && !kAlg.equalsIgnoreCase("RSA")) || + (sAlgUp.endsWith("WITHECDSA") && !kAlg.equalsIgnoreCase("EC")) || + (sAlgUp.endsWith("WITHDSA") && !kAlg.equalsIgnoreCase("DSA"))) { + throw new IllegalArgumentException( + "key algorithm not compatible with signature algorithm"); + } + } + + /** + * Returns the default signature algorithm for a private key. The digest + * part might evolve with time. Remember to update the spec of + * {@link jdk.security.jarsigner.JarSigner.Builder#getDefaultSignatureAlgorithm(PrivateKey)} + * if updated. + * + * @param k cannot be null + * @return the default alg, might be null if unsupported + */ + public static String getDefaultSigAlgForKey(PrivateKey k) { + switch (k.getAlgorithm().toUpperCase()) { + case "EC": + return ecStrength(KeyUtil.getKeySize(k)) + + "withECDSA"; + case "DSA": + return ifcFfcStrength(KeyUtil.getKeySize(k)) + + "withDSA"; + case "RSA": + return ifcFfcStrength(KeyUtil.getKeySize(k)) + + "withRSA"; + default: + return null; + } + } + + // Values from SP800-57 part 1 rev 3 tables 2 and three + private static String ecStrength (int bitLength) { + if (bitLength >= 512) { // 256 bits of strength + return "SHA512"; + } else if (bitLength >= 384) { // 192 bits of strength + return "SHA384"; + } else { // 128 bits of strength and less + return "SHA256"; + } + } + + // same values for RSA and DSA + private static String ifcFfcStrength (int bitLength) { + if (bitLength > 7680) { // 256 bits + return "SHA512"; + } else if (bitLength > 3072) { // 192 bits + return "SHA384"; + } else { // 128 bits and less + return "SHA256"; + } + } } diff --git a/jdk/src/java.base/share/classes/sun/security/x509/AuthorityKeyIdentifierExtension.java b/jdk/src/java.base/share/classes/sun/security/x509/AuthorityKeyIdentifierExtension.java index 14754b2536c..d3fb3cd5d9f 100644 --- a/jdk/src/java.base/share/classes/sun/security/x509/AuthorityKeyIdentifierExtension.java +++ b/jdk/src/java.base/share/classes/sun/security/x509/AuthorityKeyIdentifierExtension.java @@ -310,4 +310,16 @@ implements CertAttrSet { public String getName() { return (NAME); } + + /** + * Return the encoded key identifier, or null if not specified. + */ + public byte[] getEncodedKeyIdentifier() throws IOException { + if (id != null) { + DerOutputStream derOut = new DerOutputStream(); + id.encode(derOut); + return derOut.toByteArray(); + } + return null; + } } diff --git a/jdk/src/java.base/share/classes/sun/util/logging/LoggingProxy.java b/jdk/src/java.base/share/classes/sun/util/logging/LoggingProxy.java deleted file mode 100644 index c044a5a260e..00000000000 --- a/jdk/src/java.base/share/classes/sun/util/logging/LoggingProxy.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2009, 2013, 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 sun.util.logging; - -/** - * A proxy interface for the java.util.logging support. - * - * @see sun.util.logging.LoggingSupport - */ -public interface LoggingProxy { - // Methods to bridge java.util.logging.Logger methods - public Object getLogger(String name); - - public Object getLevel(Object logger); - - public void setLevel(Object logger, Object newLevel); - - public boolean isLoggable(Object logger, Object level); - - public void log(Object logger, Object level, String msg); - - public void log(Object logger, Object level, String msg, Throwable t); - - public void log(Object logger, Object level, String msg, Object... params); - - // Methods to bridge java.util.logging.LoggingMXBean methods - public java.util.List getLoggerNames(); - - public String getLoggerLevel(String loggerName); - - public void setLoggerLevel(String loggerName, String levelName); - - public String getParentLoggerName(String loggerName); - - // Methods to bridge Level.parse() and Level.getName() method - public Object parseLevel(String levelName); - - public String getLevelName(Object level); - - public int getLevelValue(Object level); - - // return the logging property - public String getProperty(String key); -} diff --git a/jdk/src/java.base/share/classes/sun/util/logging/LoggingSupport.java b/jdk/src/java.base/share/classes/sun/util/logging/LoggingSupport.java deleted file mode 100644 index 3fb06be8541..00000000000 --- a/jdk/src/java.base/share/classes/sun/util/logging/LoggingSupport.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2009, 2015, 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 sun.util.logging; - -import java.lang.reflect.Field; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.time.ZonedDateTime; - -/** - * Internal API to support JRE implementation to detect if the java.util.logging - * support is available but with no dependency on the java.util.logging - * classes. This LoggingSupport class provides several static methods to - * access the java.util.logging functionality that requires the caller - * to ensure that the logging support is {@linkplain #isAvailable available} - * before invoking it. - * - * @see sun.util.logging.PlatformLogger if you want to log messages even - * if the logging support is not available - */ -public class LoggingSupport { - private LoggingSupport() { } - - private static final LoggingProxy proxy = - AccessController.doPrivileged(new PrivilegedAction() { - public LoggingProxy run() { - try { - // create a LoggingProxyImpl instance when - // java.util.logging classes exist - Class c = Class.forName("java.util.logging.LoggingProxyImpl", true, null); - Field f = c.getDeclaredField("INSTANCE"); - f.setAccessible(true); - return (LoggingProxy) f.get(null); - } catch (ClassNotFoundException cnf) { - return null; - } catch (NoSuchFieldException e) { - throw new AssertionError(e); - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } - }}); - - /** - * Returns true if java.util.logging support is available. - */ - public static boolean isAvailable() { - return proxy != null; - } - - private static void ensureAvailable() { - if (proxy == null) - throw new AssertionError("Should not here"); - } - - public static java.util.List getLoggerNames() { - ensureAvailable(); - return proxy.getLoggerNames(); - } - public static String getLoggerLevel(String loggerName) { - ensureAvailable(); - return proxy.getLoggerLevel(loggerName); - } - - public static void setLoggerLevel(String loggerName, String levelName) { - ensureAvailable(); - proxy.setLoggerLevel(loggerName, levelName); - } - - public static String getParentLoggerName(String loggerName) { - ensureAvailable(); - return proxy.getParentLoggerName(loggerName); - } - - public static Object getLogger(String name) { - ensureAvailable(); - return proxy.getLogger(name); - } - - public static Object getLevel(Object logger) { - ensureAvailable(); - return proxy.getLevel(logger); - } - - public static void setLevel(Object logger, Object newLevel) { - ensureAvailable(); - proxy.setLevel(logger, newLevel); - } - - public static boolean isLoggable(Object logger, Object level) { - ensureAvailable(); - return proxy.isLoggable(logger,level); - } - - public static void log(Object logger, Object level, String msg) { - ensureAvailable(); - proxy.log(logger, level, msg); - } - - public static void log(Object logger, Object level, String msg, Throwable t) { - ensureAvailable(); - proxy.log(logger, level, msg, t); - } - - public static void log(Object logger, Object level, String msg, Object... params) { - ensureAvailable(); - proxy.log(logger, level, msg, params); - } - - public static Object parseLevel(String levelName) { - ensureAvailable(); - return proxy.parseLevel(levelName); - } - - public static String getLevelName(Object level) { - ensureAvailable(); - return proxy.getLevelName(level); - } - - public static int getLevelValue(Object level) { - ensureAvailable(); - return proxy.getLevelValue(level); - } - - // Since JDK 9, logging uses java.time to get more precise time stamps. - // It is possible to configure the simple format to print nano seconds (.%1$tN) - // by specifying: - // java.util.logging.SimpleFormatter.format=%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS.%1$tN %1$Tp %2$s%n%4$s: %5$s%6$s%n - // in the logging configuration - private static final String DEFAULT_FORMAT = - "%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%n%4$s: %5$s%6$s%n"; - - private static final String FORMAT_PROP_KEY = "java.util.logging.SimpleFormatter.format"; - public static String getSimpleFormat() { - return getSimpleFormat(true); - } - - // useProxy if true will cause initialization of - // java.util.logging and read its configuration - static String getSimpleFormat(boolean useProxy) { - String format = - AccessController.doPrivileged( - new PrivilegedAction() { - public String run() { - return System.getProperty(FORMAT_PROP_KEY); - } - }); - - if (useProxy && proxy != null && format == null) { - format = proxy.getProperty(FORMAT_PROP_KEY); - } - - if (format != null) { - try { - // validate the user-defined format string - String.format(format, ZonedDateTime.now(), "", "", "", "", ""); - } catch (IllegalArgumentException e) { - // illegal syntax; fall back to the default format - format = DEFAULT_FORMAT; - } - } else { - format = DEFAULT_FORMAT; - } - return format; - } - -} diff --git a/jdk/src/java.base/share/classes/sun/util/logging/PlatformLogger.java b/jdk/src/java.base/share/classes/sun/util/logging/PlatformLogger.java index 66de672d02c..655dc96a299 100644 --- a/jdk/src/java.base/share/classes/sun/util/logging/PlatformLogger.java +++ b/jdk/src/java.base/share/classes/sun/util/logging/PlatformLogger.java @@ -27,20 +27,13 @@ package sun.util.logging; import java.lang.ref.WeakReference; -import java.io.PrintStream; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; -import java.time.ZonedDateTime; import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import jdk.internal.misc.JavaLangAccess; -import jdk.internal.misc.SharedSecrets; +import java.util.ResourceBundle; +import java.util.function.Supplier; +import jdk.internal.logger.LazyLoggers; +import jdk.internal.logger.LoggerWrapper; /** * Platform logger provides an API for the JRE components to log @@ -56,18 +49,28 @@ import jdk.internal.misc.SharedSecrets; * the stack frame information issuing the log message. * * When the logging facility is enabled (at startup or runtime), - * the java.util.logging.Logger will be created for each platform + * the backend logger will be created for each platform * logger and all log messages will be forwarded to the Logger * to handle. * + * The PlatformLogger uses an underlying PlatformLogger.Bridge instance + * obtained by calling {@link PlatformLogger.Bridge#convert PlatformLogger.Bridge.convert(} + * {@link jdk.internal.logger.LazyLoggers#getLazyLogger(java.lang.String, java.lang.Class) + * jdk.internal.logger.LazyLoggers#getLazyLogger(name, PlatformLogger.class))}. + * * Logging facility is "enabled" when one of the following * conditions is met: - * 1) a system property "java.util.logging.config.class" or - * "java.util.logging.config.file" is set - * 2) java.util.logging.LogManager or java.util.logging.Logger - * is referenced that will trigger the logging initialization. + * 1) ServiceLoader.load({@link java.lang.System.LoggerFinder LoggerFinder.class}, + * ClassLoader.getSystemClassLoader()).iterator().hasNext(). + * 2) ServiceLoader.loadInstalled({@link jdk.internal.logger.DefaultLoggerFinder}).iterator().hasNext(), + * and 2.1) a system property "java.util.logging.config.class" or + * "java.util.logging.config.file" is set + * or 2.2) java.util.logging.LogManager or java.util.logging.Logger + * is referenced that will trigger the logging initialization. * * Default logging configuration: + * + * No LoggerFinder service implementation declared * global logging level = INFO * handlers = java.util.logging.ConsoleHandler * java.util.logging.ConsoleHandler.level = INFO @@ -84,71 +87,84 @@ import jdk.internal.misc.SharedSecrets; * The platform loggers are designed for JDK developers use and * this limitation can be workaround with setting * -Djava.util.logging.config.file system property. + *
    + * Calling PlatformLogger.setLevel will not work when there is a custom + * LoggerFinder installed - and as a consequence {@link #setLevel setLevel} + * is now deprecated. * * @since 1.7 */ public class PlatformLogger { - // The integer values must match that of {@code java.util.logging.Level} - // objects. - private static final int OFF = Integer.MAX_VALUE; - private static final int SEVERE = 1000; - private static final int WARNING = 900; - private static final int INFO = 800; - private static final int CONFIG = 700; - private static final int FINE = 500; - private static final int FINER = 400; - private static final int FINEST = 300; - private static final int ALL = Integer.MIN_VALUE; - /** * PlatformLogger logging levels. */ public static enum Level { // The name and value must match that of {@code java.util.logging.Level}s. // Declare in ascending order of the given value for binary search. - ALL, - FINEST, - FINER, - FINE, - CONFIG, - INFO, - WARNING, - SEVERE, - OFF; + ALL(System.Logger.Level.ALL), + FINEST(System.Logger.Level.TRACE), + FINER(System.Logger.Level.TRACE), + FINE(System.Logger.Level.DEBUG), + CONFIG(System.Logger.Level.DEBUG), + INFO(System.Logger.Level.INFO), + WARNING(System.Logger.Level.WARNING), + SEVERE(System.Logger.Level.ERROR), + OFF(System.Logger.Level.OFF); - /** - * Associated java.util.logging.Level lazily initialized in - * JavaLoggerProxy's static initializer only once - * when java.util.logging is available and enabled. - * Only accessed by JavaLoggerProxy. - */ - /* java.util.logging.Level */ Object javaLevel; + final System.Logger.Level systemLevel; + Level(System.Logger.Level systemLevel) { + this.systemLevel = systemLevel; + } + + // The integer values must match that of {@code java.util.logging.Level} + // objects. + private static final int SEVERITY_OFF = Integer.MAX_VALUE; + private static final int SEVERITY_SEVERE = 1000; + private static final int SEVERITY_WARNING = 900; + private static final int SEVERITY_INFO = 800; + private static final int SEVERITY_CONFIG = 700; + private static final int SEVERITY_FINE = 500; + private static final int SEVERITY_FINER = 400; + private static final int SEVERITY_FINEST = 300; + private static final int SEVERITY_ALL = Integer.MIN_VALUE; // ascending order for binary search matching the list of enum constants private static final int[] LEVEL_VALUES = new int[] { - PlatformLogger.ALL, PlatformLogger.FINEST, PlatformLogger.FINER, - PlatformLogger.FINE, PlatformLogger.CONFIG, PlatformLogger.INFO, - PlatformLogger.WARNING, PlatformLogger.SEVERE, PlatformLogger.OFF + SEVERITY_ALL, SEVERITY_FINEST, SEVERITY_FINER, + SEVERITY_FINE, SEVERITY_CONFIG, SEVERITY_INFO, + SEVERITY_WARNING, SEVERITY_SEVERE, SEVERITY_OFF }; + public System.Logger.Level systemLevel() { + return systemLevel; + } + public int intValue() { return LEVEL_VALUES[this.ordinal()]; } - static Level valueOf(int level) { + /** + * Maps a severity value to an effective logger level. + * @param level The severity of the messages that should be + * logged with a logger set to the returned level. + * @return The effective logger level, which is the nearest Level value + * whose severity is greater or equal to the given level. + * For level > SEVERE (OFF excluded), return SEVERE. + */ + public static Level valueOf(int level) { switch (level) { // ordering per the highest occurrences in the jdk source // finest, fine, finer, info first - case PlatformLogger.FINEST : return Level.FINEST; - case PlatformLogger.FINE : return Level.FINE; - case PlatformLogger.FINER : return Level.FINER; - case PlatformLogger.INFO : return Level.INFO; - case PlatformLogger.WARNING : return Level.WARNING; - case PlatformLogger.CONFIG : return Level.CONFIG; - case PlatformLogger.SEVERE : return Level.SEVERE; - case PlatformLogger.OFF : return Level.OFF; - case PlatformLogger.ALL : return Level.ALL; + case SEVERITY_FINEST : return Level.FINEST; + case SEVERITY_FINE : return Level.FINE; + case SEVERITY_FINER : return Level.FINER; + case SEVERITY_INFO : return Level.INFO; + case SEVERITY_WARNING : return Level.WARNING; + case SEVERITY_CONFIG : return Level.CONFIG; + case SEVERITY_SEVERE : return Level.SEVERE; + case SEVERITY_OFF : return Level.OFF; + case SEVERITY_ALL : return Level.ALL; } // return the nearest Level value >= the given level, // for level > SEVERE, return SEVERE and exclude OFF @@ -157,39 +173,110 @@ public class PlatformLogger { } } - private static final Level DEFAULT_LEVEL = Level.INFO; - private static boolean loggingEnabled; - static { - loggingEnabled = AccessController.doPrivileged( - new PrivilegedAction<>() { - public Boolean run() { - String cname = System.getProperty("java.util.logging.config.class"); - String fname = System.getProperty("java.util.logging.config.file"); - return (cname != null || fname != null); - } - }); + /** + * + * The PlatformLogger.Bridge interface is implemented by the System.Logger + * objects returned by our default JUL provider - so that JRE classes using + * PlatformLogger see no difference when JUL is the actual backend. + * + * PlatformLogger is now only a thin adaptation layer over the same + * loggers than returned by java.lang.System.getLogger(String name). + * + * The recommendation for JRE classes going forward is to use + * java.lang.System.getLogger(String name), which will + * use Lazy Loggers when possible and necessary. + * + */ + public static interface Bridge { - // force loading of all JavaLoggerProxy (sub)classes to make JIT de-optimizations - // less probable. Don't initialize JavaLoggerProxy class since - // java.util.logging may not be enabled. - try { - Class.forName("sun.util.logging.PlatformLogger$DefaultLoggerProxy", - false, - PlatformLogger.class.getClassLoader()); - Class.forName("sun.util.logging.PlatformLogger$JavaLoggerProxy", - false, // do not invoke class initializer - PlatformLogger.class.getClassLoader()); - } catch (ClassNotFoundException ex) { - throw new InternalError(ex); + /** + * Gets the name for this platform logger. + * @return the name of the platform logger. + */ + public String getName(); + + /** + * Returns true if a message of the given level would actually + * be logged by this logger. + * @param level the level + * @return whether a message of that level would be logged + */ + public boolean isLoggable(Level level); + public boolean isEnabled(); + + public void log(Level level, String msg); + public void log(Level level, String msg, Throwable thrown); + public void log(Level level, String msg, Object... params); + public void log(Level level, Supplier msgSupplier); + public void log(Level level, Throwable thrown, Supplier msgSupplier); + public void logp(Level level, String sourceClass, String sourceMethod, String msg); + public void logp(Level level, String sourceClass, String sourceMethod, + Supplier msgSupplier); + public void logp(Level level, String sourceClass, String sourceMethod, + String msg, Object... params); + public void logp(Level level, String sourceClass, String sourceMethod, + String msg, Throwable thrown); + public void logp(Level level, String sourceClass, String sourceMethod, + Throwable thrown, Supplier msgSupplier); + public void logrb(Level level, String sourceClass, String sourceMethod, + ResourceBundle bundle, String msg, Object... params); + public void logrb(Level level, String sourceClass, String sourceMethod, + ResourceBundle bundle, String msg, Throwable thrown); + public void logrb(Level level, ResourceBundle bundle, String msg, + Object... params); + public void logrb(Level level, ResourceBundle bundle, String msg, + Throwable thrown); + + + public static Bridge convert(System.Logger logger) { + if (logger instanceof PlatformLogger.Bridge) { + return (Bridge) logger; + } else { + return new LoggerWrapper<>(logger); + } + } + } + + /** + * The {@code PlatformLogger.ConfigurableBridge} interface is used to + * implement the deprecated {@link PlatformLogger#setLevel} method. + * + * PlatformLogger is now only a thin adaptation layer over the same + * loggers than returned by java.lang.System.getLogger(String name). + * + * The recommendation for JRE classes going forward is to use + * java.lang.System.getLogger(String name), which will + * use Lazy Loggers when possible and necessary. + * + */ + public static interface ConfigurableBridge { + + public abstract class LoggerConfiguration { + public abstract Level getPlatformLevel(); + public abstract void setPlatformLevel(Level level); + } + + public default LoggerConfiguration getLoggerConfiguration() { + return null; + } + + public static LoggerConfiguration getLoggerConfiguration(PlatformLogger.Bridge logger) { + if (logger instanceof PlatformLogger.ConfigurableBridge) { + return ((ConfigurableBridge) logger).getLoggerConfiguration(); + } else { + return null; + } } } // Table of known loggers. Maps names to PlatformLoggers. - private static Map> loggers = + private static final Map> loggers = new HashMap<>(); /** * Returns a PlatformLogger of a given name. + * @param name the name of the logger + * @return a PlatformLogger */ public static synchronized PlatformLogger getLogger(String name) { PlatformLogger log = null; @@ -198,56 +285,31 @@ public class PlatformLogger { log = ref.get(); } if (log == null) { - log = new PlatformLogger(name); + log = new PlatformLogger(PlatformLogger.Bridge.convert( + // We pass PlatformLogger.class rather than the actual caller + // because we want PlatformLoggers to be system loggers: we + // won't need to resolve any resource bundles anyway. + // Note: Many unit tests depend on the fact that + // PlatformLogger.getLoggerFromFinder is not caller sensitive. + LazyLoggers.getLazyLogger(name, PlatformLogger.class))); loggers.put(name, new WeakReference<>(log)); } return log; } - /** - * Initialize java.util.logging.Logger objects for all platform loggers. - * This method is called from LogManager.readPrimordialConfiguration(). - */ - public static synchronized void redirectPlatformLoggers() { - if (loggingEnabled || !LoggingSupport.isAvailable()) return; - - loggingEnabled = true; - for (Map.Entry> entry : loggers.entrySet()) { - WeakReference ref = entry.getValue(); - PlatformLogger plog = ref.get(); - if (plog != null) { - plog.redirectToJavaLoggerProxy(); - } - } - } - - /** - * Creates a new JavaLoggerProxy and redirects the platform logger to it - */ - private void redirectToJavaLoggerProxy() { - DefaultLoggerProxy lp = DefaultLoggerProxy.class.cast(this.loggerProxy); - JavaLoggerProxy jlp = new JavaLoggerProxy(lp.name, lp.level); - // the order of assignments is important - this.javaLoggerProxy = jlp; // isLoggable checks javaLoggerProxy if set - this.loggerProxy = jlp; - } - - // DefaultLoggerProxy may be replaced with a JavaLoggerProxy object - // when the java.util.logging facility is enabled - private volatile LoggerProxy loggerProxy; - // javaLoggerProxy is only set when the java.util.logging facility is enabled - private volatile JavaLoggerProxy javaLoggerProxy; - private PlatformLogger(String name) { - if (loggingEnabled) { - this.loggerProxy = this.javaLoggerProxy = new JavaLoggerProxy(name); - } else { - this.loggerProxy = new DefaultLoggerProxy(name); - } + // The system loggerProxy returned by LazyLoggers + // This may be a lazy logger - see jdk.internal.logger.LazyLoggers, + // or may be a Logger instance (or a wrapper thereof). + // + private final PlatformLogger.Bridge loggerProxy; + private PlatformLogger(PlatformLogger.Bridge loggerProxy) { + this.loggerProxy = loggerProxy; } /** * A convenience method to test if the logger is turned off. * (i.e. its level is OFF). + * @return whether the logger is turned off. */ public boolean isEnabled() { return loggerProxy.isEnabled(); @@ -255,22 +317,24 @@ public class PlatformLogger { /** * Gets the name for this platform logger. + * @return the name of the platform logger. */ public String getName() { - return loggerProxy.name; + return loggerProxy.getName(); } /** * Returns true if a message of the given level would actually * be logged by this logger. + * @param level the level + * @return whether a message of that level would be logged */ public boolean isLoggable(Level level) { if (level == null) { throw new NullPointerException(); } - // performance-sensitive method: use two monomorphic call-sites - JavaLoggerProxy jlp = javaLoggerProxy; - return jlp != null ? jlp.isLoggable(level) : loggerProxy.isLoggable(level); + + return loggerProxy.isLoggable(level); } /** @@ -281,13 +345,15 @@ public class PlatformLogger { * @return this PlatformLogger's level */ public Level level() { - return loggerProxy.getLevel(); + final ConfigurableBridge.LoggerConfiguration spi = + PlatformLogger.ConfigurableBridge.getLoggerConfiguration(loggerProxy); + return spi == null ? null : spi.getPlatformLevel(); } /** * Set the log level specifying which message levels will be * logged by this logger. Message levels lower than this - * value will be discarded. The level value {@link #OFF} + * value will be discarded. The level value {@link Level#OFF} * can be used to turn off logging. *

    * If the new level is null, it means that this node should @@ -295,366 +361,153 @@ public class PlatformLogger { * (non-null) level value. * * @param newLevel the new value for the log level (may be null) + * @deprecated Platform Loggers should not be configured programmatically. + * This method will not work if a custom {@link + * java.lang.System.LoggerFinder} is installed. */ + @Deprecated public void setLevel(Level newLevel) { - loggerProxy.setLevel(newLevel); + final ConfigurableBridge.LoggerConfiguration spi = + PlatformLogger.ConfigurableBridge.getLoggerConfiguration(loggerProxy);; + if (spi != null) { + spi.setPlatformLevel(newLevel); + } } /** * Logs a SEVERE message. + * @param msg the message */ public void severe(String msg) { - loggerProxy.doLog(Level.SEVERE, msg); + loggerProxy.log(Level.SEVERE, msg, (Object[])null); } public void severe(String msg, Throwable t) { - loggerProxy.doLog(Level.SEVERE, msg, t); + loggerProxy.log(Level.SEVERE, msg, t); } public void severe(String msg, Object... params) { - loggerProxy.doLog(Level.SEVERE, msg, params); + loggerProxy.log(Level.SEVERE, msg, params); } /** * Logs a WARNING message. + * @param msg the message */ public void warning(String msg) { - loggerProxy.doLog(Level.WARNING, msg); + loggerProxy.log(Level.WARNING, msg, (Object[])null); } public void warning(String msg, Throwable t) { - loggerProxy.doLog(Level.WARNING, msg, t); + loggerProxy.log(Level.WARNING, msg, t); } public void warning(String msg, Object... params) { - loggerProxy.doLog(Level.WARNING, msg, params); + loggerProxy.log(Level.WARNING, msg, params); } /** * Logs an INFO message. + * @param msg the message */ public void info(String msg) { - loggerProxy.doLog(Level.INFO, msg); + loggerProxy.log(Level.INFO, msg, (Object[])null); } public void info(String msg, Throwable t) { - loggerProxy.doLog(Level.INFO, msg, t); + loggerProxy.log(Level.INFO, msg, t); } public void info(String msg, Object... params) { - loggerProxy.doLog(Level.INFO, msg, params); + loggerProxy.log(Level.INFO, msg, params); } /** * Logs a CONFIG message. + * @param msg the message */ public void config(String msg) { - loggerProxy.doLog(Level.CONFIG, msg); + loggerProxy.log(Level.CONFIG, msg, (Object[])null); } public void config(String msg, Throwable t) { - loggerProxy.doLog(Level.CONFIG, msg, t); + loggerProxy.log(Level.CONFIG, msg, t); } public void config(String msg, Object... params) { - loggerProxy.doLog(Level.CONFIG, msg, params); + loggerProxy.log(Level.CONFIG, msg, params); } /** * Logs a FINE message. + * @param msg the message */ public void fine(String msg) { - loggerProxy.doLog(Level.FINE, msg); + loggerProxy.log(Level.FINE, msg, (Object[])null); } public void fine(String msg, Throwable t) { - loggerProxy.doLog(Level.FINE, msg, t); + loggerProxy.log(Level.FINE, msg, t); } public void fine(String msg, Object... params) { - loggerProxy.doLog(Level.FINE, msg, params); + loggerProxy.log(Level.FINE, msg, params); } /** * Logs a FINER message. + * @param msg the message */ public void finer(String msg) { - loggerProxy.doLog(Level.FINER, msg); + loggerProxy.log(Level.FINER, msg, (Object[])null); } public void finer(String msg, Throwable t) { - loggerProxy.doLog(Level.FINER, msg, t); + loggerProxy.log(Level.FINER, msg, t); } public void finer(String msg, Object... params) { - loggerProxy.doLog(Level.FINER, msg, params); + loggerProxy.log(Level.FINER, msg, params); } /** * Logs a FINEST message. + * @param msg the message */ public void finest(String msg) { - loggerProxy.doLog(Level.FINEST, msg); + loggerProxy.log(Level.FINEST, msg, (Object[])null); } public void finest(String msg, Throwable t) { - loggerProxy.doLog(Level.FINEST, msg, t); + loggerProxy.log(Level.FINEST, msg, t); } public void finest(String msg, Object... params) { - loggerProxy.doLog(Level.FINEST, msg, params); + loggerProxy.log(Level.FINEST, msg, params); } - /** - * Abstract base class for logging support, defining the API and common field. - */ - private abstract static class LoggerProxy { - final String name; + // ------------------------------------ + // Maps used for Level conversion + // ------------------------------------ - protected LoggerProxy(String name) { - this.name = name; - } + // This map is indexed by java.util.spi.Logger.Level.ordinal() and returns + // a PlatformLogger.Level + // + // ALL, TRACE, DEBUG, INFO, WARNING, ERROR, OFF + private static final Level[] spi2platformLevelMapping = { + Level.ALL, // mapped from ALL + Level.FINER, // mapped from TRACE + Level.FINE, // mapped from DEBUG + Level.INFO, // mapped from INFO + Level.WARNING, // mapped from WARNING + Level.SEVERE, // mapped from ERROR + Level.OFF // mapped from OFF + }; - abstract boolean isEnabled(); - - abstract Level getLevel(); - abstract void setLevel(Level newLevel); - - abstract void doLog(Level level, String msg); - abstract void doLog(Level level, String msg, Throwable thrown); - abstract void doLog(Level level, String msg, Object... params); - - abstract boolean isLoggable(Level level); + public static Level toPlatformLevel(java.lang.System.Logger.Level level) { + if (level == null) return null; + assert level.ordinal() < spi2platformLevelMapping.length; + return spi2platformLevelMapping[level.ordinal()]; } - - private static final class DefaultLoggerProxy extends LoggerProxy { - /** - * Default platform logging support - output messages to System.err - - * equivalent to ConsoleHandler with SimpleFormatter. - */ - private static PrintStream outputStream() { - return System.err; - } - - volatile Level effectiveLevel; // effective level (never null) - volatile Level level; // current level set for this node (may be null) - - DefaultLoggerProxy(String name) { - super(name); - this.effectiveLevel = deriveEffectiveLevel(null); - this.level = null; - } - - boolean isEnabled() { - return effectiveLevel != Level.OFF; - } - - Level getLevel() { - return level; - } - - void setLevel(Level newLevel) { - Level oldLevel = level; - if (oldLevel != newLevel) { - level = newLevel; - effectiveLevel = deriveEffectiveLevel(newLevel); - } - } - - void doLog(Level level, String msg) { - if (isLoggable(level)) { - outputStream().print(format(level, msg, null)); - } - } - - void doLog(Level level, String msg, Throwable thrown) { - if (isLoggable(level)) { - outputStream().print(format(level, msg, thrown)); - } - } - - void doLog(Level level, String msg, Object... params) { - if (isLoggable(level)) { - String newMsg = formatMessage(msg, params); - outputStream().print(format(level, newMsg, null)); - } - } - - boolean isLoggable(Level level) { - Level effectiveLevel = this.effectiveLevel; - return level.intValue() >= effectiveLevel.intValue() && effectiveLevel != Level.OFF; - } - - // derive effective level (could do inheritance search like j.u.l.Logger) - private Level deriveEffectiveLevel(Level level) { - return level == null ? DEFAULT_LEVEL : level; - } - - // Copied from java.util.logging.Formatter.formatMessage - private String formatMessage(String format, Object... parameters) { - // Do the formatting. - try { - if (parameters == null || parameters.length == 0) { - // No parameters. Just return format string. - return format; - } - // Is it a java.text style format? - // Ideally we could match with - // Pattern.compile("\\{\\d").matcher(format).find()) - // However the cost is 14% higher, so we cheaply check for - // 1 of the first 4 parameters - if (format.indexOf("{0") >= 0 || format.indexOf("{1") >=0 || - format.indexOf("{2") >=0|| format.indexOf("{3") >=0) { - return java.text.MessageFormat.format(format, parameters); - } - return format; - } catch (Exception ex) { - // Formatting failed: use format string. - return format; - } - } - - private static final String formatString = - LoggingSupport.getSimpleFormat(false); // don't check logging.properties - private final ZoneId zoneId = ZoneId.systemDefault(); - private synchronized String format(Level level, String msg, Throwable thrown) { - ZonedDateTime zdt = ZonedDateTime.now(zoneId); - String throwable = ""; - if (thrown != null) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - pw.println(); - thrown.printStackTrace(pw); - pw.close(); - throwable = sw.toString(); - } - - return String.format(formatString, - zdt, - getCallerInfo(), - name, - level.name(), - msg, - throwable); - } - - // Returns the caller's class and method's name; best effort - // if cannot infer, return the logger's name. - private String getCallerInfo() { - String sourceClassName = null; - String sourceMethodName = null; - - JavaLangAccess access = SharedSecrets.getJavaLangAccess(); - Throwable throwable = new Throwable(); - int depth = access.getStackTraceDepth(throwable); - - String logClassName = "sun.util.logging.PlatformLogger"; - boolean lookingForLogger = true; - for (int ix = 0; ix < depth; ix++) { - // Calling getStackTraceElement directly prevents the VM - // from paying the cost of building the entire stack frame. - StackTraceElement frame = - access.getStackTraceElement(throwable, ix); - String cname = frame.getClassName(); - if (lookingForLogger) { - // Skip all frames until we have found the first logger frame. - if (cname.equals(logClassName)) { - lookingForLogger = false; - } - } else { - if (!cname.equals(logClassName)) { - // We've found the relevant frame. - sourceClassName = cname; - sourceMethodName = frame.getMethodName(); - break; - } - } - } - - if (sourceClassName != null) { - return sourceClassName + " " + sourceMethodName; - } else { - return name; - } - } - } - - /** - * JavaLoggerProxy forwards all the calls to its corresponding - * java.util.logging.Logger object. - */ - private static final class JavaLoggerProxy extends LoggerProxy { - // initialize javaLevel fields for mapping from Level enum -> j.u.l.Level object - static { - for (Level level : Level.values()) { - level.javaLevel = LoggingSupport.parseLevel(level.name()); - } - } - - private final /* java.util.logging.Logger */ Object javaLogger; - - JavaLoggerProxy(String name) { - this(name, null); - } - - JavaLoggerProxy(String name, Level level) { - super(name); - this.javaLogger = LoggingSupport.getLogger(name); - if (level != null) { - // level has been updated and so set the Logger's level - LoggingSupport.setLevel(javaLogger, level.javaLevel); - } - } - - void doLog(Level level, String msg) { - LoggingSupport.log(javaLogger, level.javaLevel, msg); - } - - void doLog(Level level, String msg, Throwable t) { - LoggingSupport.log(javaLogger, level.javaLevel, msg, t); - } - - void doLog(Level level, String msg, Object... params) { - if (!isLoggable(level)) { - return; - } - // only pass String objects to the j.u.l.Logger which may - // be created by untrusted code - int len = (params != null) ? params.length : 0; - Object[] sparams = new String[len]; - for (int i = 0; i < len; i++) { - sparams [i] = String.valueOf(params[i]); - } - LoggingSupport.log(javaLogger, level.javaLevel, msg, sparams); - } - - boolean isEnabled() { - return LoggingSupport.isLoggable(javaLogger, Level.OFF.javaLevel); - } - - /** - * Returns the PlatformLogger.Level mapped from j.u.l.Level - * set in the logger. If the j.u.l.Logger is set to a custom Level, - * this method will return the nearest Level. - */ - Level getLevel() { - Object javaLevel = LoggingSupport.getLevel(javaLogger); - if (javaLevel == null) return null; - - try { - return Level.valueOf(LoggingSupport.getLevelName(javaLevel)); - } catch (IllegalArgumentException e) { - return Level.valueOf(LoggingSupport.getLevelValue(javaLevel)); - } - } - - void setLevel(Level level) { - LoggingSupport.setLevel(javaLogger, level == null ? null : level.javaLevel); - } - - boolean isLoggable(Level level) { - return LoggingSupport.isLoggable(javaLogger, level.javaLevel); - } - } } diff --git a/jdk/src/java.base/share/native/libjava/String.c b/jdk/src/java.base/share/native/libjava/String.c index 7c8170ed3f8..cc7f740618f 100644 --- a/jdk/src/java.base/share/native/libjava/String.c +++ b/jdk/src/java.base/share/native/libjava/String.c @@ -31,3 +31,14 @@ Java_java_lang_String_intern(JNIEnv *env, jobject this) { return JVM_InternString(env, this); } + +JNIEXPORT jboolean JNICALL +Java_java_lang_StringUTF16_isBigEndian(JNIEnv *env, jclass cls) +{ + unsigned int endianTest = 0xff000000; + if (((char*)(&endianTest))[0] != 0) { + return JNI_TRUE; + } else { + return JNI_FALSE; + } +} diff --git a/jdk/src/java.base/share/native/libjava/check_version.c b/jdk/src/java.base/share/native/libjava/check_version.c index b92bd63cdb4..6d757a0b5f2 100644 --- a/jdk/src/java.base/share/native/libjava/check_version.c +++ b/jdk/src/java.base/share/native/libjava/check_version.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2015, 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 @@ -24,10 +24,11 @@ */ #include "jni.h" +#include "jni_util.h" #include "jvm.h" JNIEXPORT jint JNICALL -JNI_OnLoad(JavaVM *vm, void *reserved) +DEF_JNI_OnLoad(JavaVM *vm, void *reserved) { jint vm_version = JVM_GetInterfaceVersion(); if (vm_version != JVM_INTERFACE_VERSION) { diff --git a/jdk/src/java.base/share/native/libjava/jio.c b/jdk/src/java.base/share/native/libjava/jio.c index 8a9cb44fe3e..efa327c2712 100644 --- a/jdk/src/java.base/share/native/libjava/jio.c +++ b/jdk/src/java.base/share/native/libjava/jio.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2015, 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 @@ -27,6 +27,7 @@ #include "jni.h" +#ifndef STATIC_BUILD /* This is a temporary solution until we figure out how to let native * libraries use jio_* without linking with the VM. @@ -63,3 +64,6 @@ jio_fprintf(FILE *fp, const char *fmt, ...) return len; } + +#endif + diff --git a/jdk/src/java.base/share/native/libjava/jni_util.h b/jdk/src/java.base/share/native/libjava/jni_util.h index 0db1f1099c8..96796571dc7 100644 --- a/jdk/src/java.base/share/native/libjava/jni_util.h +++ b/jdk/src/java.base/share/native/libjava/jni_util.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2015, 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 @@ -389,6 +389,117 @@ void buildJniFunctionName(const char *sym, const char *cname, extern size_t getLastErrorString(char *buf, size_t len); extern int getErrorString(int err, char *buf, size_t len); + +#ifdef STATIC_BUILD +/* Macros for handling declaration of static/dynamic + * JNI library Load/Unload functions + * + * Use DEF_JNI_On{Un}Load when you want a static and non-static entry points. + * Use DEF_STATIC_JNI_On{Un}Load when you only want a static one. + * + * LIBRARY_NAME must be set to the name of the library + */ + +/* These three macros are needed to get proper concatenation of + * the LIBRARY_NAME + * + * NOTE: LIBRARY_NAME must be set for static builds. + */ +#define ADD_LIB_NAME3(name, lib) name ## lib +#define ADD_LIB_NAME2(name, lib) ADD_LIB_NAME3(name, lib) +#define ADD_LIB_NAME(entry) ADD_LIB_NAME2(entry, LIBRARY_NAME) + +#define DEF_JNI_OnLoad \ +ADD_LIB_NAME(JNI_OnLoad_)(JavaVM *vm, void *reserved) \ +{ \ + jint JNICALL ADD_LIB_NAME(JNI_OnLoad_dynamic_)(JavaVM *vm, void *reserved); \ + ADD_LIB_NAME(JNI_OnLoad_dynamic_)(vm, reserved); \ + return JNI_VERSION_1_8; \ +} \ +jint JNICALL ADD_LIB_NAME(JNI_OnLoad_dynamic_) + +#define DEF_STATIC_JNI_OnLoad \ +JNIEXPORT jint JNICALL ADD_LIB_NAME(JNI_OnLoad_)(JavaVM *vm, void *reserved) { \ + return JNI_VERSION_1_8; \ +} + +#define DEF_JNI_OnUnload \ +ADD_LIB_NAME(JNI_OnUnload_)(JavaVM *vm, void *reserved) \ +{ \ + void JNICALL ADD_LIB_NAME(JNI_OnUnload_dynamic_)(JavaVM *vm, void *reserved); \ + ADD_LIB_NAME(JNI_OnUnload_dynamic_)(vm, reserved); \ +} \ +void JNICALL ADD_LIB_NAME(JNI_OnUnload_dynamic_) + +#define DEF_STATIC_JNI_OnUnload \ +ADD_LIB_NAME(JNI_OnUnload_) + +#else + +#define DEF_JNI_OnLoad JNI_OnLoad +#define DEF_STATIC_JNI_OnLoad +#define DEF_JNI_OnUnload JNI_OnUnload +#define DEF_STATIC_JNI_OnUnload +#endif + +#ifdef STATIC_BUILD +/* Macros for handling declaration of static/dynamic + * Agent library Load/Attach/Unload functions + * + * Use DEF_Agent_OnLoad, DEF_Agent_OnAttach or DEF_Agent_OnUnload + * when you want both static and non-static entry points. + * Use DEF_STATIC_Agent_OnLoad, DEF_STATIC_Agent_OnAttach or + * DEF_STATIC_Agent_OnUnload when you only want a static one. + * + * LIBRARY_NAME must be set to the name of the library for static builds. + */ + +#define DEF_Agent_OnLoad \ +ADD_LIB_NAME(Agent_OnLoad_)(JavaVM *vm, char *options, void *reserved) \ +{ \ + jint JNICALL ADD_LIB_NAME(Agent_OnLoad_dynamic_)(JavaVM *vm, char *options, void *reserved); \ + return ADD_LIB_NAME(Agent_OnLoad_dynamic_)(vm, options, reserved); \ +} \ +jint JNICALL ADD_LIB_NAME(Agent_OnLoad_dynamic_) + +#define DEF_STATIC_Agent_OnLoad \ +JNIEXPORT jint JNICALL ADD_LIB_NAME(Agent_OnLoad_)(JavaVM *vm, char *options, void *reserved) { \ + return JNI_FALSE; \ +} + +#define DEF_Agent_OnAttach \ +ADD_LIB_NAME(Agent_OnAttach_)(JavaVM *vm, char *options, void *reserved) \ +{ \ + jint JNICALL ADD_LIB_NAME(Agent_OnAttach_dynamic_)(JavaVM *vm, char *options, void *reserved); \ + return ADD_LIB_NAME(Agent_OnAttach_dynamic_)(vm, options, reserved); \ +} \ +jint JNICALL ADD_LIB_NAME(Agent_OnAttach_dynamic_) + +#define DEF_STATIC_Agent_OnAttach \ +JNIEXPORT jint JNICALL ADD_LIB_NAME(Agent_OnLoad_)(JavaVM *vm, char *options, void *reserved) { \ + return JNI_FALSE; \ +} + +#define DEF_Agent_OnUnload \ +ADD_LIB_NAME(Agent_OnUnload_)(JavaVM *vm) \ +{ \ + void JNICALL ADD_LIB_NAME(Agent_OnUnload_dynamic_)(JavaVM *vm); \ + ADD_LIB_NAME(Agent_OnUnload_dynamic_)(vm); \ +} \ +void JNICALL ADD_LIB_NAME(Agent_OnUnload_dynamic_) + +#define DEF_STATIC_Agent_OnUnload \ +ADD_LIB_NAME(Agent_OnUnload_) + +#else +#define DEF_Agent_OnLoad Agent_OnLoad +#define DEF_Agent_OnAttach Agent_OnAttach +#define DEF_Agent_OnUnload Agent_OnUnload +#define DEF_STATIC_Agent_OnLoad +#define DEF_STATIC_Agent_OnAttach +#define DEF_STATIC_Agent_OnUnload +#endif + #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ diff --git a/jdk/src/java.base/share/native/libjimage/ImageNativeSubstrate.cpp b/jdk/src/java.base/share/native/libjimage/ImageNativeSubstrate.cpp index 0f803cb4fa9..32aa7d0c29f 100644 --- a/jdk/src/java.base/share/native/libjimage/ImageNativeSubstrate.cpp +++ b/jdk/src/java.base/share/native/libjimage/ImageNativeSubstrate.cpp @@ -26,6 +26,7 @@ #include #include "jni.h" +#include "jni_util.h" #include "endian.hpp" #include "imageDecompressor.hpp" @@ -201,6 +202,9 @@ static jlong* JIMAGE_FindAttributes(JNIEnv *env, jlong* rawAttributes, jbyte* ra if (reader == NULL) return NULL; // Convert byte array to a cstring. char* path = new char[size + 1]; + if (path == NULL) { + return NULL; + } memcpy(path, rawBytes, size); path[size] = '\0'; // Locate resource location data. @@ -246,7 +250,7 @@ static unsigned int JIMAGE_AttributeOffsetsLength(JNIEnv *env, jlong id) { } JNIEXPORT jint JNICALL -JNI_OnLoad(JavaVM *vm, void *reserved) { +DEF_JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env; if (vm->GetEnv((void**) &env, JNI_VERSION_1_2) != JNI_OK) { @@ -646,6 +650,6 @@ JNIEXPORT jstring JNICALL Java_jdk_internal_jimage_ImageNativeSubstrate_JIMAGE_1 return module; } -JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { +JNIEXPORT void JNICALL DEF_JNI_OnUnload(JavaVM *vm, void *reserved) { ImageDecompressor::image_decompressor_close(); } diff --git a/jdk/src/java.base/share/native/libjimage/imageFile.cpp b/jdk/src/java.base/share/native/libjimage/imageFile.cpp index 1e067f2cdd4..b3dfe4e670f 100644 --- a/jdk/src/java.base/share/native/libjimage/imageFile.cpp +++ b/jdk/src/java.base/share/native/libjimage/imageFile.cpp @@ -118,7 +118,7 @@ void ImageLocation::set_data(u1* data) { // Deflate the attribute stream into an array of attributes. u1 byte; // Repeat until end header is found. - while ((byte = *data)) { + while ((data != NULL) && (byte = *data)) { // Extract kind from header byte. u1 kind = attribute_kind(byte); assert(kind < ATTRIBUTE_COUNT && "invalid image location attribute"); @@ -149,6 +149,7 @@ ImageModuleData::ImageModuleData(const ImageFileReader* image_file, if (found) { u8 data_size = location.get_attribute(ImageLocation::ATTRIBUTE_UNCOMPRESSED); _data = new u1[(size_t)data_size]; + assert(_data != NULL && "allocation failed"); _image_file->get_resource(location, _data); // Map out the header. _header = (Header*)_data; @@ -191,7 +192,7 @@ ImageModuleData::ImageModuleData(const ImageFileReader* image_file, // Release module data resource. ImageModuleData::~ImageModuleData() { if (_data) { - delete _data; + delete[] _data; } } @@ -254,6 +255,7 @@ const char** ImageModuleData::module_to_packages(const char* module_name) { // Construct an array of all the package entries. u4 count = data->package_count(_endian); const char** packages = new const char*[count + 1]; + assert(packages != NULL && "allocation failed"); s4 package_offset = data->package_offset(_endian); for (u4 i = 0; i < count; i++) { u4 package_name_offset = mtp_package(package_offset + i); @@ -271,10 +273,11 @@ const char** ImageModuleData::module_to_packages(const char* module_name) { // to share an open image. ImageFileReaderTable::ImageFileReaderTable() : _count(0), _max(_growth) { _table = new ImageFileReader*[_max]; + assert( _table != NULL && "allocation failed"); } ImageFileReaderTable::~ImageFileReaderTable() { - delete _table; + delete[] _table; } // Add a new image entry to the table. @@ -330,6 +333,7 @@ ImageFileReader* ImageFileReader::open(const char* name, bool big_endian) { // Retrieve table entry. ImageFileReader* reader = _reader_table.get(i); // If name matches, then reuse (bump up use count.) + assert(reader->name() != NULL && "reader->name must not be null"); if (strcmp(reader->name(), name) == 0) { reader->inc_use(); return reader; @@ -339,20 +343,20 @@ ImageFileReader* ImageFileReader::open(const char* name, bool big_endian) { // Need a new image reader. ImageFileReader* reader = new ImageFileReader(name, big_endian); - bool opened = reader->open(); - // If failed to open. - if (!opened) { + if (reader == NULL || !reader->open()) { + // Failed to open. delete reader; return NULL; } // Lock to update SimpleCriticalSectionLock cs(&_reader_table_lock); - // Search for an exist image file. + // Search for an existing image file. for (u4 i = 0; i < _reader_table.count(); i++) { // Retrieve table entry. ImageFileReader* existing_reader = _reader_table.get(i); // If name matches, then reuse (bump up use count.) + assert(reader->name() != NULL && "reader->name still must not be null"); if (strcmp(existing_reader->name(), name) == 0) { existing_reader->inc_use(); reader->close(); @@ -401,6 +405,7 @@ ImageFileReader::ImageFileReader(const char* name, bool big_endian) { // Copy the image file name. int len = (int) strlen(name) + 1; _name = new char[len]; + assert(_name != NULL && "allocation failed"); strncpy(_name, name, len); // Initialize for a closed file. _fd = -1; @@ -414,7 +419,7 @@ ImageFileReader::~ImageFileReader() { close(); // Free up name. if (_name) { - delete _name; + delete[] _name; _name = NULL; } } @@ -473,8 +478,8 @@ bool ImageFileReader::open() { // Initialize the module data ImageModuleData::module_data_name(buffer, _name); module_data = new ImageModuleData(this, buffer); - // Successful open. - return true; + // Successful open (if memory allocation succeeded). + return module_data != NULL; } // Close image file. @@ -655,6 +660,7 @@ void ImageFileReader::get_resource(ImageLocation& location, u1* uncompressed_dat if (!MemoryMapImage) { // Allocate buffer for compression. compressed_data = new u1[(u4)compressed_size]; + assert (compressed_data != NULL && "allocation failed"); // Read bytes from offset beyond the image index. bool is_read = read_at(compressed_data, compressed_size, _index_size + offset); assert(is_read && "error reading from image or short read"); @@ -668,7 +674,7 @@ void ImageFileReader::get_resource(ImageLocation& location, u1* uncompressed_dat &strings); // If not memory mapped then release temporary buffer. if (!MemoryMapImage) { - delete compressed_data; + delete[] compressed_data; } } else { // Read bytes from offset beyond the image index. diff --git a/jdk/src/java.base/share/native/libnet/net_util.c b/jdk/src/java.base/share/native/libnet/net_util.c index 9bdbad33baa..e94903fd720 100644 --- a/jdk/src/java.base/share/native/libnet/net_util.c +++ b/jdk/src/java.base/share/native/libnet/net_util.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2015, 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 @@ -38,7 +38,7 @@ JNIEXPORT jint JNICALL ipv6_available() } JNIEXPORT jint JNICALL -JNI_OnLoad(JavaVM *vm, void *reserved) +DEF_JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env; jclass iCls; diff --git a/jdk/src/java.base/share/native/libnio/nio_util.c b/jdk/src/java.base/share/native/libnio/nio_util.c index f61c65268e8..2235f0a5998 100644 --- a/jdk/src/java.base/share/native/libnio/nio_util.c +++ b/jdk/src/java.base/share/native/libnio/nio_util.c @@ -28,7 +28,7 @@ #include "jni_util.h" JNIEXPORT jint JNICALL -JNI_OnLoad(JavaVM *vm, void *reserved) +DEF_JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env; diff --git a/jdk/src/java.base/share/native/libverify/check_code.c b/jdk/src/java.base/share/native/libverify/check_code.c index 0391b366437..e22101496d7 100644 --- a/jdk/src/java.base/share/native/libverify/check_code.c +++ b/jdk/src/java.base/share/native/libverify/check_code.c @@ -86,6 +86,7 @@ #include #include "jni.h" +#include "jni_util.h" #include "jvm.h" #include "classfile_constants.h" #include "opcodes.in_out" @@ -481,6 +482,11 @@ static void print_formatted_fieldname(context_type *context, int index); static void print_formatted_methodname(context_type *context, int index); #endif +/* + * Declare library specific JNI_Onload entry if static build + */ +DEF_STATIC_JNI_OnLoad + void initialize_class_hash(context_type *context) { hash_table_type *class_hash = &(context->class_hash); diff --git a/jdk/src/java.base/share/native/libzip/ZipFile.c b/jdk/src/java.base/share/native/libzip/ZipFile.c index 51c4f77195c..d7a21a6cf88 100644 --- a/jdk/src/java.base/share/native/libzip/ZipFile.c +++ b/jdk/src/java.base/share/native/libzip/ZipFile.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2015, 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 @@ -55,6 +55,12 @@ static jfieldID jzfileID; static int OPEN_READ = java_util_zip_ZipFile_OPEN_READ; static int OPEN_DELETE = java_util_zip_ZipFile_OPEN_DELETE; + +/* + * Declare library specific JNI_Onload entry if static build + */ +DEF_STATIC_JNI_OnLoad + JNIEXPORT void JNICALL Java_java_util_zip_ZipFile_initIDs(JNIEnv *env, jclass cls) { diff --git a/jdk/src/java.base/solaris/classes/sun/nio/ch/EventPortWrapper.java b/jdk/src/java.base/solaris/classes/sun/nio/ch/EventPortWrapper.java index 5355f7f2911..30a454c070f 100644 --- a/jdk/src/java.base/solaris/classes/sun/nio/ch/EventPortWrapper.java +++ b/jdk/src/java.base/solaris/classes/sun/nio/ch/EventPortWrapper.java @@ -31,7 +31,7 @@ import java.util.BitSet; import java.util.HashMap; import java.util.Map; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import sun.security.action.GetIntegerAction; import static sun.nio.ch.SolarisEventPort.*; diff --git a/jdk/src/java.base/solaris/classes/sun/nio/ch/SolarisEventPort.java b/jdk/src/java.base/solaris/classes/sun/nio/ch/SolarisEventPort.java index f5427d6fb7a..298727dccd4 100644 --- a/jdk/src/java.base/solaris/classes/sun/nio/ch/SolarisEventPort.java +++ b/jdk/src/java.base/solaris/classes/sun/nio/ch/SolarisEventPort.java @@ -28,7 +28,7 @@ package sun.nio.ch; import java.nio.channels.spi.AsynchronousChannelProvider; import java.util.concurrent.RejectedExecutionException; import java.io.IOException; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** * Provides an AsynchronousChannelGroup implementation based on the Solaris 10 diff --git a/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java b/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java index f3ba7b3e6f0..7027bd96c2a 100644 --- a/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java +++ b/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java @@ -29,7 +29,7 @@ import java.nio.file.*; import java.nio.file.attribute.*; import java.util.*; import java.io.IOException; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import static sun.nio.fs.UnixConstants.*; import static sun.nio.fs.SolarisConstants.*; diff --git a/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisWatchService.java b/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisWatchService.java index 0c1165ff5f4..7a31d13b3a6 100644 --- a/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisWatchService.java +++ b/jdk/src/java.base/solaris/classes/sun/nio/fs/SolarisWatchService.java @@ -30,7 +30,7 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.util.*; import java.io.IOException; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import static sun.nio.fs.UnixConstants.*; diff --git a/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java b/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java index 27f58c6751a..5f9a546f4bd 100644 --- a/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java +++ b/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java @@ -224,48 +224,75 @@ final class ProcessImpl extends Process { FileOutputStream f2 = null; try { + boolean forceNullOutputStream = false; if (redirects == null) { std_fds = new int[] { -1, -1, -1 }; } else { std_fds = new int[3]; - if (redirects[0] == Redirect.PIPE) + if (redirects[0] == Redirect.PIPE) { std_fds[0] = -1; - else if (redirects[0] == Redirect.INHERIT) + } else if (redirects[0] == Redirect.INHERIT) { std_fds[0] = 0; - else { + } else if (redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) { + std_fds[0] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd()); + } else { f0 = new FileInputStream(redirects[0].file()); std_fds[0] = fdAccess.get(f0.getFD()); } - if (redirects[1] == Redirect.PIPE) + if (redirects[1] == Redirect.PIPE) { std_fds[1] = -1; - else if (redirects[1] == Redirect.INHERIT) + } else if (redirects[1] == Redirect.INHERIT) { std_fds[1] = 1; - else { + } else if (redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) { + std_fds[1] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd()); + // Force getInputStream to return a null stream, + // the fd is directly assigned to the next process. + forceNullOutputStream = true; + } else { f1 = new FileOutputStream(redirects[1].file(), redirects[1].append()); std_fds[1] = fdAccess.get(f1.getFD()); } - if (redirects[2] == Redirect.PIPE) + if (redirects[2] == Redirect.PIPE) { std_fds[2] = -1; - else if (redirects[2] == Redirect.INHERIT) + } else if (redirects[2] == Redirect.INHERIT) { std_fds[2] = 2; - else { + } else if (redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) { + std_fds[2] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd()); + } else { f2 = new FileOutputStream(redirects[2].file(), redirects[2].append()); std_fds[2] = fdAccess.get(f2.getFD()); } } - return new ProcessImpl + Process p = new ProcessImpl (toCString(cmdarray[0]), argBlock, args.length, envBlock, envc[0], toCString(dir), std_fds, + forceNullOutputStream, redirectErrorStream); + if (redirects != null) { + // Copy the fd's if they are to be redirected to another process + if (std_fds[0] >= 0 && + redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) { + fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd(), std_fds[0]); + } + if (std_fds[1] >= 0 && + redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) { + fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd(), std_fds[1]); + } + if (std_fds[2] >= 0 && + redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) { + fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd(), std_fds[2]); + } + } + return p; } finally { // In theory, close() can throw IOException // (although it is rather unlikely to happen here) @@ -311,6 +338,7 @@ final class ProcessImpl extends Process { final byte[] envBlock, final int envc, final byte[] dir, final int[] fds, + final boolean forceNullOutputStream, final boolean redirectErrorStream) throws IOException { @@ -326,7 +354,7 @@ final class ProcessImpl extends Process { try { doPrivileged((PrivilegedExceptionAction) () -> { - initStreams(fds); + initStreams(fds, forceNullOutputStream); return null; }); } catch (PrivilegedActionException ex) { @@ -340,7 +368,14 @@ final class ProcessImpl extends Process { return fileDescriptor; } - void initStreams(int[] fds) throws IOException { + /** + * Initialize the streams from the file descriptors. + * @param fds array of stdin, stdout, stderr fds + * @param forceNullOutputStream true if the stdout is being directed to + * a subsequent process. The stdout stream should be a null output stream . + * @throws IOException + */ + void initStreams(int[] fds, boolean forceNullOutputStream) throws IOException { switch (platform) { case LINUX: case BSD: @@ -348,7 +383,7 @@ final class ProcessImpl extends Process { ProcessBuilder.NullOutputStream.INSTANCE : new ProcessPipeOutputStream(fds[0]); - stdout = (fds[1] == -1) ? + stdout = (fds[1] == -1 || forceNullOutputStream) ? ProcessBuilder.NullInputStream.INSTANCE : new ProcessPipeInputStream(fds[1]); diff --git a/jdk/src/java.base/unix/native/libjava/jlong_md.h b/jdk/src/java.base/unix/native/libjava/jlong_md.h index 97b08e69a5a..40fb0af5496 100644 --- a/jdk/src/java.base/unix/native/libjava/jlong_md.h +++ b/jdk/src/java.base/unix/native/libjava/jlong_md.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2015, 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 @@ -65,11 +65,19 @@ #define jlong_zero_init ((jlong) 0L) #ifdef _LP64 -#define jlong_to_ptr(a) ((void*)(a)) -#define ptr_to_jlong(a) ((jlong)(a)) + #ifndef jlong_to_ptr + #define jlong_to_ptr(a) ((void*)(a)) + #endif + #ifndef ptr_to_jlong + #define ptr_to_jlong(a) ((jlong)(a)) + #endif #else -#define jlong_to_ptr(a) ((void*)(int)(a)) -#define ptr_to_jlong(a) ((jlong)(int)(a)) + #ifndef jlong_to_ptr + #define jlong_to_ptr(a) ((void*)(int)(a)) + #endif + #ifndef ptr_to_jlong + #define ptr_to_jlong(a) ((jlong)(int)(a)) + #endif #endif #define jint_to_jlong(a) ((jlong)(a)) diff --git a/jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java b/jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java index 6d2040fcd68..0d44a40002f 100644 --- a/jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java +++ b/jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java @@ -110,38 +110,63 @@ final class ProcessImpl extends Process { } else { stdHandles = new long[3]; - if (redirects[0] == Redirect.PIPE) + if (redirects[0] == Redirect.PIPE) { stdHandles[0] = -1L; - else if (redirects[0] == Redirect.INHERIT) + } else if (redirects[0] == Redirect.INHERIT) { stdHandles[0] = fdAccess.getHandle(FileDescriptor.in); - else { + } else if (redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) { + stdHandles[0] = fdAccess.getHandle(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd()); + } else { f0 = new FileInputStream(redirects[0].file()); stdHandles[0] = fdAccess.getHandle(f0.getFD()); } - if (redirects[1] == Redirect.PIPE) + if (redirects[1] == Redirect.PIPE) { stdHandles[1] = -1L; - else if (redirects[1] == Redirect.INHERIT) + } else if (redirects[1] == Redirect.INHERIT) { stdHandles[1] = fdAccess.getHandle(FileDescriptor.out); - else { + } else if (redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) { + stdHandles[1] = fdAccess.getHandle(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd()); + } else { f1 = newFileOutputStream(redirects[1].file(), redirects[1].append()); stdHandles[1] = fdAccess.getHandle(f1.getFD()); } - if (redirects[2] == Redirect.PIPE) + if (redirects[2] == Redirect.PIPE) { stdHandles[2] = -1L; - else if (redirects[2] == Redirect.INHERIT) + } else if (redirects[2] == Redirect.INHERIT) { stdHandles[2] = fdAccess.getHandle(FileDescriptor.err); - else { + } else if (redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) { + stdHandles[2] = fdAccess.getHandle(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd()); + } else { f2 = newFileOutputStream(redirects[2].file(), redirects[2].append()); stdHandles[2] = fdAccess.getHandle(f2.getFD()); } } - return new ProcessImpl(cmdarray, envblock, dir, + Process p = new ProcessImpl(cmdarray, envblock, dir, stdHandles, redirectErrorStream); + if (redirects != null) { + // Copy the handles's if they are to be redirected to another process + if (stdHandles[0] >= 0 + && redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) { + fdAccess.setHandle(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd(), + stdHandles[0]); + } + if (stdHandles[1] >= 0 + && redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) { + fdAccess.setHandle(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd(), + stdHandles[1]); + } + if (stdHandles[2] >= 0 + && redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) { + fdAccess.setHandle(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd(), + stdHandles[2]); + } + } + return p; } finally { // In theory, close() can throw IOException // (although it is rather unlikely to happen here) diff --git a/jdk/src/java.base/windows/classes/sun/nio/ch/Iocp.java b/jdk/src/java.base/windows/classes/sun/nio/ch/Iocp.java index 699df050030..715046a0ede 100644 --- a/jdk/src/java.base/windows/classes/sun/nio/ch/Iocp.java +++ b/jdk/src/java.base/windows/classes/sun/nio/ch/Iocp.java @@ -36,7 +36,7 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.security.AccessController; import sun.security.action.GetPropertyAction; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** * Windows implementation of AsynchronousChannelGroup encapsulating an I/O diff --git a/jdk/src/java.base/windows/classes/sun/nio/ch/PendingIoCache.java b/jdk/src/java.base/windows/classes/sun/nio/ch/PendingIoCache.java index 72d8d0ea9d9..b8f7cba96f3 100644 --- a/jdk/src/java.base/windows/classes/sun/nio/ch/PendingIoCache.java +++ b/jdk/src/java.base/windows/classes/sun/nio/ch/PendingIoCache.java @@ -27,7 +27,7 @@ package sun.nio.ch; import java.nio.channels.*; import java.util.*; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** * Maintains a mapping of pending I/O requests (identified by the address of diff --git a/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java b/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java index 991944e96c7..9d5945364b9 100644 --- a/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java +++ b/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java @@ -33,7 +33,7 @@ import java.io.IOException; import java.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedAction; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** * Windows implementation of AsynchronousServerSocketChannel using overlapped I/O. diff --git a/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java b/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java index 1880548f68f..3e96279de41 100644 --- a/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java +++ b/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java @@ -34,7 +34,7 @@ import java.io.IOException; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** * Windows implementation of AsynchronousSocketChannel using overlapped I/O. diff --git a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java index 208435dc97e..02226053f8d 100644 --- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java +++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributes.java @@ -28,7 +28,7 @@ package sun.nio.fs; import java.nio.file.attribute.*; import java.util.concurrent.TimeUnit; import java.security.AccessController; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import sun.security.action.GetPropertyAction; import static sun.nio.fs.WindowsNativeDispatcher.*; diff --git a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java index e86c385a622..cf0d23fea2b 100644 --- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java +++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java @@ -33,7 +33,7 @@ import java.util.concurrent.ExecutorService; import java.io.*; import java.util.*; import java.security.AccessController; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import sun.nio.ch.ThreadPool; import sun.security.util.SecurityConstants; diff --git a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsLinkSupport.java b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsLinkSupport.java index 56f996d99b3..80d4809b6a5 100644 --- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsLinkSupport.java +++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsLinkSupport.java @@ -30,7 +30,7 @@ import java.io.IOException; import java.io.IOError; import java.security.AccessController; import java.security.PrivilegedAction; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import static sun.nio.fs.WindowsNativeDispatcher.*; import static sun.nio.fs.WindowsConstants.*; diff --git a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java index 82a4bc3aecd..820edbfd9a8 100644 --- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java +++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java @@ -27,7 +27,7 @@ package sun.nio.fs; import java.security.AccessController; import java.security.PrivilegedAction; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; /** * Win32 and library calls. diff --git a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java index fb432271cc6..da3460592f4 100644 --- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java +++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java @@ -29,7 +29,7 @@ import java.nio.file.ProviderMismatchException; import java.nio.file.attribute.*; import java.util.*; import java.io.IOException; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import static sun.nio.fs.WindowsNativeDispatcher.*; import static sun.nio.fs.WindowsConstants.*; diff --git a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsUserDefinedFileAttributeView.java b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsUserDefinedFileAttributeView.java index 64ef12a9855..774d248840c 100644 --- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsUserDefinedFileAttributeView.java +++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsUserDefinedFileAttributeView.java @@ -31,7 +31,7 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.io.IOException; import java.util.*; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import static sun.nio.fs.WindowsNativeDispatcher.*; import static sun.nio.fs.WindowsConstants.*; diff --git a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsWatchService.java b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsWatchService.java index 0ea288e1ecb..8e28e8471ac 100644 --- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsWatchService.java +++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsWatchService.java @@ -36,7 +36,7 @@ import java.util.Map; import java.util.Set; import com.sun.nio.file.ExtendedWatchEventModifier; -import sun.misc.Unsafe; +import jdk.internal.misc.Unsafe; import static sun.nio.fs.WindowsNativeDispatcher.*; import static sun.nio.fs.WindowsConstants.*; diff --git a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m index a7784585272..805596cc118 100644 --- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m +++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m @@ -42,6 +42,7 @@ -(void) deliverResize: (NSRect) rect; -(void) resetTrackingArea; -(void) deliverJavaKeyEventHelper: (NSEvent*) event; +-(BOOL) isCodePointInUnicodeBlockNeedingIMEvent: (unichar) codePoint; @end // Uncomment this line to see fprintfs of each InputMethod API being called on this View @@ -509,6 +510,14 @@ AWT_ASSERT_APPKIT_THREAD; } } +-(BOOL) isCodePointInUnicodeBlockNeedingIMEvent: (unichar) codePoint { + if ((codePoint >= 0x3000) && (codePoint <= 0x303F)) { + // Code point is in 'CJK Symbols and Punctuation' Unicode block. + return YES; + } + return NO; +} + // NSAccessibility support - (jobject)awtComponent:(JNIEnv*)env { @@ -889,8 +898,14 @@ JNF_CLASS_CACHE(jc_CInputMethod, "sun/lwawt/macosx/CInputMethod"); // (i.e., when the user uses the Character palette or Inkwell), or when the string to insert is a complex // Unicode value. NSUInteger utf16Length = [aString lengthOfBytesUsingEncoding:NSUTF16StringEncoding]; + NSUInteger utf8Length = [aString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + BOOL aStringIsComplex = NO; + if ((utf16Length > 2) || + ((utf8Length > 1) && [self isCodePointInUnicodeBlockNeedingIMEvent:[aString characterAtIndex:0]])) { + aStringIsComplex = YES; + } - if ([self hasMarkedText] || !fProcessingKeystroke || (utf16Length > 2)) { + if ([self hasMarkedText] || !fProcessingKeystroke || aStringIsComplex) { JNIEnv *env = [ThreadUtilities getJNIEnv]; static JNF_MEMBER_CACHE(jm_selectPreviousGlyph, jc_CInputMethod, "selectPreviousGlyph", "()V"); diff --git a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.m b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.m index 1a048057ea1..8f1e269de8f 100644 --- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.m +++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.m @@ -740,7 +740,7 @@ Java_sun_lwawt_macosx_LWCToolkit_initAppkit JNF_COCOA_EXIT(env) } -JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { +JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *vm, void *reserved) { OSXAPP_SetJavaVM(vm); // We need to let Foundation know that this is a multithreaded application, if it isn't already. diff --git a/jdk/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m b/jdk/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m index bfb90ec6a59..a82afc26888 100644 --- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m +++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m @@ -307,6 +307,8 @@ JNF_COCOA_ENTER(env); JNFCallVoidMethod(env, jthis, jm_registerFont, jFontName, jFontFamilyName); + (*env)->DeleteLocalRef(env, jFontName); + (*env)->DeleteLocalRef(env, jFontFamilyName); } JNF_COCOA_EXIT(env); diff --git a/jdk/src/java.desktop/macosx/native/libjawt/jawt.m b/jdk/src/java.desktop/macosx/native/libjawt/jawt.m index e737facf9b4..cd9f52a8e5e 100644 --- a/jdk/src/java.desktop/macosx/native/libjawt/jawt.m +++ b/jdk/src/java.desktop/macosx/native/libjawt/jawt.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2015, 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 @@ -24,11 +24,17 @@ */ #import +#import "jni_util.h" #import #import "awt_DrawingSurface.h" +/* + * Declare library specific JNI_Onload entry if static build + */ +DEF_STATIC_JNI_OnLoad + /* * Get the AWT native structure. * This function returns JNI_FALSE if an error occurs. diff --git a/jdk/src/java.desktop/macosx/native/libosxapp/NSApplicationAWT.h b/jdk/src/java.desktop/macosx/native/libosxapp/NSApplicationAWT.h index 69d380032df..c84ad06f9b5 100644 --- a/jdk/src/java.desktop/macosx/native/libosxapp/NSApplicationAWT.h +++ b/jdk/src/java.desktop/macosx/native/libosxapp/NSApplicationAWT.h @@ -23,6 +23,12 @@ * questions. */ +/* + * Must include this before JavaNativeFoundation.h to get jni.h from build + */ +#include "jni.h" +#include "jni_util.h" + #import #import diff --git a/jdk/src/java.desktop/macosx/native/libosxapp/NSApplicationAWT.m b/jdk/src/java.desktop/macosx/native/libosxapp/NSApplicationAWT.m index 8a856bdabdd..579056f7117 100644 --- a/jdk/src/java.desktop/macosx/native/libosxapp/NSApplicationAWT.m +++ b/jdk/src/java.desktop/macosx/native/libosxapp/NSApplicationAWT.m @@ -33,6 +33,10 @@ #import "QueuingApplicationDelegate.h" #import "AWTIconData.h" +/* + * Declare library specific JNI_Onload entry if static build + */ +DEF_STATIC_JNI_OnLoad static BOOL sUsingDefaultNIB = YES; static NSString *SHARED_FRAMEWORK_BUNDLE = @"/System/Library/Frameworks/JavaVM.framework"; @@ -432,10 +436,10 @@ AWT_ASSERT_APPKIT_THREAD; @end -void OSXAPP_SetApplicationDelegate(id delegate) +void OSXAPP_SetApplicationDelegate(id newdelegate) { AWT_ASSERT_APPKIT_THREAD; - applicationDelegate = delegate; + applicationDelegate = newdelegate; if (NSApp != nil) { [NSApp setDelegate: applicationDelegate]; diff --git a/jdk/src/java.desktop/macosx/native/libosxui/AquaLookAndFeel.m b/jdk/src/java.desktop/macosx/native/libosxui/AquaLookAndFeel.m index cf58d7599c0..50ac4ba51e2 100644 --- a/jdk/src/java.desktop/macosx/native/libosxui/AquaLookAndFeel.m +++ b/jdk/src/java.desktop/macosx/native/libosxui/AquaLookAndFeel.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2015, 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 @@ -23,6 +23,10 @@ * questions. */ +// Must include this before JavaNativeFoundation.h to get jni.h from build +#include "jni.h" +#include "jni_util.h" + #import /* @@ -30,6 +34,7 @@ * AWT's JNI_OnLoad called multiple times * Please remove when has been resolved. */ -JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { +JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *vm, void *reserved) +{ return JNI_VERSION_1_4; } diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java index 277e3db2b25..3b12b811fb7 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java @@ -668,6 +668,40 @@ public final class SoftSynthesizer implements AudioSynthesizer, } }); + actions.add(new PrivilegedAction() { + public InputStream run() { + if (System.getProperties().getProperty("os.name") + .startsWith("Linux")) { + + File[] systemSoundFontsDir = new File[] { + /* Arch, Fedora, Mageia */ + new File("/usr/share/soundfonts/"), + new File("/usr/local/share/soundfonts/"), + /* Debian, Gentoo, OpenSUSE, Ubuntu */ + new File("/usr/share/sounds/sf2/"), + new File("/usr/local/share/sounds/sf2/"), + }; + + /* + * Look for a default.sf2 + */ + for (File systemSoundFontDir : systemSoundFontsDir) { + if (systemSoundFontDir.exists()) { + File defaultSoundFont = new File(systemSoundFontDir, "default.sf2"); + if (defaultSoundFont.exists()) { + try { + return new FileInputStream(defaultSoundFont); + } catch (IOException e) { + // continue with lookup + } + } + } + } + } + return null; + } + }); + actions.add(new PrivilegedAction() { public InputStream run() { if (System.getProperties().getProperty("os.name") diff --git a/jdk/src/java.desktop/share/classes/java/awt/KeyboardFocusManager.java b/jdk/src/java.desktop/share/classes/java/awt/KeyboardFocusManager.java index 75cf0e0c918..d4d4a92e88c 100644 --- a/jdk/src/java.desktop/share/classes/java/awt/KeyboardFocusManager.java +++ b/jdk/src/java.desktop/share/classes/java/awt/KeyboardFocusManager.java @@ -324,23 +324,6 @@ public abstract class KeyboardFocusManager "downCycleDefaultFocusTraversalKeys" }; - /** - * The default strokes for initializing the default focus traversal keys. - */ - private static final AWTKeyStroke[][] defaultFocusTraversalKeyStrokes = { - { - AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, 0, false), - AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, InputEvent.CTRL_DOWN_MASK | InputEvent.CTRL_MASK, false), - }, - { - AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_DOWN_MASK | InputEvent.SHIFT_MASK, false), - AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, - InputEvent.SHIFT_DOWN_MASK | InputEvent.SHIFT_MASK | InputEvent.CTRL_DOWN_MASK | InputEvent.CTRL_MASK, - false), - }, - {}, - {}, - }; /** * The default focus traversal keys. Each array of traversal keys will be * in effect on all Windows that have no such array of their own explicitly @@ -431,6 +414,27 @@ public abstract class KeyboardFocusManager * Initializes a KeyboardFocusManager. */ public KeyboardFocusManager() { + AWTKeyStroke[][] defaultFocusTraversalKeyStrokes = { + { + AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, 0, false), + AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, + InputEvent.CTRL_DOWN_MASK | + InputEvent.CTRL_MASK, false), + }, + { + AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, + InputEvent.SHIFT_DOWN_MASK | + InputEvent.SHIFT_MASK, false), + AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, + InputEvent.SHIFT_DOWN_MASK | + InputEvent.SHIFT_MASK | + InputEvent.CTRL_DOWN_MASK | + InputEvent.CTRL_MASK, + false), + }, + {}, + {}, + }; for (int i = 0; i < TRAVERSAL_KEY_LENGTH; i++) { Set work_set = new HashSet<>(); for (int j = 0; j < defaultFocusTraversalKeyStrokes[i].length; j++) { diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java b/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java index b5f34f35ca5..acc83c4739f 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java @@ -640,10 +640,6 @@ public abstract class AbstractRegionPainter implements Painter { // check if we can scale to the requested size Dimension canvas = ctx.canvasSize; Insets insets = ctx.stretchingInsets; - if (insets.left + insets.right > w || insets.top + insets.bottom > h) { - return; - } - if (w <= (canvas.width * ctx.maxHorizontalScaleFactor) && h <= (canvas.height * ctx.maxVerticalScaleFactor)) { // get image at canvas size VolatileImage img = getImage(g.getDeviceConfiguration(), c, canvas.width, canvas.height, extendedCacheKeys); diff --git a/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/skin.laf b/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/skin.laf index 394721e1c41..208a3b03301 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/skin.laf +++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/skin.laf @@ -1,7 +1,7 @@ + + + +Verify that Chinese full stop symbol can be entered in JTextArea with Pinyin input method (IM). + +This test is for OS X only. For other platforms please simply press "Pass". + +1. Go to "System Preferences -> Keyboard -> Input Sources" and add "Pinyin – Traditional" or "Pinyin – Simplified" IM from Chinese language group. +2. Set current IM to "Pinyin". +3. Set focus to the text area of the test and press "dot" character on the keyboard. +4. Set current IM to the IM used before "Pinyin" was set. +5. If "。" character is displayed in the text area, press "Pass", if "." character is displayed, press "Fail". + + + + diff --git a/jdk/test/java/awt/im/8132503/bug8132503.java b/jdk/test/java/awt/im/8132503/bug8132503.java new file mode 100644 index 00000000000..622a3bae3a2 --- /dev/null +++ b/jdk/test/java/awt/im/8132503/bug8132503.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* @test + @bug 8132503 + @summary [macosx] Chinese full stop symbol cannot be entered with Pinyin IM on OS X + @author Anton Litvinov + @run applet/manual=yesno bug8132503.html + */ + +import javax.swing.JApplet; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; + +public class bug8132503 extends JApplet { + @Override + public void init() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + JTextArea textArea = new JTextArea("Text area of the test.", 40, 40); + add(new JScrollPane(textArea)); + } + }); + } +} diff --git a/jdk/test/java/awt/print/PrinterJob/PaintText.java b/jdk/test/java/awt/print/PrinterJob/PaintText.java index 15a5c288f39..7e64b965c98 100644 --- a/jdk/test/java/awt/print/PrinterJob/PaintText.java +++ b/jdk/test/java/awt/print/PrinterJob/PaintText.java @@ -75,8 +75,8 @@ public class PaintText extends Component implements Printable { f.show(); /* Non-jtreg execution will display the dialog */ - if (System.getProperty("test.java") == null) { - if (!pjob.printDialog()) { + if (System.getProperty("test.jdk") == null) { + if (!pjob.printDialog()) { return; } } @@ -84,6 +84,8 @@ public class PaintText extends Component implements Printable { pjob.print(); } catch (PrinterException e) { throw new RuntimeException(e.getMessage()); + } finally { + f.dispose(); } } diff --git a/jdk/test/java/beans/XMLDecoder/8028054/Task.java b/jdk/test/java/beans/XMLDecoder/8028054/Task.java index 9aa477c0310..3aa15004ba9 100644 --- a/jdk/test/java/beans/XMLDecoder/8028054/Task.java +++ b/jdk/test/java/beans/XMLDecoder/8028054/Task.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2015, 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 @@ -22,12 +22,17 @@ */ import java.util.ArrayList; -import java.util.Enumeration; import java.util.List; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.net.*; +import java.io.*; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.ProviderNotFoundException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.function.Predicate; abstract class Task implements Runnable { private transient boolean working = true; @@ -74,26 +79,74 @@ abstract class Task implements Runnable { } static List> getClasses(int count) throws Exception { - String resource = ClassLoader.getSystemClassLoader().getResource("java/lang/Object.class").toString(); - - Pattern pattern = Pattern.compile("jar:file:(.*)!.*"); - Matcher matcher = pattern.matcher(resource); - matcher.matches(); - resource = matcher.group(1); - List> classes = new ArrayList<>(); - try (JarFile jarFile = new JarFile(resource)) { - Enumeration entries = jarFile.entries(); - while (entries.hasMoreElements()) { - String name = entries.nextElement().getName(); - if (name.startsWith("java") && name.endsWith(".class")) { - classes.add(Class.forName(name.substring(0, name.indexOf(".")).replace('/', '.'))); - if (count == classes.size()) { - break; - } - } + FileSystem fs = null; + + try { + fs = FileSystems.getFileSystem(URI.create("jrt:/")); + } catch (ProviderNotFoundException | FileSystemNotFoundException e) { + throw new RuntimeException("FAIL - JRT Filesystem not found"); + } + + List fileNames; + Path modules = fs.getPath("/modules"); + + Predicate startsWithJavaBase = path -> path.toString().startsWith("java.base/java"); + Predicate startsWithJavaDesktop = path -> path.toString().startsWith("java.desktop/java"); + Predicate startsWithJavaDataTransfer = path -> path.toString().startsWith("java.datatransfer/java"); + Predicate startsWithJavaRMI = path -> path.toString().startsWith("java.rmi/java"); + Predicate startsWithJavaSmartCardIO = path -> path.toString().startsWith("java.smartcardio/java"); + Predicate startsWithJavaManagement = path -> path.toString().startsWith("java.management/java"); + Predicate startsWithJavaXML = path -> path.toString().startsWith("java.xml/java"); + Predicate startsWithJavaXMLBind = path -> path.toString().startsWith("java.xml.bind/java"); + Predicate startsWithJavaScripting = path -> path.toString().startsWith("java.scripting/java"); + Predicate startsWithJavaNaming = path -> path.toString().startsWith("java.naming/java"); + Predicate startsWithJavaSQL = path -> path.toString().startsWith("java.sql/java"); + Predicate startsWithJavaActivation = path -> path.toString().startsWith("java.activation/java"); + Predicate startsWithJavaCompiler = path -> path.toString().startsWith("java.compiler/java"); + Predicate startsWithJavaAnnotations = path -> path.toString().startsWith("java.annotations/java"); + Predicate startsWithJavaTransaction = path -> path.toString().startsWith("java.transaction/java"); + Predicate startsWithJavaLogging = path -> path.toString().startsWith("java.logging/java"); + Predicate startsWithJavaCorba = path -> path.toString().startsWith("java.corba/java"); + Predicate startsWithJavaPrefs = path -> path.toString().startsWith("java.prefs/java"); + + fileNames = Files.walk(modules) + .map(Path::toString) + .filter(path -> path.toString().contains("java")) + .map(s -> s.substring(9)) // remove /modules/ from beginning + .filter(startsWithJavaBase + .or(startsWithJavaDesktop) + .or(startsWithJavaDataTransfer) + .or(startsWithJavaRMI) + .or(startsWithJavaSmartCardIO) + .or(startsWithJavaManagement) + .or(startsWithJavaXML) + .or(startsWithJavaXMLBind) + .or(startsWithJavaScripting) + .or(startsWithJavaNaming) + .or(startsWithJavaSQL) + .or(startsWithJavaActivation) + .or(startsWithJavaCompiler) + .or(startsWithJavaAnnotations) + .or(startsWithJavaTransaction) + .or(startsWithJavaLogging) + .or(startsWithJavaCorba) + .or(startsWithJavaPrefs)) + .map(s -> s.replace('/', '.')) + .filter(path -> path.toString().endsWith(".class")) + .map(s -> s.substring(0, s.length() - 6)) // drop .class + .map(s -> s.substring(s.indexOf("."))) + .filter(path -> path.toString().contains("java")) + .map(s -> s.substring(s.indexOf("java"))) + .collect(Collectors.toList()); + + for (String name : fileNames) { + classes.add(Class.forName(name)); + if (count == classes.size()) { + break; } } + return classes; } } diff --git a/jdk/test/java/beans/XMLDecoder/8028054/TestConstructorFinder.java b/jdk/test/java/beans/XMLDecoder/8028054/TestConstructorFinder.java index 0b8cef769e7..d940b389b89 100644 --- a/jdk/test/java/beans/XMLDecoder/8028054/TestConstructorFinder.java +++ b/jdk/test/java/beans/XMLDecoder/8028054/TestConstructorFinder.java @@ -73,8 +73,7 @@ public class TestConstructorFinder { } Task.print(working + " out of " + alive + " threads are working"); if ((working == 0) && (++alarm == 10)) { - Task.print("DEADLOCK DETECTED"); - System.exit(100); + throw new RuntimeException("FAIL - DEADLOCK DETECTED"); } Thread.sleep(1000); } diff --git a/jdk/test/java/beans/XMLDecoder/8028054/TestMethodFinder.java b/jdk/test/java/beans/XMLDecoder/8028054/TestMethodFinder.java index 2b9b8a40ff0..9d3147f520c 100644 --- a/jdk/test/java/beans/XMLDecoder/8028054/TestMethodFinder.java +++ b/jdk/test/java/beans/XMLDecoder/8028054/TestMethodFinder.java @@ -73,8 +73,7 @@ public class TestMethodFinder { } Task.print(working + " out of " + alive + " threads are working"); if ((working == 0) && (++alarm == 10)) { - Task.print("DEADLOCK DETECTED"); - System.exit(100); + throw new RuntimeException("FAIL - DEADLOCK DETECTED"); } Thread.sleep(1000); } diff --git a/jdk/test/java/lang/ProcessBuilder/Basic.java b/jdk/test/java/lang/ProcessBuilder/Basic.java index 5a39cdc454f..7be2aa6ba71 100644 --- a/jdk/test/java/lang/ProcessBuilder/Basic.java +++ b/jdk/test/java/lang/ProcessBuilder/Basic.java @@ -1248,7 +1248,7 @@ public class Basic { () -> p.toHandle(), () -> p.supportsNormalTermination(), () -> p.children(), - () -> p.allChildren()); + () -> p.descendants()); } diff --git a/jdk/test/java/lang/ProcessBuilder/PipelineTest.java b/jdk/test/java/lang/ProcessBuilder/PipelineTest.java new file mode 100644 index 00000000000..9f6ec99fb70 --- /dev/null +++ b/jdk/test/java/lang/ProcessBuilder/PipelineTest.java @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2015, 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. + * + */ + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.util.Arrays; +import java.util.List; + +/* + * @test PipelineTest + */ + +public class PipelineTest { + + private static void realMain(String[] args) throws Throwable { + t1_simplePipeline(); + t2_translatePipeline(); + t3_redirectErrorStream(); + t4_failStartPipeline(); + } + + /** + * Return a list of the varargs arguments. + * @param args elements to include in the list + * @param the type of the elements + * @return a {@code List} of the arguments + */ + @SafeVarargs + @SuppressWarnings("varargs") + static List asList(T... args) { + return Arrays.asList(args); + } + + /** + * T1 - simple copy between two processes + */ + static void t1_simplePipeline() { + try { + String s1 = "Now is the time to check!"; + verify(s1, s1, + asList(new ProcessBuilder("cat"))); + verify(s1, s1, + asList(new ProcessBuilder("cat"), + new ProcessBuilder("cat"))); + verify(s1, s1, + asList(new ProcessBuilder("cat"), + new ProcessBuilder("cat"), + new ProcessBuilder("cat"))); + } catch (Throwable t) { + unexpected(t); + } + } + + /** + * Pipeline that modifies the content. + */ + static void t2_translatePipeline() { + try { + String s2 = "Now is the time to check!"; + String r2 = s2.replace('e', 'E').replace('o', 'O'); + verify(s2, r2, + asList(new ProcessBuilder("tr", "e", "E"), + new ProcessBuilder("tr", "o", "O"))); + } catch (Throwable t) { + unexpected(t); + } + } + + /** + * Test that redirectErrorStream sends standard error of the first process + * to the standard output. The standard error of the first process should be empty. + * The standard output of the 2nd should contain the error message including the bad file name. + */ + static void t3_redirectErrorStream() { + try { + File p1err = new File("p1-test.err"); + File p2out = new File("p2-test.out"); + + List processes = ProcessBuilder.startPipeline( + asList(new ProcessBuilder("cat", "NON-EXISTENT-FILE") + .redirectErrorStream(true) + .redirectError(p1err), + new ProcessBuilder("cat").redirectOutput(p2out))); + waitForAll(processes); + + check("".equals(fileContents(p1err)), "The first process standard error should be empty"); + String p2contents = fileContents(p2out); + check(p2contents.contains("NON-EXISTENT-FILE"), + "The error from the first process should be in the output of the second: " + p2contents); + } catch (Throwable t) { + unexpected(t); + } + } + + /** + * Test that no processes are left after a failed startPipeline. + * Test illegal combinations of redirects. + */ + static void t4_failStartPipeline() { + File p1err = new File("p1-test.err"); + File p2out = new File("p2-test.out"); + + THROWS(IllegalArgumentException.class, + () -> { + // Test that output redirect != PIPE throws IAE + List processes = ProcessBuilder.startPipeline( + asList(new ProcessBuilder("cat", "NON-EXISTENT-FILE1") + .redirectOutput(p1err), + new ProcessBuilder("cat"))); + }, + () -> { + // Test that input redirect != PIPE throws IAE + List processes = ProcessBuilder.startPipeline( + asList(new ProcessBuilder("cat", "NON-EXISTENT-FILE2"), + new ProcessBuilder("cat").redirectInput(p2out))); + } + ); + + THROWS(NullPointerException.class, + () -> { + List processes = ProcessBuilder.startPipeline( + asList(new ProcessBuilder("cat", "a"), null)); + }, + () -> { + List processes = ProcessBuilder.startPipeline( + asList(null, new ProcessBuilder("cat", "b"))); + } + ); + + THROWS(IOException.class, + () -> { + List processes = ProcessBuilder.startPipeline( + asList(new ProcessBuilder("cat", "c"), + new ProcessBuilder("NON-EXISTENT-COMMAND"))); + }); + + // Check no subprocess are left behind + ProcessHandle.current().children().forEach(PipelineTest::print); + ProcessHandle.current().children() + .filter(p -> p.info().command().orElse("").contains("cat")) + .forEach(p -> fail("process should have been destroyed: " + p)); + } + + static void verify(String input, String expected, List builders) throws IOException { + File infile = new File("test.in"); + File outfile = new File("test.out"); + setFileContents(infile, expected); + for (int i = 0; i < builders.size(); i++) { + ProcessBuilder b = builders.get(i); + if (i == 0) { + b.redirectInput(infile); + } + if (i == builders.size() - 1) { + b.redirectOutput(outfile); + } + } + List processes = ProcessBuilder.startPipeline(builders); + verifyProcesses(processes); + waitForAll(processes); + String result = fileContents(outfile); + System.out.printf(" in: %s%nout: %s%n", input, expected); + check(result.equals(expected), "result not as expected"); + } + + /** + * Wait for each of the processes to be done. + * + * @param processes the list of processes to check + */ + static void waitForAll(List processes) { + processes.forEach(p -> { + try { + int status = p.waitFor(); + } catch (InterruptedException ie) { + unexpected(ie); + } + }); + } + + static void print(ProcessBuilder pb) { + if (pb != null) { + System.out.printf(" pb: %s%n", pb); + System.out.printf(" cmd: %s%n", pb.command()); + } + } + + static void print(ProcessHandle p) { + System.out.printf("process: pid: %d, info: %s%n", + p.getPid(), p.info()); + } + + // Check various aspects of the processes + static void verifyProcesses(List processes) { + for (int i = 0; i < processes.size(); i++) { + Process p = processes.get(i); + if (i != 0) { + verifyNullStream(p.getOutputStream(), "getOutputStream"); + } + if (i == processes.size() - 1) { + verifyNullStream(p.getInputStream(), "getInputStream"); + verifyNullStream(p.getErrorStream(), "getErrorStream"); + } + } + } + + static void verifyNullStream(OutputStream s, String msg) { + try { + s.write(0xff); + fail("Stream should have been a NullStream" + msg); + } catch (IOException ie) { + // expected + } + } + + static void verifyNullStream(InputStream s, String msg) { + try { + int len = s.read(); + check(len == -1, "Stream should have been a NullStream" + msg); + } catch (IOException ie) { + // expected + } + } + + static void setFileContents(File file, String contents) { + try { + Writer w = new FileWriter(file); + w.write(contents); + w.close(); + } catch (Throwable t) { unexpected(t); } + } + + static String fileContents(File file) { + try { + Reader r = new FileReader(file); + StringBuilder sb = new StringBuilder(); + char[] buffer = new char[1024]; + int n; + while ((n = r.read(buffer)) != -1) + sb.append(buffer,0,n); + r.close(); + return new String(sb); + } catch (Throwable t) { unexpected(t); return ""; } + } + + //--------------------- Infrastructure --------------------------- + static volatile int passed = 0, failed = 0; + static void pass() {passed++;} + static void fail() {failed++; Thread.dumpStack();} + static void fail(String msg) {System.err.println(msg); fail();} + static void unexpected(Throwable t) {failed++; t.printStackTrace();} + static void check(boolean cond) {if (cond) pass(); else fail();} + static void check(boolean cond, String m) {if (cond) pass(); else fail(m);} + static void equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) pass(); + else fail(">'" + x + "'<" + " not equal to " + "'" + y + "'"); + } + + public static void main(String[] args) throws Throwable { + try {realMain(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed"); + } + interface Fun {void f() throws Throwable;} + static void THROWS(Class k, Fun... fs) { + for (Fun f : fs) + try { f.f(); fail("Expected " + k.getName() + " not thrown"); } + catch (Throwable t) { + if (k.isAssignableFrom(t.getClass())) pass(); + else unexpected(t);} + } + +} diff --git a/jdk/test/java/lang/ProcessHandle/OnExitTest.java b/jdk/test/java/lang/ProcessHandle/OnExitTest.java index ea9dfc65e0d..9c7a5193db6 100644 --- a/jdk/test/java/lang/ProcessHandle/OnExitTest.java +++ b/jdk/test/java/lang/ProcessHandle/OnExitTest.java @@ -129,7 +129,7 @@ public class OnExitTest extends ProcessUtil { printf(" You can try to increase the timeout or%n"); printf(" you can try to use a faster VM (i.e. not a debug version).%n"); } - children = getAllChildren(procHandle); + children = getDescendants(procHandle); ConcurrentHashMap> completions = new ConcurrentHashMap<>(); diff --git a/jdk/test/java/lang/ProcessHandle/PermissionTest.java b/jdk/test/java/lang/ProcessHandle/PermissionTest.java index 863f421afd7..042b9d1e818 100644 --- a/jdk/test/java/lang/ProcessHandle/PermissionTest.java +++ b/jdk/test/java/lang/ProcessHandle/PermissionTest.java @@ -62,9 +62,9 @@ public class PermissionTest { } @Test - public void allChildrenWithPermission() { + public void descendantsWithPermission() { Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess"))); - currentHndl.allChildren(); + currentHndl.descendants(); } @Test @@ -122,7 +122,7 @@ public class PermissionTest { @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class) public void noPermissionAllChildren() { - currentHndl.allChildren(); + currentHndl.descendants(); } @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class) diff --git a/jdk/test/java/lang/ProcessHandle/ProcessUtil.java b/jdk/test/java/lang/ProcessHandle/ProcessUtil.java index 328012f1a30..c0c6916f971 100644 --- a/jdk/test/java/lang/ProcessHandle/ProcessUtil.java +++ b/jdk/test/java/lang/ProcessHandle/ProcessUtil.java @@ -63,8 +63,8 @@ public abstract class ProcessUtil { * @param ph the Process to get children of * @return a list of child ProcessHandles */ - public static List getAllChildren(ProcessHandle ph) { - return ph.allChildren() + public static List getDescendants(ProcessHandle ph) { + return ph.descendants() .filter(ProcessUtil::isNotWindowsConsole) .collect(Collectors.toList()); } @@ -117,7 +117,7 @@ public abstract class ProcessUtil { // ignore } } - subprocesses = getAllChildren(ph); + subprocesses = getDescendants(ph); count = subprocesses.size(); System.out.printf(" waiting for subprocesses of %s to start," + " expected: %d, current: %d%n", ph, nchildren, count); @@ -133,7 +133,7 @@ public abstract class ProcessUtil { * @return the ProcessHandle */ public static ProcessHandle destroyProcessTree(ProcessHandle p) { - Stream children = p.allChildren().filter(ProcessUtil::isNotWindowsConsole); + Stream children = p.descendants().filter(ProcessUtil::isNotWindowsConsole); children.forEach(ph -> { System.out.printf("destroyProcessTree destroyForcibly%n"); printProcess(ph); diff --git a/jdk/test/java/lang/ProcessHandle/TreeTest.java b/jdk/test/java/lang/ProcessHandle/TreeTest.java index ea12d3f361f..39b00b088b0 100644 --- a/jdk/test/java/lang/ProcessHandle/TreeTest.java +++ b/jdk/test/java/lang/ProcessHandle/TreeTest.java @@ -193,21 +193,21 @@ public class TreeTest extends ProcessUtil { } // show the complete list of children (for debug) - List allChildren = getAllChildren(p1Handle); - printf(" allChildren: %s%n", - allChildren.stream().map(p -> p.getPid()) - .collect(Collectors.toList())); + List descendants = getDescendants(p1Handle); + printf(" descendants: %s%n", + descendants.stream().map(p -> p.getPid()) + .collect(Collectors.toList())); - // Verify that all spawned children show up in the allChildren List + // Verify that all spawned children show up in the descendants List processes.forEach((p, parent) -> { Assert.assertEquals(p.isAlive(), true, "Child should be alive: " + p); - Assert.assertTrue(allChildren.contains(p), "Spawned child should be listed in allChildren: " + p); + Assert.assertTrue(descendants.contains(p), "Spawned child should be listed in descendants: " + p); }); // Closing JavaChild's InputStream will cause all children to exit p1.getOutputStream().close(); - for (ProcessHandle p : allChildren) { + for (ProcessHandle p : descendants) { try { p.onExit().get(); // wait for the child to exit } catch (ExecutionException e) { @@ -228,9 +228,9 @@ public class TreeTest extends ProcessUtil { /** * Test destroy of processes. * A JavaChild is started and it starts three children. - * Each one is then checked to be alive and listed by allChildren + * Each one is then checked to be alive and listed by descendants * and forcibly destroyed. - * After they exit they should no longer be listed by allChildren. + * After they exit they should no longer be listed by descendants. */ @Test public static void test3() { @@ -263,24 +263,24 @@ public class TreeTest extends ProcessUtil { Assert.assertTrue(spawnCount.await(Utils.adjustTimeout(30L), TimeUnit.SECONDS), "Timeout waiting for processes to start"); - // Debugging; list allChildren that are not expected in processes - List allChildren = ProcessUtil.getAllChildren(p1Handle); - long count = allChildren.stream() + // Debugging; list descendants that are not expected in processes + List descendants = ProcessUtil.getDescendants(p1Handle); + long count = descendants.stream() .filter(ph -> !processes.containsKey(ph)) .count(); if (count > 0) { - allChildren.stream() + descendants.stream() .filter(ph -> !processes.containsKey(ph)) .forEach(ph1 -> ProcessUtil.printProcess(ph1, "Extra process: ")); ProcessUtil.logTaskList(); - Assert.assertEquals(0, count, "Extra processes in allChildren"); + Assert.assertEquals(0, count, "Extra processes in descendants"); } - // Verify that all spawned children are alive, show up in the allChildren list + // Verify that all spawned children are alive, show up in the descendants list // then destroy them processes.forEach((p, parent) -> { Assert.assertEquals(p.isAlive(), true, "Child should be alive: " + p); - Assert.assertTrue(allChildren.contains(p), "Spawned child should be listed in allChildren: " + p); + Assert.assertTrue(descendants.contains(p), "Spawned child should be listed in descendants: " + p); p.destroyForcibly(); }); Assert.assertEquals(processes.size(), newChildren, "Wrong number of children"); @@ -305,8 +305,8 @@ public class TreeTest extends ProcessUtil { p1.destroyForcibly(); p1.waitFor(); - // Verify that none of the spawned children are still listed by allChildren - List remaining = getAllChildren(self); + // Verify that none of the spawned children are still listed by descendants + List remaining = getDescendants(self); Assert.assertFalse(remaining.remove(p1Handle), "Child p1 should have exited"); remaining = remaining.stream().filter(processes::containsKey).collect(Collectors.toList()); Assert.assertEquals(remaining.size(), 0, "Subprocess(es) should have exited: " + remaining); @@ -415,28 +415,28 @@ public class TreeTest extends ProcessUtil { Assert.assertTrue(spawnCount.await(Utils.adjustTimeout(30L), TimeUnit.SECONDS), "Timeout waiting for processes to start"); - // Debugging; list allChildren that are not expected in processes - List allChildren = ProcessUtil.getAllChildren(p1Handle); - long count = allChildren.stream() + // Debugging; list descendants that are not expected in processes + List descendants = ProcessUtil.getDescendants(p1Handle); + long count = descendants.stream() .filter(ph -> !processes.containsKey(ph)) .count(); if (count > 0) { - allChildren.stream() + descendants.stream() .filter(ph -> !processes.containsKey(ph)) .forEach(ph1 -> ProcessUtil.printProcess(ph1, "Extra process: ")); ProcessUtil.logTaskList(); - Assert.assertEquals(0, count, "Extra processes in allChildren"); + Assert.assertEquals(0, count, "Extra processes in descendants"); } Assert.assertEquals(getChildren(p1Handle).size(), factor, "expected direct children"); - count = getAllChildren(p1Handle).size(); + count = getDescendants(p1Handle).size(); long totalChildren = factor * factor * factor + factor * factor + factor; Assert.assertTrue(count >= totalChildren, "expected at least " + totalChildren + ", actual: " + count); - List subprocesses = getAllChildren(p1Handle); - printf(" allChildren: %s%n", + List subprocesses = getDescendants(p1Handle); + printf(" descendants: %s%n", subprocesses.stream().map(p -> p.getPid()) .collect(Collectors.toList())); diff --git a/jdk/test/java/lang/String/Chars.java b/jdk/test/java/lang/String/Chars.java new file mode 100644 index 00000000000..ab6771b8e0b --- /dev/null +++ b/jdk/test/java/lang/String/Chars.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + @test + @bug 8054307 + @summary test chars() and codePoints() +*/ + +import java.util.Arrays; +import java.util.Random; + +public class Chars { + + public static void main(String[] args) { + Random r = new Random(); + for (int i = 0; i < 10; i++) { + int n = 100 + r.nextInt(100); + char[] cc = new char[n]; + int[] ccExp = new int[n]; + int[] cpExp = new int[n]; + // latin1 + for (int j = 0; j < n; j++) { + cc[j] = (char)(ccExp[j] = cpExp[j] = r.nextInt(0x80)); + } + testChars(cc, ccExp); + testCPs(cc, cpExp); + + // bmp without surrogates + for (int j = 0; j < n; j++) { + cc[j] = (char)(ccExp[j] = cpExp[j] = r.nextInt(0x8000)); + } + testChars(cc, ccExp); + testCPs(cc, cpExp); + + // bmp with surrogates + int k = 0; + for (int j = 0; j < n; j++) { + if (j % 9 == 5 && j + 1 < n) { + int cp = 0x10000 + r.nextInt(2000); + cpExp[k++] = cp; + Character.toChars(cp, cc, j); + ccExp[j] = cc[j]; + ccExp[j + 1] = cc[j + 1]; + j++; + } else { + cc[j] = (char)(ccExp[j] = cpExp[k++] = r.nextInt(0x8000)); + } + } + cpExp = Arrays.copyOf(cpExp, k); + testChars(cc, ccExp); + testCPs(cc, cpExp); + } + } + + static void testChars(char[] cc, int[] expected) { + String str = new String(cc); + if (!Arrays.equals(expected, str.chars().toArray())) { + throw new RuntimeException("chars/codePoints() failed!"); + } + } + + static void testCPs(char[] cc, int[] expected) { + String str = new String(cc); + if (!Arrays.equals(expected, str.codePoints().toArray())) { + throw new RuntimeException("chars/codePoints() failed!"); + } + } +} diff --git a/jdk/test/java/lang/String/CompactString/CharAt.java b/jdk/test/java/lang/String/CompactString/CharAt.java new file mode 100644 index 00000000000..f70c1437e84 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/CharAt.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.util.stream.IntStream; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.charAt. + * @run testng/othervm -XX:+CompactStrings CharAt + * @run testng/othervm -XX:-CompactStrings CharAt + */ + +public class CharAt extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + new Object[] { STRING_L1, new char[] { 'A' } }, + new Object[] { STRING_L2, new char[] { 'A', 'B' } }, + new Object[] { STRING_L4, new char[] { 'A', 'B', 'C', 'D' } }, + new Object[] { STRING_LLONG, + new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' } }, + new Object[] { STRING_U1, new char[] { '\uFF21' } }, + new Object[] { STRING_U2, new char[] { '\uFF21', '\uFF22' } }, + new Object[] { STRING_M12, new char[] { '\uFF21', 'A' } }, + new Object[] { STRING_M11, new char[] { 'A', '\uFF21' } }, }; + } + + @Test(dataProvider = "provider") + public void testCharAt(String str, char[] expected) { + map.get(str) + .forEach( + (source, data) -> { + IntStream + .range(0, str.length()) + .forEach( + i -> assertEquals( + str.charAt(i), + expected[i], + String.format( + "testing String(%s).charAt(%d), source : %s, ", + escapeNonASCIIs(data), + i, source))); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/CodePointAt.java b/jdk/test/java/lang/String/CompactString/CodePointAt.java new file mode 100644 index 00000000000..5333f0bb6b8 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/CodePointAt.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.util.stream.IntStream; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.codePointAt. + * @run testng/othervm -XX:+CompactStrings CodePointAt + * @run testng/othervm -XX:-CompactStrings CodePointAt + */ + +public class CodePointAt extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_L1, new int[] { 'A' } }, + new Object[] { STRING_L2, new int[] { 'A', 'B' } }, + new Object[] { STRING_L4, new int[] { 'A', 'B', 'C', 'D' } }, + new Object[] { STRING_LLONG, + new int[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' } }, + new Object[] { STRING_U1, new int[] { '\uFF21' } }, + new Object[] { STRING_U2, new int[] { '\uFF21', '\uFF22' } }, + new Object[] { STRING_M12, new int[] { '\uFF21', 'A' } }, + new Object[] { STRING_M11, new int[] { 'A', '\uFF21' } }, + new Object[] { + STRING_SUPPLEMENTARY, + new int[] { Character.toCodePoint('\uD801', '\uDC00'), + '\uDC00', + Character.toCodePoint('\uD801', '\uDC01'), + '\uDC01', '\uFF21', 'A' }, } }; + } + + @Test(dataProvider = "provider") + public void testCodePointAt(String str, int[] expected) { + map.get(str) + .forEach( + (source, data) -> { + IntStream + .range(0, str.length()) + .forEach( + i -> assertEquals( + str.codePointAt(i), + expected[i], + String.format( + "testing String(%s).codePointAt(%d), source : %s, ", + escapeNonASCIIs(data), + i, source))); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/CodePointBefore.java b/jdk/test/java/lang/String/CompactString/CodePointBefore.java new file mode 100644 index 00000000000..81f16bce54c --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/CodePointBefore.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.util.stream.IntStream; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.codePointBefore. + * @run testng/othervm -XX:+CompactStrings CodePointBefore + * @run testng/othervm -XX:-CompactStrings CodePointBefore + */ + +public class CodePointBefore extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_L1, new int[] { 'A' } }, + new Object[] { STRING_L2, new int[] { 'A', 'B' } }, + new Object[] { STRING_L4, new int[] { 'A', 'B', 'C', 'D' } }, + new Object[] { STRING_LLONG, + new int[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' } }, + new Object[] { STRING_U1, new int[] { '\uFF21' } }, + new Object[] { STRING_U2, new int[] { '\uFF21', '\uFF22' } }, + new Object[] { STRING_M12, new int[] { '\uFF21', 'A' } }, + new Object[] { STRING_M11, new int[] { 'A', '\uFF21' } }, + new Object[] { + STRING_SUPPLEMENTARY, + new int[] { '\uD801', Character.toCodePoint('\uD801', '\uDC00'), + '\uD801', Character.toCodePoint('\uD801', '\uDC01'), + '\uFF21', 'A' }, } }; + } + + @Test(dataProvider = "provider") + public void testCodePointBefore(String str, int[] expected) { + map.get(str) + .forEach( + (source, data) -> { + IntStream + .range(0, str.length()) + .forEach( + i -> assertEquals( + str.codePointBefore(i + 1), + expected[i], + String.format( + "testing String(%s).codePointBefore(%d), source : %s, ", + escapeNonASCIIs(data), + i + 1, source))); + }); + } + +} diff --git a/jdk/test/java/lang/String/CompactString/CodePointCount.java b/jdk/test/java/lang/String/CompactString/CodePointCount.java new file mode 100644 index 00000000000..0ddc82add5d --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/CodePointCount.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.codePointCount. + * @run testng/othervm -XX:+CompactStrings CodePointCount + * @run testng/othervm -XX:-CompactStrings CodePointCount + */ + +public class CodePointCount extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { new Object[] { STRING_EMPTY, 0, 0, 0 }, + new Object[] { STRING_L1, 0, 1, 1 }, + new Object[] { STRING_L1, 1, 1, 0 }, + new Object[] { STRING_L2, 0, 2, 2 }, + new Object[] { STRING_L2, 0, 1, 1 }, + new Object[] { STRING_L2, 1, 2, 1 }, + new Object[] { STRING_L4, 0, 4, 4 }, + new Object[] { STRING_L4, 0, 1, 1 }, + new Object[] { STRING_L4, 2, 4, 2 }, + new Object[] { STRING_LLONG, 0, 8, 8 }, + new Object[] { STRING_LLONG, 0, 5, 5 }, + new Object[] { STRING_LLONG, 4, 8, 4 }, + new Object[] { STRING_LLONG, 0, 7, 7 }, + new Object[] { STRING_U1, 0, 1, 1 }, + new Object[] { STRING_U2, 0, 2, 2 }, + new Object[] { STRING_U2, 0, 1, 1 }, + new Object[] { STRING_U2, 1, 2, 1 }, + new Object[] { STRING_M12, 0, 2, 2 }, + new Object[] { STRING_M12, 0, 1, 1 }, + new Object[] { STRING_M12, 1, 2, 1 }, + new Object[] { STRING_M11, 0, 2, 2 }, + new Object[] { STRING_M11, 0, 1, 1 }, + new Object[] { STRING_M11, 1, 2, 1 }, + new Object[] { STRING_SUPPLEMENTARY, 0, 1, 1 }, + new Object[] { STRING_SUPPLEMENTARY, 0, 2, 1 }, + new Object[] { STRING_SUPPLEMENTARY, 0, 3, 2 }, + new Object[] { STRING_SUPPLEMENTARY, 0, 5, 3 }, + new Object[] { STRING_SUPPLEMENTARY, 0, 6, 4 }, + new Object[] { STRING_SUPPLEMENTARY, 1, 4, 2 }, + new Object[] { STRING_SUPPLEMENTARY, 1, 6, 4 }, + new Object[] { STRING_SUPPLEMENTARY, 2, 4, 1 },}; + } + + @Test(dataProvider = "provider") + public void testCodePointCount(String str, int beginIndex, int endIndex, + int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.codePointCount(beginIndex, endIndex), + expected, + String.format( + "testing String(%s).codePointCount(%d, %d), source : %s, ", + escapeNonASCIIs(data), beginIndex, + endIndex, source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/CompactString.java b/jdk/test/java/lang/String/CompactString/CompactString.java new file mode 100644 index 00000000000..c22eacc1c0f --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/CompactString.java @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; + +import org.testng.annotations.BeforeClass; + +/* + * Base class of tests for Compact String. + * + */ +public class CompactString { + + final Map> map = new HashMap<>(); + + enum StringSources { + EMPTY(STRING_EMPTY, BYTE_ARRAY_EMTPY, CHAR_ARRAY_EMPTY, + POINT_ARRAY_EMTPY), LDUPLICATE(STRING_LDUPLICATE, + BYTE_ARRAY_LDUPLICATE, CHAR_ARRAY_LDUPLICATE, + POINT_ARRAY_LDUPLICATE), LLONG(STRING_LLONG, BYTE_ARRAY_LLONG, + CHAR_ARRAY_LLONG, POINT_ARRAY_LLONG), L1(STRING_L1, + BYTE_ARRAY_L1, CHAR_ARRAY_L1, POINT_ARRAY_L1), L2(STRING_L2, + BYTE_ARRAY_L2, CHAR_ARRAY_L2, POINT_ARRAY_L2), L4(STRING_L4, + BYTE_ARRAY_L4, CHAR_ARRAY_L4, POINT_ARRAY_L4), UDUPLICATE( + STRING_UDUPLICATE, BYTE_ARRAY_UDUPLICATE, + CHAR_ARRAY_UDUPLICATE, POINT_ARRAY_UDUPLICATE), U1(STRING_U1, + BYTE_ARRAY_U1, CHAR_ARRAY_U1, POINT_ARRAY_U1), U2(STRING_U2, + BYTE_ARRAY_U2, CHAR_ARRAY_U2, POINT_ARRAY_U2), MDUPLICATE1( + STRING_MDUPLICATE1, BYTE_ARRAY_MDUPLICATE1, + CHAR_ARRAY_MDUPLICATE1, POINT_ARRAY_MDUPLICATE1), MDUPLICATE2( + STRING_MDUPLICATE2, BYTE_ARRAY_MDUPLICATE2, + CHAR_ARRAY_MDUPLICATE2, POINT_ARRAY_MDUPLICATE2), MLONG1( + STRING_MLONG1, BYTE_ARRAY_MLONG1, CHAR_ARRAY_MLONG1, + POINT_ARRAY_MLONG1), MLONG2(STRING_MLONG2, BYTE_ARRAY_MLONG2, + CHAR_ARRAY_MLONG2, POINT_ARRAY_MLONG2), M11(STRING_M11, + BYTE_ARRAY_M11, CHAR_ARRAY_M11, POINT_ARRAY_M11), M12( + STRING_M12, BYTE_ARRAY_M12, CHAR_ARRAY_M12, POINT_ARRAY_M12), SUPPLEMENTARY( + STRING_SUPPLEMENTARY, BYTE_ARRAY_SUPPLEMENTARY, + CHAR_ARRAY_SUPPLEMENTARY, POINT_ARRAY_SUPPLEMENTARY), SUPPLEMENTARY_LOWERCASE( + STRING_SUPPLEMENTARY_LOWERCASE, + BYTE_ARRAY_SUPPLEMENTARY_LOWERCASE, + CHAR_ARRAY_SUPPLEMENTARY_LOWERCASE, + POINT_ARRAY_SUPPLEMENTARY_LOWERCASE); + + private StringSources(String s, byte[] b, char[] c, int[] i) { + str = s; + ba = b; + ca = c; + ia = i; + } + + String getString() { + return str; + } + + byte[] getByteArray() { + return ba; + } + + char[] getCharArray() { + return ca; + } + + int[] getIntArray() { + return ia; + } + + private final String str; + private final byte[] ba; + private final char[] ca; + private final int[] ia; + } + + protected static final String DEFAULT_CHARSET_NAME = "UTF-8"; + protected static final Charset DEFAULT_CHARSET = Charset + .forName(DEFAULT_CHARSET_NAME); + + protected static final String STRING_EMPTY = ""; + protected static final byte[] BYTE_ARRAY_EMTPY = new byte[0]; + protected static final char[] CHAR_ARRAY_EMPTY = new char[0]; + protected static final int[] POINT_ARRAY_EMTPY = new int[0]; + + protected static final String STRING_LDUPLICATE = "ABABABABAB"; + protected static final byte[] BYTE_ARRAY_LDUPLICATE = new byte[] { 'A', 'B', + 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B' }; + protected static final char[] CHAR_ARRAY_LDUPLICATE = new char[] { 'A', 'B', + 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B' }; + protected static final int[] POINT_ARRAY_LDUPLICATE = new int[] { 'A', 'B', + 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B' }; + + protected static final String STRING_LLONG = "ABCDEFGH"; + protected static final byte[] BYTE_ARRAY_LLONG = new byte[] { 'A', 'B', 'C', + 'D', 'E', 'F', 'G', 'H' }; + protected static final char[] CHAR_ARRAY_LLONG = new char[] { 'A', 'B', 'C', + 'D', 'E', 'F', 'G', 'H' }; + protected static final int[] POINT_ARRAY_LLONG = new int[] { 'A', 'B', 'C', + 'D', 'E', 'F', 'G', 'H' }; + + protected static final String STRING_L1 = "A"; + protected static final byte[] BYTE_ARRAY_L1 = new byte[] { 'A' }; + protected static final char[] CHAR_ARRAY_L1 = new char[] { 'A' }; + protected static final int[] POINT_ARRAY_L1 = new int[] { 'A' }; + + protected static final String STRING_L2 = "AB"; + protected static final byte[] BYTE_ARRAY_L2 = new byte[] { 'A', 'B' }; + protected static final char[] CHAR_ARRAY_L2 = new char[] { 'A', 'B' }; + protected static final int[] POINT_ARRAY_L2 = new int[] { 'A', 'B' }; + + protected static final String STRING_L4 = "ABCD"; + protected static final byte[] BYTE_ARRAY_L4 = new byte[] { 'A', 'B', 'C', 'D' }; + protected static final char[] CHAR_ARRAY_L4 = new char[] { 'A', 'B', 'C', 'D' }; + protected static final int[] POINT_ARRAY_L4 = new int[] { 'A', 'B', 'C', 'D' }; + + /* + * Because right now ASCII is the default encoding parameter for source code + * in JDK build environment, so we escape them. same as below. + */ + protected static final String STRING_UDUPLICATE = "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22"; + protected static final byte[] BYTE_ARRAY_UDUPLICATE = getBytes(STRING_UDUPLICATE); + protected static final char[] CHAR_ARRAY_UDUPLICATE = new char[] { '\uFF21', + '\uFF22', '\uFF21', '\uFF22', '\uFF21', '\uFF22', '\uFF21', + '\uFF22', '\uFF21', '\uFF22' }; + protected static final int[] POINT_ARRAY_UDUPLICATE = new int[] { '\uFF21', + '\uFF22', '\uFF21', '\uFF22', '\uFF21', '\uFF22', '\uFF21', + '\uFF22', '\uFF21', '\uFF22' }; + + protected static final String STRING_U1 = "\uFF21"; + protected static final byte[] BYTE_ARRAY_U1 = getBytes(STRING_U1); + protected static final char[] CHAR_ARRAY_U1 = new char[] { '\uFF21' }; + protected static final int[] POINT_ARRAY_U1 = new int[] { '\uFF21' }; + + protected static final String STRING_U2 = "\uFF21\uFF22"; + protected static final byte[] BYTE_ARRAY_U2 = getBytes(STRING_U2); + protected static final char[] CHAR_ARRAY_U2 = new char[] { '\uFF21', '\uFF22' }; + protected static final int[] POINT_ARRAY_U2 = new int[] { '\uFF21', '\uFF22' }; + + protected static final String STRING_MDUPLICATE1 = "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A"; + protected static final byte[] BYTE_ARRAY_MDUPLICATE1 = getBytes(STRING_MDUPLICATE1); + protected static final char[] CHAR_ARRAY_MDUPLICATE1 = new char[] { '\uFF21', + 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A' }; + protected static final int[] POINT_ARRAY_MDUPLICATE1 = new int[] { '\uFF21', + 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A' }; + + protected static final String STRING_MDUPLICATE2 = "A\uFF21A\uFF21A\uFF21A\uFF21A\uFF21"; + protected static final byte[] BYTE_ARRAY_MDUPLICATE2 = getBytes(STRING_MDUPLICATE2); + protected static final char[] CHAR_ARRAY_MDUPLICATE2 = new char[] { 'A', + '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', + '\uFF21' }; + protected static final int[] POINT_ARRAY_MDUPLICATE2 = new int[] { 'A', + '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', + '\uFF21' }; + + protected static final String STRING_MLONG1 = "A\uFF21B\uFF22C\uFF23D\uFF24E\uFF25F\uFF26G\uFF27H\uFF28"; + protected static final byte[] BYTE_ARRAY_MLONG1 = getBytes(STRING_MLONG1); + protected static final char[] CHAR_ARRAY_MLONG1 = new char[] { 'A', '\uFF21', + 'B', '\uFF22', 'C', '\uFF23', 'D', '\uFF24', 'E', '\uFF25', 'F', + '\uFF26', 'G', '\uFF27', 'H', '\uFF28' }; + protected static final int[] POINT_ARRAY_MLONG1 = new int[] { 'A', '\uFF21', + 'B', '\uFF22', 'C', '\uFF23', 'D', '\uFF24', 'E', '\uFF25', 'F', + '\uFF26', 'G', '\uFF27', 'H', '\uFF28' }; + + protected static final String STRING_MLONG2 = "\uFF21A\uFF22B\uFF23C\uFF24D\uFF25E\uFF26F\uFF27G\uFF28H"; + protected static final byte[] BYTE_ARRAY_MLONG2 = getBytes(STRING_MLONG2); + protected static final char[] CHAR_ARRAY_MLONG2 = new char[] { '\uFF21', 'A', + '\uFF22', 'B', '\uFF23', 'C', '\uFF24', 'D', '\uFF25', 'E', + '\uFF26', 'F', '\uFF27', 'G', '\uFF28', 'H' }; + protected static final int[] POINT_ARRAY_MLONG2 = new int[] { '\uFF21', 'A', + '\uFF22', 'B', '\uFF23', 'C', '\uFF24', 'D', '\uFF25', 'E', + '\uFF26', 'F', '\uFF27', 'G', '\uFF28', 'H' }; + + protected static final String STRING_M11 = "A\uFF21"; + protected static final byte[] BYTE_ARRAY_M11 = getBytes(STRING_M11); + protected static final char[] CHAR_ARRAY_M11 = new char[] { 'A', '\uFF21' }; + protected static final int[] POINT_ARRAY_M11 = new int[] { 'A', '\uFF21' }; + + protected static final String STRING_M12 = "\uFF21A"; + protected static final byte[] BYTE_ARRAY_M12 = getBytes(STRING_M12); + protected static final char[] CHAR_ARRAY_M12 = new char[] { '\uFF21', 'A' }; + protected static final int[] POINT_ARRAY_M12 = new int[] { '\uFF21', 'A' }; + + protected static final String STRING_SUPPLEMENTARY = "\uD801\uDC00\uD801\uDC01\uFF21A"; + protected static final byte[] BYTE_ARRAY_SUPPLEMENTARY = getBytes(STRING_SUPPLEMENTARY); + protected static final char[] CHAR_ARRAY_SUPPLEMENTARY = new char[] { + '\uD801', '\uDC00', '\uD801', '\uDC01', '\uFF21', 'A' }; + protected static final int[] POINT_ARRAY_SUPPLEMENTARY = new int[] { + '\uD801', '\uDC00', '\uD801', '\uDC01', '\uFF21', 'A' }; + + protected static final String STRING_SUPPLEMENTARY_LOWERCASE = "\uD801\uDC28\uD801\uDC29\uFF41a"; + protected static final byte[] BYTE_ARRAY_SUPPLEMENTARY_LOWERCASE = getBytes(STRING_SUPPLEMENTARY_LOWERCASE); + protected static final char[] CHAR_ARRAY_SUPPLEMENTARY_LOWERCASE = new char[] { + '\uD801', '\uDC28', '\uD801', '\uDC29', '\uFF41', 'a' }; + protected static final int[] POINT_ARRAY_SUPPLEMENTARY_LOWERCASE = new int[] { + '\uD801', '\uDC28', '\uD801', '\uDC29', '\uFF41', 'a' }; + + protected static final String SRC_BYTE_ARRAY_WITH_CHARSETNAME = "source from byte array with charset name"; + protected static final String SRC_BYTE_ARRAY_WITH_CHARSET = "source from byte array with charset"; + protected static final String SRC_CHAR_ARRAY = "source from char array"; + protected static final String SRC_POINT_ARRAY = "source from code point array"; + protected static final String SRC_STRING = "source from String"; + protected static final String SRC_STRINGBUFFER = "source from StringBuffer"; + protected static final String SRC_STRINGBUILDER = "source from StringBuilder"; + protected static final String SRC_COPYVALUEOF = "source from copyValueOf from char array"; + protected static final String SRC_VALUEOF = "source from valueOf from char array"; + + static { + System.out + .println(String + .format("====== The platform's default charset is \"%s\", we're using \"%s\" for testing.", + Charset.defaultCharset().name(), + DEFAULT_CHARSET_NAME)); + } + + private static byte[] getBytes(String str) { + byte[] res = null; + try { + res = str.getBytes(DEFAULT_CHARSET_NAME); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + throw new RuntimeException("caught UnsupportedEncodingException!!!", e); + } + return res; + } + + private void setUpOneString(String content, byte[] ba, char[] ca, int[] cpa) + throws UnsupportedEncodingException { + final Map m = new HashMap<>(); + m.put(SRC_BYTE_ARRAY_WITH_CHARSETNAME, new String(ba, + DEFAULT_CHARSET_NAME)); + m.put(SRC_BYTE_ARRAY_WITH_CHARSET, new String(ba, DEFAULT_CHARSET)); + m.put(SRC_CHAR_ARRAY, new String(ca)); + m.put(SRC_POINT_ARRAY, new String(cpa, 0, cpa.length)); + m.put(SRC_STRING, new String(content)); + m.put(SRC_STRINGBUFFER, new String(new StringBuffer(content))); + m.put(SRC_STRINGBUILDER, new String(new StringBuilder(content))); + m.put(SRC_COPYVALUEOF, String.copyValueOf(ca)); + m.put(SRC_VALUEOF, String.valueOf(ca)); + map.put(content, m); + } + + /* + * Set up the test data, use 9 ways to construct one String. + * + * @throws UnsupportedEncodingException + * If the named charset is not supported in setUpOneString(xxx). + */ + @BeforeClass + public void setUp() throws UnsupportedEncodingException { + for (StringSources src : StringSources.values()) { + setUpOneString(src.getString(), src.getByteArray(), + src.getCharArray(), src.getIntArray()); + } + } + + /* + * Because right now system default charset in JPRT environment is only + * guaranteed to support ASCII characters in log, so we escape them. + */ + protected String escapeNonASCIIs(String str) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c > 0x7F) { + sb.append("\\u").append(Integer.toHexString((int) c)); + } else { + sb.append(c); + } + } + return sb.toString(); + } + + /* + * Because right now system default charset in JPRT environment is only + * guaranteed to support ASCII characters in log, so we escape them. + */ + protected String escapeNonASCII(char c) { + StringBuilder sb = new StringBuilder(); + if (c > 0x7F) { + sb.append("\\u").append(Integer.toHexString((int) c)); + } else { + sb.append(c); + } + return sb.toString(); + } +} diff --git a/jdk/test/java/lang/String/CompactString/CompareTo.java b/jdk/test/java/lang/String/CompactString/CompareTo.java new file mode 100644 index 00000000000..96f07cdf610 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/CompareTo.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.compareTo. + * @run testng/othervm -XX:+CompactStrings CompareTo + * @run testng/othervm -XX:-CompactStrings CompareTo + */ + +public class CompareTo extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "A", -1 }, + new Object[] { STRING_EMPTY, "\uFF21", -1 }, + new Object[] { STRING_L1, "AB", -1 }, + new Object[] { STRING_L1, "A", 0 }, + new Object[] { STRING_L1, "a", -32 }, + new Object[] { STRING_L1, "\uFF21", -65248 }, + new Object[] { STRING_L2, "AB", 0 }, + new Object[] { STRING_L2, "Ab", -32 }, + new Object[] { STRING_L2, "AA", 1 }, + new Object[] { STRING_L2, "\uFF21", -65248 }, + new Object[] { STRING_L2, "A\uFF21", -65247 }, + new Object[] { STRING_L4, "ABC", 1 }, + new Object[] { STRING_L4, "AB", 2 }, + new Object[] { STRING_L4, "ABcD", -32 }, + new Object[] { STRING_L4, "ABCD\uFF21\uFF21", -2 }, + new Object[] { STRING_L4, "ABCD\uFF21", -1 }, + new Object[] { STRING_LLONG, "ABCDEFG\uFF21", -65241 }, + new Object[] { STRING_LLONG, "AB", 6 }, + new Object[] { STRING_LLONG, "ABCD", 4 }, + new Object[] { STRING_LLONG, "ABCDEFGH\uFF21\uFF21", -2 }, + new Object[] { STRING_U1, "\uFF21", 0 }, + new Object[] { STRING_U1, "\uFF22", -1 }, + new Object[] { STRING_U1, "\uFF21\uFF22", -1 }, + new Object[] { STRING_U1, "A", 65248 }, + new Object[] { STRING_U2, "\uFF21\uFF22", 0 }, + new Object[] { STRING_U2, "\uFF22", -1 }, + new Object[] { STRING_U2, "\uFF21\uFF21", 1 }, + new Object[] { STRING_U2, "A", 65248 }, + new Object[] { STRING_M12, "\uFF21A", 0 }, + new Object[] { STRING_M12, "A\uFF21", 65248 }, + new Object[] { STRING_M12, "\uFF21\uFF21", -65248 }, + new Object[] { STRING_M11, "A\uFF21", 0 }, + new Object[] { STRING_M11, "\uFF21A", -65248 }, + new Object[] { STRING_M11, "AA", 65248 }, }; + } + + @Test(dataProvider = "provider") + public void testCompareTo(String str, String anotherString, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.compareTo(anotherString), + expected, + String.format( + "testing String(%s).compareTo(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/CompareToIgnoreCase.java b/jdk/test/java/lang/String/CompactString/CompareToIgnoreCase.java new file mode 100644 index 00000000000..3b9cef794c0 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/CompareToIgnoreCase.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.compareToIgnoreCase. + * @run testng/othervm -XX:+CompactStrings CompareToIgnoreCase + * @run testng/othervm -XX:-CompactStrings CompareToIgnoreCase + */ + +public class CompareToIgnoreCase extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "A", -1 }, + new Object[] { STRING_L1, "a", 0 }, + new Object[] { STRING_L1, "A", 0 }, + new Object[] { STRING_L1, "\uFF21", -65248 }, + new Object[] { STRING_L1, "B", -1 }, + new Object[] { STRING_L2, "AB", 0 }, + new Object[] { STRING_L2, "aB", 0 }, + new Object[] { STRING_L2, "\uFF21", -65248 }, + new Object[] { STRING_L2, "A\uFF21", -65247 }, + new Object[] { STRING_L4, "ABCD", 0 }, + new Object[] { STRING_L4, "abcd", 0 }, + new Object[] { STRING_L4, "ABc\uFF21", -65245 }, + new Object[] { STRING_LLONG, "ABCDEFGH", 0 }, + new Object[] { STRING_LLONG, "abcdefgh", 0 }, + new Object[] { STRING_LLONG, "ABCDEFG\uFF21", -65241 }, + new Object[] { STRING_LLONG, "abcdefg\uFF21", -65241 }, + new Object[] { STRING_U1, "\uFF41", 0 }, + new Object[] { STRING_U1, + "\uFF41\uFF42\uFF43\uFF44\uFF45\uFF46\uFF47\uFF48", -7 }, + new Object[] { STRING_U1, "A", 65248 }, + new Object[] { STRING_U2, "\uFF41", 1 }, + new Object[] { STRING_U2, "\uFF41\uFF42", 0 }, + new Object[] { STRING_U2, + "\uFF41\uFF42\uFF43\uFF44\uFF45\uFF46\uFF47\uFF48", -6 }, + new Object[] { STRING_M12, "\uFF41a", 0 }, + new Object[] { STRING_M12, "\uFF41\uFF42", -65249 }, + new Object[] { STRING_M11, "a\uFF41", 0 }, + new Object[] { STRING_M11, "a\uFF42", -1 }, }; + } + + @Test(dataProvider = "provider") + public void testCompareToIgnoreCase(String str, String anotherString, + int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.compareToIgnoreCase(anotherString), + expected, + String.format( + "testing String(%s).compareToIgnoreCase(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/Concat.java b/jdk/test/java/lang/String/CompactString/Concat.java new file mode 100644 index 00000000000..de1238930c6 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/Concat.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.concat. + * @run testng/othervm -XX:+CompactStrings Concat + * @run testng/othervm -XX:-CompactStrings Concat + */ + +public class Concat extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + new Object[] { STRING_EMPTY, "ABC", "ABC" }, + new Object[] { STRING_EMPTY, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "ABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_EMPTY, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_L1, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "AABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_L1, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "A\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_L2, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "ABABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_L2, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "AB\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_L4, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "ABCDABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_L4, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "ABCD\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_LLONG, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "ABCDEFGHABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_LLONG, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "ABCDEFGH\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_U1, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "\uFF21ABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_U1, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "\uFF21\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_U2, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "\uFF21\uFF22ABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_U2, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "\uFF21\uFF22\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_M12, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "\uFF21AABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_M12, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "\uFF21A\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_M11, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "A\uFF21ABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_M11, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "A\uFF21\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, }; + } + + @Test(dataProvider = "provider") + public void testConcat(String str, String anotherString, String expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.concat(anotherString), + expected, + String.format( + "testing String(%s).concat(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/Contains.java b/jdk/test/java/lang/String/CompactString/Contains.java new file mode 100644 index 00000000000..ad182b8212e --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/Contains.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.contains. + * @run testng/othervm -XX:+CompactStrings Contains + * @run testng/othervm -XX:-CompactStrings Contains + */ + +public class Contains extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "", true }, + new Object[] { STRING_EMPTY, "A", false }, + new Object[] { STRING_EMPTY, "\uFF21", false }, + new Object[] { STRING_L1, "", true }, + new Object[] { STRING_L1, "A", true }, + new Object[] { STRING_L1, "\uFF21", false }, + new Object[] { STRING_L2, "", true }, + new Object[] { STRING_L2, "A", true }, + new Object[] { STRING_L2, "AB", true }, + new Object[] { STRING_L2, "B", true }, + new Object[] { STRING_L2, "ABC", false }, + new Object[] { STRING_L2, "ab", false }, + new Object[] { STRING_L4, "ABCD", true }, + new Object[] { STRING_L4, "BC", true }, + new Object[] { STRING_LLONG, "ABCDEFGH", true }, + new Object[] { STRING_LLONG, "BCDEFGH", true }, + new Object[] { STRING_LLONG, "EF", true }, + new Object[] { STRING_U1, "", true }, + new Object[] { STRING_U1, "\uFF21", true }, + new Object[] { STRING_U1, "a", false }, + new Object[] { STRING_U1, "\uFF21B", false }, + new Object[] { STRING_U2, "", true }, + new Object[] { STRING_U2, "\uFF21\uFF22", true }, + new Object[] { STRING_U2, "a", false }, + new Object[] { STRING_U2, "\uFF21B", false }, + new Object[] { STRING_M12, "\uFF21A", true }, + new Object[] { STRING_M12, "\uFF21", true }, + new Object[] { STRING_M12, "A", true }, + new Object[] { STRING_M12, "A\uFF21", false }, + new Object[] { STRING_M11, "A\uFF21", true }, + new Object[] { STRING_M11, "Ab", false }, }; + } + + @Test(dataProvider = "provider") + public void testContains(String str, String anotherString, boolean expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.contains(anotherString), + expected, + String.format( + "testing String(%s).contains(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/EndsWith.java b/jdk/test/java/lang/String/CompactString/EndsWith.java new file mode 100644 index 00000000000..07485b4eb71 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/EndsWith.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.endsWith. + * @run testng/othervm -XX:+CompactStrings EndsWith + * @run testng/othervm -XX:-CompactStrings EndsWith + */ + +public class EndsWith extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { new Object[] { STRING_EMPTY, "", true }, + new Object[] { STRING_EMPTY, "A", false }, + new Object[] { STRING_L1, "A", true }, + new Object[] { STRING_L1, "", true }, + new Object[] { STRING_L1, " ", false }, + new Object[] { STRING_L2, "AB", true }, + new Object[] { STRING_L2, "B", true }, + new Object[] { STRING_L2, "", true }, + new Object[] { STRING_L2, "A", false }, + new Object[] { STRING_L4, "ABCD", true }, + new Object[] { STRING_L4, "CD", true }, + new Object[] { STRING_L4, "D", true }, + new Object[] { STRING_L4, "", true }, + new Object[] { STRING_L4, "BC", false }, + new Object[] { STRING_LLONG, "ABCDEFGH", true }, + new Object[] { STRING_LLONG, "EFGH", true }, + new Object[] { STRING_LLONG, "", true }, + new Object[] { STRING_LLONG, "CDEF", false }, + new Object[] { STRING_LLONG, "\uFF28", false }, + new Object[] { STRING_U1, "\uFF21", true }, + new Object[] { STRING_U1, "", true }, + new Object[] { STRING_U1, "\uFF22", false }, + new Object[] { STRING_U1, "B", false }, + new Object[] { STRING_U2, "\uFF21\uFF22", true }, + new Object[] { STRING_U2, "\uFF22", true }, + new Object[] { STRING_U2, "", true }, + new Object[] { STRING_U2, "\uFF21", false }, + new Object[] { STRING_M12, "\uFF21A", true }, + new Object[] { STRING_M12, "A", true }, + new Object[] { STRING_M12, "", true }, + new Object[] { STRING_M12, "AA", false }, + new Object[] { STRING_M11, "A\uFF21", true }, + new Object[] { STRING_M11, "\uFF21", true }, + new Object[] { STRING_M11, "", true }, + new Object[] { STRING_M11, "\uFF21\uFF21", false }, }; + } + + @Test(dataProvider = "provider") + public void testEndsWith(String str, String suffix, boolean expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.endsWith(suffix), + expected, + String.format( + "testing String(%s).endsWith(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(suffix), source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/Equals.java b/jdk/test/java/lang/String/CompactString/Equals.java new file mode 100644 index 00000000000..101a015b400 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/Equals.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.equals. + * @run testng/othervm -XX:+CompactStrings Equals + * @run testng/othervm -XX:-CompactStrings Equals + */ + +public class Equals extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + new Object[] { STRING_EMPTY, "", true }, + new Object[] { STRING_EMPTY, "A", false }, + new Object[] { STRING_EMPTY, new StringBuffer(""), false }, + new Object[] { STRING_L1, "A", true }, + new Object[] { STRING_L1, "", false }, + new Object[] { STRING_L1, new StringBuffer("A"), false }, + new Object[] { STRING_L2, "AB", true }, + new Object[] { STRING_L2, "", false }, + new Object[] { STRING_L2, new StringBuilder("AB"), false }, + new Object[] { STRING_L4, "ABCD", true }, + new Object[] { STRING_L4, "abc", false }, + new Object[] { STRING_L4, "", false }, + new Object[] { STRING_LLONG, "ABCDEFGH", true }, + new Object[] { STRING_LLONG, "ABCDEFG", false }, + new Object[] { STRING_LLONG, new StringBuilder("ABCDEFGH"), + false }, + new Object[] { STRING_U1, "\uFF21", true }, + new Object[] { STRING_U1, "", false }, + new Object[] { STRING_U2, "\uFF21\uFF22", true }, + new Object[] { STRING_U2, "\uFF21", false }, + new Object[] { STRING_U2, "", false }, + new Object[] { STRING_U2, new StringBuilder("\uFF21\uFF22"), + false }, + new Object[] { STRING_M12, "\uFF21A", true }, + new Object[] { STRING_M12, "A\uFF21", false }, + new Object[] { STRING_M11, "A\uFF21", true }, + new Object[] { STRING_M11, new StringBuilder("\uFF21A"), false }, }; + } + + @Test(dataProvider = "provider") + public void testEquals(String str, Object obj, boolean expected) { + map.get(str).forEach( + (source, data) -> { + assertEquals(data.equals(obj), expected, String.format( + "testing String(%s).equals(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(obj.toString()), source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/EqualsIgnoreCase.java b/jdk/test/java/lang/String/CompactString/EqualsIgnoreCase.java new file mode 100644 index 00000000000..0721c07b617 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/EqualsIgnoreCase.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.equalsIgnoreCase. + * @run testng/othervm -XX:+CompactStrings EqualsIgnoreCase + * @run testng/othervm -XX:-CompactStrings EqualsIgnoreCase + */ + +public class EqualsIgnoreCase extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "", true }, + new Object[] { STRING_L1, "a", true }, + new Object[] { STRING_L2, "aB", true }, + new Object[] { STRING_L4, "AbCd", true }, + new Object[] { STRING_LLONG, "aBcDeFgH", true }, + new Object[] { STRING_U1, "\uFF41", true }, + new Object[] { STRING_U1, "\uFF21", true }, + new Object[] { STRING_U2, "\uFF41\uFF42", true }, + new Object[] { STRING_U2, "\uFF41\uFF22", true }, + new Object[] { STRING_U2, "\uFF21\uFF42", true }, + new Object[] { STRING_M12, "\uFF41a", true }, + new Object[] { STRING_M12, "\uFF21A", true }, + new Object[] { STRING_M11, "a\uFF41", true }, + new Object[] { STRING_M11, "A\uFF21", true }, + + }; + } + + @Test(dataProvider = "provider") + public void testEqualsIgnoreCase(String str, String anotherString, + boolean expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.equalsIgnoreCase(anotherString), + expected, + String.format( + "testing String(%s).equalsIgnoreCase(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/GetChars.java b/jdk/test/java/lang/String/CompactString/GetChars.java new file mode 100644 index 00000000000..3ea3dce9566 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/GetChars.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.util.Arrays; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.getChars. + * @run testng/othervm -XX:+CompactStrings GetChars + * @run testng/othervm -XX:-CompactStrings GetChars + */ + +public class GetChars extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, 0, STRING_EMPTY.length(), + new char[STRING_EMPTY.length()], 0, CHAR_ARRAY_EMPTY }, + new Object[] { STRING_L1, 0, STRING_L1.length(), + new char[STRING_L1.length()], 0, CHAR_ARRAY_L1 }, + new Object[] { STRING_L2, 0, STRING_L2.length(), + new char[STRING_L2.length()], 0, CHAR_ARRAY_L2 }, + new Object[] { STRING_L4, 0, STRING_L4.length(), + new char[STRING_L4.length()], 0, CHAR_ARRAY_L4 }, + new Object[] { STRING_LLONG, 0, STRING_LLONG.length(), + new char[STRING_LLONG.length()], 0, CHAR_ARRAY_LLONG }, + new Object[] { STRING_U1, 0, STRING_U1.length(), + new char[STRING_U1.length()], 0, CHAR_ARRAY_U1 }, + new Object[] { STRING_U2, 0, STRING_U2.length(), + new char[STRING_U2.length()], 0, CHAR_ARRAY_U2 }, + new Object[] { STRING_M12, 0, STRING_M12.length(), + new char[STRING_M12.length()], 0, CHAR_ARRAY_M12 }, + new Object[] { STRING_M11, 0, STRING_M11.length(), + new char[STRING_M11.length()], 0, CHAR_ARRAY_M11 }, + new Object[] { STRING_UDUPLICATE, 0, + STRING_UDUPLICATE.length(), + new char[STRING_UDUPLICATE.length()], 0, + CHAR_ARRAY_UDUPLICATE }, + new Object[] { STRING_MDUPLICATE1, 0, + STRING_MDUPLICATE1.length(), + new char[STRING_MDUPLICATE1.length()], 0, + CHAR_ARRAY_MDUPLICATE1 }, }; + } + + @Test(dataProvider = "provider") + public void testGetChars(String str, int srcBegin, int srcEnd, char[] dst, + int dstBegin, char[] expected) { + map.get(str) + .forEach( + (source, data) -> { + data.getChars(srcBegin, srcEnd, dst, dstBegin); + assertTrue( + Arrays.equals(dst, expected), + String.format( + "testing String(%s).getChars(%d, %d, %s, %d), source : %s, ", + escapeNonASCIIs(data), srcBegin, + srcEnd, escapeNonASCIIs(Arrays + .toString(dst)), dstBegin, + source)); + }); + } + +} diff --git a/jdk/test/java/lang/String/CompactString/IndexOf.java b/jdk/test/java/lang/String/CompactString/IndexOf.java new file mode 100644 index 00000000000..49fd9e54d05 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/IndexOf.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.indexOf. + * @run testng/othervm -XX:+CompactStrings IndexOf + * @run testng/othervm -XX:-CompactStrings IndexOf + */ + +public class IndexOf extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, (int) 'A', -1 }, + new Object[] { STRING_L1, (int) 'A', 0 }, + new Object[] { STRING_L2, (int) 'A', 0 }, + new Object[] { STRING_L2, (int) 'B', 1 }, + new Object[] { STRING_L4, (int) 'A', 0 }, + new Object[] { STRING_L4, (int) 'D', 3 }, + new Object[] { STRING_L4, (int) 'E', -1 }, + new Object[] { STRING_LLONG, (int) 'A', 0 }, + new Object[] { STRING_LLONG, (int) 'H', 7 }, + new Object[] { STRING_U1, (int) '\uFF21', 0 }, + new Object[] { STRING_U1, (int) 'A', -1 }, + new Object[] { STRING_U2, (int) '\uFF21', 0 }, + new Object[] { STRING_U2, (int) '\uFF22', 1 }, + new Object[] { STRING_M12, (int) '\uFF21', 0 }, + new Object[] { STRING_M12, (int) 'A', 1 }, + new Object[] { STRING_M11, (int) 'A', 0 }, + new Object[] { STRING_M11, (int) '\uFF21', 1 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF21', 0 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF22', 1 }, + new Object[] { STRING_SUPPLEMENTARY, 'A', 5 }, + new Object[] { STRING_SUPPLEMENTARY, '\uFF21', 4 }, + new Object[] { STRING_SUPPLEMENTARY, + Character.toCodePoint('\uD801', '\uDC00'), 0 }, + new Object[] { STRING_SUPPLEMENTARY, + Character.toCodePoint('\uD801', '\uDC01'), 2 }, }; + } + + @Test(dataProvider = "provider") + public void testIndexOf(String str, int ch, int expected) { + map.get(str).forEach( + (source, data) -> { + assertEquals(data.indexOf(ch), expected, String.format( + "testing String(%s).indexOf(%d), source : %s, ", + escapeNonASCIIs(data), ch, source)); + }); + } + + @DataProvider + public Object[][] provider2() { + return new Object[][] { + + new Object[] { STRING_EMPTY, (int) 'A', 0, -1 }, + new Object[] { STRING_L1, (int) 'A', 0, 0 }, + new Object[] { STRING_L1, (int) 'A', 1, -1 }, + new Object[] { STRING_L1, (int) 'B', 0, -1 }, + new Object[] { STRING_L2, (int) 'A', 0, 0 }, + new Object[] { STRING_L2, (int) 'A', 1, -1 }, + new Object[] { STRING_L2, (int) 'B', 0, 1 }, + new Object[] { STRING_L2, (int) 'B', 1, 1 }, + new Object[] { STRING_L4, (int) 'A', 0, 0 }, + new Object[] { STRING_L4, (int) 'D', 2, 3 }, + new Object[] { STRING_L4, (int) 'B', 2, -1 }, + new Object[] { STRING_LLONG, (int) 'A', 0, 0 }, + new Object[] { STRING_LLONG, (int) 'H', 5, 7 }, + new Object[] { STRING_U1, (int) '\uFF21', 0, 0 }, + new Object[] { STRING_U1, (int) 'A', 0, -1 }, + new Object[] { STRING_U2, (int) '\uFF21', 0, 0 }, + new Object[] { STRING_U2, (int) '\uFF22', 0, 1 }, + new Object[] { STRING_M12, (int) '\uFF21', 0, 0 }, + new Object[] { STRING_M12, (int) 'A', 1, 1 }, + new Object[] { STRING_M11, (int) 'A', 0, 0 }, + new Object[] { STRING_M11, (int) '\uFF21', 1, 1 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF21', 1, 2 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF22', 1, 1 }, }; + } + + @Test(dataProvider = "provider2") + public void testIndexOf(String str, int ch, int fromIndex, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.indexOf(ch, fromIndex), + expected, + String.format( + "testing String(%s).indexOf(%d, %d), source : %s, ", + escapeNonASCIIs(data), ch, + fromIndex, source)); + }); + } + + @DataProvider + public Object[][] provider3() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "A", -1 }, + new Object[] { STRING_L1, "A", 0 }, + new Object[] { STRING_L1, "AB", -1 }, + new Object[] { STRING_L2, "A", 0 }, + new Object[] { STRING_L2, "B", 1 }, + new Object[] { STRING_L2, "AB", 0 }, + new Object[] { STRING_L2, "AC", -1 }, + new Object[] { STRING_L2, "ABC", -1 }, + new Object[] { STRING_L4, "ABCD", 0 }, + new Object[] { STRING_L4, "D", 3 }, + new Object[] { STRING_LLONG, "ABCDEFGH", 0 }, + new Object[] { STRING_LLONG, "EFGH", 4 }, + new Object[] { STRING_LLONG, "EFGHI", -1 }, + new Object[] { STRING_U1, "\uFF21", 0 }, + new Object[] { STRING_U1, "\uFF21A", -1 }, + new Object[] { STRING_U2, "\uFF21\uFF22", 0 }, + new Object[] { STRING_U2, "\uFF22", 1 }, + new Object[] { STRING_U2, "A\uFF22", -1 }, + new Object[] { STRING_M12, "\uFF21A", 0 }, + new Object[] { STRING_M12, "A", 1 }, + new Object[] { STRING_M12, "\uFF21\uFF21", -1 }, + new Object[] { STRING_M11, "A\uFF21", 0 }, + new Object[] { STRING_M11, "\uFF21", 1 }, + new Object[] { STRING_M11, "A", 0 }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", + 0 }, + new Object[] { STRING_UDUPLICATE, + "\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21", 1 }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21", + -1 }, }; + } + + @Test(dataProvider = "provider3") + public void testIndexOf(String str, String anotherString, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.indexOf(anotherString), + expected, + String.format( + "testing String(%s).indexOf(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + source)); + }); + } + + @DataProvider + public Object[][] provider4() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "A", 0, -1 }, + new Object[] { STRING_L1, "A", 0, 0 }, + new Object[] { STRING_L1, "A", 1, -1 }, + new Object[] { STRING_L1, "AB", 0, -1 }, + new Object[] { STRING_L2, "A", 0, 0 }, + new Object[] { STRING_L2, "B", 0, 1 }, + new Object[] { STRING_L2, "AB", 0, 0 }, + new Object[] { STRING_L2, "AB", 1, -1 }, + new Object[] { STRING_L4, "ABCD", 0, 0 }, + new Object[] { STRING_L4, "BC", 0, 1 }, + new Object[] { STRING_L4, "A", 0, 0 }, + new Object[] { STRING_L4, "CD", 0, 2 }, + new Object[] { STRING_L4, "A", 2, -1 }, + new Object[] { STRING_L4, "ABCDE", 0, -1 }, + new Object[] { STRING_LLONG, "ABCDEFGH", 0, 0 }, + new Object[] { STRING_LLONG, "DEFGH", 0, 3 }, + new Object[] { STRING_LLONG, "A", 0, 0 }, + new Object[] { STRING_LLONG, "GHI", 0, -1 }, + new Object[] { STRING_U1, "\uFF21", 0, 0 }, + new Object[] { STRING_U1, "\uFF21A", 0, -1 }, + new Object[] { STRING_U2, "\uFF21\uFF22", 0, 0 }, + new Object[] { STRING_U2, "\uFF22", 0, 1 }, + new Object[] { STRING_U2, "\uFF21", 1, -1 }, + new Object[] { STRING_M12, "\uFF21A", 0, 0 }, + new Object[] { STRING_M12, "A", 1, 1 }, + new Object[] { STRING_M12, "\uFF21A", 1, -1 }, + new Object[] { STRING_M12, "\uFF21", 0, 0 }, + new Object[] { STRING_M11, "A\uFF21", 0, 0 }, + new Object[] { STRING_M11, "\uFF21", 1, 1 }, + new Object[] { STRING_M11, "A\uFF21", 1, -1 }, + new Object[] { STRING_M11, "A\uFF21A", 0, -1 }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", + 0, 0 }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", + 1, -1 }, + new Object[] { + STRING_UDUPLICATE, + "\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", + 1, 1 }, + new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22\uFF21\uFF22", + 4, 4 }, + new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22\uFF21\uFF22", + 7, -1 }, }; + } + + @Test(dataProvider = "provider4") + public void testIndexOf(String str, String anotherString, int fromIndex, + int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.indexOf(anotherString, fromIndex), + expected, + String.format( + "testing String(%s).indexOf(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + fromIndex, source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/Intern.java b/jdk/test/java/lang/String/CompactString/Intern.java new file mode 100644 index 00000000000..fc2b06d29eb --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/Intern.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.intern. + * @run testng/othervm -XX:+CompactStrings Intern + * @run testng/othervm -XX:-CompactStrings Intern + */ + +public class Intern extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "" }, + new Object[] { STRING_L1, "A" }, + new Object[] { STRING_LLONG, "ABCDEFGH" }, + new Object[] { STRING_U1, "\uFF21" }, + new Object[] { STRING_U2, "\uFF21\uFF22" }, + new Object[] { STRING_M12, "\uFF21A" }, + new Object[] { STRING_M11, "A\uFF21" }, + new Object[] { STRING_MDUPLICATE1, + "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A" }, }; + } + + @Test(dataProvider = "provider") + public void testIntern(String str, String expected) { + map.get(str).forEach( + (source, data) -> { + assertTrue(data.intern() == expected, String.format( + "testing String(%s).intern(), source : %s, ", + escapeNonASCIIs(data), source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/LastIndexOf.java b/jdk/test/java/lang/String/CompactString/LastIndexOf.java new file mode 100644 index 00000000000..eccc9cc41ad --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/LastIndexOf.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.lastIndexOf. + * @run testng/othervm -XX:+CompactStrings LastIndexOf + * @run testng/othervm -XX:-CompactStrings LastIndexOf + */ + +public class LastIndexOf extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, (int) 'A', -1 }, + new Object[] { STRING_L1, (int) 'A', 0 }, + new Object[] { STRING_L2, (int) 'A', 0 }, + new Object[] { STRING_L2, (int) 'B', 1 }, + new Object[] { STRING_L4, (int) 'A', 0 }, + new Object[] { STRING_L4, (int) 'D', 3 }, + new Object[] { STRING_LLONG, (int) 'A', 0 }, + new Object[] { STRING_LLONG, (int) 'H', 7 }, + new Object[] { STRING_U1, (int) '\uFF21', 0 }, + new Object[] { STRING_U1, (int) 'B', -1 }, + new Object[] { STRING_U2, (int) '\uFF21', 0 }, + new Object[] { STRING_U2, (int) '\uFF22', 1 }, + new Object[] { STRING_M12, (int) '\uFF21', 0 }, + new Object[] { STRING_M12, (int) 'A', 1 }, + new Object[] { STRING_M11, (int) 'A', 0 }, + new Object[] { STRING_M11, (int) '\uFF21', 1 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF22', 9 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF21', 8 }, + new Object[] { STRING_SUPPLEMENTARY, + Character.toCodePoint('\uD801', '\uDC01'), 2 }, }; + } + + @Test(dataProvider = "provider") + public void testLastIndexOf(String str, int ch, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.lastIndexOf(ch), + expected, + String.format( + "testing String(%s).lastIndexOf(%d), source : %s, ", + escapeNonASCIIs(data), ch, source)); + }); + } + + @DataProvider + public Object[][] provider2() { + return new Object[][] { + + new Object[] { STRING_EMPTY, (int) 'A', 0, -1 }, + new Object[] { STRING_L1, (int) 'A', 0, 0 }, + new Object[] { STRING_L1, (int) 'A', 1, 0 }, + new Object[] { STRING_L2, (int) 'A', 0, 0 }, + new Object[] { STRING_L2, (int) 'B', 1, 1 }, + new Object[] { STRING_L2, (int) 'B', 2, 1 }, + new Object[] { STRING_L4, (int) 'A', 0, 0 }, + new Object[] { STRING_L4, (int) 'C', 2, 2 }, + new Object[] { STRING_L4, (int) 'C', 1, -1 }, + new Object[] { STRING_LLONG, (int) 'A', 0, 0 }, + new Object[] { STRING_LLONG, (int) 'H', 7, 7 }, + new Object[] { STRING_LLONG, (int) 'H', 6, -1 }, + new Object[] { STRING_U1, (int) '\uFF21', 0, 0 }, + new Object[] { STRING_U1, (int) '\uFF21', 7, 0 }, + new Object[] { STRING_U2, (int) '\uFF21', 0, 0 }, + new Object[] { STRING_U2, (int) '\uFF22', 0, -1 }, + new Object[] { STRING_M12, (int) '\uFF21', 0, 0 }, + new Object[] { STRING_M12, (int) 'A', 1, 1 }, + new Object[] { STRING_M12, (int) 'A', 0, -1 }, + new Object[] { STRING_M11, (int) 'A', 0, 0 }, + new Object[] { STRING_M11, (int) '\uFF21', 1, 1 }, + new Object[] { STRING_M11, (int) '\uFF21', 0, -1 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF21', 5, 4 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF21', 6, 6 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF22', 5, 5 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF22', 6, 5 }, }; + } + + @Test(dataProvider = "provider2") + public void testLastIndexOf(String str, int ch, int fromIndex, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.lastIndexOf(ch, fromIndex), + expected, + String.format( + "testing String(%s).lastIndexOf(%d, %d), source : %s, ", + escapeNonASCIIs(data), ch, + fromIndex, source)); + }); + } + + @DataProvider + public Object[][] provider3() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "A", -1 }, + new Object[] { STRING_L1, "A", 0 }, + new Object[] { STRING_L1, "AB", -1 }, + + new Object[] { STRING_L2, "AB", 0 }, + new Object[] { STRING_L2, "B", 1 }, + new Object[] { STRING_L4, "ABCD", 0 }, + new Object[] { STRING_L4, "B", 1 }, + new Object[] { STRING_LLONG, "ABCD", 0 }, + new Object[] { STRING_LLONG, "GH", 6 }, + new Object[] { STRING_U1, "\uFF21", 0 }, + new Object[] { STRING_U1, "\uFF22", -1 }, + new Object[] { STRING_U2, "\uFF21\uFF22", 0 }, + new Object[] { STRING_U2, "\uFF22", 1 }, + new Object[] { STRING_M12, "\uFF21A", 0 }, + new Object[] { STRING_M12, "A", 1 }, + new Object[] { STRING_M11, "A\uFF21", 0 }, + new Object[] { STRING_M11, "\uFF21", 1 }, + new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22\uFF21\uFF22", 6 }, + new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22", 8 }, }; + } + + @Test(dataProvider = "provider3") + public void testLastIndexOf(String str, String anotherString, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.lastIndexOf(anotherString), + expected, + String.format( + "testing String(%s).lastIndexOf(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + source)); + }); + } + + @DataProvider + public Object[][] provider4() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "A", 0, -1 }, + new Object[] { STRING_L2, "AB", 0, 0 }, + + new Object[] { STRING_L1, "AB", -1, -1 }, + + new Object[] { STRING_L2, "B", 1, 1 }, + new Object[] { STRING_L2, "B", 0, -1 }, + new Object[] { STRING_L4, "ABC", 3, 0 }, + new Object[] { STRING_L4, "ABC", 0, 0 }, + new Object[] { STRING_L4, "ABC", 1, 0 }, + new Object[] { STRING_L4, "BC", 1, 1 }, + new Object[] { STRING_L4, "BC", 0, -1 }, + new Object[] { STRING_LLONG, "ABCDEFGH", 0, 0 }, + new Object[] { STRING_LLONG, "EFGH", 7, 4 }, + new Object[] { STRING_LLONG, "EFGH", 3, -1 }, + new Object[] { STRING_U1, "\uFF21", 0, 0 }, + new Object[] { STRING_U1, "\uFF21", 7, 0 }, + new Object[] { STRING_U2, "\uFF21\uFF22", 0, 0 }, + new Object[] { STRING_U2, "\uFF21\uFF22", 1, 0 }, + new Object[] { STRING_M12, "\uFF21A", 0, 0 }, + new Object[] { STRING_M12, "A", 1, 1 }, + new Object[] { STRING_M12, "A", 0, -1 }, + new Object[] { STRING_M11, "A\uFF21", 0, 0 }, + new Object[] { STRING_M11, "A\uFF21", 1, 0 }, + new Object[] { STRING_M11, "\uFF21", 0, -1 }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", + 9, 0 }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", + 0, 0 }, + new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22", 6, 6 }, + new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22\uFF21", 6, 6 }, }; + } + + @Test(dataProvider = "provider4") + public void testLastIndexOf(String str, String anotherString, + int fromIndex, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.lastIndexOf(anotherString, fromIndex), + expected, + String.format( + "testing String(%s).lastIndexOf(%s, %d), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + fromIndex, source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/Length.java b/jdk/test/java/lang/String/CompactString/Length.java new file mode 100644 index 00000000000..ab2b9d55082 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/Length.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.length. + * @run testng/othervm -XX:+CompactStrings Length + * @run testng/othervm -XX:-CompactStrings Length + */ + +public class Length extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, 0 }, new Object[] { STRING_L1, 1 }, + new Object[] { STRING_L2, 2 }, + new Object[] { STRING_LLONG, 8 }, + new Object[] { STRING_U1, 1 }, new Object[] { STRING_U2, 2 }, + new Object[] { STRING_M12, 2 }, new Object[] { STRING_M11, 2 }, + new Object[] { STRING_UDUPLICATE, 10 }, + new Object[] { STRING_SUPPLEMENTARY, 6 }, }; + } + + @Test(dataProvider = "provider") + public void testLength(String str, int expected) { + map.get(str).forEach( + (source, data) -> { + assertEquals(data.length(), expected, String.format( + "testing String(%s).length(), source : %s, ", + escapeNonASCIIs(data), source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/Numbers.java b/jdk/test/java/lang/String/CompactString/Numbers.java new file mode 100644 index 00000000000..ad9f17aaace --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/Numbers.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2015, 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. + */ + + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is testing + * Integer/Long's methods related to String. + * @run testng/othervm -XX:+CompactStrings Numbers + * @run testng/othervm -XX:-CompactStrings Numbers + */ + +public class Numbers { + + /* + * Data provider for testIntegerLong + * + * @return input parameter for testIntegerLong + */ + @DataProvider + public Object[][] numbers() { + return new Object[][] { + { Integer.toBinaryString(Integer.MAX_VALUE), + "1111111111111111111111111111111" }, + { Integer.toBinaryString(Integer.MIN_VALUE), + "10000000000000000000000000000000" }, + { Integer.toBinaryString(7), "111" }, + { Integer.toBinaryString(0), "0" }, + { Integer.toOctalString(Integer.MAX_VALUE), "17777777777" }, + { Integer.toOctalString(Integer.MIN_VALUE), "20000000000" }, + { Integer.toOctalString(9), "11" }, + { Integer.toOctalString(0), "0" }, + { Integer.toHexString(Integer.MAX_VALUE), "7fffffff" }, + { Integer.toHexString(Integer.MIN_VALUE), "80000000" }, + { Integer.toHexString(17), "11" }, + { Integer.toHexString(0), "0" }, + { Integer.toString(Integer.MAX_VALUE, 2), + "1111111111111111111111111111111" }, + { Integer.toString(Integer.MIN_VALUE, 2), + "-10000000000000000000000000000000" }, + { Integer.toString(7, 2), "111" }, + { Integer.toString(0, 2), "0" }, + { Integer.toString(Integer.MAX_VALUE, 8), "17777777777" }, + { Integer.toString(Integer.MIN_VALUE, 8), "-20000000000" }, + { Integer.toString(9, 8), "11" }, + { Integer.toString(Integer.MAX_VALUE, 16), "7fffffff" }, + { Integer.toString(Integer.MIN_VALUE, 16), "-80000000" }, + { Integer.toString(17, 16), "11" }, + { Long.toBinaryString(Long.MAX_VALUE), + "111111111111111111111111111111111111111111111111111111111111111" }, + { Long.toBinaryString(Long.MIN_VALUE), + "1000000000000000000000000000000000000000000000000000000000000000" }, + { Long.toOctalString(Long.MAX_VALUE), "777777777777777777777" }, + { Long.toOctalString(Long.MIN_VALUE), "1000000000000000000000" }, + { Long.toHexString(Long.MAX_VALUE), "7fffffffffffffff" }, + { Long.toHexString(Long.MIN_VALUE), "8000000000000000" }, + { Long.toString(Long.MAX_VALUE, 2), + "111111111111111111111111111111111111111111111111111111111111111" }, + { Long.toString(Long.MIN_VALUE, 2), + "-1000000000000000000000000000000000000000000000000000000000000000" }, + { Long.toString(Long.MAX_VALUE, 8), "777777777777777777777" }, + { Long.toString(Long.MIN_VALUE, 8), "-1000000000000000000000" }, + { Long.toString(Long.MAX_VALUE, 16), "7fffffffffffffff" }, + { Long.toString(Long.MIN_VALUE, 16), "-8000000000000000" } }; + } + + /* + * test Integer/Long's methods related to String. + * + * @param res + * real result + * @param expected + * expected result + */ + @Test(dataProvider = "numbers") + public void testIntegerLong(String res, String expected) { + assertEquals(res, expected); + } + +} diff --git a/jdk/test/java/lang/String/CompactString/OffsetByCodePoints.java b/jdk/test/java/lang/String/CompactString/OffsetByCodePoints.java new file mode 100644 index 00000000000..44ed6071db7 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/OffsetByCodePoints.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.offsetByCodePoints. + * @run testng/othervm -XX:+CompactStrings OffsetByCodePoints + * @run testng/othervm -XX:-CompactStrings OffsetByCodePoints + */ + +public class OffsetByCodePoints extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_SUPPLEMENTARY, 0, 1, 2 }, + new Object[] { STRING_SUPPLEMENTARY, 0, 3, 5 }, + new Object[] { STRING_SUPPLEMENTARY, 1, 1, 2 }, + new Object[] { STRING_SUPPLEMENTARY, 1, 3, 5 }, + new Object[] { STRING_SUPPLEMENTARY, 2, 1, 4 }, + new Object[] { STRING_SUPPLEMENTARY, 2, 2, 5 }, + new Object[] { STRING_SUPPLEMENTARY, 2, 3, 6 }, + new Object[] { STRING_SUPPLEMENTARY, 3, 1, 4 }, + new Object[] { STRING_SUPPLEMENTARY, 3, 2, 5 }, + new Object[] { STRING_SUPPLEMENTARY, 3, 3, 6 }, }; + } + + @Test(dataProvider = "provider") + public void testOffsetByCodePoints(String str, int index, + int codePointOffset, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.offsetByCodePoints(index, + codePointOffset), + expected, + String.format( + "testing String(%s).offsetByCodePoints(%d, %d), source : %s, ", + escapeNonASCIIs(data), index, + codePointOffset, source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/RegionMatches.java b/jdk/test/java/lang/String/CompactString/RegionMatches.java new file mode 100644 index 00000000000..5301c0eef31 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/RegionMatches.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.regionMatches. + * @run testng/othervm -XX:+CompactStrings RegionMatches + * @run testng/othervm -XX:-CompactStrings RegionMatches + */ + +public class RegionMatches extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, true, 0, "", 0, 0, true }, + new Object[] { STRING_EMPTY, true, 0, "", 0, 1, false }, + new Object[] { STRING_EMPTY, true, 0, "A", 0, 0, true }, + new Object[] { STRING_EMPTY, true, 0, "", 0, 0, true }, + new Object[] { STRING_EMPTY, true, 0, "", 0, 1, false }, + new Object[] { STRING_L1, false, 0, "a", 0, 1, false }, + new Object[] { STRING_L1, false, 0, "BA", 1, 1, true }, + new Object[] { STRING_L1, false, 0, "Ba", 1, 1, false }, + new Object[] { STRING_L1, true, 0, "a", 0, 1, true }, + new Object[] { STRING_L1, true, 0, "BA", 1, 1, true }, + new Object[] { STRING_L1, true, 0, "Ba", 1, 1, true }, + new Object[] { STRING_L2, true, 1, "b", 0, 1, true }, + new Object[] { STRING_L2, true, 1, "B", 0, 1, true }, + new Object[] { STRING_L2, true, 0, "xaBc", 1, 2, true }, + new Object[] { STRING_L2, false, 0, "AB", 0, 2, true }, + new Object[] { STRING_L2, false, 0, "Ab", 0, 2, false }, + new Object[] { STRING_L2, false, 1, "BAB", 2, 1, true }, + new Object[] { STRING_LLONG, true, 1, "bCdEF", 0, 5, true }, + new Object[] { STRING_LLONG, false, 2, "CDEFG", 0, 5, true }, + new Object[] { STRING_LLONG, true, 2, "CDEFg", 0, 5, true }, + new Object[] { STRING_U1, true, 0, "\uFF41", 0, 1, true }, + new Object[] { STRING_U1, false, 0, "\uFF41", 0, 1, false }, + new Object[] { STRING_MDUPLICATE1, true, 0, "\uFF41a\uFF41", 0, + 3, true }, + new Object[] { STRING_MDUPLICATE1, false, 0, "\uFF21a\uFF21", + 0, 3, false }, + new Object[] { STRING_SUPPLEMENTARY, true, 1, "\uDC00\uD801", + 0, 2, true }, + new Object[] { STRING_SUPPLEMENTARY, true, 4, "\uFF21", 0, 1, + true }, + new Object[] { STRING_SUPPLEMENTARY, true, 5, "A", 0, 1, true }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, false, 0, + "\uD801\uDC28\uD801\uDC29", 0, 4, true }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, true, 1, + "\uDC28\uD801", 0, 2, true }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, true, 1, + "\uDC00\uD801", 0, 2, false }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, true, 4, + "\uFF21", 0, 1, true }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, false, 4, + "\uFF21", 0, 1, false }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, false, 4, + "\uFF41", 0, 1, true }, }; + } + + @Test(dataProvider = "provider") + public void testRegionMatches(String str, boolean ignoreCase, int toffset, + String other, int ooffset, int len, boolean expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.regionMatches(ignoreCase, toffset, + other, ooffset, len), + expected, + String.format( + "testing String(%s).regionMatches(%b, %d, %s, %d, %d), source : %s, ", + escapeNonASCIIs(data), ignoreCase, + toffset, escapeNonASCIIs(other), + ooffset, len, source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/Replace.java b/jdk/test/java/lang/String/CompactString/Replace.java new file mode 100644 index 00000000000..95540f15152 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/Replace.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.replace. + * @run testng/othervm -XX:+CompactStrings Replace + * @run testng/othervm -XX:-CompactStrings Replace + */ + +public class Replace extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_L1, 'A', 'B', "B" }, + new Object[] { STRING_L1, 'A', 'A', "A" }, + new Object[] { STRING_L1, 'A', '\uFF21', "\uFF21" }, + new Object[] { STRING_L2, 'A', 'B', "BB" }, + new Object[] { STRING_L2, 'B', 'A', "AA" }, + new Object[] { STRING_L2, 'C', 'A', "AB" }, + new Object[] { STRING_L2, 'B', '\uFF21', "A\uFF21" }, + new Object[] { STRING_U1, '\uFF21', 'A', "A" }, + new Object[] { STRING_U1, '\uFF22', 'A', "\uFF21" }, + new Object[] { STRING_U2, '\uFF22', 'A', "\uFF21A" }, + new Object[] { STRING_M12, 'A', '\uFF21', "\uFF21\uFF21" }, + new Object[] { STRING_M11, '\uFF21', 'A', "AA" }, + new Object[] { STRING_UDUPLICATE, '\uFF21', 'A', + "A\uFF22A\uFF22A\uFF22A\uFF22A\uFF22" }, + new Object[] { STRING_MDUPLICATE1, '\uFF21', 'A', "AAAAAAAAAA" }, + new Object[] { STRING_MDUPLICATE1, 'A', '\uFF21', + "\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21" }, }; + } + + @Test(dataProvider = "provider") + public void testReplace(String str, char oldChar, char newChar, + String expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.replace(oldChar, newChar), + expected, + String.format( + "testing String(%s).replace(%s, %s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCII(oldChar), + escapeNonASCII(newChar), source)); + }); + } + + @DataProvider + public Object[][] provider2() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "", "ABC", "ABC" }, + new Object[] { STRING_EMPTY, "", "", "" }, + new Object[] { STRING_L1, "A", "B", "B" }, + new Object[] { STRING_L1, "A", "A", "A" }, + new Object[] { STRING_L2, "B", "\uFF21", "A\uFF21" }, + new Object[] { STRING_LLONG, "BCD", "\uFF21", "A\uFF21EFGH" }, + new Object[] { STRING_U1, "\uFF21", "A", "A" }, + new Object[] { STRING_U1, "\uFF21", "A\uFF21", "A\uFF21" }, + new Object[] { STRING_U2, "\uFF21", "A", "A\uFF22" }, + new Object[] { STRING_U2, "\uFF22", "A", "\uFF21A" }, + new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22", "AB", + "ABABABABAB" }, + new Object[] { STRING_MDUPLICATE1, "\uFF21", "A", "AAAAAAAAAA" }, + new Object[] { STRING_MDUPLICATE1, "A", "\uFF21", + "\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21" }, }; + } + + @Test(dataProvider = "provider2") + public void testReplace(String str, CharSequence target, + CharSequence replacement, String expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.replace(target, replacement), + expected, + String.format( + "testing String(%s).replace(%s, %s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(target.toString()), + escapeNonASCIIs(replacement + .toString()), source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/SerializationTest.java b/jdk/test/java/lang/String/CompactString/SerializationTest.java new file mode 100644 index 00000000000..e4c94c573ba --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/SerializationTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static jdk.testlibrary.SerializationUtils.*; +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @library /lib/testlibrary + * @build jdk.testlibrary.SerializationUtils + * @summary Tests Compact String. This one is testing String serialization + * among -XX:+CompactStrings/-XX:-CompactStrings/LegacyString + * @run testng/othervm -XX:+CompactStrings SerializationTest + * @run testng/othervm -XX:-CompactStrings SerializationTest + */ + +public class SerializationTest { + @DataProvider + public Object[][] provider() { + return new Object[][] { + // every byte array is serialized from corresponding String object + // by previous JDK(build 1.8.0_45-b14). + new Object[] { "", new byte[] { -84, -19, 0, 5, 116, 0, 0 } }, + new Object[] { "A", new byte[] { -84, -19, 0, 5, 116, 0, 1, 65 } }, + new Object[] { "AB", new byte[] { -84, -19, 0, 5, 116, 0, 2, 65, 66 } }, + new Object[] { "abcdefghijk", + new byte[] {-84, -19, 0, 5, 116, 0, 11, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107 } }, + new Object[] { "\uff21", new byte[] { -84, -19, 0, 5, 116, 0, 3, -17, -68, -95 } }, + new Object[] { "\uff21\uff22", new byte[] { -84, -19, 0, 5, 116, 0, 6, -17, -68, + -95, -17, -68, -94 } }, + new Object[] { "\uff21A\uff21A\uff21A\uff21A\uff21A", + new byte[] { -84, -19, 0, 5, 116, 0, 20, -17, -68, -95, 65, -17, -68, + -95, 65, -17, -68, -95, 65, -17, -68, -95, 65, -17, -68, -95, 65 } }, + new Object[] { "A\uff21B\uff22C\uff23D\uff24E\uff25F\uff26G\uff27H\uff28", + new byte[] { -84, -19, 0, 5, 116, 0, 32, 65, -17, -68, -95, 66, -17, -68, + -94, 67, -17, -68, -93, 68, -17, -68, -92, 69, -17, -68, -91, 70, -17, + -68, -90, 71, -17, -68, -89, 72, -17, -68, -88 } }, + new Object[] { "\uff21A\uff22B\uff23C\uff24D\uff25E\uff26F\uff27G\uff28H", + new byte[] { -84, -19, 0, 5, 116, 0, 32, -17, -68, -95, 65, -17, -68, + -94, 66, -17, -68, -93, 67, -17, -68, -92, 68, -17, -68, -91, 69, -17, + -68, -90, 70, -17, -68, -89, 71, -17, -68, -88, 72 } }, + new Object[] { "\ud801\udc00\ud801\udc01\uff21A", + new byte[] { -84, -19, 0, 5, 116, 0, 16, -19, -96, -127, -19, -80, -128, + -19, -96, -127, -19, -80, -127, -17, -68, -95, 65 } }, + new Object[] { "\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22", + new byte[] { -84, -19, 0, 5, 116, 0, 30, -17, -68, -95, -17, -68, -94, -17, + -68, -95, -17, -68, -94, -17, -68, -95, -17, -68, -94, -17, -68, -95, -17, + -68, -94, -17, -68, -95, -17, -68, -94 } } }; + } + + /* + * Verify serialization works between Compact String/Legacy String + */ + @Test(dataProvider = "provider") + public void test(String strContent, byte[] baInJDK8) throws Exception { + // Serialize a String object into byte array. + byte[] ba = serialize(strContent); + assertEquals(ba, baInJDK8); + // Deserialize a String object from byte array which is generated by previous JDK(build 1.8.0_45-b14). + Object obj = deserialize(ba); + assertEquals(obj.getClass(), String.class); + assertEquals((String)obj, strContent); + } +} diff --git a/jdk/test/java/lang/String/CompactString/Split.java b/jdk/test/java/lang/String/CompactString/Split.java new file mode 100644 index 00000000000..2707ae34edf --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/Split.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.util.Arrays; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.split. + * @run testng/othervm -XX:+CompactStrings Split + * @run testng/othervm -XX:-CompactStrings Split + */ + +public class Split extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + new Object[] { STRING_L1, "", 0, new String[] { "A" } }, + new Object[] { STRING_L1, "", 1, new String[] { "A" } }, + new Object[] { STRING_L1, "", 2, new String[] { "A", "" } }, + new Object[] { STRING_L1, "A", 0, new String[] {} }, + new Object[] { STRING_L2, "A", 0, new String[] { "", "B" } }, + new Object[] { STRING_L2, "B", 0, new String[] { "A" } }, + new Object[] { STRING_LLONG, "D", 0, + new String[] { "ABC", "EFGH" } }, + new Object[] { STRING_LLONG, "[D]", 0, + new String[] { "ABC", "EFGH" } }, + new Object[] { STRING_LLONG, "CD", 0, + new String[] { "AB", "EFGH" } }, + new Object[] { STRING_LLONG, "DC", 0, + new String[] { "ABCDEFGH" } }, + new Object[] { STRING_LLONG, "[CF]", 0, + new String[] { "AB", "DE", "GH" } }, + new Object[] { STRING_LLONG, "[CF]", 1, + new String[] { "ABCDEFGH" } }, + new Object[] { STRING_LLONG, "[CF]", 2, + new String[] { "AB", "DEFGH" } }, + new Object[] { STRING_LLONG, "[FC]", 0, + new String[] { "AB", "DE", "GH" } }, + new Object[] { STRING_LLONG, "[FC]", 1, + new String[] { "ABCDEFGH" } }, + new Object[] { STRING_LLONG, "[FC]", 2, + new String[] { "AB", "DEFGH" } }, + new Object[] { STRING_U1, "", 0, new String[] { "\uFF21" } }, + new Object[] { STRING_U1, "", 1, new String[] { "\uFF21" } }, + new Object[] { STRING_U1, "", 2, new String[] { "\uFF21", "" } }, + new Object[] { STRING_U1, "\uFF21", 0, new String[] {} }, + new Object[] { STRING_M12, "\uFF21", 0, + new String[] { "", "A" } }, + new Object[] { STRING_M12, "A", 0, new String[] { "\uFF21" } }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21", + 0, + new String[] { "", "\uFF22", "\uFF22", "\uFF22", + "\uFF22", "\uFF22" } }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21", + 2, + new String[] { "", + "\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22" } }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21", + 4, + new String[] { "", "\uFF22", "\uFF22", + "\uFF22\uFF21\uFF22\uFF21\uFF22" } }, + new Object[] { + STRING_UDUPLICATE, + "\uFF22", + 0, + new String[] { "\uFF21", "\uFF21", "\uFF21", "\uFF21", + "\uFF21" } }, + new Object[] { + STRING_UDUPLICATE, + "\uFF22", + 3, + new String[] { "\uFF21", "\uFF21", + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22" } }, + + new Object[] { STRING_MDUPLICATE1, "\uFF21", 0, + new String[] { "", "A", "A", "A", "A", "A" } }, + new Object[] { STRING_MDUPLICATE1, "\uFF21", 3, + new String[] { "", "A", "A\uFF21A\uFF21A\uFF21A" } }, + new Object[] { + STRING_MDUPLICATE1, + "A", + 0, + new String[] { "\uFF21", "\uFF21", "\uFF21", "\uFF21", + "\uFF21" } }, + new Object[] { + STRING_MDUPLICATE1, + "A", + 4, + new String[] { "\uFF21", "\uFF21", "\uFF21", + "\uFF21A\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY, "\uD801\uDC01", 0, + new String[] { "\uD801\uDC00", "\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY, "\uDC01", 0, + new String[] { "\uD801\uDC00\uD801\uDC01\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY, "\uD801\uDC01", 0, + new String[] { "\uD801\uDC00", "\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY, "[\uD801\uDC01\uFF21]", 0, + new String[] { "\uD801\uDC00", "", "A" } }, + new Object[] { STRING_SUPPLEMENTARY, "[\uD801\uDC01\uFF21]", 1, + new String[] { "\uD801\uDC00\uD801\uDC01\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY, "[\uD801\uDC01\uFF21]", 2, + new String[] { "\uD801\uDC00", "\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY, "[\uFF21\uD801\uDC01]", 0, + new String[] { "\uD801\uDC00", "", "A" } }, + new Object[] { STRING_SUPPLEMENTARY, "[\uFF21\uD801\uDC01]", 1, + new String[] { "\uD801\uDC00\uD801\uDC01\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY, "[\uFF21\uD801\uDC01]", 2, + new String[] { "\uD801\uDC00", "\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, "\uDC01", 0, + new String[] { "\uD801\uDC28\uD801\uDC29\uFF41a" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, "\uD801\uDC29", + 0, new String[] { "\uD801\uDC28", "\uFF41a" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "[\uD801\uDC29\uFF41]", 0, + new String[] { "\uD801\uDC28", "", "a" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "[\uD801\uDC29\uFF41]", 1, + new String[] { "\uD801\uDC28\uD801\uDC29\uFF41a" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "[\uD801\uDC29\uFF41]", 2, + new String[] { "\uD801\uDC28", "\uFF41a" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "[\uFF41\uD801\uDC29]", 0, + new String[] { "\uD801\uDC28", "", "a" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "[\uFF41\uD801\uDC29]", 1, + new String[] { "\uD801\uDC28\uD801\uDC29\uFF41a" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "[\uFF41\uD801\uDC29]", 2, + new String[] { "\uD801\uDC28", "\uFF41a" } }, }; + } + + @Test(dataProvider = "provider") + public void testSplit(String str, String regex, int limit, String[] expected) { + map.get(str) + .forEach( + (source, data) -> { + assertTrue( + Arrays.equals(data.split(regex, limit), + expected), + String.format( + "testing String(%s).split(%s, %d), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(regex), limit, + source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/StartsWith.java b/jdk/test/java/lang/String/CompactString/StartsWith.java new file mode 100644 index 00000000000..97109bb1529 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/StartsWith.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.startsWith. + * @run testng/othervm -XX:+CompactStrings StartsWith + * @run testng/othervm -XX:-CompactStrings StartsWith + */ + +public class StartsWith extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + new Object[] {STRING_EMPTY, "", 0, true}, + new Object[] {STRING_EMPTY, "A", 0, false}, + new Object[] {STRING_EMPTY, "", 0, true}, + new Object[] {STRING_EMPTY, "", -1, false}, + new Object[] {STRING_L1, "A", 0, true}, + new Object[] {STRING_L1, "A", -1, false}, + new Object[] {STRING_L1, "A", 1, false}, + new Object[] {STRING_L2, "B", 1, true}, + new Object[] {STRING_L2, "B", 0, false}, + new Object[] {STRING_L2, "A", 0, true}, + new Object[] {STRING_L2, "AB", 1, false}, + new Object[] {STRING_L4, "ABC", 0, true}, + new Object[] {STRING_LLONG, "ABCDEFGH", 0, true}, + new Object[] {STRING_LLONG, "ABCDE", 0, true}, + new Object[] {STRING_LLONG, "CDE", 0, false}, + new Object[] {STRING_LLONG, "FG", 5, true}, + new Object[] {STRING_U1, "\uFF21", 0, true}, + new Object[] {STRING_U1, "", 1, true}, + new Object[] {STRING_U1, "\uFF21", 0, true}, + new Object[] {STRING_U1, "A", 0, false}, + new Object[] {STRING_U2, "\uFF21\uFF22", 0, true}, + new Object[] {STRING_U2, "\uFF21", 0, true}, + new Object[] {STRING_U2, "\uFF22", 0, false}, + new Object[] {STRING_U2, "", 0, true}, + new Object[] {STRING_M12, "\uFF21", 0, true}, + new Object[] {STRING_M12, "\uFF21A", 0, true}, + new Object[] {STRING_M12, "A", 0, false}, + new Object[] {STRING_M12, "\uFF21A", 0, true}, + new Object[] {STRING_M12, "A", 1, true}, + new Object[] {STRING_M11, "A", 0, true}, + new Object[] {STRING_M11, "A\uFF21", 0, true}, + new Object[] {STRING_M11, "A\uFF21", 0, true}, + new Object[] {STRING_M11, "\uFF21", 1, true}, + new Object[] {STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", + 0, true}, + new Object[] {STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", 0, true}, + new Object[] {STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", 2, true}, + new Object[] {STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", 5, false}, + new Object[] {STRING_MDUPLICATE1, + "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A", 0, true}, + new Object[] {STRING_MDUPLICATE1, + "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21", 0, true}, + new Object[] {STRING_MDUPLICATE1, "A\uFF21A\uFF21A\uFF21A", 1, true}, + new Object[] {STRING_SUPPLEMENTARY, "\uDC01\uFF21", 3, true}, + }; + } + + @Test(dataProvider = "provider") + public void testStartsWith(String str, String prefix, int toffset, + boolean expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.startsWith(prefix, toffset), + expected, + String.format( + "testing String(%s).startsWith(%s, %d), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(prefix), toffset, + source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/SubString.java b/jdk/test/java/lang/String/CompactString/SubString.java new file mode 100644 index 00000000000..f34f16168b5 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/SubString.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.subString. + * @run testng/othervm -XX:+CompactStrings SubString + * @run testng/othervm -XX:-CompactStrings SubString + */ + +public class SubString extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, 0, 0, "" }, + new Object[] { STRING_L1, 0, 1, "A" }, + new Object[] { STRING_L1, 1, 1, "" }, + new Object[] { STRING_L2, 0, 2, "AB" }, + new Object[] { STRING_L2, 1, 2, "B" }, + new Object[] { STRING_LLONG, 0, 8, "ABCDEFGH" }, + new Object[] { STRING_LLONG, 7, 8, "H" }, + new Object[] { STRING_LLONG, 8, 8, "" }, + new Object[] { STRING_LLONG, 3, 7, "DEFG" }, + new Object[] { STRING_U1, 0, 1, "\uFF21" }, + new Object[] { STRING_U1, 1, 1, "" }, + new Object[] { STRING_U1, 0, 0, "" }, + new Object[] { STRING_U2, 0, 2, "\uFF21\uFF22" }, + new Object[] { STRING_U2, 1, 2, "\uFF22" }, + new Object[] { STRING_U2, 2, 2, "" }, + new Object[] { STRING_U2, 0, 2, "\uFF21\uFF22" }, + new Object[] { STRING_U2, 1, 2, "\uFF22" }, + new Object[] { STRING_M12, 1, 2, "A" }, + new Object[] { STRING_M11, 0, 1, "A" }, + new Object[] { STRING_M11, 1, 2, "\uFF21" }, + new Object[] { STRING_UDUPLICATE, 1, 5, + "\uFF22\uFF21\uFF22\uFF21" }, + new Object[] { STRING_MDUPLICATE1, 9, 10, "A" }, + new Object[] { STRING_MDUPLICATE1, 7, 8, "A" }, }; + } + + @Test(dataProvider = "provider") + public void testSubstring(String str, int beginIndex, int endIndex, + String expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.substring(beginIndex, endIndex), + expected, + String.format( + "testing String(%s).substring(%d, %d), source : %s, ", + escapeNonASCIIs(data), beginIndex, + endIndex, source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/ToCharArray.java b/jdk/test/java/lang/String/CompactString/ToCharArray.java new file mode 100644 index 00000000000..bf7dbdefd57 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/ToCharArray.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.util.Arrays; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.toCharArray. + * @run testng/othervm -XX:+CompactStrings ToCharArray + * @run testng/othervm -XX:-CompactStrings ToCharArray + */ + +public class ToCharArray extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + new Object[] { STRING_EMPTY, new char[] {} }, + new Object[] { STRING_L1, new char[] { 'A' } }, + new Object[] { STRING_L2, new char[] { 'A', 'B' } }, + new Object[] { STRING_LLONG, + new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' } }, + new Object[] { STRING_U1, new char[] { '\uFF21' } }, + new Object[] { STRING_U2, new char[] { '\uFF21', '\uFF22' } }, + new Object[] { STRING_M12, new char[] { '\uFF21', 'A' } }, + new Object[] { STRING_M11, new char[] { 'A', '\uFF21' } }, }; + } + + @Test(dataProvider = "provider") + public void testToCharArray(String str, char[] expected) { + map.get(str) + .forEach( + (source, data) -> { + assertTrue( + Arrays.equals(data.toCharArray(), expected), + String.format( + "testing String(%s).toCharArray(), source : %s, ", + escapeNonASCIIs(data), source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/ToLowerCase.java b/jdk/test/java/lang/String/CompactString/ToLowerCase.java new file mode 100644 index 00000000000..aa49aadbfca --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/ToLowerCase.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.toLowerCase. + * @run testng/othervm -XX:+CompactStrings ToLowerCase + * @run testng/othervm -XX:-CompactStrings ToLowerCase + */ + +public class ToLowerCase extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + new Object[] { STRING_EMPTY, "" }, + new Object[] { STRING_L1, "a" }, + new Object[] { STRING_L2, "ab" }, + new Object[] { STRING_U1, "\uFF41" }, + new Object[] { STRING_MDUPLICATE1, + "\uFF41a\uFF41a\uFF41a\uFF41a\uFF41a" }, + new Object[] { STRING_SUPPLEMENTARY, + "\uD801\uDC28\uD801\uDC29\uFF41a" }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "\uD801\uDC28\uD801\uDC29\uFF41a" }, + new Object[] { STRING_SUPPLEMENTARY, + STRING_SUPPLEMENTARY_LOWERCASE } }; + } + + @Test(dataProvider = "provider") + public void testToLowerCase(String str, String expected) { + map.get(str).forEach( + (source, data) -> { + assertEquals(data.toLowerCase(), expected, String.format( + "testing String(%s).toLowerCase(), source : %s, ", + escapeNonASCIIs(data), source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/ToUpperCase.java b/jdk/test/java/lang/String/CompactString/ToUpperCase.java new file mode 100644 index 00000000000..d3eced31104 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/ToUpperCase.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.toUpperCase. + * @run testng/othervm -XX:+CompactStrings ToUpperCase + * @run testng/othervm -XX:-CompactStrings ToUpperCase + */ + +public class ToUpperCase extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "" }, + new Object[] { STRING_L1, "A" }, + new Object[] { STRING_L2, "AB" }, + new Object[] { STRING_U1, "\uFF21" }, + new Object[] { STRING_MDUPLICATE1, + "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A" }, + new Object[] { STRING_SUPPLEMENTARY, + "\uD801\uDC00\uD801\uDC01\uFF21A" }, + + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "\uD801\uDC00\uD801\uDC01\uFF21A" }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + STRING_SUPPLEMENTARY }, }; + } + + @Test(dataProvider = "provider") + public void testToUpperCase(String str, String expected) { + map.get(str).forEach( + (source, data) -> { + assertEquals(data.toUpperCase(), expected, String.format( + "testing String(%s).toUpperCase(), source : %s, ", + escapeNonASCIIs(data), source)); + }); + } +} diff --git a/jdk/test/java/lang/String/CompactString/Trim.java b/jdk/test/java/lang/String/CompactString/Trim.java new file mode 100644 index 00000000000..03d0f9fce1a --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/Trim.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.trim. + * @run testng/othervm -XX:+CompactStrings Trim + * @run testng/othervm -XX:-CompactStrings Trim + */ + +public class Trim { + + /* + * Data provider for testTrim + * + * @return input parameter for testTrim + */ + @DataProvider + public Object[][] trims() { + return new Object[][] { + { " \t \t".trim(), "" }, + { "\t \t ".trim(), "" }, + { "\t A B C\t ".trim(), "A B C" }, + { " \t A B C \t".trim(), "A B C" }, + { "\t \uFF21 \uFF22 \uFF23\t ".trim(), "\uFF21 \uFF22 \uFF23" }, + { " \t \uFF21 \uFF22 \uFF23 \t".trim(), "\uFF21 \uFF22 \uFF23" }, + { " \t \uFF41 \uFF42 \uFF43 \t".trim(), "\uFF41 \uFF42 \uFF43" }, + { " \t A\uFF21 B\uFF22 C\uFF23 \t".trim(), + "A\uFF21 B\uFF22 C\uFF23" } }; + } + + /* + * test trim(). + * + * @param res + * real result + * @param expected + * expected result + */ + @Test(dataProvider = "trims") + public void testTrim(String res, String expected) { + assertEquals(res, expected); + } + +} diff --git a/jdk/test/java/lang/String/CompactString/VMOptionsTest.java b/jdk/test/java/lang/String/CompactString/VMOptionsTest.java new file mode 100644 index 00000000000..01eed446292 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/VMOptionsTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.io.*; +import java.lang.reflect.Field; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is testing + * if Compact String enable/disable VM Options is indeed working in String class, + * it's verified by testing if the VM option affect coder and + * COMPACT_STRINGS field in String class. + * @run testng/othervm -XX:+CompactStrings -DCompactStringEnabled=true VMOptionsTest + * @run testng/othervm -XX:-CompactStrings -DCompactStringEnabled=false VMOptionsTest + */ + +public class VMOptionsTest { + boolean compactStringEnabled; + // corresponding "COMPACT_STRINGS" field in String class. + Field COMPACT_STRINGS; + // corresponding "coder" field in String class. + Field coder; + + // corresponding coder type in String class. + final byte LATIN1 = 0; + final byte UTF16 = 1; + + @BeforeClass + public void setUp() throws Exception { + compactStringEnabled = Boolean.valueOf(System.getProperty("CompactStringEnabled", null)); + COMPACT_STRINGS = String.class.getDeclaredField("COMPACT_STRINGS"); + COMPACT_STRINGS.setAccessible(true); + coder = String.class.getDeclaredField("coder"); + coder.setAccessible(true); + } + + @DataProvider + public Object[][] provider() { + return new Object[][] { + new Object[] {"", LATIN1}, + new Object[] {"abc", LATIN1}, + new Object[] {"A\uff21", UTF16}, + new Object[] {"\uff21\uff22", UTF16} + }; + } + + /* + * verify the coder field in String objects. + */ + @Test(dataProvider = "provider") + public void testCoder(String str, byte expected) throws Exception { + byte c = (byte) coder.get(str); + expected = compactStringEnabled ? expected : UTF16; + assertEquals(c, expected); + } + + /* + * verify the COMPACT_STRINGS flag in String objects. + */ + @Test(dataProvider = "provider") + public void testCompactStringFlag(String str, byte ignore) throws Exception { + assertTrue(COMPACT_STRINGS.get(str).equals(compactStringEnabled)); + } +} diff --git a/jdk/test/java/lang/String/CompactString/ValueOf.java b/jdk/test/java/lang/String/CompactString/ValueOf.java new file mode 100644 index 00000000000..cf47416c6f1 --- /dev/null +++ b/jdk/test/java/lang/String/CompactString/ValueOf.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015, 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. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.valueOf. + * valueOf(char[] data) is not tested here. + * @run testng/othervm -XX:+CompactStrings ValueOf + * @run testng/othervm -XX:-CompactStrings ValueOf + */ + +public class ValueOf { + + /* + * Data provider for testValueOf + * + * @return input parameter for testValueOf + */ + @DataProvider + public Object[][] valueOfs() { + return new Object[][] { { String.valueOf(true), "true" }, + { String.valueOf(false), "false" }, + { String.valueOf(1.0f), "1.0" }, + { String.valueOf(0.0f), "0.0" }, + { String.valueOf(Float.MAX_VALUE), "3.4028235E38" }, + { String.valueOf(Float.MIN_VALUE), "1.4E-45" }, + { String.valueOf(1.0d), "1.0" }, + { String.valueOf(0.0d), "0.0" }, + { String.valueOf(Double.MAX_VALUE), "1.7976931348623157E308" }, + { String.valueOf(Double.MIN_VALUE), "4.9E-324" }, + { String.valueOf(1), "1" }, { String.valueOf(0), "0" }, + { String.valueOf(Integer.MAX_VALUE), "2147483647" }, + { String.valueOf(Integer.MIN_VALUE), "-2147483648" }, + { String.valueOf(1L), "1" }, { String.valueOf(0L), "0" }, + { String.valueOf(Long.MAX_VALUE), "9223372036854775807" }, + { String.valueOf(Long.MIN_VALUE), "-9223372036854775808" } }; + } + + /* + * test String.valueOf(xxx). + * + * @param res + * real result + * @param expected + * expected result + */ + @Test(dataProvider = "valueOfs") + public void testValueOf(String res, String expected) { + assertEquals(res, expected); + } + +} diff --git a/jdk/test/java/lang/String/LiteralReplace.java b/jdk/test/java/lang/String/LiteralReplace.java index f72e1050033..32fc779408f 100644 --- a/jdk/test/java/lang/String/LiteralReplace.java +++ b/jdk/test/java/lang/String/LiteralReplace.java @@ -22,7 +22,7 @@ */ /* @test - * @bug 8058779 + * @bug 8058779 8054307 * @library /lib/testlibrary/ * @build jdk.testlibrary.RandomFactory * @run testng LiteralReplace @@ -104,6 +104,109 @@ public class LiteralReplace { {"abcdefgh", "[a-h]", "X", "abcdefgh"}, {"aa+", "a+", "", "a"}, {"^abc$", "abc", "x", "^x$"}, + + // more with non-latin1 characters + {"\u4e00\u4e00\u4e00", + "\u4e00\u4e00", + "\u4e01", + "\u4e01\u4e00"}, + + {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08", + "\u4e03\u4e04\u4e05", + "\u4e10\u4e11\u4e12", + "\u4e00\u4e01\u4e02\u4e10\u4e11\u4e12\u4e06\u4e07\u4e08"}, + + {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08", + "ABC", + "\u4e10\u4e11\u4e12", + "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08"}, + + {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e02\u4e03\u4e07\u4e08", + "\u4e02\u4e03", + "\u4e12\u4e13", + "\u4e00\u4e01\u4e12\u4e13\u4e04\u4e12\u4e13\u4e07\u4e08"}, + + {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e02\u4e03\u4e07\u4e08", + "\u4e02\u4e03", + "ab", + "\u4e00\u4e01ab\u4e04ab\u4e07\u4e08"}, + + {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07", + "", + "_", + "_\u4e00_\u4e01_\u4e02_\u4e03_\u4e04_\u4e05_\u4e06_\u4e07_"}, + {"^\u4e00\u4e01\u4e02$", + "\u4e00\u4e01\u4e02", + "\u4e03", + "^\u4e03$"}, + + {"", "\u4e00", "\u4e01", ""}, + {"", "", "\u4e00\u4e01\u4e02", "\u4e00\u4e01\u4e02"}, + + {"^\u4e00\u4e01\u4e02$", + "\u4e00\u4e01\u4e02", + "X", + "^X$"}, + + {"abcdefgh", + "def", + "\u4e01", + "abc\u4e01gh"}, + + {"abcdefgh", + "def", + "\u4e01\u4e02", + "abc\u4e01\u4e02gh"}, + + {"abcdefabcgh", + "abc", + "\u4e01\u4e02", + "\u4e01\u4e02def\u4e01\u4e02gh"}, + + {"abcdefabcghabc", + "abc", + "\u4e01\u4e02", + "\u4e01\u4e02def\u4e01\u4e02gh\u4e01\u4e02"}, + + {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05", + "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05", + "abcd", + "abcd"}, + + {"\u4e00\u4e01", + "\u4e00\u4e01", + "abcdefg", + "abcdefg"}, + + {"\u4e00\u4e01xyz", + "\u4e00\u4e01", + "abcdefg", + "abcdefgxyz"}, + + {"\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00", + "\u4e00\u4e00", + "\u4e00\u4e00\u4e00", + "\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00"}, + + {"\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00", + "\u4e00\u4e00\u4e00", + "\u4e00\u4e00", + "\u4e00\u4e00\u4e00\u4e00"}, + + {"\u4e00.\u4e01.\u4e02.\u4e03.\u4e04.", + ".", + "-", + "\u4e00-\u4e01-\u4e02-\u4e03-\u4e04-"}, + + {"\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00", + "\u4e00", + "", + ""}, + + {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05", + "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05", + "", + ""}, }; } diff --git a/jdk/test/java/lang/String/ToLowerCase.java b/jdk/test/java/lang/String/ToLowerCase.java index 784f810e052..9b5574d6f96 100644 --- a/jdk/test/java/lang/String/ToLowerCase.java +++ b/jdk/test/java/lang/String/ToLowerCase.java @@ -23,7 +23,7 @@ /* @test - @bug 4217441 4533872 4900935 8020037 8032012 8041791 8042589 + @bug 4217441 4533872 4900935 8020037 8032012 8041791 8042589 8054307 @summary toLowerCase should lower-case Greek Sigma correctly depending on the context (final/non-final). Also it should handle Locale specific (lt, tr, and az) lowercasings and supplementary @@ -134,14 +134,60 @@ public class ToLowerCase { } test(src.toString(), Locale.US, exp.toString()); + // test latin1 + src = new StringBuilder(0x100); + exp = new StringBuilder(0x100); + for (int cp = 0; cp < 0x100; cp++) { + int lowerCase = Character.toLowerCase(cp); + if (lowerCase == -1) { //Character.ERROR + continue; + } + src.appendCodePoint(cp); + exp.appendCodePoint(lowerCase); + } + test(src.toString(), Locale.US, exp.toString()); + + // test non-latin1 -> latin1 + src = new StringBuilder(0x100).append("abc"); + exp = new StringBuilder(0x100).append("abc"); + for (int cp = 0x100; cp < 0x10000; cp++) { + int lowerCase = Character.toLowerCase(cp); + if (lowerCase < 0x100 && cp != '\u0130') { + src.appendCodePoint(cp); + exp.appendCodePoint(lowerCase); + } + } + test(src.toString(), Locale.US, exp.toString()); } static void test(String in, Locale locale, String expected) { + test0(in, locale,expected); + for (String[] ss : new String[][] { + new String[] {"abc", "abc"}, + new String[] {"aBc", "abc"}, + new String[] {"ABC", "abc"}, + new String[] {"ab\u4e00", "ab\u4e00"}, + new String[] {"aB\u4e00", "ab\u4e00"}, + new String[] {"AB\u4e00", "ab\u4e00"}, + new String[] {"ab\uD800\uDC00", "ab\uD800\uDC00"}, + new String[] {"aB\uD800\uDC00", "ab\uD800\uDC00"}, + new String[] {"AB\uD800\uDC00", "ab\uD800\uDC00"}, + new String[] {"ab\uD801\uDC1C", "ab\uD801\uDC44"}, + new String[] {"aB\uD801\uDC1C", "ab\uD801\uDC44"}, + new String[] {"AB\uD801\uDC1C", "ab\uD801\uDC44"}, + + }) { + test0(ss[0] + " " + in, locale, ss[1] + " " + expected); + test0(in + " " + ss[0], locale, expected + " " + ss[1]); + } + } + + static void test0(String in, Locale locale, String expected) { String result = in.toLowerCase(locale); if (!result.equals(expected)) { System.err.println("input: " + in + ", locale: " + locale + ", expected: " + expected + ", actual: " + result); throw new RuntimeException(); } - } + } } diff --git a/jdk/test/java/lang/String/ToUpperCase.java b/jdk/test/java/lang/String/ToUpperCase.java index 020c2ae02da..b7cebc1a302 100644 --- a/jdk/test/java/lang/String/ToUpperCase.java +++ b/jdk/test/java/lang/String/ToUpperCase.java @@ -23,7 +23,7 @@ /* @test - @bug 4219630 4304573 4533872 4900935 8042589 + @bug 4219630 4304573 4533872 4900935 8042589 8054307 @summary toUpperCase should upper-case German sharp s correctly even if it's the only character in the string. should also uppercase all of the 1:M char mappings correctly. Also it should handle @@ -97,14 +97,66 @@ public class ToUpperCase { test("A\uD801\uDC44", Locale.ROOT, "A\uD801\uDC1c"); test("a\uD801\uDC28\uD801\uDC29\uD801\uDC2A", Locale.US, "A\uD801\uDC00\uD801\uDC01\uD801\uDC02"); test("A\uD801\uDC28a\uD801\uDC29b\uD801\uDC2Ac", Locale.US, "A\uD801\uDC00A\uD801\uDC01B\uD801\uDC02C"); + + // test latin1 only case + StringBuilder src = new StringBuilder(0x100); + StringBuilder exp = new StringBuilder(0x100); + for (int cp = 0; cp < 0x100; cp++) { + int upperCase = Character.toUpperCase(cp); + if (upperCase == -1) { //Character.ERROR + continue; + } + src.appendCodePoint(cp); + if (cp == '\u00df') { + exp.append("SS"); // need Character.toUpperCaseEx() + } else { + exp.appendCodePoint(upperCase); + } + } + test(src.toString(), Locale.US, exp.toString()); + + // test non-latin1 -> latin1 + src = new StringBuilder(0x100).append("ABC"); + exp = new StringBuilder(0x100).append("ABC"); + for (int cp = 0x100; cp < 0x10000; cp++) { + int upperCase = Character.toUpperCase(cp); + if (upperCase < 0x100) { + src.appendCodePoint(cp); + exp.appendCodePoint(upperCase); + } + } + test(src.toString(), Locale.US, exp.toString()); + } static void test(String in, Locale locale, String expected) { + test0(in, locale,expected); + // trigger different code paths + for (String[] ss : new String[][] { + new String[] {"abc", "ABC"}, + new String[] {"AbC", "ABC"}, + new String[] {"ABC", "ABC"}, + new String[] {"AB\u4e00", "AB\u4e00"}, + new String[] {"ab\u4e00", "AB\u4e00"}, + new String[] {"aB\u4e00", "AB\u4e00"}, + new String[] {"AB\uD800\uDC00", "AB\uD800\uDC00"}, + new String[] {"Ab\uD800\uDC00", "AB\uD800\uDC00"}, + new String[] {"ab\uD800\uDC00", "AB\uD800\uDC00"}, + new String[] {"AB\uD801\uDC44", "AB\uD801\uDC1C"}, + new String[] {"Ab\uD801\uDC44", "AB\uD801\uDC1C"}, + new String[] {"ab\uD801\uDC44", "AB\uD801\uDC1C"}, + }) { + test0(ss[0] + " " + in, locale, ss[1] + " " + expected); + test0(in + " " + ss[0], locale, expected + " " + ss[1]); + } + } + + static void test0(String in, Locale locale, String expected) { String result = in.toUpperCase(locale); if (!result.equals(expected)) { System.err.println("input: " + in + ", locale: " + locale + ", expected: " + expected + ", actual: " + result); throw new RuntimeException(); } - } + } } diff --git a/jdk/test/java/lang/StringBuffer/CompactStringBuffer.java b/jdk/test/java/lang/StringBuffer/CompactStringBuffer.java new file mode 100644 index 00000000000..bc0ec5f6d08 --- /dev/null +++ b/jdk/test/java/lang/StringBuffer/CompactStringBuffer.java @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.util.Arrays; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This test is testing StringBuffer + * behavior related to Compact String. + * @run testng/othervm -XX:+CompactStrings CompactStringBuffer + * @run testng/othervm -XX:-CompactStrings CompactStringBuffer + */ + +public class CompactStringBuffer { + + /* + * Tests for "A" + */ + @Test + public void testCompactStringBufferForLatinA() { + final String ORIGIN = "A"; + /* + * Because right now ASCII is the default encoding parameter for source + * code in JDK build environment, so we escape them. same as below. + */ + check(new StringBuffer(ORIGIN).append(new char[] { '\uFF21' }), + "A\uFF21"); + check(new StringBuffer(ORIGIN).append(new StringBuffer("\uFF21")), + "A\uFF21"); + check(new StringBuffer(ORIGIN).append("\uFF21"), "A\uFF21"); + check(new StringBuffer(ORIGIN).append(new StringBuffer("\uFF21")), + "A\uFF21"); + check(new StringBuffer(ORIGIN).delete(0, 1), ""); + check(new StringBuffer(ORIGIN).delete(0, 0), "A"); + check(new StringBuffer(ORIGIN).deleteCharAt(0), ""); + assertEquals(new StringBuffer(ORIGIN).indexOf("A", 0), 0); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21", 0), -1); + assertEquals(new StringBuffer(ORIGIN).indexOf("", 0), 0); + assertEquals(new StringBuffer(ORIGIN).insert(1, "\uD801\uDC00") + .indexOf("A", 0), 0); + assertEquals(new StringBuffer(ORIGIN).insert(0, "\uD801\uDC00") + .indexOf("A", 0), 2); + check(new StringBuffer(ORIGIN).insert(0, new char[] {}), "A"); + check(new StringBuffer(ORIGIN).insert(1, new char[] { '\uFF21' }), + "A\uFF21"); + check(new StringBuffer(ORIGIN).insert(0, new char[] { '\uFF21' }), + "\uFF21A"); + check(new StringBuffer(ORIGIN).insert(0, new StringBuffer("\uFF21")), + "\uFF21A"); + check(new StringBuffer(ORIGIN).insert(1, new StringBuffer("\uFF21")), + "A\uFF21"); + check(new StringBuffer(ORIGIN).insert(0, ""), "A"); + check(new StringBuffer(ORIGIN).insert(0, "\uFF21"), "\uFF21A"); + check(new StringBuffer(ORIGIN).insert(1, "\uFF21"), "A\uFF21"); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), -1); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf(""), 1); + check(new StringBuffer(ORIGIN).replace(0, 0, "\uFF21"), "\uFF21A"); + check(new StringBuffer(ORIGIN).replace(0, 1, "\uFF21"), "\uFF21"); + checkSetCharAt(new StringBuffer(ORIGIN), 0, '\uFF21', "\uFF21"); + checkSetLength(new StringBuffer(ORIGIN), 0, ""); + checkSetLength(new StringBuffer(ORIGIN), 1, "A"); + check(new StringBuffer(ORIGIN).substring(0), "A"); + check(new StringBuffer(ORIGIN).substring(1), ""); + } + + /* + * Tests for "\uFF21" + */ + @Test + public void testCompactStringBufferForNonLatinA() { + final String ORIGIN = "\uFF21"; + check(new StringBuffer(ORIGIN).append(new char[] { 'A' }), "\uFF21A"); + check(new StringBuffer(ORIGIN).append(new StringBuffer("A")), "\uFF21A"); + check(new StringBuffer(ORIGIN).append("A"), "\uFF21A"); + check(new StringBuffer(ORIGIN).append(new StringBuffer("A")), "\uFF21A"); + check(new StringBuffer(ORIGIN).delete(0, 1), ""); + check(new StringBuffer(ORIGIN).delete(0, 0), "\uFF21"); + check(new StringBuffer(ORIGIN).deleteCharAt(0), ""); + assertEquals(new StringBuffer(ORIGIN).indexOf("A", 0), -1); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21", 0), 0); + assertEquals(new StringBuffer(ORIGIN).indexOf("", 0), 0); + check(new StringBuffer(ORIGIN).insert(0, new char[] {}), "\uFF21"); + check(new StringBuffer(ORIGIN).insert(1, new char[] { 'A' }), "\uFF21A"); + check(new StringBuffer(ORIGIN).insert(0, new char[] { 'A' }), "A\uFF21"); + check(new StringBuffer(ORIGIN).insert(0, new StringBuffer("A")), + "A\uFF21"); + check(new StringBuffer(ORIGIN).insert(1, new StringBuffer("A")), + "\uFF21A"); + check(new StringBuffer(ORIGIN).insert(0, ""), "\uFF21"); + check(new StringBuffer(ORIGIN).insert(0, "A"), "A\uFF21"); + check(new StringBuffer(ORIGIN).insert(1, "A"), "\uFF21A"); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), -1); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), 0); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf(""), 1); + check(new StringBuffer(ORIGIN).replace(0, 0, "A"), "A\uFF21"); + check(new StringBuffer(ORIGIN).replace(0, 1, "A"), "A"); + checkSetCharAt(new StringBuffer(ORIGIN), 0, 'A', "A"); + checkSetLength(new StringBuffer(ORIGIN), 0, ""); + checkSetLength(new StringBuffer(ORIGIN), 1, "\uFF21"); + check(new StringBuffer(ORIGIN).substring(0), "\uFF21"); + check(new StringBuffer(ORIGIN).substring(1), ""); + } + + /* + * Tests for "\uFF21A" + */ + @Test + public void testCompactStringBufferForMixedA1() { + final String ORIGIN = "\uFF21A"; + check(new StringBuffer(ORIGIN).delete(0, 1), "A"); + check(new StringBuffer(ORIGIN).delete(1, 2), "\uFF21"); + check(new StringBuffer(ORIGIN).deleteCharAt(1), "\uFF21"); + check(new StringBuffer(ORIGIN).deleteCharAt(0), "A"); + assertEquals(new StringBuffer(ORIGIN).indexOf("A", 0), 1); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21", 0), 0); + assertEquals(new StringBuffer(ORIGIN).indexOf("", 0), 0); + check(new StringBuffer(ORIGIN).insert(1, new char[] { 'A' }), "\uFF21AA"); + check(new StringBuffer(ORIGIN).insert(0, new char[] { '\uFF21' }), + "\uFF21\uFF21A"); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), 1); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), 0); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf(""), 2); + check(new StringBuffer(ORIGIN).replace(0, 0, "A"), "A\uFF21A"); + check(new StringBuffer(ORIGIN).replace(0, 1, "A"), "AA"); + checkSetCharAt(new StringBuffer(ORIGIN), 0, 'A', "AA"); + checkSetLength(new StringBuffer(ORIGIN), 0, ""); + checkSetLength(new StringBuffer(ORIGIN), 1, "\uFF21"); + check(new StringBuffer(ORIGIN).substring(0), "\uFF21A"); + check(new StringBuffer(ORIGIN).substring(1), "A"); + } + + /* + * Tests for "A\uFF21" + */ + @Test + public void testCompactStringBufferForMixedA2() { + final String ORIGIN = "A\uFF21"; + check(new StringBuffer(ORIGIN).replace(1, 2, "A"), "AA"); + checkSetLength(new StringBuffer(ORIGIN), 1, "A"); + check(new StringBuffer(ORIGIN).substring(0), "A\uFF21"); + check(new StringBuffer(ORIGIN).substring(1), "\uFF21"); + check(new StringBuffer(ORIGIN).substring(0, 1), "A"); + } + + /* + * Tests for "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A" + */ + @Test + public void testCompactStringBufferForDuplicatedMixedA1() { + final String ORIGIN = "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A"; + checkSetLength(new StringBuffer(ORIGIN), 1, "\uFF21"); + assertEquals(new StringBuffer(ORIGIN).indexOf("A", 5), 5); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21", 5), 6); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), 9); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), 8); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf(""), 10); + check(new StringBuffer(ORIGIN).substring(9), "A"); + check(new StringBuffer(ORIGIN).substring(8), "\uFF21A"); + } + + /* + * Tests for "A\uFF21A\uFF21A\uFF21A\uFF21A\uFF21" + */ + @Test + public void testCompactStringBufferForDuplicatedMixedA2() { + final String ORIGIN = "A\uFF21A\uFF21A\uFF21A\uFF21A\uFF21"; + checkSetLength(new StringBuffer(ORIGIN), 1, "A"); + assertEquals(new StringBuffer(ORIGIN).indexOf("A", 5), 6); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21", 5), 5); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), 8); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), 9); + check(new StringBuffer(ORIGIN).substring(9), "\uFF21"); + check(new StringBuffer(ORIGIN).substring(8), "A\uFF21"); + } + + /* + * Tests for "\uD801\uDC00\uD801\uDC01" + */ + @Test + public void testCompactStringForSupplementaryCodePoint() { + final String ORIGIN = "\uD801\uDC00\uD801\uDC01"; + check(new StringBuffer(ORIGIN).append("A"), "\uD801\uDC00\uD801\uDC01A"); + check(new StringBuffer(ORIGIN).append("\uFF21"), + "\uD801\uDC00\uD801\uDC01\uFF21"); + check(new StringBuffer(ORIGIN).appendCodePoint('A'), + "\uD801\uDC00\uD801\uDC01A"); + check(new StringBuffer(ORIGIN).appendCodePoint('\uFF21'), + "\uD801\uDC00\uD801\uDC01\uFF21"); + assertEquals(new StringBuffer(ORIGIN).charAt(0), '\uD801'); + assertEquals(new StringBuffer(ORIGIN).codePointAt(0), + Character.codePointAt(ORIGIN, 0)); + assertEquals(new StringBuffer(ORIGIN).codePointAt(1), + Character.codePointAt(ORIGIN, 1)); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(2), + Character.codePointAt(ORIGIN, 0)); + assertEquals(new StringBuffer(ORIGIN).codePointCount(1, 3), 2); + check(new StringBuffer(ORIGIN).delete(0, 2), "\uD801\uDC01"); + check(new StringBuffer(ORIGIN).delete(0, 3), "\uDC01"); + check(new StringBuffer(ORIGIN).deleteCharAt(1), "\uD801\uD801\uDC01"); + checkGetChars(new StringBuffer(ORIGIN), 0, 3, new char[] { '\uD801', + '\uDC00', '\uD801' }); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uD801\uDC01"), 2); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uDC01"), 3); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21"), -1); + assertEquals(new StringBuffer(ORIGIN).indexOf("A"), -1); + check(new StringBuffer(ORIGIN).insert(0, "\uFF21"), + "\uFF21\uD801\uDC00\uD801\uDC01"); + check(new StringBuffer(ORIGIN).insert(1, "\uFF21"), + "\uD801\uFF21\uDC00\uD801\uDC01"); + check(new StringBuffer(ORIGIN).insert(1, "A"), + "\uD801A\uDC00\uD801\uDC01"); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uDC00\uD801"), 1); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uD801"), 2); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), -1); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), -1); + assertEquals(new StringBuffer(ORIGIN).length(), 4); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(0, 1), 2); + check(new StringBuffer(ORIGIN).replace(0, 2, "A"), "A\uD801\uDC01"); + check(new StringBuffer(ORIGIN).replace(0, 3, "A"), "A\uDC01"); + check(new StringBuffer(ORIGIN).replace(0, 2, "\uFF21"), + "\uFF21\uD801\uDC01"); + check(new StringBuffer(ORIGIN).replace(0, 3, "\uFF21"), "\uFF21\uDC01"); + check(new StringBuffer(ORIGIN).reverse(), "\uD801\uDC01\uD801\uDC00"); + checkSetCharAt(new StringBuffer(ORIGIN), 1, '\uDC01', + "\uD801\uDC01\uD801\uDC01"); + checkSetCharAt(new StringBuffer(ORIGIN), 1, 'A', "\uD801A\uD801\uDC01"); + checkSetLength(new StringBuffer(ORIGIN), 2, "\uD801\uDC00"); + checkSetLength(new StringBuffer(ORIGIN), 3, "\uD801\uDC00\uD801"); + check(new StringBuffer(ORIGIN).substring(1, 3), "\uDC00\uD801"); + } + + /* + * Tests for "A\uD801\uDC00\uFF21" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed1() { + final String ORIGIN = "A\uD801\uDC00\uFF21"; + assertEquals(new StringBuffer(ORIGIN).codePointBefore(3), + Character.codePointAt(ORIGIN, 1)); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(2), '\uD801'); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(1), 'A'); + assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 3), 2); + assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 4), 3); + check(new StringBuffer(ORIGIN).delete(0, 1), "\uD801\uDC00\uFF21"); + check(new StringBuffer(ORIGIN).delete(0, 1).delete(2, 3), "\uD801\uDC00"); + check(new StringBuffer(ORIGIN).deleteCharAt(3).deleteCharAt(0), + "\uD801\uDC00"); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21"), 3); + assertEquals(new StringBuffer(ORIGIN).indexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), 3); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(0, 1), 1); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(1, 1), 3); + check(new StringBuffer(ORIGIN).replace(1, 3, "A"), "AA\uFF21"); + check(new StringBuffer(ORIGIN).replace(1, 4, "A"), "AA"); + check(new StringBuffer(ORIGIN).replace(1, 4, ""), "A"); + check(new StringBuffer(ORIGIN).reverse(), "\uFF21\uD801\uDC00A"); + checkSetLength(new StringBuffer(ORIGIN), 1, "A"); + check(new StringBuffer(ORIGIN).substring(0, 1), "A"); + } + + /* + * Tests for "\uD801\uDC00\uFF21A" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed2() { + final String ORIGIN = "\uD801\uDC00\uFF21A"; + assertEquals(new StringBuffer(ORIGIN).codePointBefore(3), + Character.codePointAt(ORIGIN, 2)); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(2), + Character.codePointAt(ORIGIN, 0)); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(1), '\uD801'); + assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 3), 2); + assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 4), 3); + check(new StringBuffer(ORIGIN).delete(0, 2), "\uFF21A"); + check(new StringBuffer(ORIGIN).delete(0, 3), "A"); + check(new StringBuffer(ORIGIN).deleteCharAt(0).deleteCharAt(0) + .deleteCharAt(0), "A"); + assertEquals(new StringBuffer(ORIGIN).indexOf("A"), 3); + assertEquals(new StringBuffer(ORIGIN).delete(0, 3).indexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).replace(0, 3, "B").indexOf("A"), + 1); + assertEquals(new StringBuffer(ORIGIN).substring(3, 4).indexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(0, 1), 2); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(2, 1), 3); + check(new StringBuffer(ORIGIN).replace(0, 3, "B"), "BA"); + check(new StringBuffer(ORIGIN).reverse(), "A\uFF21\uD801\uDC00"); + } + + /* + * Tests for "\uD801A\uDC00\uFF21" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed3() { + final String ORIGIN = "\uD801A\uDC00\uFF21"; + assertEquals(new StringBuffer(ORIGIN).codePointAt(1), 'A'); + assertEquals(new StringBuffer(ORIGIN).codePointAt(3), '\uFF21'); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(1), '\uD801'); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(2), 'A'); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(3), '\uDC00'); + assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 3), 3); + assertEquals(new StringBuffer(ORIGIN).codePointCount(1, 3), 2); + assertEquals(new StringBuffer(ORIGIN).delete(0, 1).delete(1, 3) + .indexOf("A"), 0); + assertEquals( + new StringBuffer(ORIGIN).replace(0, 1, "B").replace(2, 4, "C") + .indexOf("A"), 1); + assertEquals(new StringBuffer(ORIGIN).substring(1, 4).substring(0, 1) + .indexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(0, 1), 1); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(2, 1), 3); + check(new StringBuffer(ORIGIN).reverse(), "\uFF21\uDC00A\uD801"); + } + + /* + * Tests for "A\uDC01\uFF21\uD801" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed4() { + final String ORIGIN = "A\uDC01\uFF21\uD801"; + assertEquals(new StringBuffer(ORIGIN).codePointAt(1), '\uDC01'); + assertEquals(new StringBuffer(ORIGIN).codePointAt(3), '\uD801'); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(1), 'A'); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(2), '\uDC01'); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(3), '\uFF21'); + assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 3), 3); + assertEquals(new StringBuffer(ORIGIN).codePointCount(1, 3), 2); + assertEquals(new StringBuffer(ORIGIN).delete(1, 4).indexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).replace(1, 4, "B").indexOf("A"), + 0); + assertEquals(new StringBuffer(ORIGIN).substring(0, 1).indexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(0, 1), 1); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(2, 1), 3); + check(new StringBuffer(ORIGIN).reverse(), "\uD801\uFF21\uDC01A"); + } + + @Test + public void testCompactStringMisc() { + String ascii = "abcdefgh"; + String asciiMixed = "abc" + "\u4e00\u4e01\u4e02" + "fgh"; + String bmp = "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08"; + String bmpMixed = "\u4e00\u4e01\u4e02" + "ABC" + "\u4e06\u4e07\u4e08"; + + check(new StringBuffer().append(ascii).delete(0, 20).toString(), + ""); + check(new StringBuffer().append(ascii).delete(3, 20).toString(), + "abc"); + check(new StringBuffer().append(ascii).delete(3, 6).toString(), + "abcgh"); + check(new StringBuffer().append(ascii).deleteCharAt(0).toString(), + "bcdefgh"); + check(new StringBuffer().append(ascii).deleteCharAt(3).toString(), + "abcefgh"); + check(new StringBuffer().append(asciiMixed).delete(3, 6).toString(), + "abcfgh"); + check(new StringBuffer().append(asciiMixed).deleteCharAt(3).toString(), + "abc\u4e01\u4e02fgh"); + check(new StringBuffer().append(asciiMixed).deleteCharAt(3) + .deleteCharAt(3) + .deleteCharAt(3).toString(), + "abcfgh"); + check(new StringBuffer().append(bmp).delete(0, 20).toString(), + ""); + check(new StringBuffer().append(bmp).delete(3, 20).toString(), + "\u4e00\u4e01\u4e02"); + check(new StringBuffer().append(bmp).delete(3, 6).toString(), + "\u4e00\u4e01\u4e02\u4e06\u4e07\u4e08"); + check(new StringBuffer().append(bmp).deleteCharAt(0).toString(), + "\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08"); + check(new StringBuffer().append(bmp).deleteCharAt(3).toString(), + "\u4e00\u4e01\u4e02\u4e04\u4e05\u4e06\u4e07\u4e08"); + check(new StringBuffer().append(bmpMixed).delete(3, 6).toString(), + "\u4e00\u4e01\u4e02\u4e06\u4e07\u4e08"); + + //////////////////////////////////////////////////////////////////// + check(new StringBuffer().append(ascii).replace(3, 6, "AB").toString(), + "abcABgh"); + check(new StringBuffer().append(asciiMixed).replace(3, 6, "AB").toString(), + "abcABfgh"); + check(new StringBuffer().append(bmp).replace(3, 6, "AB").toString(), + "\u4e00\u4e01\u4e02AB\u4e06\u4e07\u4e08"); + + check(new StringBuffer().append(bmpMixed).replace(3, 6, "").toString(), + "\u4e00\u4e01\u4e02\u4e06\u4e07\u4e08"); + + check(new StringBuffer().append(ascii).replace(3, 6, "\u4e01\u4e02").toString(), + "abc\u4e01\u4e02gh"); + + //////////////////////////////////////////////////////////////////// + check(new StringBuffer().append(ascii).insert(3, "").toString(), + "abcdefgh"); + check(new StringBuffer().append(ascii).insert(3, "AB").toString(), + "abcABdefgh"); + check(new StringBuffer().append(ascii).insert(3, "\u4e01\u4e02").toString(), + "abc\u4e01\u4e02defgh"); + + check(new StringBuffer().append(asciiMixed).insert(0, 'A').toString(), + "Aabc\u4e00\u4e01\u4e02fgh"); + check(new StringBuffer().append(asciiMixed).insert(3, "A").toString(), + "abcA\u4e00\u4e01\u4e02fgh"); + + check(new StringBuffer().append(ascii).insert(3, 1234567).toString(), + "abc1234567defgh"); + check(new StringBuffer().append(bmp).insert(3, 1234567).toString(), + "\u4e00\u4e01\u4e021234567\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08"); + + //////////////////////////////////////////////////////////////////// + check(new StringBuffer().append(ascii).append(1.23456).toString(), + "abcdefgh1.23456"); + check(new StringBuffer().append(bmp).append(1.23456).toString(), + "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e081.23456"); + } + + private void checkGetChars(StringBuffer sb, int srcBegin, int srcEnd, + char expected[]) { + char[] dst = new char[srcEnd - srcBegin]; + sb.getChars(srcBegin, srcEnd, dst, 0); + assertTrue(Arrays.equals(dst, expected)); + } + + private void checkSetCharAt(StringBuffer sb, int index, char ch, + String expected) { + sb.setCharAt(index, ch); + check(sb, expected); + } + + private void checkSetLength(StringBuffer sb, int newLength, String expected) { + sb.setLength(newLength); + check(sb, expected); + } + + private void check(StringBuffer sb, String expected) { + check(sb.toString(), expected); + } + + private void check(String str, String expected) { + assertTrue(str.equals(expected), String.format( + "Get (%s) but expect (%s), ", escapeNonASCIIs(str), + escapeNonASCIIs(expected))); + } + + /* + * Because right now system default charset in JPRT environment is only + * guaranteed to support ASCII characters in log, so we escape them. + */ + private String escapeNonASCIIs(String str) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c > 0x7F) { + sb.append("\\u").append(Integer.toHexString((int) c)); + } else { + sb.append(c); + } + } + return sb.toString(); + } +} diff --git a/jdk/test/java/lang/StringBuffer/CompactStringBufferSerialization.java b/jdk/test/java/lang/StringBuffer/CompactStringBufferSerialization.java new file mode 100644 index 00000000000..346c824ed12 --- /dev/null +++ b/jdk/test/java/lang/StringBuffer/CompactStringBufferSerialization.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.io.*; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static jdk.testlibrary.SerializationUtils.*; +import static org.testng.Assert.*; + +/* + * @test + * @bug 8077559 + * @library /lib/testlibrary + * @build jdk.testlibrary.SerializationUtils + * @summary Tests Compact String. This one is testing StringBuffer serialization + * among -XX:+CompactStrings/-XX:-CompactStrings/LegacyStringBuffer + * @run testng/othervm -XX:+CompactStrings CompactStringBufferSerialization + * @run testng/othervm -XX:-CompactStrings CompactStringBufferSerialization + */ + +public class CompactStringBufferSerialization { + @DataProvider + public Object[][] provider() { + return new Object[][] { + // every byte array is serialized from corresponding StringBuilder object + // by previous JDK(build 1.8.0_45-b14). + new Object[] { + new StringBuffer(""), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 0, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("A"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 1, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 17, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("AB"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 2, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 18, 0, 65, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("abcdefghijk"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 11, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 27, 0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0, 105, 0, + 106, 0, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("\uff21"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 1, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 17, -1, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("\uff21\uff22"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 2, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 18, -1, 33, -1, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("\uff21A\uff21A\uff21A\uff21A\uff21A"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 10, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 26, -1, 33, 0, 65, -1, 33, 0, 65, -1, 33, 0, 65, -1, 33, 0, 65, -1, 33, 0, 65, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("A\uff21B\uff22C\uff23D\uff24E\uff25F\uff26G\uff27H\uff28"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 16, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 32, 0, 65, -1, 33, 0, 66, -1, 34, 0, 67, -1, 35, 0, 68, -1, 36, 0, 69, -1, 37, + 0, 70, -1, 38, 0, 71, -1, 39, 0, 72, -1, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("\uff21A\uff22B\uff23C\uff24D\uff25E\uff26F\uff27G\uff28H"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 16, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 32, -1, 33, 0, 65, -1, 34, 0, 66, -1, 35, 0, 67, -1, 36, 0, 68, -1, 37, 0, 69, + -1, 38, 0, 70, -1, 39, 0, 71, -1, 40, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("\ud801\udc00\ud801\udc01\uff21A"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 6, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 22, -40, 1, -36, 0, -40, 1, -36, 1, -1, 33, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 10, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 26, -1, 33, -1, 34, -1, 33, -1, 34, -1, 33, -1, 34, -1, 33, -1, 34, -1, 33, -1, + 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } } }; + } + + /* + * Verify serialization works between Compact StringBuffer/Legacy StringBuffer + */ + @Test(dataProvider = "provider") + public void test(StringBuffer sbContent, byte[] baInJDK8) throws Exception { + // Serialize a StringBuffer object into byte array. + byte[] ba = serialize(sbContent); + assertEquals(ba, baInJDK8); + // Deserialize a StringBuffer object from byte array which is generated by previous JDK(build 1.8.0_45-b14). + Object obj = deserialize(ba); + assertEquals(obj.getClass(), StringBuffer.class); + assertTrue(equals((StringBuffer)obj, sbContent)); + } + + boolean equals(StringBuffer sb, StringBuffer expected) { + if(sb.length() == expected.length() + && sb.capacity() == expected.capacity() + && sb.toString().equals(expected.toString())) { + return true; + } + return false; + } +} diff --git a/jdk/test/java/lang/StringBuffer/Exceptions.java b/jdk/test/java/lang/StringBuffer/Exceptions.java index 5f68b800b56..061b3d75d7c 100644 --- a/jdk/test/java/lang/StringBuffer/Exceptions.java +++ b/jdk/test/java/lang/StringBuffer/Exceptions.java @@ -94,7 +94,7 @@ public class Exceptions { System.out.println("StringBuffer.replace(int start, int end, String str)"); tryCatch(" -1, 2, \" \"", - new StringIndexOutOfBoundsException(-1), + new StringIndexOutOfBoundsException("start -1, end 2, length 7"), new Runnable() { public void run() { StringBuffer sb = new StringBuffer("hilbert"); @@ -102,14 +102,14 @@ public class Exceptions { }}); tryCatch(" 7, 8, \" \"", - new StringIndexOutOfBoundsException("start > length()"), + new StringIndexOutOfBoundsException("start 7, end 6, length 6"), new Runnable() { public void run() { StringBuffer sb = new StringBuffer("banach"); sb.replace(7, 8, " "); }}); tryCatch(" 2, 1, \" \"", - new StringIndexOutOfBoundsException("start > end"), + new StringIndexOutOfBoundsException("start 2, end 1, length 7"), new Runnable() { public void run() { StringBuffer sb = new StringBuffer("riemann"); diff --git a/jdk/test/java/lang/StringBuilder/BuilderForwarding.java b/jdk/test/java/lang/StringBuilder/BuilderForwarding.java index a78c3232b17..82e40108d4c 100644 --- a/jdk/test/java/lang/StringBuilder/BuilderForwarding.java +++ b/jdk/test/java/lang/StringBuilder/BuilderForwarding.java @@ -264,4 +264,3 @@ public class BuilderForwarding { } } } - diff --git a/jdk/test/java/lang/StringBuilder/CompactStringBuilder.java b/jdk/test/java/lang/StringBuilder/CompactStringBuilder.java new file mode 100644 index 00000000000..7c69326aca8 --- /dev/null +++ b/jdk/test/java/lang/StringBuilder/CompactStringBuilder.java @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.util.Arrays; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/* + * @test + * @bug 8054307 8077559 + * @summary Tests Compact String. This test is testing StringBuilder + * behavior related to Compact String. + * @run testng/othervm -XX:+CompactStrings CompactStringBuilder + * @run testng/othervm -XX:-CompactStrings CompactStringBuilder + */ + +public class CompactStringBuilder { + + /* + * Tests for "A" + */ + @Test + public void testCompactStringBuilderForLatinA() { + final String ORIGIN = "A"; + /* + * Because right now ASCII is the default encoding parameter for source + * code in JDK build environment, so we escape them. same as below. + */ + check(new StringBuilder(ORIGIN).append(new char[] { '\uFF21' }), + "A\uFF21"); + check(new StringBuilder(ORIGIN).append(new StringBuffer("\uFF21")), + "A\uFF21"); + check(new StringBuilder(ORIGIN).append("\uFF21"), "A\uFF21"); + check(new StringBuilder(ORIGIN).append(new StringBuffer("\uFF21")), + "A\uFF21"); + check(new StringBuilder(ORIGIN).delete(0, 1), ""); + check(new StringBuilder(ORIGIN).delete(0, 0), "A"); + check(new StringBuilder(ORIGIN).deleteCharAt(0), ""); + assertEquals(new StringBuilder(ORIGIN).indexOf("A", 0), 0); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21", 0), -1); + assertEquals(new StringBuilder(ORIGIN).indexOf("", 0), 0); + assertEquals(new StringBuilder(ORIGIN).insert(1, "\uD801\uDC00") + .indexOf("A", 0), 0); + assertEquals(new StringBuilder(ORIGIN).insert(0, "\uD801\uDC00") + .indexOf("A", 0), 2); + check(new StringBuilder(ORIGIN).insert(0, new char[] {}), "A"); + check(new StringBuilder(ORIGIN).insert(1, new char[] { '\uFF21' }), + "A\uFF21"); + check(new StringBuilder(ORIGIN).insert(0, new char[] { '\uFF21' }), + "\uFF21A"); + check(new StringBuilder(ORIGIN).insert(0, new StringBuffer("\uFF21")), + "\uFF21A"); + check(new StringBuilder(ORIGIN).insert(1, new StringBuffer("\uFF21")), + "A\uFF21"); + check(new StringBuilder(ORIGIN).insert(0, ""), "A"); + check(new StringBuilder(ORIGIN).insert(0, "\uFF21"), "\uFF21A"); + check(new StringBuilder(ORIGIN).insert(1, "\uFF21"), "A\uFF21"); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), -1); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf(""), 1); + check(new StringBuilder(ORIGIN).replace(0, 0, "\uFF21"), "\uFF21A"); + check(new StringBuilder(ORIGIN).replace(0, 1, "\uFF21"), "\uFF21"); + checkSetCharAt(new StringBuilder(ORIGIN), 0, '\uFF21', "\uFF21"); + checkSetLength(new StringBuilder(ORIGIN), 0, ""); + checkSetLength(new StringBuilder(ORIGIN), 1, "A"); + check(new StringBuilder(ORIGIN).substring(0), "A"); + check(new StringBuilder(ORIGIN).substring(1), ""); + } + + /* + * Tests for "\uFF21" + */ + @Test + public void testCompactStringBuilderForNonLatinA() { + final String ORIGIN = "\uFF21"; + check(new StringBuilder(ORIGIN).append(new char[] { 'A' }), "\uFF21A"); + check(new StringBuilder(ORIGIN).append(new StringBuffer("A")), "\uFF21A"); + check(new StringBuilder(ORIGIN).append("A"), "\uFF21A"); + check(new StringBuilder(ORIGIN).append(new StringBuffer("A")), "\uFF21A"); + check(new StringBuilder(ORIGIN).delete(0, 1), ""); + check(new StringBuilder(ORIGIN).delete(0, 0), "\uFF21"); + check(new StringBuilder(ORIGIN).deleteCharAt(0), ""); + assertEquals(new StringBuilder(ORIGIN).indexOf("A", 0), -1); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21", 0), 0); + assertEquals(new StringBuilder(ORIGIN).indexOf("", 0), 0); + check(new StringBuilder(ORIGIN).insert(0, new char[] {}), "\uFF21"); + check(new StringBuilder(ORIGIN).insert(1, new char[] { 'A' }), "\uFF21A"); + check(new StringBuilder(ORIGIN).insert(0, new char[] { 'A' }), "A\uFF21"); + check(new StringBuilder(ORIGIN).insert(0, new StringBuffer("A")), + "A\uFF21"); + check(new StringBuilder(ORIGIN).insert(1, new StringBuffer("A")), + "\uFF21A"); + check(new StringBuilder(ORIGIN).insert(0, ""), "\uFF21"); + check(new StringBuilder(ORIGIN).insert(0, "A"), "A\uFF21"); + check(new StringBuilder(ORIGIN).insert(1, "A"), "\uFF21A"); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), -1); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), 0); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf(""), 1); + check(new StringBuilder(ORIGIN).replace(0, 0, "A"), "A\uFF21"); + check(new StringBuilder(ORIGIN).replace(0, 1, "A"), "A"); + checkSetCharAt(new StringBuilder(ORIGIN), 0, 'A', "A"); + checkSetLength(new StringBuilder(ORIGIN), 0, ""); + checkSetLength(new StringBuilder(ORIGIN), 1, "\uFF21"); + check(new StringBuilder(ORIGIN).substring(0), "\uFF21"); + check(new StringBuilder(ORIGIN).substring(1), ""); + } + + /* + * Tests for "\uFF21A" + */ + @Test + public void testCompactStringBuilderForMixedA1() { + final String ORIGIN = "\uFF21A"; + check(new StringBuilder(ORIGIN).delete(0, 1), "A"); + check(new StringBuilder(ORIGIN).delete(1, 2), "\uFF21"); + check(new StringBuilder(ORIGIN).deleteCharAt(1), "\uFF21"); + check(new StringBuilder(ORIGIN).deleteCharAt(0), "A"); + assertEquals(new StringBuilder(ORIGIN).indexOf("A", 0), 1); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21", 0), 0); + assertEquals(new StringBuilder(ORIGIN).indexOf("", 0), 0); + check(new StringBuilder(ORIGIN).insert(1, new char[] { 'A' }), + "\uFF21AA"); + check(new StringBuilder(ORIGIN).insert(0, new char[] { '\uFF21' }), + "\uFF21\uFF21A"); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), 1); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), 0); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf(""), 2); + check(new StringBuilder(ORIGIN).replace(0, 0, "A"), "A\uFF21A"); + check(new StringBuilder(ORIGIN).replace(0, 1, "A"), "AA"); + checkSetCharAt(new StringBuilder(ORIGIN), 0, 'A', "AA"); + checkSetLength(new StringBuilder(ORIGIN), 0, ""); + checkSetLength(new StringBuilder(ORIGIN), 1, "\uFF21"); + check(new StringBuilder(ORIGIN).substring(0), "\uFF21A"); + check(new StringBuilder(ORIGIN).substring(1), "A"); + } + + /* + * Tests for "A\uFF21" + */ + @Test + public void testCompactStringBuilderForMixedA2() { + final String ORIGIN = "A\uFF21"; + check(new StringBuilder(ORIGIN).replace(1, 2, "A"), "AA"); + checkSetLength(new StringBuilder(ORIGIN), 1, "A"); + check(new StringBuilder(ORIGIN).substring(0), "A\uFF21"); + check(new StringBuilder(ORIGIN).substring(1), "\uFF21"); + check(new StringBuilder(ORIGIN).substring(0, 1), "A"); + } + + /* + * Tests for "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A" + */ + @Test + public void testCompactStringBuilderForDuplicatedMixedA1() { + final String ORIGIN = "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A"; + checkSetLength(new StringBuilder(ORIGIN), 1, "\uFF21"); + assertEquals(new StringBuilder(ORIGIN).indexOf("A", 5), 5); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21", 5), 6); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), 9); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), 8); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf(""), 10); + check(new StringBuilder(ORIGIN).substring(9), "A"); + check(new StringBuilder(ORIGIN).substring(8), "\uFF21A"); + } + + /* + * Tests for "A\uFF21A\uFF21A\uFF21A\uFF21A\uFF21" + */ + @Test + public void testCompactStringBuilderForDuplicatedMixedA2() { + final String ORIGIN = "A\uFF21A\uFF21A\uFF21A\uFF21A\uFF21"; + checkSetLength(new StringBuilder(ORIGIN), 1, "A"); + assertEquals(new StringBuilder(ORIGIN).indexOf("A", 5), 6); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21", 5), 5); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), 8); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), 9); + check(new StringBuilder(ORIGIN).substring(9), "\uFF21"); + check(new StringBuilder(ORIGIN).substring(8), "A\uFF21"); + } + + /* + * Tests for "\uD801\uDC00\uD801\uDC01" + */ + @Test + public void testCompactStringForSupplementaryCodePoint() { + final String ORIGIN = "\uD801\uDC00\uD801\uDC01"; + check(new StringBuilder(ORIGIN).append("A"), "\uD801\uDC00\uD801\uDC01A"); + check(new StringBuilder(ORIGIN).append("\uFF21"), + "\uD801\uDC00\uD801\uDC01\uFF21"); + check(new StringBuilder(ORIGIN).appendCodePoint('A'), + "\uD801\uDC00\uD801\uDC01A"); + check(new StringBuilder(ORIGIN).appendCodePoint('\uFF21'), + "\uD801\uDC00\uD801\uDC01\uFF21"); + assertEquals(new StringBuilder(ORIGIN).charAt(0), '\uD801'); + assertEquals(new StringBuilder(ORIGIN).codePointAt(0), + Character.codePointAt(ORIGIN, 0)); + assertEquals(new StringBuilder(ORIGIN).codePointAt(1), + Character.codePointAt(ORIGIN, 1)); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(2), + Character.codePointAt(ORIGIN, 0)); + assertEquals(new StringBuilder(ORIGIN).codePointCount(1, 3), 2); + check(new StringBuilder(ORIGIN).delete(0, 2), "\uD801\uDC01"); + check(new StringBuilder(ORIGIN).delete(0, 3), "\uDC01"); + check(new StringBuilder(ORIGIN).deleteCharAt(1), "\uD801\uD801\uDC01"); + checkGetChars(new StringBuilder(ORIGIN), 0, 3, new char[] { '\uD801', + '\uDC00', '\uD801' }); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uD801\uDC01"), 2); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uDC01"), 3); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21"), -1); + assertEquals(new StringBuilder(ORIGIN).indexOf("A"), -1); + check(new StringBuilder(ORIGIN).insert(0, "\uFF21"), + "\uFF21\uD801\uDC00\uD801\uDC01"); + check(new StringBuilder(ORIGIN).insert(1, "\uFF21"), + "\uD801\uFF21\uDC00\uD801\uDC01"); + check(new StringBuilder(ORIGIN).insert(1, "A"), + "\uD801A\uDC00\uD801\uDC01"); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uDC00\uD801"), 1); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uD801"), 2); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), -1); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), -1); + assertEquals(new StringBuilder(ORIGIN).length(), 4); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(0, 1), 2); + check(new StringBuilder(ORIGIN).replace(0, 2, "A"), "A\uD801\uDC01"); + check(new StringBuilder(ORIGIN).replace(0, 3, "A"), "A\uDC01"); + check(new StringBuilder(ORIGIN).replace(0, 2, "\uFF21"), + "\uFF21\uD801\uDC01"); + check(new StringBuilder(ORIGIN).replace(0, 3, "\uFF21"), "\uFF21\uDC01"); + check(new StringBuilder(ORIGIN).reverse(), "\uD801\uDC01\uD801\uDC00"); + checkSetCharAt(new StringBuilder(ORIGIN), 1, '\uDC01', + "\uD801\uDC01\uD801\uDC01"); + checkSetCharAt(new StringBuilder(ORIGIN), 1, 'A', "\uD801A\uD801\uDC01"); + checkSetLength(new StringBuilder(ORIGIN), 2, "\uD801\uDC00"); + checkSetLength(new StringBuilder(ORIGIN), 3, "\uD801\uDC00\uD801"); + check(new StringBuilder(ORIGIN).substring(1, 3), "\uDC00\uD801"); + } + + /* + * Tests for "A\uD801\uDC00\uFF21" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed1() { + final String ORIGIN = "A\uD801\uDC00\uFF21"; + assertEquals(new StringBuilder(ORIGIN).codePointBefore(3), + Character.codePointAt(ORIGIN, 1)); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(2), '\uD801'); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(1), 'A'); + assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 3), 2); + assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 4), 3); + check(new StringBuilder(ORIGIN).delete(0, 1), "\uD801\uDC00\uFF21"); + check(new StringBuilder(ORIGIN).delete(0, 1).delete(2, 3), + "\uD801\uDC00"); + check(new StringBuilder(ORIGIN).deleteCharAt(3).deleteCharAt(0), + "\uD801\uDC00"); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21"), 3); + assertEquals(new StringBuilder(ORIGIN).indexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), 3); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(0, 1), 1); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(1, 1), 3); + check(new StringBuilder(ORIGIN).replace(1, 3, "A"), "AA\uFF21"); + check(new StringBuilder(ORIGIN).replace(1, 4, "A"), "AA"); + check(new StringBuilder(ORIGIN).replace(1, 4, ""), "A"); + check(new StringBuilder(ORIGIN).reverse(), "\uFF21\uD801\uDC00A"); + checkSetLength(new StringBuilder(ORIGIN), 1, "A"); + check(new StringBuilder(ORIGIN).substring(0, 1), "A"); + } + + /* + * Tests for "\uD801\uDC00\uFF21A" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed2() { + final String ORIGIN = "\uD801\uDC00\uFF21A"; + assertEquals(new StringBuilder(ORIGIN).codePointBefore(3), + Character.codePointAt(ORIGIN, 2)); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(2), + Character.codePointAt(ORIGIN, 0)); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(1), '\uD801'); + assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 3), 2); + assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 4), 3); + check(new StringBuilder(ORIGIN).delete(0, 2), "\uFF21A"); + check(new StringBuilder(ORIGIN).delete(0, 3), "A"); + check(new StringBuilder(ORIGIN).deleteCharAt(0).deleteCharAt(0) + .deleteCharAt(0), "A"); + assertEquals(new StringBuilder(ORIGIN).indexOf("A"), 3); + assertEquals(new StringBuilder(ORIGIN).delete(0, 3).indexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).replace(0, 3, "B").indexOf("A"), + 1); + assertEquals(new StringBuilder(ORIGIN).substring(3, 4).indexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(0, 1), 2); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(2, 1), 3); + check(new StringBuilder(ORIGIN).replace(0, 3, "B"), "BA"); + check(new StringBuilder(ORIGIN).reverse(), "A\uFF21\uD801\uDC00"); + } + + /* + * Tests for "\uD801A\uDC00\uFF21" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed3() { + final String ORIGIN = "\uD801A\uDC00\uFF21"; + assertEquals(new StringBuilder(ORIGIN).codePointAt(1), 'A'); + assertEquals(new StringBuilder(ORIGIN).codePointAt(3), '\uFF21'); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(1), '\uD801'); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(2), 'A'); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(3), '\uDC00'); + assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 3), 3); + assertEquals(new StringBuilder(ORIGIN).codePointCount(1, 3), 2); + assertEquals(new StringBuilder(ORIGIN).delete(0, 1).delete(1, 3) + .indexOf("A"), 0); + assertEquals( + new StringBuilder(ORIGIN).replace(0, 1, "B").replace(2, 4, "C") + .indexOf("A"), 1); + assertEquals(new StringBuilder(ORIGIN).substring(1, 4).substring(0, 1) + .indexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(0, 1), 1); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(2, 1), 3); + check(new StringBuilder(ORIGIN).reverse(), "\uFF21\uDC00A\uD801"); + } + + /* + * Tests for "A\uDC01\uFF21\uD801" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed4() { + final String ORIGIN = "A\uDC01\uFF21\uD801"; + assertEquals(new StringBuilder(ORIGIN).codePointAt(1), '\uDC01'); + assertEquals(new StringBuilder(ORIGIN).codePointAt(3), '\uD801'); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(1), 'A'); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(2), '\uDC01'); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(3), '\uFF21'); + assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 3), 3); + assertEquals(new StringBuilder(ORIGIN).codePointCount(1, 3), 2); + assertEquals(new StringBuilder(ORIGIN).delete(1, 4).indexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).replace(1, 4, "B").indexOf("A"), + 0); + assertEquals(new StringBuilder(ORIGIN).substring(0, 1).indexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(0, 1), 1); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(2, 1), 3); + check(new StringBuilder(ORIGIN).reverse(), "\uD801\uFF21\uDC01A"); + } + + private void checkGetChars(StringBuilder sb, int srcBegin, int srcEnd, + char expected[]) { + char[] dst = new char[srcEnd - srcBegin]; + sb.getChars(srcBegin, srcEnd, dst, 0); + assertTrue(Arrays.equals(dst, expected)); + } + + private void checkSetCharAt(StringBuilder sb, int index, char ch, + String expected) { + sb.setCharAt(index, ch); + check(sb, expected); + } + + private void checkSetLength(StringBuilder sb, int newLength, String expected) { + sb.setLength(newLength); + check(sb, expected); + } + + private void check(StringBuilder sb, String expected) { + check(sb.toString(), expected); + } + + private void check(String str, String expected) { + assertTrue(str.equals(expected), String.format( + "Get (%s) but expect (%s), ", escapeNonASCIIs(str), + escapeNonASCIIs(expected))); + } + + /* + * Because right now system default charset in JPRT environment is only + * guaranteed to support ASCII characters in log, so we escape them. + */ + private String escapeNonASCIIs(String str) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c > 0x7F) { + sb.append("\\u").append(Integer.toHexString((int) c)); + } else { + sb.append(c); + } + } + return sb.toString(); + } +} diff --git a/jdk/test/java/lang/StringBuilder/CompactStringBuilderSerialization.java b/jdk/test/java/lang/StringBuilder/CompactStringBuilderSerialization.java new file mode 100644 index 00000000000..0c622d78665 --- /dev/null +++ b/jdk/test/java/lang/StringBuilder/CompactStringBuilderSerialization.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.io.*; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static jdk.testlibrary.SerializationUtils.*; +import static org.testng.Assert.*; + +/* + * @test + * @bug 8077559 + * @library /lib/testlibrary + * @build jdk.testlibrary.SerializationUtils + * @summary Tests Compact String. This one is testing StringBuilder serialization + * among -XX:+CompactStrings/-XX:-CompactStrings/LegacyStringBuilder + * @run testng/othervm -XX:+CompactStrings CompactStringBuilderSerialization + * @run testng/othervm -XX:-CompactStrings CompactStringBuilderSerialization + */ + +public class CompactStringBuilderSerialization { + @DataProvider + public Object[][] provider() { + return new Object[][] { + // every byte array is serialized from corresponding StringBuffer object + // by previous JDK(build 1.8.0_45-b14). + new Object[] { + new StringBuilder(""), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 0, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("A"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 1, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 17, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("AB"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 2, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 18, 0, 65, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("abcdefghijk"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 11, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 27, 0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0, + 105, 0, 106, 0, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("\uff21"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 1, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 17, -1, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("\uff21\uff22"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 2, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 18, -1, 33, -1, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("\uff21A\uff21A\uff21A\uff21A\uff21A"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 10, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 26, -1, 33, 0, 65, -1, 33, 0, 65, -1, 33, 0, 65, -1, 33, 0, 65, -1, + 33, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("A\uff21B\uff22C\uff23D\uff24E\uff25F\uff26G\uff27H\uff28"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 16, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 32, 0, 65, -1, 33, 0, 66, -1, 34, 0, 67, -1, 35, 0, 68, -1, 36, 0, + 69, -1, 37, 0, 70, -1, 38, 0, 71, -1, 39, 0, 72, -1, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("\uff21A\uff22B\uff23C\uff24D\uff25E\uff26F\uff27G\uff28H"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 16, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 32, -1, 33, 0, 65, -1, 34, 0, 66, -1, 35, 0, 67, -1, 36, 0, 68, -1, + 37, 0, 69, -1, 38, 0, 70, -1, 39, 0, 71, -1, 40, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("\ud801\udc00\ud801\udc01\uff21A"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 6, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 22, -40, 1, -36, 0, -40, 1, -36, 1, -1, 33, 0, 65, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 10, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 26, -1, 33, -1, 34, -1, 33, -1, 34, -1, 33, -1, 34, -1, 33, -1, 34, + -1, 33, -1, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } } }; + } + + /* + * Verify serialization works between Compact StringBuilder/Legacy StringBuilder + */ + @Test(dataProvider = "provider") + public void test(StringBuilder sbContent, byte[] baInJDK8) throws Exception { + // Serialize a StringBuilder object into byte array. + byte[] ba = serialize(sbContent); + assertEquals(ba, baInJDK8); + // Deserialize a StringBuilder object from byte array which is generated by previous JDK(build 1.8.0_45-b14). + Object obj = deserialize(ba); + assertEquals(obj.getClass(), StringBuilder.class); + assertTrue(equals((StringBuilder)obj, sbContent)); + } + + boolean equals(StringBuilder sb, StringBuilder expected) { + if(sb.length() == expected.length() + && sb.capacity() == expected.capacity() + && sb.toString().equals(expected.toString())) { + return true; + } + return false; + } +} diff --git a/jdk/test/java/lang/StringBuilder/Exceptions.java b/jdk/test/java/lang/StringBuilder/Exceptions.java index e1686b01091..bee53bcea92 100644 --- a/jdk/test/java/lang/StringBuilder/Exceptions.java +++ b/jdk/test/java/lang/StringBuilder/Exceptions.java @@ -94,21 +94,21 @@ public class Exceptions { System.out.println("StringBuilder.replace(int start, int end, String str)"); tryCatch(" -1, 2, \" \"", - new StringIndexOutOfBoundsException(-1), + new StringIndexOutOfBoundsException("start -1, end 2, length 7"), new Runnable() { public void run() { StringBuilder sb = new StringBuilder("hilbert"); sb.replace(-1, 2, " "); }}); tryCatch(" 7, 8, \" \"", - new StringIndexOutOfBoundsException("start > length()"), + new StringIndexOutOfBoundsException("start 7, end 6, length 6"), new Runnable() { public void run() { StringBuilder sb = new StringBuilder("banach"); sb.replace(7, 8, " "); }}); tryCatch(" 2, 1, \" \"", - new StringIndexOutOfBoundsException("start > end"), + new StringIndexOutOfBoundsException("start 2, end 1, length 7"), new Runnable() { public void run() { StringBuilder sb = new StringBuilder("riemann"); diff --git a/jdk/test/java/lang/System/Logger/Level/LoggerLevelTest.java b/jdk/test/java/lang/System/Logger/Level/LoggerLevelTest.java new file mode 100644 index 00000000000..d93d2ee94e0 --- /dev/null +++ b/jdk/test/java/lang/System/Logger/Level/LoggerLevelTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.lang.System.Logger.Level; +import java.util.EnumSet; +import java.util.Objects; +import java.util.Set; +/** + * @test + * @bug 8140364 + * @summary Tests System.Logger.Level names and severity. + * @author danielfuchs + */ +public class LoggerLevelTest { + public static void main(String[] args) { + Set untested = EnumSet.allOf(Level.class); + testLevel(untested, Level.ALL, java.util.logging.Level.ALL); + testLevel(untested, Level.TRACE, java.util.logging.Level.FINER); + testLevel(untested, Level.DEBUG, java.util.logging.Level.FINE); + testLevel(untested, Level.INFO, java.util.logging.Level.INFO); + testLevel(untested, Level.WARNING, java.util.logging.Level.WARNING); + testLevel(untested, Level.ERROR, java.util.logging.Level.SEVERE); + testLevel(untested, Level.OFF, java.util.logging.Level.OFF); + if (!untested.isEmpty()) { + throw new RuntimeException("Some level values were not tested: " + untested); + } + } + + private static void testLevel(Set untested, Level systemLevel, java.util.logging.Level julLevel) { + untested.remove(systemLevel); + assertEquals(systemLevel.getName(), systemLevel.name(), + "System.Logger.Level." + systemLevel.name() + ".getName()"); + assertEquals(systemLevel.getSeverity(), julLevel.intValue(), + "System.Logger.Level." + systemLevel.name() + ".getSeverity"); + } + + private static void assertEquals(Object actual, Object expected, String what) { + if (!Objects.equals(actual, expected)) { + throw new RuntimeException("Bad value for " + what + + "\n\t expected: " + expected + + "\n\t actual: " + actual); + } else { + System.out.println("Got expected value for " + what + ": " + actual); + } + } + + private static void assertEquals(int actual, int expected, String what) { + if (!Objects.equals(actual, expected)) { + throw new RuntimeException("Bad value for " + what + + "\n\t expected: " + toString(expected) + + "\n\t actual: " + toString(actual)); + } else { + System.out.println("Got expected value for " + what + ": " + toString(actual)); + } + } + + private static String toString(int value) { + switch (value) { + case Integer.MAX_VALUE: return "Integer.MAX_VALUE"; + case Integer.MIN_VALUE: return "Integer.MIN_VALUE"; + default: + return Integer.toString(value); + } + } + +} diff --git a/jdk/test/java/lang/System/Logger/custom/AccessSystemLogger.java b/jdk/test/java/lang/System/Logger/custom/AccessSystemLogger.java new file mode 100644 index 00000000000..b25947d64cd --- /dev/null +++ b/jdk/test/java/lang/System/Logger/custom/AccessSystemLogger.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.io.IOException; +import java.lang.System.Logger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ResourceBundle; + +/** + * + * @author danielfuchs + */ +public final class AccessSystemLogger { + + public AccessSystemLogger() { + this(check()); + } + + private AccessSystemLogger(Void unused) { + } + + private static Void check() { + if (AccessSystemLogger.class.getClassLoader() != null) { + throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader"); + } + return null; + } + + public Logger getLogger(String name) { + Logger logger = System.getLogger(name); + System.out.println("System.getLogger(\"" + name + "\"): " + logger); + return logger; + } + + public Logger getLogger(String name, ResourceBundle bundle) { + Logger logger = System.getLogger(name, bundle); + System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger); + return logger; + } + + // copy AccessSystemLogger.class to ./boot + public static void main(String[] args) throws IOException { + Path testDir = Paths.get(System.getProperty("user.dir", ".")); + Path bootDir = Paths.get(testDir.toString(), "boot"); + Path classes = Paths.get(System.getProperty("test.classes", "build/classes")); + Path thisClass = Paths.get(classes.toString(), + AccessSystemLogger.class.getSimpleName()+".class"); + if (Files.notExists(bootDir)) { + Files.createDirectory(bootDir); + } + Path dest = Paths.get(bootDir.toString(), + AccessSystemLogger.class.getSimpleName()+".class"); + Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING); + } + +} diff --git a/jdk/test/java/lang/System/Logger/custom/CustomLoggerTest.java b/jdk/test/java/lang/System/Logger/custom/CustomLoggerTest.java new file mode 100644 index 00000000000..2506905ec7c --- /dev/null +++ b/jdk/test/java/lang/System/Logger/custom/CustomLoggerTest.java @@ -0,0 +1,728 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Queue; +import java.util.ResourceBundle; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; +import java.lang.System.LoggerFinder; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.util.stream.Stream; + +/** + * @test + * @bug 8140364 + * @summary Tests loggers returned by System.getLogger with a naive implementation + * of LoggerFinder, and in particular the default body of + * System.Logger methods. + * @build CustomLoggerTest AccessSystemLogger + * @run driver AccessSystemLogger + * @run main/othervm -Xbootclasspath/a:boot CustomLoggerTest NOSECURITY + * @run main/othervm -Xbootclasspath/a:boot CustomLoggerTest NOPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot CustomLoggerTest WITHPERMISSIONS + * @author danielfuchs + */ +public class CustomLoggerTest { + + final static AtomicLong sequencer = new AtomicLong(); + final static boolean VERBOSE = false; + static final ThreadLocal allowControl = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + + public static class MyBundle extends ResourceBundle { + + final ConcurrentHashMap map = new ConcurrentHashMap<>(); + + @Override + protected Object handleGetObject(String key) { + if (key.contains(" (translated)")) { + throw new RuntimeException("Unexpected key: " + key); + } + return map.computeIfAbsent(key, k -> k + " (translated)"); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(map.keySet()); + } + + } + public static class MyLoggerBundle extends MyBundle { + + } + + + public static class BaseLoggerFinder extends LoggerFinder { + final ConcurrentHashMap system = new ConcurrentHashMap<>(); + final ConcurrentHashMap user = new ConcurrentHashMap<>(); + public Queue eventQueue = new ArrayBlockingQueue<>(128); + + // changing this to true requires changing the logic in the + // test in order to load this class with a protection domain + // that has the CONTROL_PERMISSION (e.g. by using a custom + // system class loader. + final boolean doChecks = false; + + public static final class LogEvent { + + public LogEvent() { + this(sequencer.getAndIncrement()); + } + + LogEvent(long sequenceNumber) { + this.sequenceNumber = sequenceNumber; + } + + long sequenceNumber; + boolean isLoggable; + String loggerName; + Level level; + ResourceBundle bundle; + Throwable thrown; + Object[] args; + Supplier supplier; + String msg; + + Object[] toArray() { + return new Object[] { + sequenceNumber, + isLoggable, + loggerName, + level, + bundle, + thrown, + args, + supplier, + msg, + }; + } + + @Override + public String toString() { + return Arrays.deepToString(toArray()); + } + + + + @Override + public boolean equals(Object obj) { + return obj instanceof LogEvent + && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray()); + } + + @Override + public int hashCode() { + return Objects.hash(toArray()); + } + + + public static LogEvent of(boolean isLoggable, String name, + Level level, ResourceBundle bundle, + String key, Throwable thrown) { + LogEvent evt = new LogEvent(); + evt.isLoggable = isLoggable; + evt.loggerName = name; + evt.level = level; + evt.args = null; + evt.bundle = bundle; + evt.thrown = thrown; + evt.supplier = null; + evt.msg = key; + return evt; + } + + public static LogEvent of(boolean isLoggable, String name, + Level level, ResourceBundle bundle, + String key, Object... params) { + LogEvent evt = new LogEvent(); + evt.isLoggable = isLoggable; + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = null; + evt.supplier = null; + evt.msg = key; + return evt; + } + + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + Level level, ResourceBundle bundle, + String key, Supplier supplier, + Throwable thrown, Object... params) { + LogEvent evt = new LogEvent(sequenceNumber); + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = thrown; + evt.supplier = supplier; + evt.msg = key; + evt.isLoggable = isLoggable; + return evt; + } + + } + + public class LoggerImpl implements Logger { + private final String name; + private Level level = Level.INFO; + + public LoggerImpl(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isLoggable(Level level) { + return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity(); + } + + @Override + public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) { + log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown)); + } + + @Override + public void log(Level level, ResourceBundle bundle, String format, Object... params) { + log(LogEvent.of(isLoggable(level), name, level, bundle, format, params)); + } + + void log(LogEvent event) { + eventQueue.add(event); + } + } + + @Override + public Logger getLogger(String name, Class caller) { + // We should check the permission to obey the API contract, but + // what happens if we don't? + // This is the main difference compared with what we test in + // java/lang/System/LoggerFinder/BaseLoggerFinderTest + SecurityManager sm = System.getSecurityManager(); + if (sm != null && doChecks) { + sm.checkPermission(SimplePolicy.LOGGERFINDER_PERMISSION); + } + + PrivilegedAction pa = () -> caller.getClassLoader(); + ClassLoader callerLoader = AccessController.doPrivileged(pa); + if (callerLoader == null) { + return system.computeIfAbsent(name, (n) -> new LoggerImpl(n)); + } else { + return user.computeIfAbsent(name, (n) -> new LoggerImpl(n)); + } + } + } + + static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger(); + + static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS}; + + static void setSecurityManager() { + if (System.getSecurityManager() == null) { + Policy.setPolicy(new SimplePolicy(allowControl)); + System.setSecurityManager(new SecurityManager()); + } + } + public static void main(String[] args) { + if (args.length == 0) + args = new String[] { + "NOSECURITY", + "NOPERMISSIONS", + "WITHPERMISSIONS" + }; + + // 1. Obtain destination loggers directly from the LoggerFinder + // - LoggerFinder.getLogger("foo", type) + BaseLoggerFinder provider = + BaseLoggerFinder.class.cast(LoggerFinder.getLoggerFinder()); + BaseLoggerFinder.LoggerImpl appSink = + BaseLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", CustomLoggerTest.class)); + BaseLoggerFinder.LoggerImpl sysSink = + BaseLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class)); + + + Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> { + switch (testCase) { + case NOSECURITY: + System.out.println("\n*** Without Security Manager\n"); + test(provider, true, appSink, sysSink); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case NOPERMISSIONS: + System.out.println("\n*** With Security Manager, without permissions\n"); + setSecurityManager(); + test(provider, false, appSink, sysSink); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case WITHPERMISSIONS: + System.out.println("\n*** With Security Manager, with control permission\n"); + setSecurityManager(); + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + test(provider, true, appSink, sysSink); + } finally { + allowControl.get().set(control); + } + break; + default: + throw new RuntimeException("Unknown test case: " + testCase); + } + }); + System.out.println("\nPASSED: Tested " + sequencer.get() + " cases."); + } + + public static void test(BaseLoggerFinder provider, boolean hasRequiredPermissions, + BaseLoggerFinder.LoggerImpl appSink, BaseLoggerFinder.LoggerImpl sysSink) { + + ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName()); + final Map loggerDescMap = new HashMap<>(); + + + // 1. Test loggers returned by: + // - System.getLogger("foo") + // - and AccessSystemLogger.getLogger("foo") + Logger appLogger1 = System.getLogger("foo"); + loggerDescMap.put(appLogger1, "System.getLogger(\"foo\");"); + + Logger sysLogger1 = null; + try { + sysLogger1 = accessSystemLogger.getLogger("foo"); + loggerDescMap.put(sysLogger1, "AccessSystemLogger.getLogger(\"foo\")"); + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + throw new RuntimeException("unexpected exception: " + acx, acx); + } + + if (appLogger1 == sysLogger1) { + throw new RuntimeException("identical loggers"); + } + + if (provider.system.contains(appLogger1)) { + throw new RuntimeException("app logger in system map"); + } + if (provider.user.contains(sysLogger1)) { + throw new RuntimeException("sys logger in appplication map"); + } + if (provider.system.contains(sysLogger1)) { + // sysLogger should be a a LazyLoggerWrapper + throw new RuntimeException("sys logger is in system map (should be wrapped)"); + } + + + // 2. Test loggers returned by: + // - System.getLogger(\"foo\", loggerBundle) + // - and AccessSystemLogger.getLogger(\"foo\", loggerBundle) + Logger appLogger2 = + System.getLogger("foo", loggerBundle); + loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)"); + + Logger sysLogger2 = null; + try { + sysLogger2 = accessSystemLogger.getLogger("foo", loggerBundle); + loggerDescMap.put(sysLogger2, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)"); + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + throw new RuntimeException("unexpected exception: " + acx, acx); + } + if (appLogger2 == sysLogger2) { + throw new RuntimeException("identical loggers"); + } + if (appLogger2 == appSink) { + throw new RuntimeException("identical loggers"); + } + if (sysLogger2 == sysSink) { + throw new RuntimeException("identical loggers"); + } + + if (provider.system.contains(appLogger2)) { + throw new RuntimeException("localized app logger in system map"); + } + if (provider.user.contains(appLogger2)) { + throw new RuntimeException("localized app logger in appplication map"); + } + if (provider.user.contains(sysLogger2)) { + throw new RuntimeException("localized sys logger in appplication map"); + } + if (provider.system.contains(sysLogger2)) { + throw new RuntimeException("localized sys logger not in system map"); + } + + testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink); + testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink); + testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink); + testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink); + } + + public static class Foo { + + } + + static void verbose(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + + // Calls the 8 methods defined on Logger and verify the + // parameters received by the underlying BaseLoggerFinder.LoggerImpl + // logger. + private static void testLogger(BaseLoggerFinder provider, + Map loggerDescMap, + String name, + ResourceBundle loggerBundle, + Logger logger, + BaseLoggerFinder.LoggerImpl sink) { + + System.out.println("Testing " + loggerDescMap.get(logger)); + + Foo foo = new Foo(); + String fooMsg = foo.toString(); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, foo): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + BaseLoggerFinder.LogEvent expected = + BaseLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0, + name, messageLevel, (ResourceBundle)null, + fooMsg, null, (Throwable)null, (Object[])null); + logger.log(messageLevel, foo); + if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (provider.eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + String msg = "blah"; + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, \"blah\"): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + BaseLoggerFinder.LogEvent expected = + BaseLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF, + name, messageLevel, loggerBundle, + msg, null, (Throwable)null, (Object[])null); + logger.log(messageLevel, msg); + BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + + Supplier fooSupplier = new Supplier() { + @Override + public String get() { + return this.toString(); + } + }; + + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, fooSupplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + BaseLoggerFinder.LogEvent expected = + BaseLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0, + name, messageLevel, (ResourceBundle)null, + fooSupplier.get(), null, + (Throwable)null, (Object[])null); + logger.log(messageLevel, fooSupplier); + if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (provider.eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + String format = "two params [{1} {2}]"; + Object arg1 = foo; + Object arg2 = msg; + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, format, params...): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + BaseLoggerFinder.LogEvent expected = + BaseLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF, + name, messageLevel, loggerBundle, + format, null, (Throwable)null, new Object[] {arg1, arg2}); + logger.log(messageLevel, format, arg1, arg2); + BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + + Throwable thrown = new Exception("OK: log me!"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + BaseLoggerFinder.LogEvent expected = + BaseLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF, + name, messageLevel, loggerBundle, + msg, null, thrown, (Object[]) null); + logger.log(messageLevel, msg, thrown); + BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + + + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + BaseLoggerFinder.LogEvent expected = + BaseLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0, + name, messageLevel, (ResourceBundle)null, + fooSupplier.get(), null, + (Throwable)thrown, (Object[])null); + logger.log(messageLevel, fooSupplier, thrown); + if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (provider.eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName()); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + BaseLoggerFinder.LogEvent expected = + BaseLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF, + name, messageLevel, bundle, + format, null, (Throwable)null, new Object[] {foo, msg}); + logger.log(messageLevel, bundle, format, foo, msg); + BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + BaseLoggerFinder.LogEvent expected = + BaseLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF, + name, messageLevel, bundle, + msg, null, thrown, (Object[]) null); + logger.log(messageLevel, bundle, msg, thrown); + BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + final Permissions permissions; + final Permissions allPermissions; + final ThreadLocal allowControl; + public SimplePolicy(ThreadLocal allowControl) { + this.allowControl = allowControl; + permissions = new Permissions(); + + // these are used for configuring the test itself... + allPermissions = new Permissions(); + allPermissions.add(LOGGERFINDER_PERMISSION); + + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + if (allowControl.get().get()) return allPermissions.implies(permission); + return permissions.implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return new PermissionsBuilder().addAll(allowControl.get().get() + ? allPermissions : permissions).toPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return new PermissionsBuilder().addAll(allowControl.get().get() + ? allPermissions : permissions).toPermissions(); + } + } +} diff --git a/jdk/test/java/lang/System/Logger/custom/META-INF/services/java.lang.System$LoggerFinder b/jdk/test/java/lang/System/Logger/custom/META-INF/services/java.lang.System$LoggerFinder new file mode 100644 index 00000000000..94ab76c4c88 --- /dev/null +++ b/jdk/test/java/lang/System/Logger/custom/META-INF/services/java.lang.System$LoggerFinder @@ -0,0 +1 @@ +CustomLoggerTest$BaseLoggerFinder diff --git a/jdk/test/java/lang/System/Logger/default/AccessSystemLogger.java b/jdk/test/java/lang/System/Logger/default/AccessSystemLogger.java new file mode 100644 index 00000000000..263af31338c --- /dev/null +++ b/jdk/test/java/lang/System/Logger/default/AccessSystemLogger.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.io.IOException; +import java.lang.System.Logger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ResourceBundle; +import java.util.logging.LogManager; + +/** + * + * @author danielfuchs + */ +public final class AccessSystemLogger { + + public AccessSystemLogger() { + this(check()); + } + + private AccessSystemLogger(Void unused) { + } + + private static Void check() { + if (AccessSystemLogger.class.getClassLoader() != null) { + throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader"); + } + return null; + } + + public Logger getLogger(String name) { + Logger logger = System.getLogger(name); + System.out.println("System.getLogger(\"" + name + "\"): " + logger); + return logger; + } + + public Logger getLogger(String name, ResourceBundle bundle) { + Logger logger = System.getLogger(name, bundle); + System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger); + return logger; + } + + public java.util.logging.Logger demandSystemLogger(String name) { + return java.util.logging.Logger.getLogger(name); + } + + // copy AccessSystemLogger.class to ./boot + public static void main(String[] args) throws IOException { + Path testDir = Paths.get(System.getProperty("user.dir", ".")); + Path bootDir = Paths.get(testDir.toString(), "boot"); + Path classes = Paths.get(System.getProperty("test.classes", "build/classes")); + Path thisClass = Paths.get(classes.toString(), + AccessSystemLogger.class.getSimpleName()+".class"); + if (Files.notExists(bootDir)) { + Files.createDirectory(bootDir); + } + Path dest = Paths.get(bootDir.toString(), + AccessSystemLogger.class.getSimpleName()+".class"); + Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING); + } + +} diff --git a/jdk/test/java/lang/System/Logger/default/DefaultLoggerTest.java b/jdk/test/java/lang/System/Logger/default/DefaultLoggerTest.java new file mode 100644 index 00000000000..128b90f0fc4 --- /dev/null +++ b/jdk/test/java/lang/System/Logger/default/DefaultLoggerTest.java @@ -0,0 +1,726 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.security.AccessControlException; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Queue; +import java.util.ResourceBundle; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; +import java.lang.System.LoggerFinder; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.stream.Stream; + +/** + * @test + * @bug 8140364 + * @summary Tests default loggers returned by System.getLogger, and in + * particular the implementation of the the System.Logger method + * performed by the default binding. + * + * @build DefaultLoggerTest AccessSystemLogger + * @run driver AccessSystemLogger + * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerTest NOSECURITY + * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerTest NOPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerTest WITHPERMISSIONS + * @author danielfuchs + */ +public class DefaultLoggerTest { + + final static AtomicLong sequencer = new AtomicLong(); + final static boolean VERBOSE = false; + static final ThreadLocal allowControl = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAll = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + + public static final Queue eventQueue = new ArrayBlockingQueue<>(128); + + public static final class LogEvent { + + public LogEvent() { + this(sequencer.getAndIncrement()); + } + + LogEvent(long sequenceNumber) { + this.sequenceNumber = sequenceNumber; + } + + long sequenceNumber; + boolean isLoggable; + String loggerName; + java.util.logging.Level level; + ResourceBundle bundle; + Throwable thrown; + Object[] args; + String msg; + String className; + String methodName; + + Object[] toArray() { + return new Object[] { + sequenceNumber, + isLoggable, + loggerName, + level, + bundle, + thrown, + args, + msg, + className, + methodName, + }; + } + + @Override + public String toString() { + return Arrays.deepToString(toArray()); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof LogEvent + && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray()); + } + + @Override + public int hashCode() { + return Objects.hash(toArray()); + } + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + java.util.logging.Level level, ResourceBundle bundle, + String key, Throwable thrown, Object... params) { + return LogEvent.of(sequenceNumber, isLoggable, name, + DefaultLoggerTest.class.getName(), + "testLogger", level, bundle, key, + thrown, params); + } + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + String className, String methodName, + java.util.logging.Level level, ResourceBundle bundle, + String key, Throwable thrown, Object... params) { + LogEvent evt = new LogEvent(sequenceNumber); + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = thrown; + evt.msg = key; + evt.isLoggable = isLoggable; + evt.className = className; + evt.methodName = methodName; + return evt; + } + + } + + static java.util.logging.Level mapToJul(Level level) { + switch (level) { + case ALL: return java.util.logging.Level.ALL; + case TRACE: return java.util.logging.Level.FINER; + case DEBUG: return java.util.logging.Level.FINE; + case INFO: return java.util.logging.Level.INFO; + case WARNING: return java.util.logging.Level.WARNING; + case ERROR: return java.util.logging.Level.SEVERE; + case OFF: return java.util.logging.Level.OFF; + } + throw new InternalError("No such level: " + level); + } + + static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) { + boolean before = allowAll.get().get(); + try { + allowAll.get().set(true); + sink.setLevel(loggerLevel); + } finally { + allowAll.get().set(before); + } + } + + public static class MyHandler extends Handler { + + @Override + public java.util.logging.Level getLevel() { + return java.util.logging.Level.ALL; + } + + @Override + public void publish(LogRecord record) { + eventQueue.add(LogEvent.of(sequencer.getAndIncrement(), + true, record.getLoggerName(), + record.getSourceClassName(), + record.getSourceMethodName(), + record.getLevel(), + record.getResourceBundle(), record.getMessage(), + record.getThrown(), record.getParameters())); + } + @Override + public void flush() { + } + @Override + public void close() throws SecurityException { + } + + } + public static class MyBundle extends ResourceBundle { + + final ConcurrentHashMap map = new ConcurrentHashMap<>(); + + @Override + protected Object handleGetObject(String key) { + if (key.contains(" (translated)")) { + throw new RuntimeException("Unexpected key: " + key); + } + return map.computeIfAbsent(key, k -> k + " (translated)"); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(map.keySet()); + } + + } + public static class MyLoggerBundle extends MyBundle { + + } + + static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger(); + + static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS}; + + static void setSecurityManager() { + if (System.getSecurityManager() == null) { + Policy.setPolicy(new SimplePolicy(allowControl, allowAll)); + System.setSecurityManager(new SecurityManager()); + } + } + public static void main(String[] args) { + if (args.length == 0) + args = new String[] { + "NOSECURITY", + "NOPERMISSIONS", + "WITHPERMISSIONS" + }; + + // 1. Obtain destination loggers directly from the LoggerFinder + // - LoggerFinder.getLogger("foo", type) + + + Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> { + switch (testCase) { + case NOSECURITY: + System.out.println("\n*** Without Security Manager\n"); + test(true); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case NOPERMISSIONS: + System.out.println("\n*** With Security Manager, without permissions\n"); + setSecurityManager(); + test(false); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case WITHPERMISSIONS: + System.out.println("\n*** With Security Manager, with control permission\n"); + setSecurityManager(); + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + test(true); + } finally { + allowControl.get().set(control); + } + break; + default: + throw new RuntimeException("Unknown test case: " + testCase); + } + }); + System.out.println("\nPASSED: Tested " + sequencer.get() + " cases."); + } + + public static void test(boolean hasRequiredPermissions) { + + ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName()); + final Map loggerDescMap = new HashMap<>(); + + + // 1. Test loggers returned by: + // - System.getLogger("foo") + // - and AccessSystemLogger.getLogger("foo") + Logger sysLogger1 = null; + try { + sysLogger1 = accessSystemLogger.getLogger("foo"); + loggerDescMap.put(sysLogger1, "AccessSystemLogger.getLogger(\"foo\")"); + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + throw new RuntimeException("unexpected exception: " + acx, acx); + } + + Logger appLogger1 = System.getLogger("foo"); + loggerDescMap.put(appLogger1, "System.getLogger(\"foo\");"); + + if (appLogger1 == sysLogger1) { + throw new RuntimeException("identical loggers"); + } + + // 2. Test loggers returned by: + // - System.getLogger(\"foo\", loggerBundle) + // - and AccessSystemLogger.getLogger(\"foo\", loggerBundle) + Logger appLogger2 = + System.getLogger("foo", loggerBundle); + loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)"); + + Logger sysLogger2 = null; + try { + sysLogger2 = accessSystemLogger.getLogger("foo", loggerBundle); + loggerDescMap.put(sysLogger2, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)"); + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + throw new RuntimeException("unexpected exception: " + acx, acx); + } + if (appLogger2 == sysLogger2) { + throw new RuntimeException("identical loggers"); + } + + final java.util.logging.Logger appSink; + final java.util.logging.Logger sysSink; + final java.util.logging.Handler appHandler; + final java.util.logging.Handler sysHandler; + final LoggerFinder provider; + allowAll.get().set(true); + try { + appSink = java.util.logging.Logger.getLogger("foo"); + sysSink = accessSystemLogger.demandSystemLogger("foo"); + appSink.addHandler(appHandler = new MyHandler()); + sysSink.addHandler(sysHandler = new MyHandler()); + appSink.setUseParentHandlers(false); + sysSink.setUseParentHandlers(false); + provider = LoggerFinder.getLoggerFinder(); + } finally { + allowAll.get().set(false); + } + try { + testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink); + testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink); + testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink); + testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink); + } finally { + allowAll.get().set(true); + try { + appSink.removeHandler(appHandler); + sysSink.removeHandler(sysHandler); + sysSink.setLevel(null); + appSink.setLevel(null); + } finally { + allowAll.get().set(false); + } + } + } + + public static class Foo { + + } + + static void verbose(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + + // Calls the 8 methods defined on Logger and verify the + // parameters received by the underlying BaseLoggerFinder.LoggerImpl + // logger. + private static void testLogger(LoggerFinder provider, + Map loggerDescMap, + String name, + ResourceBundle loggerBundle, + Logger logger, + java.util.logging.Logger sink) { + + System.out.println("Testing " + loggerDescMap.get(logger)); + + Foo foo = new Foo(); + String fooMsg = foo.toString(); + for (Level loggerLevel : Level.values()) { + setLevel(sink, mapToJul(loggerLevel)); + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, foo): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + + LogEvent expected = + LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0, + name, mapToJul(messageLevel), (ResourceBundle)null, + fooMsg, (Throwable)null, (Object[])null); + logger.log(messageLevel, foo); + if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + String msg = "blah"; + for (Level loggerLevel : Level.values()) { + setLevel(sink, mapToJul(loggerLevel)); + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, \"blah\"): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF, + name, mapToJul(messageLevel), loggerBundle, + msg, (Throwable)null, (Object[])null); + logger.log(messageLevel, msg); + if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + Supplier fooSupplier = new Supplier() { + @Override + public String get() { + return this.toString(); + } + }; + + for (Level loggerLevel : Level.values()) { + setLevel(sink, mapToJul(loggerLevel)); + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, fooSupplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0, + name, mapToJul(messageLevel), (ResourceBundle)null, + fooSupplier.get(), + (Throwable)null, (Object[])null); + logger.log(messageLevel, fooSupplier); + if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + String format = "two params [{1} {2}]"; + Object arg1 = foo; + Object arg2 = msg; + for (Level loggerLevel : Level.values()) { + setLevel(sink, mapToJul(loggerLevel)); + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, format, params...): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF, + name, mapToJul(messageLevel), loggerBundle, + format, (Throwable)null, new Object[] {arg1, arg2}); + logger.log(messageLevel, format, arg1, arg2); + if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + Throwable thrown = new Exception("OK: log me!"); + for (Level loggerLevel : Level.values()) { + setLevel(sink, mapToJul(loggerLevel)); + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF, + name, mapToJul(messageLevel), loggerBundle, + msg, thrown, (Object[]) null); + logger.log(messageLevel, msg, thrown); + if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + + for (Level loggerLevel : Level.values()) { + setLevel(sink, mapToJul(loggerLevel)); + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0, + name, mapToJul(messageLevel), (ResourceBundle)null, + fooSupplier.get(), + (Throwable)thrown, (Object[])null); + logger.log(messageLevel, fooSupplier, thrown); + if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName()); + for (Level loggerLevel : Level.values()) { + setLevel(sink, mapToJul(loggerLevel)); + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF, + name, mapToJul(messageLevel), bundle, + format, (Throwable)null, new Object[] {foo, msg}); + logger.log(messageLevel, bundle, format, foo, msg); + if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + for (Level loggerLevel : Level.values()) { + setLevel(sink, mapToJul(loggerLevel)); + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF, + name, mapToJul(messageLevel), bundle, + msg, thrown, (Object[]) null); + logger.log(messageLevel, bundle, msg, thrown); + if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + final Permissions permissions; + final Permissions allPermissions; + final Permissions controlPermissions; + final ThreadLocal allowControl; + final ThreadLocal allowAll; + public SimplePolicy(ThreadLocal allowControl, ThreadLocal allowAll) { + this.allowControl = allowControl; + this.allowAll = allowAll; + permissions = new Permissions(); + + // these are used for configuring the test itself... + controlPermissions = new Permissions(); + controlPermissions.add(LOGGERFINDER_PERMISSION); + allPermissions = new Permissions(); + allPermissions.add(new java.security.AllPermission()); + + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + if (allowAll.get().get()) return allPermissions.implies(permission); + if (allowControl.get().get()) return controlPermissions.implies(permission); + return permissions.implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return new PermissionsBuilder().addAll(allowAll.get().get() + ? allPermissions : allowControl.get().get() + ? controlPermissions : permissions).toPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return new PermissionsBuilder().addAll(allowAll.get().get() + ? allPermissions : allowControl.get().get() + ? controlPermissions : permissions).toPermissions(); + } + } +} diff --git a/jdk/test/java/lang/System/Logger/interface/LoggerInterfaceTest.java b/jdk/test/java/lang/System/Logger/interface/LoggerInterfaceTest.java new file mode 100644 index 00000000000..727a10f54de --- /dev/null +++ b/jdk/test/java/lang/System/Logger/interface/LoggerInterfaceTest.java @@ -0,0 +1,592 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.util.ResourceBundle; +import java.util.function.Consumer; +import java.lang.System.Logger.Level; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.Objects; +import java.util.Queue; +import java.util.function.Supplier; + +/** + * @test + * @bug 8140364 + * @summary Tests the default body of the System.Logger interface. + * @author danielfuchs + */ +public class LoggerInterfaceTest { + + public static class LoggerImpl implements System.Logger { + + public static class LogEvent implements Cloneable { + Level level; + ResourceBundle bundle; + String msg; + Throwable thrown; + Object[] params; + StackTraceElement[] callStack; + + @Override + protected LogEvent clone() { + try { + return (LogEvent)super.clone(); + } catch (CloneNotSupportedException x) { + throw new RuntimeException(x); + } + } + + + } + + public static class LogEventBuilder { + private LogEvent event = new LogEvent(); + public LogEventBuilder level(Level level) { + event.level = level; + return this; + } + public LogEventBuilder stack(StackTraceElement... stack) { + event.callStack = stack; + return this; + } + public LogEventBuilder bundle(ResourceBundle bundle) { + event.bundle = bundle; + return this; + } + public LogEventBuilder msg(String msg) { + event.msg = msg; + return this; + } + public LogEventBuilder thrown(Throwable thrown) { + event.thrown = thrown; + return this; + } + public LogEventBuilder params(Object... params) { + event.params = params; + return this; + } + public LogEvent build() { + return event.clone(); + } + + public LogEventBuilder clear() { + event = new LogEvent(); + return this; + } + + } + + Level level = Level.WARNING; + Consumer consumer; + final LogEventBuilder builder = new LogEventBuilder(); + + @Override + public String getName() { + return "noname"; + } + + @Override + public boolean isLoggable(Level level) { + return level.getSeverity() >= this.level.getSeverity(); + } + + @Override + public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) { + builder.clear().level(level).bundle(bundle).msg(msg).thrown(thrown) + .stack(new Exception().getStackTrace()); + consumer.accept(builder.build()); + } + + @Override + public void log(Level level, ResourceBundle bundle, String format, Object... params) { + builder.clear().level(level).bundle(bundle).msg(format).params(params) + .stack(new Exception().getStackTrace()); + consumer.accept(builder.build()); + } + + } + + static class Throwing { + @Override + public String toString() { + throw new RuntimeException("should not have been called"); + } + } + static class NotTrowing { + private final String toString; + private int count = 0; + public NotTrowing(String toString) { + this.toString = toString; + } + + @Override + public String toString() { + return toString + "[" + (++count) + "]"; + } + } + + public static void main(String[] args) { + final LoggerImpl loggerImpl = new LoggerImpl(); + final System.Logger logger = loggerImpl; + final Queue events = new LinkedList<>(); + loggerImpl.consumer = (x) -> events.add(x); + + System.out.println("\nlogger.isLoggable(Level)"); + assertTrue(logger.isLoggable(Level.WARNING), "logger.isLoggable(Level.WARNING)"," "); + assertFalse(logger.isLoggable(Level.INFO), "logger.isLoggable(Level.INFO)", " "); + + + System.out.println("\nlogger.log(Level, Object)"); + for (Level l : Level.values()) { + boolean logged = l.compareTo(Level.WARNING) >= 0; + Object[][] cases = new Object[][] { + {null}, {"baz"} + }; + for (Object[] p : cases) { + String msg = (String)p[0]; + final Object obj = msg == null ? null : logged ? new NotTrowing(msg) : new Throwing(); + String par1 = msg == null ? "(Object)null" + : logged ? "new NotTrowing(\""+ msg+"\")" : "new Throwing()"; + System.out.println(" logger.log(" + l + ", " + par1 + ")"); + try { + logger.log(l, obj); + if (obj == null) { + throw new RuntimeException("Expected NullPointerException not thrown for" + + " logger.log(" + l + ", " + par1 + ")"); + } + } catch (NullPointerException x) { + if (obj == null) { + System.out.println(" Got expected exception: " + x); + continue; + } else { + throw x; + } + } + LoggerImpl.LogEvent e = events.poll(); + if (logged) { + assertNonNull(e, "e", " "); + assertEquals(l, e.level, "e.level", " "); + assertToString(e.msg, msg, 1, "e.msg", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.params, null, "e.params", " "); + assertEquals(e.thrown, null, "e.thrown", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.callStack[0].getMethodName(), "log", + "e.callStack[0].getMethodName()", " "); + assertEquals(e.callStack[0].getClassName(), + logger.getClass().getName(), + "e.callStack[0].getClassName() ", " "); + assertEquals(e.callStack[1].getMethodName(), "log", + "e.callStack[1].getMethodName()", " "); + assertEquals(e.callStack[1].getClassName(), + System.Logger.class.getName(), + "e.callStack[1].getClassName() ", " "); + assertEquals(e.callStack[2].getMethodName(), "main", + "e.callStack[2].getMethodName()", " "); + } else { + assertEquals(e, null, "e", " "); + } + } + } + System.out.println(" logger.log(" + null + ", " + + "new NotThrowing(\"foobar\")" + ")"); + try { + logger.log(null, new NotTrowing("foobar")); + throw new RuntimeException("Expected NullPointerException not thrown for" + + " logger.log(" + null + ", " + + "new NotThrowing(\"foobar\")" + ")"); + } catch (NullPointerException x) { + System.out.println(" Got expected exception: " + x); + } + + + System.out.println("\nlogger.log(Level, String)"); + for (Level l : Level.values()) { + boolean logged = l.compareTo(Level.WARNING) >= 0; + String par = "bar"; + System.out.println(" logger.log(" + l + ", \"" + par +"\");"); + logger.log(l, par); + LoggerImpl.LogEvent e = events.poll(); + assertNonNull(e, "e", " "); + assertEquals(e.level, l, "e.level", " "); + assertEquals(e.msg, "bar", "e.msg", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.params, null, "e.params", " "); + assertEquals(e.thrown, null, "e.thrown", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.callStack[0].getMethodName(), "log", + "e.callStack[0].getMethodName()", " "); + assertEquals(e.callStack[0].getClassName(), + logger.getClass().getName(), + "e.callStack[0].getClassName() ", " "); + assertEquals(e.callStack[1].getMethodName(), "log", + "e.callStack[1].getMethodName()", " "); + assertEquals(e.callStack[1].getClassName(), + System.Logger.class.getName(), + "e.callStack[1].getClassName() ", " "); + assertEquals(e.callStack[2].getMethodName(), "main", + "e.callStack[2].getMethodName()", " "); + + System.out.println(" logger.log(" + l + ", (String)null);"); + logger.log(l, (String)null); + e = events.poll(); + assertNonNull(e, "e", " "); + assertEquals(e.level, l, "e.level", " "); + assertEquals(e.msg, null, "e.msg", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.params, null, "e.params", " "); + assertEquals(e.thrown, null, "e.thrown", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.callStack[0].getMethodName(), "log", + "e.callStack[0].getMethodName()", " "); + assertEquals(e.callStack[0].getClassName(), + logger.getClass().getName(), + "e.callStack[0].getClassName() ", " "); + assertEquals(e.callStack[1].getMethodName(), "log", + "e.callStack[1].getMethodName()", " "); + assertEquals(e.callStack[1].getClassName(), + System.Logger.class.getName(), + "e.callStack[1].getClassName() ", " "); + assertEquals(e.callStack[2].getMethodName(), "main", + "e.callStack[2].getMethodName()", " "); + } + + System.out.println("\nlogger.log(Level, Supplier)"); + for (Level l : Level.values()) { + boolean logged = l.compareTo(Level.WARNING) >= 0; + Object[][] cases = new Object[][] { + {null}, {"baz"} + }; + for (Object[] p : cases) { + String msg = (String)p[0]; + final Object obj = msg == null ? null : logged ? new NotTrowing(msg) : new Throwing(); + final Supplier s = msg == null ? null : () -> obj.toString(); + String par1 = msg == null ? "(Supplier)null" + : logged ? "() -> new NotTrowing(\""+ msg+"\").toString()" : "new Throwing()"; + System.out.println(" logger.log(" + l + ", " + par1 + ")"); + try { + logger.log(l, s); + if (s == null) { + throw new RuntimeException("Expected NullPointerException not thrown for" + + " logger.log(" + l + ", " + par1 + ")"); + } + } catch (NullPointerException x) { + if (s == null) { + System.out.println(" Got expected exception: " + x); + continue; + } else { + throw x; + } + } + LoggerImpl.LogEvent e = events.poll(); + if (logged) { + assertNonNull(e, "e", " "); + assertEquals(l, e.level, "e.level", " "); + assertToString(e.msg, msg, 1, "e.msg", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.params, null, "e.params", " "); + assertEquals(e.thrown, null, "e.thrown", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.callStack[0].getMethodName(), "log", + "e.callStack[0].getMethodName()", " "); + assertEquals(e.callStack[0].getClassName(), + logger.getClass().getName(), + "e.callStack[0].getClassName() ", " "); + assertEquals(e.callStack[1].getMethodName(), "log", + "e.callStack[1].getMethodName()", " "); + assertEquals(e.callStack[1].getClassName(), + System.Logger.class.getName(), + "e.callStack[1].getClassName() ", " "); + assertEquals(e.callStack[2].getMethodName(), "main", + "e.callStack[2].getMethodName()", " "); + } else { + assertEquals(e, null, "e", " "); + } + } + } + System.out.println(" logger.log(" + null + ", " + "() -> \"biz\"" + ")"); + try { + logger.log(null, () -> "biz"); + throw new RuntimeException("Expected NullPointerException not thrown for" + + " logger.log(" + null + ", " + + "() -> \"biz\"" + ")"); + } catch (NullPointerException x) { + System.out.println(" Got expected exception: " + x); + } + + System.out.println("\nlogger.log(Level, String, Object...)"); + for (Level l : Level.values()) { + boolean logged = l.compareTo(Level.WARNING) >= 0; + String par = "bam"; + Object[] params = null; + System.out.println(" logger.log(" + l + ", \"" + par +"\", null);"); + logger.log(l, par, params); + LoggerImpl.LogEvent e = events.poll(); + assertNonNull(e, "e", " "); + assertEquals(l, e.level, "e.level", " "); + assertEquals(e.msg, "bam", "e.msg", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.params, null, "e.params", " "); + assertEquals(e.thrown, null, "e.thrown", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.callStack[0].getMethodName(), "log", + "e.callStack[0].getMethodName()", " "); + assertEquals(e.callStack[0].getClassName(), + logger.getClass().getName(), + "e.callStack[0].getClassName() ", " "); + assertEquals(e.callStack[1].getMethodName(), "log", + "e.callStack[1].getMethodName()", " "); + assertEquals(e.callStack[1].getClassName(), + System.Logger.class.getName(), + "e.callStack[1].getClassName() ", " "); + assertEquals(e.callStack[2].getMethodName(), "main", + "e.callStack[2].getMethodName()", " "); + + params = new Object[] {new NotTrowing("one")}; + par = "bam {0}"; + System.out.println(" logger.log(" + l + ", \"" + par + + "\", new NotTrowing(\"one\"));"); + logger.log(l, par, params[0]); + e = events.poll(); + assertNonNull(e, "e", " "); + assertEquals(l, e.level, "e.level", " "); + assertEquals(e.msg, par, "e.msg", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertArrayEquals(e.params, params, "e.params", " "); + assertEquals(e.thrown, null, "e.thrown", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.callStack[0].getMethodName(), "log", + "e.callStack[0].getMethodName()", " "); + assertEquals(e.callStack[0].getClassName(), + logger.getClass().getName(), + "e.callStack[0].getClassName() ", " "); + assertEquals(e.callStack[1].getMethodName(), "log", + "e.callStack[1].getMethodName()", " "); + assertEquals(e.callStack[1].getClassName(), + System.Logger.class.getName(), + "e.callStack[1].getClassName() ", " "); + assertEquals(e.callStack[2].getMethodName(), "main", + "e.callStack[2].getMethodName()", " "); + + params = new Object[] {new NotTrowing("fisrt"), new NotTrowing("second")}; + par = "bam {0} {1}"; + System.out.println(" logger.log(" + l + ", \"" + par + + "\", new NotTrowing(\"fisrt\")," + + " new NotTrowing(\"second\"));"); + logger.log(l, par, params[0], params[1]); + e = events.poll(); + assertNonNull(e, "e", " "); + assertEquals(l, e.level, "e.level", " "); + assertEquals(e.msg, par, "e.msg", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertArrayEquals(e.params, params, "e.params", " "); + assertEquals(e.thrown, null, "e.thrown", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.callStack[0].getMethodName(), "log", + "e.callStack[0].getMethodName()", " "); + assertEquals(e.callStack[0].getClassName(), + logger.getClass().getName(), + "e.callStack[0].getClassName() ", " "); + assertEquals(e.callStack[1].getMethodName(), "log", + "e.callStack[1].getMethodName()", " "); + assertEquals(e.callStack[1].getClassName(), + System.Logger.class.getName(), + "e.callStack[1].getClassName() ", " "); + assertEquals(e.callStack[2].getMethodName(), "main", + "e.callStack[2].getMethodName()", " "); + + params = new Object[] {new NotTrowing("third"), new NotTrowing("fourth")}; + par = "bam {2}"; + System.out.println(" logger.log(" + l + ", \"" + par + + "\", new Object[] {new NotTrowing(\"third\")," + + " new NotTrowing(\"fourth\")});"); + logger.log(l, par, params); + e = events.poll(); + assertNonNull(e, "e", " "); + assertEquals(l, e.level, "e.level", " "); + assertEquals(e.msg, par, "e.msg", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertArrayEquals(e.params, params, "e.params", " "); + assertEquals(e.thrown, null, "e.thrown", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.callStack[0].getMethodName(), "log", + "e.callStack[0].getMethodName()", " "); + assertEquals(e.callStack[0].getClassName(), logger.getClass().getName(), + "e.callStack[0].getClassName() ", " "); + assertEquals(e.callStack[1].getMethodName(), "log", + "e.callStack[1].getMethodName()", " "); + assertEquals(e.callStack[1].getClassName(), + System.Logger.class.getName(), + "e.callStack[1].getClassName() ", " "); + assertEquals(e.callStack[2].getMethodName(), "main", + "e.callStack[2].getMethodName()", " "); + } + + System.out.println("\nlogger.log(Level, String, Throwable)"); + for (Level l : Level.values()) { + boolean logged = l.compareTo(Level.WARNING) >= 0; + Object[][] cases = new Object[][] { + {null, null}, {null, new Throwable()}, {"biz", null}, {"boz", new Throwable()} + }; + for (Object[] p : cases) { + String msg = (String)p[0]; + Throwable thrown = (Throwable)p[1]; + String par1 = msg == null ? "(String)null" : "\"" + msg + "\""; + String par2 = thrown == null ? "(Throwable)null" : "new Throwable()"; + System.out.println(" logger.log(" + l + ", " + par1 +", " + par2 + ")"); + logger.log(l, msg, thrown); + LoggerImpl.LogEvent e = events.poll(); + assertNonNull(e, "e", " "); + assertEquals(e.level, l, "e.level", " "); + assertEquals(e.msg, msg, "e.msg", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.params, null, "e.params", " "); + assertEquals(e.thrown, thrown, "e.thrown", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.callStack[0].getMethodName(), + "log", "e.callStack[0].getMethodName()", " "); + assertEquals(e.callStack[0].getClassName(), + logger.getClass().getName(), + "e.callStack[0].getClassName() ", " "); + assertEquals(e.callStack[1].getMethodName(), "log", + "e.callStack[1].getMethodName()", " "); + assertEquals(e.callStack[1].getClassName(), + System.Logger.class.getName(), + "e.callStack[1].getClassName() ", " "); + assertEquals(e.callStack[2].getMethodName(), "main", + "e.callStack[2].getMethodName()", " "); + } + } + + System.out.println("\nlogger.log(Level, Supplier, Throwable)"); + for (Level l : Level.values()) { + boolean logged = l.compareTo(Level.WARNING) >= 0; + Object[][] cases = new Object[][] { + {null, null}, {null, new Throwable()}, {"biz", null}, {"boz", new Throwable()} + }; + for (Object[] p : cases) { + String msg = (String)p[0]; + Throwable thrown = (Throwable)p[1]; + final Object obj = msg == null ? null : logged ? new NotTrowing(msg) : new Throwing(); + final Supplier s = msg == null ? null : () -> obj.toString(); + String par1 = msg == null ? "(Supplier)null" + : logged ? "() -> new NotTrowing(\""+ msg+"\").toString()" : "new Throwing()"; + String par2 = thrown == null ? "(Throwable)null" : "new Throwable()"; + System.out.println(" logger.log(" + l + ", " + par1 +", " + par2 + ")"); + try { + logger.log(l, s, thrown); + if (s== null) { + throw new RuntimeException("Expected NullPointerException not thrown for" + + " logger.log(" + l + ", " + par1 +", " + par2 + ")"); + } + } catch (NullPointerException x) { + if (s == null) { + System.out.println(" Got expected exception: " + x); + continue; + } else { + throw x; + } + } + LoggerImpl.LogEvent e = events.poll(); + if (logged) { + assertNonNull(e, "e", " "); + assertEquals(l, e.level, "e.level", " "); + assertToString(e.msg, msg, 1, "e.msg", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.params, null, "e.params", " "); + assertEquals(e.thrown, thrown, "e.thrown", " "); + assertEquals(e.bundle, null, "e.bundle", " "); + assertEquals(e.callStack[0].getMethodName(), "log", + "e.callStack[0].getMethodName()", " "); + assertEquals(e.callStack[0].getClassName(), + logger.getClass().getName(), + "e.callStack[0].getClassName() ", " "); + assertEquals(e.callStack[1].getMethodName(), "log", + "e.callStack[1].getMethodName()", " "); + assertEquals(e.callStack[1].getClassName(), + System.Logger.class.getName(), + "e.callStack[1].getClassName() ", " "); + assertEquals(e.callStack[2].getMethodName(), "main", + "e.callStack[2].getMethodName()", " "); + } else { + assertEquals(e, null, "e", " "); + } + } + } + System.out.println(" logger.log(" + null + ", " + "() -> \"biz\"" + + ", " + "new Throwable()" + ")"); + try { + logger.log(null, () -> "biz", new Throwable()); + throw new RuntimeException("Expected NullPointerException not thrown for" + + " logger.log(" + null + ", " + + "() -> \"biz\"" + ", " + + "new Throwable()" + ")"); + } catch (NullPointerException x) { + System.out.println(" Got expected exception: " + x); + } + + System.out.println("Checking that we have no spurious events in the queue"); + assertEquals(events.poll(), null, "events.poll()", " "); + } + + static void assertTrue(boolean test, String what, String prefix) { + if (!test) { + throw new RuntimeException("Expected true for " + what); + } + System.out.println(prefix + "Got expected " + what + ": " + test); + } + static void assertFalse(boolean test, String what, String prefix) { + if (test) { + throw new RuntimeException("Expected false for " + what); + } + System.out.println(prefix + "Got expected " + what + ": " + test); + } + static void assertToString(String actual, String expected, int count, String what, String prefix) { + assertEquals(actual, expected + "["+count+"]", what, prefix); + } + static void assertEquals(Object actual, Object expected, String what, String prefix) { + if (!Objects.equals(actual, expected)) { + throw new RuntimeException("Bad " + what + ":" + + "\n\t expected: " + expected + + "\n\t actual: " + actual); + } + System.out.println(prefix + "Got expected " + what + ": " + actual); + } + static void assertArrayEquals(Object[] actual, Object[] expected, String what, String prefix) { + if (!Objects.deepEquals(actual, expected)) { + throw new RuntimeException("Bad " + what + ":" + + "\n\t expected: " + expected == null ? "null" : Arrays.deepToString(expected) + + "\n\t actual: " + actual == null ? "null" : Arrays.deepToString(actual)); + } + System.out.println(prefix + "Got expected " + what + ": " + Arrays.deepToString(actual)); + } + static void assertNonNull(Object actual, String what, String prefix) { + if (Objects.equals(actual, null)) { + throw new RuntimeException("Bad " + what + ":" + + "\n\t expected: non null" + + "\n\t actual: " + actual); + } + System.out.println(prefix + "Got expected " + what + ": " + "non null"); + } +} diff --git a/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/AccessSystemLogger.java b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/AccessSystemLogger.java new file mode 100644 index 00000000000..b25947d64cd --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/AccessSystemLogger.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.io.IOException; +import java.lang.System.Logger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ResourceBundle; + +/** + * + * @author danielfuchs + */ +public final class AccessSystemLogger { + + public AccessSystemLogger() { + this(check()); + } + + private AccessSystemLogger(Void unused) { + } + + private static Void check() { + if (AccessSystemLogger.class.getClassLoader() != null) { + throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader"); + } + return null; + } + + public Logger getLogger(String name) { + Logger logger = System.getLogger(name); + System.out.println("System.getLogger(\"" + name + "\"): " + logger); + return logger; + } + + public Logger getLogger(String name, ResourceBundle bundle) { + Logger logger = System.getLogger(name, bundle); + System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger); + return logger; + } + + // copy AccessSystemLogger.class to ./boot + public static void main(String[] args) throws IOException { + Path testDir = Paths.get(System.getProperty("user.dir", ".")); + Path bootDir = Paths.get(testDir.toString(), "boot"); + Path classes = Paths.get(System.getProperty("test.classes", "build/classes")); + Path thisClass = Paths.get(classes.toString(), + AccessSystemLogger.class.getSimpleName()+".class"); + if (Files.notExists(bootDir)) { + Files.createDirectory(bootDir); + } + Path dest = Paths.get(bootDir.toString(), + AccessSystemLogger.class.getSimpleName()+".class"); + Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING); + } + +} diff --git a/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/BaseLoggerFinder.java b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/BaseLoggerFinder.java new file mode 100644 index 00000000000..30daa232248 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/BaseLoggerFinder.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.lang.System.LoggerFinder; +import java.lang.System.Logger; + +public class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder { + + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + @Override + public Logger getLogger(String name, Class caller) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(LOGGERFINDER_PERMISSION); + } + PrivilegedAction pa = () -> caller.getClassLoader(); + ClassLoader callerLoader = AccessController.doPrivileged(pa); + if (callerLoader == null) { + return system.computeIfAbsent(name, (n) -> new LoggerImpl(n)); + } else { + return user.computeIfAbsent(name, (n) -> new LoggerImpl(n)); + } + } +} diff --git a/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/BaseLoggerFinderTest.java b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/BaseLoggerFinderTest.java new file mode 100644 index 00000000000..2cb57d78559 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/BaseLoggerFinderTest.java @@ -0,0 +1,694 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.security.AccessControlException; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.stream.Stream; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; +import java.lang.System.LoggerFinder; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; + +/** + * @test + * @bug 8140364 + * @summary Tests a naive implementation of LoggerFinder, and in particular + * the default body of System.Logger methods. + * @build AccessSystemLogger BaseLoggerFinderTest CustomSystemClassLoader BaseLoggerFinder TestLoggerFinder + * @run driver AccessSystemLogger + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerFinderTest NOSECURITY + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerFinderTest NOPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerFinderTest WITHPERMISSIONS + * @author danielfuchs + */ +public class BaseLoggerFinderTest { + + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + final static boolean VERBOSE = false; + static final ThreadLocal allowControl = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAccess = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + + final static AccessSystemLogger accessSystemLogger = new AccessSystemLogger(); + static final Class providerClass; + static { + try { + providerClass = ClassLoader.getSystemClassLoader().loadClass("BaseLoggerFinder"); + } catch (ClassNotFoundException ex) { + throw new ExceptionInInitializerError(ex); + } + } + + public static class MyBundle extends ResourceBundle { + + final ConcurrentHashMap map = new ConcurrentHashMap<>(); + + @Override + protected Object handleGetObject(String key) { + if (key.contains(" (translated)")) { + throw new RuntimeException("Unexpected key: " + key); + } + return map.computeIfAbsent(key, k -> k + " (translated)"); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(map.keySet()); + } + + } + public static class MyLoggerBundle extends MyBundle { + + } + + static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS}; + + static void setSecurityManager() { + if (System.getSecurityManager() == null) { + Policy.setPolicy(new SimplePolicy(allowControl, allowAccess)); + System.setSecurityManager(new SecurityManager()); + } + } + + public static void main(String[] args) { + if (args.length == 0) + args = new String[] { + //"NOSECURITY", + "NOPERMISSIONS", + "WITHPERMISSIONS" + }; + + System.out.println("Using provider class: " + providerClass + "[" + providerClass.getClassLoader() + "]"); + + Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> { + TestLoggerFinder provider; + switch (testCase) { + case NOSECURITY: + System.out.println("\n*** Without Security Manager\n"); + provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder()); + test(provider, true); + System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get()); + break; + case NOPERMISSIONS: + System.out.println("\n*** With Security Manager, without permissions\n"); + setSecurityManager(); + try { + provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder()); + throw new RuntimeException("Expected exception not raised"); + } catch (AccessControlException x) { + if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) { + throw new RuntimeException("Unexpected permission check", x); + } + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder()); + } finally { + allowControl.get().set(control); + } + } + test(provider, false); + System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get()); + break; + case WITHPERMISSIONS: + System.out.println("\n*** With Security Manager, with control permission\n"); + setSecurityManager(); + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder()); + test(provider, true); + } finally { + allowControl.get().set(control); + } + break; + default: + throw new RuntimeException("Unknown test case: " + testCase); + } + }); + System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases."); + } + + public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) { + + ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName()); + final Map loggerDescMap = new HashMap<>(); + + + // 1. Test loggers returned by LoggerFinder, both for system callers + // and not system callers. + TestLoggerFinder.LoggerImpl appLogger1 = null; + try { + appLogger1 = + TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerFinderTest.class)); + loggerDescMap.put(appLogger1, "provider.getLogger(\"foo\", BaseLoggerFinderTest.class)"); + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for logger: " + acx); + final boolean old = allowControl.get().get(); + allowControl.get().set(true); + try { + appLogger1 = + TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerFinderTest.class)); + loggerDescMap.put(appLogger1, "provider.getLogger(\"foo\", BaseLoggerFinderTest.class)"); + } finally { + allowControl.get().set(old); + } + } + + TestLoggerFinder.LoggerImpl sysLogger1 = null; + try { + sysLogger1 = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class)); + loggerDescMap.put(sysLogger1, "provider.getLogger(\"foo\", Thread.class)"); + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a system logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for system logger: " + acx); + final boolean old = allowControl.get().get(); + allowControl.get().set(true); + try { + sysLogger1 = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class)); + loggerDescMap.put(sysLogger1, "provider.getLogger(\"foo\", Thread.class)"); + } finally { + allowControl.get().set(old); + } + } + if (appLogger1 == sysLogger1) { + throw new RuntimeException("identical loggers"); + } + + if (provider.system.contains(appLogger1)) { + throw new RuntimeException("app logger in system map"); + } + if (!provider.user.contains(appLogger1)) { + throw new RuntimeException("app logger not in appplication map"); + } + if (provider.user.contains(sysLogger1)) { + throw new RuntimeException("sys logger in appplication map"); + } + if (!provider.system.contains(sysLogger1)) { + throw new RuntimeException("sys logger not in system map"); + } + + testLogger(provider, loggerDescMap, "foo", null, appLogger1, appLogger1); + testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysLogger1); + + // 2. Test localized loggers returned LoggerFinder, both for system + // callers and non system callers + Logger appLogger2 = null; + try { + appLogger2 = provider.getLocalizedLogger("foo", loggerBundle, BaseLoggerFinderTest.class); + loggerDescMap.put(appLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, BaseLoggerFinderTest.class)"); + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for logger: " + acx); + final boolean old = allowControl.get().get(); + allowControl.get().set(true); + try { + appLogger2 = provider.getLocalizedLogger("foo", loggerBundle, BaseLoggerFinderTest.class); + loggerDescMap.put(appLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, BaseLoggerFinderTest.class)"); + } finally { + allowControl.get().set(old); + } + } + + Logger sysLogger2 = null; + try { + sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class); + loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class)"); + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a system logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for localized system logger: " + acx); + final boolean old = allowControl.get().get(); + allowControl.get().set(true); + try { + sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class); + loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class))"); + } finally { + allowControl.get().set(old); + } + } + if (appLogger2 == sysLogger2) { + throw new RuntimeException("identical loggers"); + } + if (appLogger2 == appLogger1) { + throw new RuntimeException("identical loggers"); + } + if (sysLogger2 == sysLogger1) { + throw new RuntimeException("identical loggers"); + } + + if (provider.system.contains(appLogger2)) { + throw new RuntimeException("localized app logger in system map"); + } + if (provider.user.contains(appLogger2)) { + throw new RuntimeException("localized app logger in appplication map"); + } + if (provider.user.contains(sysLogger2)) { + throw new RuntimeException("localized sys logger in appplication map"); + } + if (provider.system.contains(sysLogger2)) { + throw new RuntimeException("localized sys logger not in system map"); + } + + testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appLogger1); + testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysLogger1); + + // 3 Test loggers returned by: + // 3.1: System.getLogger("foo") + Logger appLogger3 = System.getLogger("foo"); + loggerDescMap.put(appLogger3, "System.getLogger(\"foo\")"); + testLogger(provider, loggerDescMap, "foo", null, appLogger3, appLogger1); + + // 3.2: System.getLogger("foo") + // Emulate what System.getLogger() does when the caller is a + // platform classes + Logger sysLogger3 = accessSystemLogger.getLogger("foo"); + loggerDescMap.put(sysLogger3, "AccessSystemLogger.getLogger(\"foo\")"); + + if (appLogger3 == sysLogger3) { + throw new RuntimeException("identical loggers"); + } + + testLogger(provider, loggerDescMap, "foo", null, sysLogger3, sysLogger1); + + // 4. Test loggers returned by: + // 4.1 System.getLogger("foo", loggerBundle) + Logger appLogger4 = + System.getLogger("foo", loggerBundle); + loggerDescMap.put(appLogger4, "System.getLogger(\"foo\", loggerBundle)"); + if (appLogger4 == appLogger1) { + throw new RuntimeException("identical loggers"); + } + + testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger4, appLogger1); + + // 4.2: System.getLogger("foo", loggerBundle) + // Emulate what System.getLogger() does when the caller is a + // platform classes + Logger sysLogger4 = accessSystemLogger.getLogger("foo", loggerBundle); + loggerDescMap.put(sysLogger4, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)"); + if (appLogger4 == sysLogger4) { + throw new RuntimeException("identical loggers"); + } + + testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger4, sysLogger1); + + } + + public static class Foo { + + } + + static void verbose(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + + // Calls the 8 methods defined on Logger and verify the + // parameters received by the underlying TestProvider.LoggerImpl + // logger. + private static void testLogger(TestLoggerFinder provider, + Map loggerDescMap, + String name, + ResourceBundle loggerBundle, + Logger logger, + TestLoggerFinder.LoggerImpl sink) { + + System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]"); + AtomicLong sequencer = TestLoggerFinder.sequencer; + + Foo foo = new Foo(); + String fooMsg = foo.toString(); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, foo): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0, + name, messageLevel, (ResourceBundle)null, + fooMsg, null, (Throwable)null, (Object[])null); + logger.log(messageLevel, foo); + if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (provider.eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + TestLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + String msg = "blah"; + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, \"blah\"): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF, + name, messageLevel, loggerBundle, + msg, null, (Throwable)null, (Object[])null); + logger.log(messageLevel, msg); + TestLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + + Supplier fooSupplier = new Supplier() { + @Override + public String get() { + return this.toString(); + } + }; + + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, fooSupplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0, + name, messageLevel, (ResourceBundle)null, + fooSupplier.get(), null, + (Throwable)null, (Object[])null); + logger.log(messageLevel, fooSupplier); + if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (provider.eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + TestLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + String format = "two params [{1} {2}]"; + Object arg1 = foo; + Object arg2 = msg; + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, format, params...): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF, + name, messageLevel, loggerBundle, + format, null, (Throwable)null, new Object[] {foo, msg}); + logger.log(messageLevel, format, foo, msg); + TestLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + + Throwable thrown = new Exception("OK: log me!"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF, + name, messageLevel, loggerBundle, + msg, null, thrown, (Object[]) null); + logger.log(messageLevel, msg, thrown); + TestLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + + + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0, + name, messageLevel, (ResourceBundle)null, + fooSupplier.get(), null, + (Throwable)thrown, (Object[])null); + logger.log(messageLevel, fooSupplier, thrown); + if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (provider.eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + TestLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName()); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF, + name, messageLevel, bundle, + format, null, (Throwable)null, new Object[] {foo, msg}); + logger.log(messageLevel, bundle, format, foo, msg); + TestLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF, + name, messageLevel, bundle, + msg, null, thrown, (Object[]) null); + logger.log(messageLevel, bundle, msg, thrown); + TestLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION; + final static RuntimePermission ACCESS = new RuntimePermission("accessClassInPackage.jdk.internal.logger"); + + final Permissions permissions; + final ThreadLocal allowControl; + final ThreadLocal allowAccess; + public SimplePolicy(ThreadLocal allowControl, ThreadLocal allowAccess) { + this.allowControl = allowControl; + this.allowAccess = allowAccess; + permissions = new Permissions(); + } + + Permissions getPermissions() { + if (allowControl.get().get() || allowAccess.get().get()) { + PermissionsBuilder builder = new PermissionsBuilder() + .addAll(permissions); + if (allowControl.get().get()) { + builder.add(CONTROL); + } + if (allowAccess.get().get()) { + builder.add(ACCESS); + } + return builder.toPermissions(); + } + return permissions; + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + return getPermissions().implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + } +} diff --git a/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/CustomSystemClassLoader.java b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/CustomSystemClassLoader.java new file mode 100644 index 00000000000..711771f9690 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/CustomSystemClassLoader.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.security.AllPermission; +import java.security.Permissions; +import java.security.ProtectionDomain; + + +/** + * A custom ClassLoader to load the concrete LoggerFinder class + * with all permissions. + * + * @author danielfuchs + */ +public class CustomSystemClassLoader extends ClassLoader { + + + Class finderClass = null; + + public CustomSystemClassLoader() { + super(); + } + public CustomSystemClassLoader(ClassLoader parent) { + super(parent); + } + + private Class defineFinderClass(String name) + throws ClassNotFoundException { + final Object obj = getClassLoadingLock(name); + synchronized(obj) { + if (finderClass != null) return finderClass; + + URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation(); + File file = new File(url.getPath(), name+".class"); + if (file.canRead()) { + try { + byte[] b = Files.readAllBytes(file.toPath()); + Permissions perms = new Permissions(); + perms.add(new AllPermission()); + finderClass = defineClass( + name, b, 0, b.length, new ProtectionDomain( + this.getClass().getProtectionDomain().getCodeSource(), + perms)); + System.out.println("Loaded " + name); + return finderClass; + } catch (Throwable ex) { + ex.printStackTrace(); + throw new ClassNotFoundException(name, ex); + } + } else { + throw new ClassNotFoundException(name, + new IOException(file.toPath() + ": can't read")); + } + } + } + + @Override + public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (name.equals("BaseLoggerFinder")) { + Class c = defineFinderClass(name); + if (resolve) { + resolveClass(c); + } + return c; + } + return super.loadClass(name, resolve); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + if (name.equals("BaseLoggerFinder")) { + return defineFinderClass(name); + } + return super.findClass(name); + } + +} diff --git a/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/META-INF/services/java.lang.System$LoggerFinder b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/META-INF/services/java.lang.System$LoggerFinder new file mode 100644 index 00000000000..fecd6622497 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/META-INF/services/java.lang.System$LoggerFinder @@ -0,0 +1 @@ +BaseLoggerFinder diff --git a/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/TestLoggerFinder.java b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/TestLoggerFinder.java new file mode 100644 index 00000000000..716f8b8faec --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/TestLoggerFinder.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.util.Arrays; +import java.util.Objects; +import java.util.Queue; +import java.util.ResourceBundle; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; +import java.lang.System.Logger; + +/** + * What our test provider needs to implement. + * @author danielfuchs + */ +public interface TestLoggerFinder { + public final static AtomicLong sequencer = new AtomicLong(); + public final ConcurrentHashMap system = new ConcurrentHashMap<>(); + public final ConcurrentHashMap user = new ConcurrentHashMap<>(); + public final Queue eventQueue = new ArrayBlockingQueue<>(128); + + public static final class LogEvent { + + public LogEvent() { + this(sequencer.getAndIncrement()); + } + + LogEvent(long sequenceNumber) { + this.sequenceNumber = sequenceNumber; + } + + long sequenceNumber; + boolean isLoggable; + String loggerName; + Logger.Level level; + ResourceBundle bundle; + Throwable thrown; + Object[] args; + Supplier supplier; + String msg; + + Object[] toArray() { + return new Object[] { + sequenceNumber, + isLoggable, + loggerName, + level, + bundle, + thrown, + args, + supplier, + msg, + }; + } + + @Override + public String toString() { + return Arrays.deepToString(toArray()); + } + + + + @Override + public boolean equals(Object obj) { + return obj instanceof LogEvent + && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray()); + } + + @Override + public int hashCode() { + return Objects.hash(toArray()); + } + + + public static LogEvent of(boolean isLoggable, String name, + Logger.Level level, ResourceBundle bundle, + String key, Throwable thrown) { + LogEvent evt = new LogEvent(); + evt.isLoggable = isLoggable; + evt.loggerName = name; + evt.level = level; + evt.args = null; + evt.bundle = bundle; + evt.thrown = thrown; + evt.supplier = null; + evt.msg = key; + return evt; + } + + public static LogEvent of(boolean isLoggable, String name, + Logger.Level level, ResourceBundle bundle, + String key, Object... params) { + LogEvent evt = new LogEvent(); + evt.isLoggable = isLoggable; + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = null; + evt.supplier = null; + evt.msg = key; + return evt; + } + + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + Logger.Level level, ResourceBundle bundle, + String key, Supplier supplier, + Throwable thrown, Object... params) { + LogEvent evt = new LogEvent(sequenceNumber); + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = thrown; + evt.supplier = supplier; + evt.msg = key; + evt.isLoggable = isLoggable; + return evt; + } + + } + + public class LoggerImpl implements Logger { + final String name; + Logger.Level level = Logger.Level.INFO; + + public LoggerImpl(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isLoggable(Logger.Level level) { + return this.level != Logger.Level.OFF && this.level.getSeverity() <= level.getSeverity(); + } + + @Override + public void log(Logger.Level level, ResourceBundle bundle, String key, Throwable thrown) { + log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown)); + } + + @Override + public void log(Logger.Level level, ResourceBundle bundle, String format, Object... params) { + log(LogEvent.of(isLoggable(level), name, level, bundle, format, params)); + } + + void log(LogEvent event) { + eventQueue.add(event); + } + } + + public Logger getLogger(String name, Class caller); + public Logger getLocalizedLogger(String name, ResourceBundle bundle, Class caller); +} diff --git a/jdk/test/java/lang/System/LoggerFinder/DefaultLoggerFinderTest/AccessSystemLogger.java b/jdk/test/java/lang/System/LoggerFinder/DefaultLoggerFinderTest/AccessSystemLogger.java new file mode 100644 index 00000000000..45d3f9d1a0e --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/DefaultLoggerFinderTest/AccessSystemLogger.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.io.IOException; +import java.lang.System.Logger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ResourceBundle; + +/** + * + * @author danielfuchs + */ +public final class AccessSystemLogger { + + public AccessSystemLogger() { + this(check()); + } + + private AccessSystemLogger(Void unused) { + } + + private static Void check() { + if (AccessSystemLogger.class.getClassLoader() != null) { + throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader"); + } + return null; + } + + public Logger getLogger(String name) { + Logger logger = System.getLogger(name); + System.out.println("System.getLogger(\"" + name + "\"): " + logger); + return logger; + } + + public Logger getLogger(String name, ResourceBundle bundle) { + Logger logger = System.getLogger(name, bundle); + System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger); + return logger; + } + + public java.util.logging.Logger demandSystemLogger(String name) { + return java.util.logging.Logger.getLogger(name); + } + + // copy AccessSystemLogger.class to ./boot + public static void main(String[] args) throws IOException { + Path testDir = Paths.get(System.getProperty("user.dir", ".")); + Path bootDir = Paths.get(testDir.toString(), "boot"); + Path classes = Paths.get(System.getProperty("test.classes", "build/classes")); + Path thisClass = Paths.get(classes.toString(), + AccessSystemLogger.class.getSimpleName()+".class"); + if (Files.notExists(bootDir)) { + Files.createDirectory(bootDir); + } + Path dest = Paths.get(bootDir.toString(), + AccessSystemLogger.class.getSimpleName()+".class"); + Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING); + } + +} diff --git a/jdk/test/java/lang/System/LoggerFinder/DefaultLoggerFinderTest/DefaultLoggerFinderTest.java b/jdk/test/java/lang/System/LoggerFinder/DefaultLoggerFinderTest/DefaultLoggerFinderTest.java new file mode 100644 index 00000000000..0f7524ec242 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/DefaultLoggerFinderTest/DefaultLoggerFinderTest.java @@ -0,0 +1,887 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.security.AccessControlException; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Queue; +import java.util.ResourceBundle; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.lang.System.LoggerFinder; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.util.stream.Stream; + +/** + * @test + * @bug 8140364 + * @summary Tests the default implementation of System.Logger, when + * JUL is the default backend. + * @build AccessSystemLogger DefaultLoggerFinderTest + * @run driver AccessSystemLogger + * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerFinderTest NOSECURITY + * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerFinderTest NOPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerFinderTest WITHPERMISSIONS + * @author danielfuchs + */ +public class DefaultLoggerFinderTest { + + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + final static AtomicLong sequencer = new AtomicLong(); + final static boolean VERBOSE = false; + static final ThreadLocal allowControl = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAll = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + + static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger(); + + public static final Queue eventQueue = new ArrayBlockingQueue<>(128); + + public static final class LogEvent { + + public LogEvent() { + this(sequencer.getAndIncrement()); + } + + LogEvent(long sequenceNumber) { + this.sequenceNumber = sequenceNumber; + } + + long sequenceNumber; + boolean isLoggable; + String loggerName; + java.util.logging.Level level; + ResourceBundle bundle; + Throwable thrown; + Object[] args; + String msg; + String className; + String methodName; + + Object[] toArray() { + return new Object[] { + sequenceNumber, + isLoggable, + loggerName, + level, + bundle, + thrown, + args, + msg, + className, + methodName, + }; + } + + @Override + public String toString() { + return Arrays.deepToString(toArray()); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof LogEvent + && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray()); + } + + @Override + public int hashCode() { + return Objects.hash(toArray()); + } + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + java.util.logging.Level level, ResourceBundle bundle, + String key, Throwable thrown, Object... params) { + return LogEvent.of(sequenceNumber, isLoggable, name, + DefaultLoggerFinderTest.class.getName(), + "testLogger", level, bundle, key, + thrown, params); + } + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + String className, String methodName, + java.util.logging.Level level, ResourceBundle bundle, + String key, Throwable thrown, Object... params) { + LogEvent evt = new LogEvent(sequenceNumber); + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = thrown; + evt.msg = key; + evt.isLoggable = isLoggable; + evt.className = className; + evt.methodName = methodName; + return evt; + } + + } + + static java.util.logging.Level mapToJul(Level level) { + switch (level) { + case ALL: return java.util.logging.Level.ALL; + case TRACE: return java.util.logging.Level.FINER; + case DEBUG: return java.util.logging.Level.FINE; + case INFO: return java.util.logging.Level.INFO; + case WARNING: return java.util.logging.Level.WARNING; + case ERROR: return java.util.logging.Level.SEVERE; + case OFF: return java.util.logging.Level.OFF; + } + throw new InternalError("No such level: " + level); + } + + static final java.util.logging.Level[] julLevels = { + java.util.logging.Level.ALL, + new java.util.logging.Level("FINER_THAN_FINEST", java.util.logging.Level.FINEST.intValue() - 10) {}, + java.util.logging.Level.FINEST, + new java.util.logging.Level("FINER_THAN_FINER", java.util.logging.Level.FINER.intValue() - 10) {}, + java.util.logging.Level.FINER, + new java.util.logging.Level("FINER_THAN_FINE", java.util.logging.Level.FINE.intValue() - 10) {}, + java.util.logging.Level.FINE, + new java.util.logging.Level("FINER_THAN_CONFIG", java.util.logging.Level.FINE.intValue() + 10) {}, + java.util.logging.Level.CONFIG, + new java.util.logging.Level("FINER_THAN_INFO", java.util.logging.Level.INFO.intValue() - 10) {}, + java.util.logging.Level.INFO, + new java.util.logging.Level("FINER_THAN_WARNING", java.util.logging.Level.INFO.intValue() + 10) {}, + java.util.logging.Level.WARNING, + new java.util.logging.Level("FINER_THAN_SEVERE", java.util.logging.Level.SEVERE.intValue() - 10) {}, + java.util.logging.Level.SEVERE, + new java.util.logging.Level("FATAL", java.util.logging.Level.SEVERE.intValue() + 10) {}, + java.util.logging.Level.OFF, + }; + + static final Level[] mappedLevels = { + Level.ALL, // ALL + Level.DEBUG, // FINER_THAN_FINEST + Level.DEBUG, // FINEST + Level.DEBUG, // FINER_THAN_FINER + Level.TRACE, // FINER + Level.TRACE, // FINER_THAN_FINE + Level.DEBUG, // FINE + Level.DEBUG, // FINER_THAN_CONFIG + Level.DEBUG, // CONFIG + Level.DEBUG, // FINER_THAN_INFO + Level.INFO, // INFO + Level.INFO, // FINER_THAN_WARNING + Level.WARNING, // WARNING + Level.WARNING, // FINER_THAN_SEVERE + Level.ERROR, // SEVERE + Level.ERROR, // FATAL + Level.OFF, // OFF + }; + + final static Map julToSpiMap; + static { + Map map = new HashMap<>(); + if (mappedLevels.length != julLevels.length) { + throw new ExceptionInInitializerError("Array lengths differ" + + "\n\tjulLevels=" + Arrays.deepToString(julLevels) + + "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels)); + } + for (int i=0; i map = new ConcurrentHashMap<>(); + + @Override + protected Object handleGetObject(String key) { + if (key.contains(" (translated)")) { + throw new RuntimeException("Unexpected key: " + key); + } + return map.computeIfAbsent(key, k -> k + " (translated)"); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(map.keySet()); + } + + } + + public static class MyHandler extends Handler { + + @Override + public java.util.logging.Level getLevel() { + return java.util.logging.Level.ALL; + } + + @Override + public void publish(LogRecord record) { + eventQueue.add(LogEvent.of(sequencer.getAndIncrement(), + true, record.getLoggerName(), + record.getSourceClassName(), + record.getSourceMethodName(), + record.getLevel(), + record.getResourceBundle(), record.getMessage(), + record.getThrown(), record.getParameters())); + } + @Override + public void flush() { + } + @Override + public void close() throws SecurityException { + } + + } + + public static class MyLoggerBundle extends MyBundle { + + } + + + static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS}; + + static void setSecurityManager() { + if (System.getSecurityManager() == null) { + Policy.setPolicy(new SimplePolicy(allowAll, allowControl)); + System.setSecurityManager(new SecurityManager()); + } + } + + public static void main(String[] args) { + if (args.length == 0) + args = new String[] { + "NOSECURITY", + "NOPERMISSIONS", + "WITHPERMISSIONS" + }; + + final java.util.logging.Logger appSink = java.util.logging.Logger.getLogger("foo"); + final java.util.logging.Logger sysSink = accessSystemLogger.demandSystemLogger("foo"); + appSink.addHandler(new MyHandler()); + sysSink.addHandler(new MyHandler()); + appSink.setUseParentHandlers(VERBOSE); + sysSink.setUseParentHandlers(VERBOSE); + + Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> { + LoggerFinder provider; + switch (testCase) { + case NOSECURITY: + System.out.println("\n*** Without Security Manager\n"); + provider = LoggerFinder.getLoggerFinder(); + test(provider, true, appSink, sysSink); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case NOPERMISSIONS: + System.out.println("\n*** With Security Manager, without permissions\n"); + setSecurityManager(); + try { + provider = LoggerFinder.getLoggerFinder(); + throw new RuntimeException("Expected exception not raised"); + } catch (AccessControlException x) { + if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) { + throw new RuntimeException("Unexpected permission check", x); + } + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = LoggerFinder.getLoggerFinder(); + } finally { + allowControl.get().set(control); + } + } + test(provider, false, appSink, sysSink); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case WITHPERMISSIONS: + System.out.println("\n*** With Security Manager, with control permission\n"); + setSecurityManager(); + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = LoggerFinder.getLoggerFinder(); + test(provider, true, appSink, sysSink); + } finally { + allowControl.get().set(control); + } + break; + default: + throw new RuntimeException("Unknown test case: " + testCase); + } + }); + System.out.println("\nPASSED: Tested " + sequencer.get() + " cases."); + } + + public static void test(LoggerFinder provider, + boolean hasRequiredPermissions, + java.util.logging.Logger appSink, + java.util.logging.Logger sysSink) { + + ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName()); + final Map loggerDescMap = new HashMap<>(); + + + Logger appLogger1 = null; + try { + appLogger1 = provider.getLogger("foo", DefaultLoggerFinderTest.class); + loggerDescMap.put(appLogger1, "provider.getApplicationLogger(\"foo\")"); + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for logger: " + acx); + boolean old = allowControl.get().get(); + allowControl.get().set(true); + try { + appLogger1 =provider.getLogger("foo", DefaultLoggerFinderTest.class); + loggerDescMap.put(appLogger1, "provider.getApplicationLogger(\"foo\")"); + } finally { + allowControl.get().set(old); + } + } + + Logger sysLogger1 = null; + try { + sysLogger1 = provider.getLogger("foo", Thread.class); + loggerDescMap.put(sysLogger1, "provider.getSystemLogger(\"foo\")"); + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a system logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for system logger: " + acx); + boolean old = allowControl.get().get(); + allowControl.get().set(true); + try { + sysLogger1 = provider.getLogger("foo", Thread.class); + loggerDescMap.put(sysLogger1, "provider.getSystemLogger(\"foo\")"); + } finally { + allowControl.get().set(old); + } + } + if (appLogger1 == sysLogger1) { + throw new RuntimeException("identical loggers"); + } + + Logger appLogger2 = null; + try { + appLogger2 = provider.getLocalizedLogger("foo", loggerBundle, DefaultLoggerFinderTest.class); + loggerDescMap.put(appLogger2, "provider.getLocalizedApplicationLogger(\"foo\", loggerBundle)"); + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for logger: " + acx); + boolean old = allowControl.get().get(); + allowControl.get().set(true); + try { + appLogger2 = provider.getLocalizedLogger("foo", loggerBundle, DefaultLoggerFinderTest.class); + loggerDescMap.put(appLogger2, "provider.getLocalizedApplicationLogger(\"foo\", loggerBundle)"); + } finally { + allowControl.get().set(old); + } + } + + Logger sysLogger2 = null; + try { + sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class); + loggerDescMap.put(sysLogger2, "provider.getLocalizedSystemLogger(\"foo\", loggerBundle)"); + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a system logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for localized system logger: " + acx); + boolean old = allowControl.get().get(); + allowControl.get().set(true); + try { + sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class); + loggerDescMap.put(sysLogger2, "provider.getLocalizedSystemLogger(\"foo\", loggerBundle)"); + } finally { + allowControl.get().set(old); + } + } + if (appLogger2 == sysLogger2) { + throw new RuntimeException("identical loggers"); + } + if (appLogger2 == appLogger1) { + throw new RuntimeException("identical loggers"); + } + if (sysLogger2 == sysLogger1) { + throw new RuntimeException("identical loggers"); + } + + + testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink); + testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink); + testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink); + testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink); + + + Logger appLogger3 = System.getLogger("foo"); + loggerDescMap.put(appLogger3, "System.getLogger(\"foo\")"); + + testLogger(provider, loggerDescMap, "foo", null, appLogger3, appSink); + + Logger appLogger4 = + System.getLogger("foo", loggerBundle); + loggerDescMap.put(appLogger4, "System.getLogger(\"foo\", loggerBundle)"); + + if (appLogger4 == appLogger1) { + throw new RuntimeException("identical loggers"); + } + + testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger4, appSink); + + Logger sysLogger3 = accessSystemLogger.getLogger("foo"); + loggerDescMap.put(sysLogger3, "AccessSystemLogger.getLogger(\"foo\")"); + + testLogger(provider, loggerDescMap, "foo", null, sysLogger3, sysSink); + + Logger sysLogger4 = + accessSystemLogger.getLogger("foo", loggerBundle); + loggerDescMap.put(appLogger4, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)"); + + if (sysLogger4 == sysLogger1) { + throw new RuntimeException("identical loggers"); + } + + testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger4, sysSink); + + } + + public static class Foo { + + } + + static void verbose(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + + static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) { + boolean before = allowAll.get().get(); + try { + allowAll.get().set(true); + sink.setLevel(loggerLevel); + } finally { + allowAll.get().set(before); + } + } + + + // Calls the 8 methods defined on Logger and verify the + // parameters received by the underlying Logger Impl + // logger. + private static void testLogger(LoggerFinder provider, + Map loggerDescMap, + String name, + ResourceBundle loggerBundle, + Logger logger, + java.util.logging.Logger sink) { + + System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger + "]"); + final java.util.logging.Level OFF = java.util.logging.Level.OFF; + + Foo foo = new Foo(); + String fooMsg = foo.toString(); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (Level messageLevel : Level.values()) { + java.util.logging.Level julLevel = mapToJul(messageLevel); + String desc = "logger.log(messageLevel, foo): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + julLevel.intValue() >= loggerLevel.intValue(), + name, julLevel, (ResourceBundle)null, + fooMsg, (Throwable)null, (Object[])null); + logger.log(messageLevel, foo); + if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + String msg = "blah"; + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (Level messageLevel : Level.values()) { + java.util.logging.Level julLevel = mapToJul(messageLevel); + String desc = "logger.log(messageLevel, \"blah\"): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + julLevel.intValue() >= loggerLevel.intValue(), + name, julLevel, loggerBundle, + msg, (Throwable)null, (Object[])null); + logger.log(messageLevel, msg); + if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + Supplier fooSupplier = new Supplier() { + @Override + public String get() { + return this.toString(); + } + }; + + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (Level messageLevel : Level.values()) { + java.util.logging.Level julLevel = mapToJul(messageLevel); + String desc = "logger.log(messageLevel, fooSupplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + julLevel.intValue() >= loggerLevel.intValue(), + name, julLevel, (ResourceBundle)null, + fooSupplier.get(), + (Throwable)null, (Object[])null); + logger.log(messageLevel, fooSupplier); + if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + String format = "two params [{1} {2}]"; + Object arg1 = foo; + Object arg2 = msg; + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (Level messageLevel : Level.values()) { + java.util.logging.Level julLevel = mapToJul(messageLevel); + String desc = "logger.log(messageLevel, format, params...): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + julLevel.intValue() >= loggerLevel.intValue(), + name, julLevel, loggerBundle, + format, (Throwable)null, new Object[] {arg1, arg2}); + logger.log(messageLevel, format, arg1, arg2); + if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + Throwable thrown = new Exception("OK: log me!"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (Level messageLevel : Level.values()) { + java.util.logging.Level julLevel = mapToJul(messageLevel); + String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + julLevel.intValue() >= loggerLevel.intValue(), + name, julLevel, loggerBundle, + msg, thrown, (Object[]) null); + logger.log(messageLevel, msg, thrown); + if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (Level messageLevel : Level.values()) { + java.util.logging.Level julLevel = mapToJul(messageLevel); + String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + julLevel.intValue() >= loggerLevel.intValue(), + name, julLevel, (ResourceBundle)null, + fooSupplier.get(), + (Throwable)thrown, (Object[])null); + logger.log(messageLevel, fooSupplier, thrown); + if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName()); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (Level messageLevel : Level.values()) { + java.util.logging.Level julLevel = mapToJul(messageLevel); + String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + julLevel.intValue() >= loggerLevel.intValue(), + name, julLevel, bundle, + format, (Throwable)null, new Object[] {foo, msg}); + logger.log(messageLevel, bundle, format, foo, msg); + if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (Level messageLevel : Level.values()) { + java.util.logging.Level julLevel = mapToJul(messageLevel); + String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + julLevel.intValue() >= loggerLevel.intValue(), + name, julLevel, bundle, + msg, thrown, (Object[]) null); + logger.log(messageLevel, bundle, msg, thrown); + if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) { + if (eventQueue.poll() != null) { + throw new RuntimeException("unexpected event in queue for " + desc); + } + } else { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + } + } + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + + final Permissions permissions; + final Permissions withControlPermissions; + final Permissions allPermissions; + final ThreadLocal allowAll; + final ThreadLocal allowControl; + public SimplePolicy(ThreadLocal allowAll, + ThreadLocal allowControl) { + this.allowAll = allowAll; + this.allowControl = allowControl; + permissions = new Permissions(); + + withControlPermissions = new Permissions(); + withControlPermissions.add(LOGGERFINDER_PERMISSION); + + // these are used for configuring the test itself... + allPermissions = new Permissions(); + allPermissions.add(new java.security.AllPermission()); + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + if (allowAll.get().get()) return allPermissions.implies(permission); + if (allowControl.get().get()) return withControlPermissions.implies(permission); + return permissions.implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return new PermissionsBuilder().addAll( + allowAll.get().get() ? allPermissions : + allowControl.get().get() + ? withControlPermissions : permissions).toPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return new PermissionsBuilder().addAll( + allowAll.get().get() ? allPermissions : + allowControl.get().get() + ? withControlPermissions : permissions).toPermissions(); + } + } +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/AccessSystemLogger.java b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/AccessSystemLogger.java new file mode 100644 index 00000000000..928dc97987e --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/AccessSystemLogger.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.io.IOException; +import java.lang.System.Logger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ResourceBundle; + +/** + * + * @author danielfuchs + */ +public final class AccessSystemLogger { + + public AccessSystemLogger() { + this(check()); + } + + private AccessSystemLogger(Void unused) { + } + + private static Void check() { + if (AccessSystemLogger.class.getClassLoader() != null) { + throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader"); + } + return null; + } + + public Logger getLogger(String name) { + Logger logger = System.getLogger(name); + System.out.println("System.getLogger(\"" + name + "\"): " + logger); + return logger; + } + + public Logger getLogger(String name, ResourceBundle bundle) { + Logger logger = System.getLogger(name, bundle); + System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger); + return logger; + } + + static final Class[] toCopy = { AccessSystemLogger.class, CustomSystemClassLoader.class }; + + // copy AccessSystemLogger.class to ./boot + public static void main(String[] args) throws IOException { + Path testDir = Paths.get(System.getProperty("user.dir", ".")); + Path bootDir = Paths.get(testDir.toString(), "boot"); + Path classes = Paths.get(System.getProperty("test.classes", "build/classes")); + if (Files.notExists(bootDir)) { + Files.createDirectory(bootDir); + } + for (Class c : toCopy) { + Path thisClass = Paths.get(classes.toString(), + c.getSimpleName()+".class"); + Path dest = Paths.get(bootDir.toString(), + c.getSimpleName()+".class"); + Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING); + } + } + +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/BaseDefaultLoggerFinderTest.java b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/BaseDefaultLoggerFinderTest.java new file mode 100644 index 00000000000..28deedf6231 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/BaseDefaultLoggerFinderTest.java @@ -0,0 +1,768 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UncheckedIOException; +import java.security.AccessControlException; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.stream.Stream; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; +import java.lang.System.LoggerFinder; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Locale; +import java.util.concurrent.atomic.AtomicReference; +import jdk.internal.logger.DefaultLoggerFinder; +import jdk.internal.logger.SimpleConsoleLogger; +import sun.util.logging.PlatformLogger; + +/** + * @test + * @bug 8140364 + * @summary JDK implementation specific unit test for the base DefaultLoggerFinder. + * Tests the behavior of DefaultLoggerFinder and SimpleConsoleLogger + * implementation. + * @modules java.base/sun.util.logging + * java.base/jdk.internal.logger + * @build AccessSystemLogger BaseDefaultLoggerFinderTest CustomSystemClassLoader + * @run driver AccessSystemLogger + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest NOSECURITY + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest NOPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest WITHPERMISSIONS + * @author danielfuchs + */ +public class BaseDefaultLoggerFinderTest { + + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + final static boolean VERBOSE = false; + static final ThreadLocal allowControl = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAccess = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + + final static AccessSystemLogger accessSystemLogger = new AccessSystemLogger(); + static final Class[] providerClass; + static { + try { + providerClass = new Class[] { + ClassLoader.getSystemClassLoader().loadClass("BaseDefaultLoggerFinderTest$BaseLoggerFinder"), + }; + } catch (ClassNotFoundException ex) { + throw new ExceptionInInitializerError(ex); + } + } + + /** + * What our test provider needs to implement. + */ + public static interface TestLoggerFinder { + public final static AtomicBoolean fails = new AtomicBoolean(); + public final static AtomicReference conf = new AtomicReference<>(""); + public final static AtomicLong sequencer = new AtomicLong(); + + + public Logger getLogger(String name, Class caller); + public Logger getLocalizedLogger(String name, ResourceBundle bundle, Class caller); + void setLevel(Logger logger, Level level, Class caller); + void setLevel(Logger logger, PlatformLogger.Level level, Class caller); + PlatformLogger.Bridge asPlatformLoggerBridge(Logger logger); + } + + public static class BaseLoggerFinder extends DefaultLoggerFinder implements TestLoggerFinder { + + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + public BaseLoggerFinder() { + if (fails.get()) { + throw new RuntimeException("Simulate exception while loading provider"); + } + } + + @Override + public void setLevel(Logger logger, Level level, Class caller) { + PrivilegedAction pa = () -> { + setLevel(logger, PlatformLogger.toPlatformLevel(level), caller); + return null; + }; + AccessController.doPrivileged(pa); + } + + @Override + public void setLevel(Logger logger, PlatformLogger.Level level, Class caller) { + PrivilegedAction pa = () -> demandLoggerFor(logger.getName(), caller); + Logger impl = AccessController.doPrivileged(pa); + SimpleConsoleLogger.class.cast(impl) + .getLoggerConfiguration() + .setPlatformLevel(level); + } + + @Override + public PlatformLogger.Bridge asPlatformLoggerBridge(Logger logger) { + PrivilegedAction pa = () -> + PlatformLogger.Bridge.convert(logger); + return AccessController.doPrivileged(pa); + } + + } + + public static class MyBundle extends ResourceBundle { + + final ConcurrentHashMap map = new ConcurrentHashMap<>(); + + @Override + protected Object handleGetObject(String key) { + if (key.contains(" (translated)")) { + throw new RuntimeException("Unexpected key: " + key); + } + return map.computeIfAbsent(key, k -> k.toUpperCase(Locale.ROOT) + " (translated)"); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(map.keySet()); + } + + } + public static class MyLoggerBundle extends MyBundle { + + } + + static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS}; + + static void setSecurityManager() { + if (System.getSecurityManager() == null) { + Policy.setPolicy(new SimplePolicy(allowControl, allowAccess)); + System.setSecurityManager(new SecurityManager()); + } + } + + static TestLoggerFinder getLoggerFinder(Class expectedClass) { + LoggerFinder provider = null; + try { + TestLoggerFinder.sequencer.incrementAndGet(); + provider = LoggerFinder.getLoggerFinder(); + } catch(AccessControlException a) { + throw a; + } + ErrorStream.errorStream.store(); + System.out.println("*** Actual LoggerFinder class is: " + provider.getClass().getName()); + expectedClass.cast(provider); + return TestLoggerFinder.class.cast(provider); + } + + + static class ErrorStream extends PrintStream { + + static AtomicBoolean forward = new AtomicBoolean(); + ByteArrayOutputStream out; + String saved = ""; + public ErrorStream(ByteArrayOutputStream out) { + super(out); + this.out = out; + } + + @Override + public void write(int b) { + super.write(b); + if (forward.get()) err.write(b); + } + + @Override + public void write(byte[] b) throws IOException { + super.write(b); + if (forward.get()) err.write(b); + } + + @Override + public void write(byte[] buf, int off, int len) { + super.write(buf, off, len); + if (forward.get()) err.write(buf, off, len); + } + + public String peek() { + flush(); + return out.toString(); + } + + public String drain() { + flush(); + String res = out.toString(); + out.reset(); + return res; + } + + public void store() { + flush(); + saved = out.toString(); + out.reset(); + } + + public void restore() { + out.reset(); + try { + out.write(saved.getBytes()); + } catch(IOException io) { + throw new UncheckedIOException(io); + } + } + + static final PrintStream err = System.err; + static final ErrorStream errorStream = new ErrorStream(new ByteArrayOutputStream()); + } + + private static StringBuilder appendProperty(StringBuilder b, String name) { + String value = System.getProperty(name); + if (value == null) return b; + return b.append(name).append("=").append(value).append('\n'); + } + + public static void main(String[] args) { + if (args.length == 0) { + args = new String[] { + //"NOSECURITY", + "NOPERMISSIONS", + "WITHPERMISSIONS" + }; + } + Locale.setDefault(Locale.ENGLISH); + System.setErr(ErrorStream.errorStream); + //System.setProperty("jdk.logger.finder.error", "ERROR"); + //System.setProperty("jdk.logger.finder.singleton", "true"); + //System.setProperty("test.fails", "true"); + TestLoggerFinder.fails.set(Boolean.getBoolean("test.fails")); + StringBuilder c = new StringBuilder(); + appendProperty(c, "jdk.logger.packages"); + appendProperty(c, "jdk.logger.finder.error"); + appendProperty(c, "jdk.logger.finder.singleton"); + appendProperty(c, "test.fails"); + TestLoggerFinder.conf.set(c.toString()); + try { + test(args); + } finally { + try { + System.setErr(ErrorStream.err); + } catch (Error | RuntimeException x) { + x.printStackTrace(ErrorStream.err); + } + } + } + + + public static void test(String[] args) { + + final Class expectedClass = jdk.internal.logger.DefaultLoggerFinder.class; + + System.out.println("Declared provider class: " + providerClass[0] + + "[" + providerClass[0].getClassLoader() + "]"); + + Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> { + TestLoggerFinder provider; + ErrorStream.errorStream.restore(); + switch (testCase) { + case NOSECURITY: + System.out.println("\n*** Without Security Manager\n"); + System.out.println(TestLoggerFinder.conf.get()); + provider = getLoggerFinder(expectedClass); + if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) { + throw new RuntimeException("Unexpected provider: " + provider.getClass().getName()); + } + test(provider, true); + System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get()); + break; + case NOPERMISSIONS: + System.out.println("\n*** With Security Manager, without permissions\n"); + System.out.println(TestLoggerFinder.conf.get()); + setSecurityManager(); + try { + provider = getLoggerFinder(expectedClass); + throw new RuntimeException("Expected exception not raised"); + } catch (AccessControlException x) { + if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) { + throw new RuntimeException("Unexpected permission check", x); + } + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = getLoggerFinder(expectedClass); + if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) { + throw new RuntimeException("Unexpected provider: " + provider.getClass().getName()); + } + } finally { + allowControl.get().set(control); + } + } + test(provider, false); + System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get()); + break; + case WITHPERMISSIONS: + System.out.println("\n*** With Security Manager, with control permission\n"); + System.out.println(TestLoggerFinder.conf.get()); + setSecurityManager(); + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = getLoggerFinder(expectedClass); + if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) { + throw new RuntimeException("Unexpected provider: " + provider.getClass().getName()); + } + test(provider, true); + } finally { + allowControl.get().set(control); + } + break; + default: + throw new RuntimeException("Unknown test case: " + testCase); + } + }); + System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases."); + } + + public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) { + + ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName()); + final Map loggerDescMap = new HashMap<>(); + + System.Logger sysLogger = accessSystemLogger.getLogger("foo"); + loggerDescMap.put(sysLogger, "accessSystemLogger.getLogger(\"foo\")"); + System.Logger localizedSysLogger = accessSystemLogger.getLogger("fox", loggerBundle); + loggerDescMap.put(localizedSysLogger, "accessSystemLogger.getLogger(\"fox\", loggerBundle)"); + System.Logger appLogger = System.getLogger("bar"); + loggerDescMap.put(appLogger,"System.getLogger(\"bar\")"); + System.Logger localizedAppLogger = System.getLogger("baz", loggerBundle); + loggerDescMap.put(localizedAppLogger,"System.getLogger(\"baz\", loggerBundle)"); + + testLogger(provider, loggerDescMap, "foo", null, sysLogger, accessSystemLogger.getClass()); + testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedSysLogger, accessSystemLogger.getClass()); + testLogger(provider, loggerDescMap, "foo", null, appLogger, BaseDefaultLoggerFinderTest.class); + testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedAppLogger, BaseDefaultLoggerFinderTest.class); + } + + public static class Foo { + + } + + static void verbose(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + + // Calls the 8 methods defined on Logger and verify the + // parameters received by the underlying TestProvider.LoggerImpl + // logger. + private static void testLogger(TestLoggerFinder provider, + Map loggerDescMap, + String name, + ResourceBundle loggerBundle, + Logger logger, + Class caller) { + + System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]"); + AtomicLong sequencer = TestLoggerFinder.sequencer; + + Foo foo = new Foo(); + String fooMsg = foo.toString(); + for (Level loggerLevel : Level.values()) { + provider.setLevel(logger, loggerLevel, caller); + for (Level messageLevel : Level.values()) { + ErrorStream.errorStream.drain(); + String desc = "logger.log(messageLevel, foo): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, foo); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + if (!logged.contains("BaseDefaultLoggerFinderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + fooMsg)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] BaseDefaultLoggerFinderTest testLogger\n" + + messageLevel.getName() + " " + fooMsg + + "\n>>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + String msg = "blah"; + for (Level loggerLevel : Level.values()) { + provider.setLevel(logger, loggerLevel, caller); + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, \"blah\"): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, msg); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg); + if (!logged.contains("BaseDefaultLoggerFinderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + msgText)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] BaseDefaultLoggerFinderTest testLogger\n" + + messageLevel.getName() + " " + msgText + + "\n>>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + Supplier fooSupplier = new Supplier() { + @Override + public String get() { + return this.toString(); + } + }; + + for (Level loggerLevel : Level.values()) { + provider.setLevel(logger, loggerLevel, caller); + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, fooSupplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, fooSupplier); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + if (!logged.contains("BaseDefaultLoggerFinderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] BaseDefaultLoggerFinderTest testLogger\n" + + messageLevel.getName() + " " + fooSupplier.get() + + "\n>>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + + String format = "two params [{1} {2}]"; + Object arg1 = foo; + Object arg2 = msg; + for (Level loggerLevel : Level.values()) { + provider.setLevel(logger, loggerLevel, caller); + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, format, params...): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, format, foo, msg); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + String msgFormat = loggerBundle == null ? format : loggerBundle.getString(format); + String text = java.text.MessageFormat.format(msgFormat, foo, msg); + if (!logged.contains("BaseDefaultLoggerFinderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + text)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] BaseDefaultLoggerFinderTest testLogger\n" + + messageLevel.getName() + " " + text + + "\n>>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + Throwable thrown = new Exception("OK: log me!"); + for (Level loggerLevel : Level.values()) { + provider.setLevel(logger, loggerLevel, caller); + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, msg, thrown); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + thrown.printStackTrace(new PrintStream(baos)); + String text = baos.toString(); + String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg); + if (!logged.contains("BaseDefaultLoggerFinderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + msgText) + || !logged.contains(text)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] BaseDefaultLoggerFinderTest testLogger\n" + + messageLevel.getName() + " " + msgText +"\n" + + text + + ">>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + + for (Level loggerLevel : Level.values()) { + provider.setLevel(logger, loggerLevel, caller); + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, fooSupplier, thrown); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + thrown.printStackTrace(new PrintStream(baos)); + String text = baos.toString(); + if (!logged.contains("BaseDefaultLoggerFinderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get()) + || !logged.contains(text)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] BaseDefaultLoggerFinderTest testLogger\n" + + messageLevel.getName() + " " + fooSupplier.get() +"\n" + + text + + ">>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName()); + for (Level loggerLevel : Level.values()) { + provider.setLevel(logger, loggerLevel, caller); + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, bundle, format, foo, msg); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + String text = java.text.MessageFormat.format(bundle.getString(format), foo, msg); + if (!logged.contains("BaseDefaultLoggerFinderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + text)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] BaseDefaultLoggerFinderTest testLogger\n" + + messageLevel.getName() + " " + text + + "\n>>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + for (Level loggerLevel : Level.values()) { + provider.setLevel(logger, loggerLevel, caller); + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, bundle, msg, thrown); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + String textMsg = bundle.getString(msg); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + thrown.printStackTrace(new PrintStream(baos)); + String text = baos.toString(); + if (!logged.contains("BaseDefaultLoggerFinderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + textMsg) + || !logged.contains(text)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] BaseDefaultLoggerFinderTest testLogger\n" + + messageLevel.getName() + " " + textMsg +"\n" + + text + + ">>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION; + final static RuntimePermission ACCESS = new RuntimePermission("accessClassInPackage.jdk.internal.logger"); + + final Permissions permissions; + final ThreadLocal allowControl; + final ThreadLocal allowAccess; + public SimplePolicy(ThreadLocal allowControl, ThreadLocal allowAccess) { + this.allowControl = allowControl; + this.allowAccess = allowAccess; + permissions = new Permissions(); + permissions.add(new RuntimePermission("setIO")); + } + + Permissions getPermissions() { + if (allowControl.get().get() || allowAccess.get().get()) { + PermissionsBuilder builder = new PermissionsBuilder() + .addAll(permissions); + if (allowControl.get().get()) { + builder.add(CONTROL); + } + if (allowAccess.get().get()) { + builder.add(ACCESS); + } + return builder.toPermissions(); + } + return permissions; + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + return getPermissions().implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + } +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/CustomSystemClassLoader.java b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/CustomSystemClassLoader.java new file mode 100644 index 00000000000..c948b0b58b1 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/CustomSystemClassLoader.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.security.AllPermission; +import java.security.Permissions; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * A custom ClassLoader to load the concrete LoggerFinder class + * with all permissions. The CustomSystemClassLoader class must be + * in the BCL, otherwise when system classes - such as + * ZoneDateTime try to load their resource bundle a MissingResourceBundle + * caused by a SecurityException may be thrown, as the CustomSystemClassLoader + * code base will be found in the stack called by doPrivileged. + * + * @author danielfuchs + */ +public class CustomSystemClassLoader extends ClassLoader { + + + final List finderClassNames = + Arrays.asList("BaseDefaultLoggerFinderTest$BaseLoggerFinder"); + final Map> finderClasses = new HashMap<>(); + Class testLoggerFinderClass; + + public CustomSystemClassLoader() { + super(); + } + public CustomSystemClassLoader(ClassLoader parent) { + super(parent); + } + + private Class defineFinderClass(String name) + throws ClassNotFoundException { + final Object obj = getClassLoadingLock(name); + synchronized(obj) { + if (finderClasses.get(name) != null) return finderClasses.get(name); + if (testLoggerFinderClass == null) { + // Hack: we load testLoggerFinderClass to get its code source. + // we can't use this.getClass() since we are in the boot. + testLoggerFinderClass = super.loadClass("BaseDefaultLoggerFinderTest$TestLoggerFinder"); + } + URL url = testLoggerFinderClass.getProtectionDomain().getCodeSource().getLocation(); + File file = new File(url.getPath(), name+".class"); + if (file.canRead()) { + try { + byte[] b = Files.readAllBytes(file.toPath()); + Permissions perms = new Permissions(); + perms.add(new AllPermission()); + Class finderClass = defineClass( + name, b, 0, b.length, new ProtectionDomain( + this.getClass().getProtectionDomain().getCodeSource(), + perms)); + System.out.println("Loaded " + name); + finderClasses.put(name, finderClass); + return finderClass; + } catch (Throwable ex) { + ex.printStackTrace(); + throw new ClassNotFoundException(name, ex); + } + } else { + throw new ClassNotFoundException(name, + new IOException(file.toPath() + ": can't read")); + } + } + } + + @Override + public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (finderClassNames.contains(name)) { + Class c = defineFinderClass(name); + if (resolve) { + resolveClass(c); + } + return c; + } + return super.loadClass(name, resolve); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + if (finderClassNames.contains(name)) { + return defineFinderClass(name); + } + return super.findClass(name); + } + +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/META-INF/services/java.lang.System$LoggerFinder b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/META-INF/services/java.lang.System$LoggerFinder new file mode 100644 index 00000000000..843cae617f1 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/META-INF/services/java.lang.System$LoggerFinder @@ -0,0 +1 @@ +BaseDefaultLoggerFinderTest$BaseLoggerFinder diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/BaseLoggerBridgeTest/BaseLoggerBridgeTest.java b/jdk/test/java/lang/System/LoggerFinder/internal/BaseLoggerBridgeTest/BaseLoggerBridgeTest.java new file mode 100644 index 00000000000..5319f0e5fbf --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseLoggerBridgeTest/BaseLoggerBridgeTest.java @@ -0,0 +1,1058 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Queue; +import java.util.ResourceBundle; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; +import sun.util.logging.PlatformLogger; +import java.lang.System.LoggerFinder; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.util.stream.Stream; + +/** + * @test + * @bug 8140364 + * @summary JDK implementation specific unit test for JDK internal artifacts. + * Tests a naive implementation of System.Logger, and in particular + * the default mapping provided by PlatformLogger.Bridge. + * @modules java.base/sun.util.logging java.base/jdk.internal.logger + * @build CustomSystemClassLoader BaseLoggerBridgeTest + * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest NOSECURITY + * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest NOPERMISSIONS + * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest WITHPERMISSIONS + * @author danielfuchs + */ +public class BaseLoggerBridgeTest { + + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + final static AtomicLong sequencer = new AtomicLong(); + final static boolean VERBOSE = false; + // whether the implementation of Logger try to do a best + // effort for logp... Our base logger finder stub doesn't + // support logp, and thus the logp() implementation comes from + // LoggerWrapper - which does a best effort. + static final boolean BEST_EFFORT_FOR_LOGP = true; + static final ThreadLocal allowControl = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAccess = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAll = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + + static final Class providerClass; + static { + try { + providerClass = ClassLoader.getSystemClassLoader().loadClass("BaseLoggerBridgeTest$BaseLoggerFinder"); + } catch (ClassNotFoundException ex) { + throw new ExceptionInInitializerError(ex); + } + } + + static final sun.util.logging.PlatformLogger.Level[] julLevels = { + sun.util.logging.PlatformLogger.Level.ALL, + sun.util.logging.PlatformLogger.Level.FINEST, + sun.util.logging.PlatformLogger.Level.FINER, + sun.util.logging.PlatformLogger.Level.FINE, + sun.util.logging.PlatformLogger.Level.CONFIG, + sun.util.logging.PlatformLogger.Level.INFO, + sun.util.logging.PlatformLogger.Level.WARNING, + sun.util.logging.PlatformLogger.Level.SEVERE, + sun.util.logging.PlatformLogger.Level.OFF, + }; + + static final Level[] mappedLevels = { + Level.ALL, // ALL + Level.TRACE, // FINEST + Level.TRACE, // FINER + Level.DEBUG, // FINE + Level.DEBUG, // CONFIG + Level.INFO, // INFO + Level.WARNING, // WARNING + Level.ERROR, // SEVERE + Level.OFF, // OFF + }; + + final static Map julToSpiMap; + static { + Map map = new HashMap<>(); + if (mappedLevels.length != julLevels.length) { + throw new ExceptionInInitializerError("Array lengths differ" + + "\n\tjulLevels=" + Arrays.deepToString(julLevels) + + "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels)); + } + for (int i=0; i map = new ConcurrentHashMap<>(); + + @Override + protected Object handleGetObject(String key) { + if (key.contains(" (translated)")) { + throw new RuntimeException("Unexpected key: " + key); + } + return map.computeIfAbsent(key, k -> k + " (translated)"); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(map.keySet()); + } + + } + public static class MyLoggerBundle extends MyBundle { + + } + + public static interface TestLoggerFinder { + final ConcurrentHashMap system = new ConcurrentHashMap<>(); + final ConcurrentHashMap user = new ConcurrentHashMap<>(); + public Queue eventQueue = new ArrayBlockingQueue<>(128); + + public static final class LogEvent implements Cloneable { + + public LogEvent() { + this(sequencer.getAndIncrement()); + } + + LogEvent(long sequenceNumber) { + this.sequenceNumber = sequenceNumber; + } + + boolean callSupplier = false; + long sequenceNumber; + boolean isLoggable; + String loggerName; + Level level; + ResourceBundle bundle; + Throwable thrown; + Object[] args; + Supplier supplier; + String msg; + + Object[] toArray(boolean callSupplier) { + return new Object[] { + sequenceNumber, + isLoggable, + loggerName, + level, + bundle, + thrown, + args, + callSupplier && supplier != null ? supplier.get() : supplier, + msg, + }; + } + + boolean callSupplier(Object obj) { + return callSupplier || ((LogEvent)obj).callSupplier; + } + + @Override + public String toString() { + return Arrays.deepToString(toArray(false)); + } + + + + @Override + public boolean equals(Object obj) { + return obj instanceof LogEvent + && Objects.deepEquals(toArray(callSupplier(obj)), ((LogEvent)obj).toArray(callSupplier(obj))); + } + + @Override + public int hashCode() { + return Objects.hash(toArray(true)); + } + + public LogEvent cloneWith(long sequenceNumber) + throws CloneNotSupportedException { + LogEvent cloned = (LogEvent)super.clone(); + cloned.sequenceNumber = sequenceNumber; + return cloned; + } + + public static LogEvent of(boolean isLoggable, String name, + Level level, ResourceBundle bundle, + String key, Throwable thrown) { + LogEvent evt = new LogEvent(); + evt.isLoggable = isLoggable; + evt.loggerName = name; + evt.level = level; + evt.args = null; + evt.bundle = bundle; + evt.thrown = thrown; + evt.supplier = null; + evt.msg = key; + return evt; + } + + public static LogEvent of(boolean isLoggable, String name, + Level level, Throwable thrown, Supplier supplier) { + LogEvent evt = new LogEvent(); + evt.isLoggable = isLoggable; + evt.loggerName = name; + evt.level = level; + evt.args = null; + evt.bundle = null; + evt.thrown = thrown; + evt.supplier = supplier; + evt.msg = null; + return evt; + } + + public static LogEvent of(boolean isLoggable, String name, + Level level, ResourceBundle bundle, + String key, Object... params) { + LogEvent evt = new LogEvent(); + evt.isLoggable = isLoggable; + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = null; + evt.supplier = null; + evt.msg = key; + return evt; + } + + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + Level level, ResourceBundle bundle, + String key, Supplier supplier, + Throwable thrown, Object... params) { + LogEvent evt = new LogEvent(sequenceNumber); + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = thrown; + evt.supplier = supplier; + evt.msg = key; + evt.isLoggable = isLoggable; + return evt; + } + + public static LogEvent ofp(boolean callSupplier, LogEvent evt) { + evt.callSupplier = callSupplier; + return evt; + } + } + + public class LoggerImpl implements Logger { + private final String name; + private Level level = Level.INFO; + + public LoggerImpl(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isLoggable(Level level) { + return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity(); + } + + @Override + public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) { + log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown)); + } + + @Override + public void log(Level level, ResourceBundle bundle, String format, Object... params) { + log(LogEvent.of(isLoggable(level), name, level, bundle, format, params)); + } + + void log(LogEvent event) { + eventQueue.add(event); + } + + @Override + public void log(Level level, Supplier msgSupplier) { + log(LogEvent.of(isLoggable(level), name, level, null, msgSupplier)); + } + + @Override + public void log(Level level, Supplier msgSupplier, Throwable thrown) { + log(LogEvent.of(isLoggable(level), name, level, thrown, msgSupplier)); + } + + + + } + + public Logger getLogger(String name, Class caller); + public Logger getLocalizedLogger(String name, ResourceBundle bundle, Class caller); + } + + public static class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder { + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + @Override + public Logger getLogger(String name, Class caller) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(LOGGERFINDER_PERMISSION); + } + PrivilegedAction pa = () -> caller.getClassLoader(); + ClassLoader callerLoader = AccessController.doPrivileged(pa); + if (callerLoader == null) { + return system.computeIfAbsent(name, (n) -> new LoggerImpl(n)); + } else { + return user.computeIfAbsent(name, (n) -> new LoggerImpl(n)); + } + } + } + + static PlatformLogger.Bridge convert(Logger logger) { + boolean old = allowAll.get().get(); + allowAccess.get().set(true); + try { + return PlatformLogger.Bridge.convert(logger); + } finally { + allowAccess.get().set(old); + } + } + + static Logger getLogger(String name, Class caller) { + boolean old = allowAll.get().get(); + allowAccess.get().set(true); + try { + return jdk.internal.logger.LazyLoggers.getLogger(name, caller); + } finally { + allowAccess.get().set(old); + } + } + + static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS}; + + static void setSecurityManager() { + if (System.getSecurityManager() == null) { + // Ugly test hack: preload the resources needed by String.format + // We need to do that before setting the security manager + // because our implementation of CustomSystemClassLoader + // doesn't have the required permission. + System.out.println(String.format("debug: %s", "Setting security manager")); + Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll)); + System.setSecurityManager(new SecurityManager()); + } + } + + public static void main(String[] args) { + if (args.length == 0) + args = new String[] { + "NOSECURITY", + "NOPERMISSIONS", + "WITHPERMISSIONS" + }; + + + Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> { + TestLoggerFinder provider; + switch (testCase) { + case NOSECURITY: + System.out.println("\n*** Without Security Manager\n"); + provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder()); + test(provider, true); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case NOPERMISSIONS: + System.out.println("\n*** With Security Manager, without permissions\n"); + setSecurityManager(); + try { + provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder()); + throw new RuntimeException("Expected exception not raised"); + } catch (AccessControlException x) { + if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) { + throw new RuntimeException("Unexpected permission check", x); + } + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder()); + } finally { + allowControl.get().set(control); + } + } + test(provider, false); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case WITHPERMISSIONS: + System.out.println("\n*** With Security Manager, with control permission\n"); + setSecurityManager(); + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder()); + test(provider, true); + } finally { + allowControl.get().set(control); + } + break; + default: + throw new RuntimeException("Unknown test case: " + testCase); + } + }); + System.out.println("\nPASSED: Tested " + sequencer.get() + " cases."); + } + + public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) { + + ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName()); + final Map loggerDescMap = new HashMap<>(); + + + TestLoggerFinder.LoggerImpl appSink = null; + try { + appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class)); + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a system logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for logger: " + acx); + boolean old = allowControl.get().get(); + allowControl.get().set(true); + try { + appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class)); + } finally { + allowControl.get().set(old); + } + } + + + TestLoggerFinder.LoggerImpl sysSink = null; + try { + sysSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class)); + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a system logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for system logger: " + acx); + } + if (hasRequiredPermissions && appSink == sysSink) { + throw new RuntimeException("identical loggers"); + } + + if (provider.system.contains(appSink)) { + throw new RuntimeException("app logger in system map"); + } + if (!provider.user.contains(appSink)) { + throw new RuntimeException("app logger not in appplication map"); + } + if (hasRequiredPermissions && provider.user.contains(sysSink)) { + throw new RuntimeException("sys logger in appplication map"); + } + if (hasRequiredPermissions && !provider.system.contains(sysSink)) { + throw new RuntimeException("sys logger not in system map"); + } + + Logger appLogger1 = System.getLogger("foo"); + loggerDescMap.put(appLogger1, "System.getLogger(\"foo\")"); + PlatformLogger.Bridge bridge = convert(appLogger1); + loggerDescMap.putIfAbsent(bridge, "PlatformLogger.Bridge.convert(System.getLogger(\"foo\"))"); + testLogger(provider, loggerDescMap, "foo", null, bridge, appSink); + + Logger sysLogger1 = null; + try { + sysLogger1 = getLogger("foo", Thread.class); + loggerDescMap.put(sysLogger1, + "jdk.internal.logger.LazyLoggers.getLogger(\"foo\", Thread.class)"); + + if (!hasRequiredPermissions) { + // check that the provider would have thrown an exception + provider.getLogger("foo", Thread.class); + throw new RuntimeException("Managed to obtain a system logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for system logger: " + acx); + } + + if (hasRequiredPermissions) { + // if we don't have permissions sysSink will be null. + testLogger(provider, loggerDescMap, "foo", null, + PlatformLogger.Bridge.convert(sysLogger1), sysSink); + } + + Logger appLogger2 = + System.getLogger("foo", loggerBundle); + loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)"); + + if (appLogger2 == appLogger1) { + throw new RuntimeException("identical loggers"); + } + + if (provider.system.contains(appLogger2)) { + throw new RuntimeException("localized app logger in system map"); + } + if (provider.user.contains(appLogger2)) { + throw new RuntimeException("localized app logger in appplication map"); + } + + testLogger(provider, loggerDescMap, "foo", loggerBundle, + PlatformLogger.Bridge.convert(appLogger2), appSink); + + Logger sysLogger2 = null; + try { + sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class); + loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class)"); + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a system logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for localized system logger: " + acx); + } + if (hasRequiredPermissions && appLogger2 == sysLogger2) { + throw new RuntimeException("identical loggers"); + } + if (hasRequiredPermissions && sysLogger2 == sysLogger1) { + throw new RuntimeException("identical loggers"); + } + if (hasRequiredPermissions && provider.user.contains(sysLogger2)) { + throw new RuntimeException("localized sys logger in appplication map"); + } + if (hasRequiredPermissions && provider.system.contains(sysLogger2)) { + throw new RuntimeException("localized sys logger not in system map"); + } + + if (hasRequiredPermissions) { + // if we don't have permissions sysSink will be null. + testLogger(provider, loggerDescMap, "foo", loggerBundle, + PlatformLogger.Bridge.convert(sysLogger2), sysSink); + } + + } + + public static class Foo { + + } + + static void verbose(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + + static void checkLogEvent(TestLoggerFinder provider, String desc, + TestLoggerFinder.LogEvent expected) { + TestLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!Objects.equals(expected, actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + + static void checkLogEvent(TestLoggerFinder provider, String desc, + TestLoggerFinder.LogEvent expected, boolean expectNotNull) { + TestLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (actual == null && !expectNotNull) return; + if (actual != null && !expectNotNull) { + throw new RuntimeException("Unexpected log event found for " + desc + + "\n\tgot: " + actual); + } + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + + static Supplier logpMessage(ResourceBundle bundle, + String className, String methodName, Supplier msg) { + if (BEST_EFFORT_FOR_LOGP && bundle == null + && (className != null || methodName != null)) { + final String cName = className == null ? "" : className; + final String mName = methodName == null ? "" : methodName; + return () -> String.format("[%s %s] %s", cName, mName, msg.get()); + } else { + return msg; + } + } + + static String logpMessage(ResourceBundle bundle, + String className, String methodName, String msg) { + if (BEST_EFFORT_FOR_LOGP && bundle == null + && (className != null || methodName != null)) { + final String cName = className == null ? "" : className; + final String mName = methodName == null ? "" : methodName; + return String.format("[%s %s] %s", cName, mName, msg == null ? "" : msg); + } else { + return msg; + } + } + + // Calls the methods defined on LogProducer and verify the + // parameters received by the underlying TestLoggerFinder.LoggerImpl + // logger. + private static void testLogger(TestLoggerFinder provider, + Map loggerDescMap, + String name, + ResourceBundle loggerBundle, + PlatformLogger.Bridge logger, + TestLoggerFinder.LoggerImpl sink) { + + if (loggerDescMap.get(logger) == null) { + throw new RuntimeException("Test bug: Missing description"); + } + System.out.println("Testing " + loggerDescMap.get(logger) +" [" + logger + "]"); + + Foo foo = new Foo(); + String fooMsg = foo.toString(); + System.out.println("\tlogger.log(messageLevel, fooMsg)"); + System.out.println("\tlogger.(fooMsg)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.log(messageLevel, fooMsg): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0, + name, expectedMessageLevel, loggerBundle, + fooMsg, null, (Throwable)null, (Object[])null); + logger.log(messageLevel, fooMsg); + checkLogEvent(provider, desc, expected); + } + } + + Supplier supplier = new Supplier() { + @Override + public String get() { + return this.toString(); + } + }; + System.out.println("\tlogger.log(messageLevel, supplier)"); + System.out.println("\tlogger.(supplier)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.log(messageLevel, supplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0, + name, expectedMessageLevel, (ResourceBundle) null, + null, supplier, (Throwable)null, (Object[])null); + logger.log(messageLevel, supplier); + checkLogEvent(provider, desc, expected); + } + } + + String format = "two params [{1} {2}]"; + Object arg1 = foo; + Object arg2 = fooMsg; + System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0, + name, expectedMessageLevel, loggerBundle, + format, null, (Throwable)null, arg1, arg2); + logger.log(messageLevel, format, arg1, arg2); + checkLogEvent(provider, desc, expected); + } + } + + Throwable thrown = new Exception("OK: log me!"); + System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0, + name, expectedMessageLevel, loggerBundle, + fooMsg, null, thrown, (Object[])null); + logger.log(messageLevel, fooMsg, thrown); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.log(messageLevel, thrown, supplier)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0, + name, expectedMessageLevel, (ResourceBundle)null, + null, supplier, thrown, (Object[])null); + logger.log(messageLevel, thrown, supplier); + checkLogEvent(provider, desc, expected); + } + } + + String sourceClass = "blah.Blah"; + String sourceMethod = "blih"; + System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0; + TestLoggerFinder.LogEvent expected = + isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP? + TestLoggerFinder.LogEvent.of( + sequencer.get(), + isLoggable, + name, expectedMessageLevel, loggerBundle, + logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg), + null, (Throwable)null, (Object[]) null) : null; + logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0; + TestLoggerFinder.LogEvent expected = isLoggable ? + TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP, + TestLoggerFinder.LogEvent.of( + sequencer.get(), + isLoggable, + name, expectedMessageLevel, null, null, + logpMessage(null, sourceClass, sourceMethod, supplier), + (Throwable)null, (Object[]) null)) : null; + logger.logp(messageLevel, sourceClass, sourceMethod, supplier); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0; + TestLoggerFinder.LogEvent expected = + isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP? + TestLoggerFinder.LogEvent.of( + sequencer.get(), + loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0, + name, expectedMessageLevel, loggerBundle, + logpMessage(loggerBundle, sourceClass, sourceMethod, format), + null, (Throwable)null, arg1, arg2) : null; + logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0; + TestLoggerFinder.LogEvent expected = + isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP ? + TestLoggerFinder.LogEvent.of( + sequencer.get(), + loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0, + name, expectedMessageLevel, loggerBundle, + logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg), + null, thrown, (Object[])null) : null; + logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0; + TestLoggerFinder.LogEvent expected = isLoggable ? + TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP, + TestLoggerFinder.LogEvent.of( + sequencer.get(), + loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0, + name, expectedMessageLevel, null, null, + logpMessage(null, sourceClass, sourceMethod, supplier), + thrown, (Object[])null)) : null; + logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier); + checkLogEvent(provider, desc, expected); + } + } + + ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName()); + System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0, + name, expectedMessageLevel, bundle, + format, null, (Throwable)null, arg1, arg2); + logger.logrb(messageLevel, bundle, format, arg1, arg2); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0, + name, expectedMessageLevel, bundle, + fooMsg, null, thrown, (Object[])null); + logger.logrb(messageLevel, bundle, fooMsg, thrown); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0, + name, expectedMessageLevel, bundle, + format, null, (Throwable)null, arg1, arg2); + logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0, + name, expectedMessageLevel, bundle, + fooMsg, null, thrown, (Object[])null); + logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, fooMsg, thrown); + checkLogEvent(provider, desc, expected); + } + } + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION; + final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger"); + final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging"); + + final Permissions permissions; + final Permissions allPermissions; + final ThreadLocal allowControl; + final ThreadLocal allowAccess; + final ThreadLocal allowAll; + public SimplePolicy(ThreadLocal allowControl, + ThreadLocal allowAccess, + ThreadLocal allowAll) { + this.allowControl = allowControl; + this.allowAccess = allowAccess; + this.allowAll = allowAll; + permissions = new Permissions(); + allPermissions = new PermissionsBuilder() + .add(new java.security.AllPermission()) + .toPermissions(); + } + + Permissions getPermissions() { + if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) { + PermissionsBuilder builder = new PermissionsBuilder() + .addAll(permissions); + if (allowControl.get().get()) { + builder.add(CONTROL); + } + if (allowAccess.get().get()) { + builder.add(ACCESS_LOGGER); + builder.add(ACCESS_LOGGING); + } + if (allowAll.get().get()) { + builder.addAll(allPermissions); + } + return builder.toPermissions(); + } + return permissions; + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + return getPermissions().implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + } +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/BaseLoggerBridgeTest/CustomSystemClassLoader.java b/jdk/test/java/lang/System/LoggerFinder/internal/BaseLoggerBridgeTest/CustomSystemClassLoader.java new file mode 100644 index 00000000000..f903b43dba2 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseLoggerBridgeTest/CustomSystemClassLoader.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.security.AllPermission; +import java.security.Permissions; +import java.security.ProtectionDomain; + + +/** + * A custom ClassLoader to load the concrete LoggerFinder class + * with all permissions. + * + * @author danielfuchs + */ +public class CustomSystemClassLoader extends ClassLoader { + + + Class finderClass = null; + + public CustomSystemClassLoader() { + super(); + } + public CustomSystemClassLoader(ClassLoader parent) { + super(parent); + } + + private Class defineFinderClass(String name) + throws ClassNotFoundException { + final Object obj = getClassLoadingLock(name); + synchronized(obj) { + if (finderClass != null) return finderClass; + + URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation(); + File file = new File(url.getPath(), name+".class"); + if (file.canRead()) { + try { + byte[] b = Files.readAllBytes(file.toPath()); + Permissions perms = new Permissions(); + perms.add(new AllPermission()); + finderClass = defineClass( + name, b, 0, b.length, new ProtectionDomain( + this.getClass().getProtectionDomain().getCodeSource(), + perms)); + System.out.println("Loaded " + name); + return finderClass; + } catch (Throwable ex) { + ex.printStackTrace(); + throw new ClassNotFoundException(name, ex); + } + } else { + throw new ClassNotFoundException(name, + new IOException(file.toPath() + ": can't read")); + } + } + } + + @Override + public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (name.endsWith("$BaseLoggerFinder")) { + Class c = defineFinderClass(name); + if (resolve) { + resolveClass(c); + } + return c; + } + return super.loadClass(name, resolve); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + if (name.endsWith("$BaseLoggerFinder")) { + return defineFinderClass(name); + } + return super.findClass(name); + } + +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/BaseLoggerBridgeTest/META-INF/services/java.lang.System$LoggerFinder b/jdk/test/java/lang/System/LoggerFinder/internal/BaseLoggerBridgeTest/META-INF/services/java.lang.System$LoggerFinder new file mode 100644 index 00000000000..4fc04019eb7 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseLoggerBridgeTest/META-INF/services/java.lang.System$LoggerFinder @@ -0,0 +1 @@ +BaseLoggerBridgeTest$BaseLoggerFinder diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/BasePlatformLoggerTest/BasePlatformLoggerTest.java b/jdk/test/java/lang/System/LoggerFinder/internal/BasePlatformLoggerTest/BasePlatformLoggerTest.java new file mode 100644 index 00000000000..da8c2d73b16 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/BasePlatformLoggerTest/BasePlatformLoggerTest.java @@ -0,0 +1,732 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.security.AccessController; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Queue; +import java.util.ResourceBundle; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; +import java.lang.System.LoggerFinder; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.security.AccessControlException; +import java.util.stream.Stream; +import sun.util.logging.PlatformLogger; + +/** + * @test + * @bug 8140364 + * @summary JDK implementation specific unit test for JDK internal API. + * Tests a naive implementation of System.Logger, and in particular + * the default mapping provided by PlatformLogger. + * @modules java.base/sun.util.logging + * @build CustomSystemClassLoader BasePlatformLoggerTest + * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader BasePlatformLoggerTest NOSECURITY + * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader BasePlatformLoggerTest NOPERMISSIONS + * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader BasePlatformLoggerTest WITHPERMISSIONS + * @author danielfuchs + */ +public class BasePlatformLoggerTest { + + public static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + + final static AtomicLong sequencer = new AtomicLong(); + final static boolean VERBOSE = false; + static final ThreadLocal allowControl = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAccess = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAll = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + + static final Class providerClass; + static { + try { + providerClass = ClassLoader.getSystemClassLoader().loadClass("BasePlatformLoggerTest$BaseLoggerFinder"); + } catch (ClassNotFoundException ex) { + throw new ExceptionInInitializerError(ex); + } + } + + static final PlatformLogger.Level[] julLevels = { + PlatformLogger.Level.ALL, + PlatformLogger.Level.FINEST, + PlatformLogger.Level.FINER, + PlatformLogger.Level.FINE, + PlatformLogger.Level.CONFIG, + PlatformLogger.Level.INFO, + PlatformLogger.Level.WARNING, + PlatformLogger.Level.SEVERE, + PlatformLogger.Level.OFF, + }; + + static final Level[] mappedLevels = { + Level.ALL, // ALL + Level.TRACE, // FINEST + Level.TRACE, // FINER + Level.DEBUG, // FINE + Level.DEBUG, // CONFIG + Level.INFO, // INFO + Level.WARNING, // WARNING + Level.ERROR, // SEVERE + Level.OFF, // OFF + }; + + final static Map julToSpiMap; + static { + Map map = new HashMap<>(); + if (mappedLevels.length != julLevels.length) { + throw new ExceptionInInitializerError("Array lengths differ" + + "\n\tjulLevels=" + Arrays.deepToString(julLevels) + + "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels)); + } + for (int i=0; i map = new ConcurrentHashMap<>(); + + @Override + protected Object handleGetObject(String key) { + if (key.contains(" (translated)")) { + throw new RuntimeException("Unexpected key: " + key); + } + return map.computeIfAbsent(key, k -> k + " (translated)"); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(map.keySet()); + } + + } + public static class MyLoggerBundle extends MyBundle { + + } + + + public static interface TestLoggerFinder { + final ConcurrentHashMap system = new ConcurrentHashMap<>(); + final ConcurrentHashMap user = new ConcurrentHashMap<>(); + public Queue eventQueue = new ArrayBlockingQueue<>(128); + + public static final class LogEvent implements Cloneable { + + public LogEvent() { + this(sequencer.getAndIncrement()); + } + + LogEvent(long sequenceNumber) { + this.sequenceNumber = sequenceNumber; + } + + long sequenceNumber; + boolean isLoggable; + String loggerName; + Level level; + ResourceBundle bundle; + Throwable thrown; + Object[] args; + Supplier supplier; + String msg; + + Object[] toArray() { + return new Object[] { + sequenceNumber, + isLoggable, + loggerName, + level, + bundle, + thrown, + args, + supplier, + msg, + }; + } + + @Override + public String toString() { + return Arrays.deepToString(toArray()); + } + + + + @Override + public boolean equals(Object obj) { + return obj instanceof LogEvent + && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray()); + } + + @Override + public int hashCode() { + return Objects.hash(toArray()); + } + + public LogEvent cloneWith(long sequenceNumber) + throws CloneNotSupportedException { + LogEvent cloned = (LogEvent)super.clone(); + cloned.sequenceNumber = sequenceNumber; + return cloned; + } + + public static LogEvent of(boolean isLoggable, String name, + Level level, ResourceBundle bundle, + String key, Throwable thrown) { + LogEvent evt = new LogEvent(); + evt.isLoggable = isLoggable; + evt.loggerName = name; + evt.level = level; + evt.args = null; + evt.bundle = bundle; + evt.thrown = thrown; + evt.supplier = null; + evt.msg = key; + return evt; + } + + public static LogEvent of(boolean isLoggable, String name, + Level level, Throwable thrown, Supplier supplier) { + LogEvent evt = new LogEvent(); + evt.isLoggable = isLoggable; + evt.loggerName = name; + evt.level = level; + evt.args = null; + evt.bundle = null; + evt.thrown = thrown; + evt.supplier = supplier; + evt.msg = null; + return evt; + } + + public static LogEvent of(boolean isLoggable, String name, + Level level, ResourceBundle bundle, + String key, Object... params) { + LogEvent evt = new LogEvent(); + evt.isLoggable = isLoggable; + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = null; + evt.supplier = null; + evt.msg = key; + return evt; + } + + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + Level level, ResourceBundle bundle, + String key, Supplier supplier, + Throwable thrown, Object... params) { + LogEvent evt = new LogEvent(sequenceNumber); + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = thrown; + evt.supplier = supplier; + evt.msg = key; + evt.isLoggable = isLoggable; + return evt; + } + + } + + public class LoggerImpl implements Logger { + private final String name; + private Level level = Level.INFO; + + public LoggerImpl(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isLoggable(Level level) { + return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity(); + } + + @Override + public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) { + log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown)); + } + + @Override + public void log(Level level, ResourceBundle bundle, String format, Object... params) { + log(LogEvent.of(isLoggable(level), name, level, bundle, format, params)); + } + + void log(LogEvent event) { + eventQueue.add(event); + } + + @Override + public void log(Level level, Supplier msgSupplier) { + log(LogEvent.of(isLoggable(level), name, level, null, msgSupplier)); + } + + @Override + public void log(Level level, Supplier msgSupplier, Throwable thrown) { + log(LogEvent.of(isLoggable(level), name, level, thrown, msgSupplier)); + } + } + + public Logger getLogger(String name, Class caller); + } + + public static class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder { + @Override + public Logger getLogger(String name, Class caller) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(LOGGERFINDER_PERMISSION); + } + PrivilegedAction pa = () -> caller.getClassLoader(); + ClassLoader callerLoader = AccessController.doPrivileged(pa); + if (callerLoader == null) { + return system.computeIfAbsent(name, (n) -> new LoggerImpl(n)); + } else { + return user.computeIfAbsent(name, (n) -> new LoggerImpl(n)); + } + } + } + + static PlatformLogger getPlatformLogger(String name) { + boolean old = allowAccess.get().get(); + allowAccess.get().set(true); + try { + return PlatformLogger.getLogger(name); + } finally { + allowAccess.get().set(old); + } + } + + static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS}; + + static void setSecurityManager() { + if (System.getSecurityManager() == null) { + Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll)); + System.setSecurityManager(new SecurityManager()); + } + } + + public static void main(String[] args) { + if (args.length == 0) + args = new String[] { + "NOSECURITY", + "NOPERMISSIONS", + "WITHPERMISSIONS" + }; + + + Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> { + TestLoggerFinder provider; + switch (testCase) { + case NOSECURITY: + System.out.println("\n*** Without Security Manager\n"); + provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder()); + test(provider, true); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case NOPERMISSIONS: + System.out.println("\n*** With Security Manager, without permissions\n"); + setSecurityManager(); + try { + provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder()); + throw new RuntimeException("Expected exception not raised"); + } catch (AccessControlException x) { + if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) { + throw new RuntimeException("Unexpected permission check", x); + } + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder()); + } finally { + allowControl.get().set(control); + } + } + test(provider, false); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case WITHPERMISSIONS: + System.out.println("\n*** With Security Manager, with control permission\n"); + setSecurityManager(); + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder()); + test(provider, true); + } finally { + allowControl.get().set(control); + } + break; + default: + throw new RuntimeException("Unknown test case: " + testCase); + } + }); + System.out.println("\nPASSED: Tested " + sequencer.get() + " cases."); + } + + public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) { + + final Map loggerDescMap = new HashMap<>(); + + TestLoggerFinder.LoggerImpl appSink; + boolean before = allowControl.get().get(); + try { + allowControl.get().set(true); + appSink = TestLoggerFinder.LoggerImpl.class.cast( + provider.getLogger("foo", BasePlatformLoggerTest.class)); + } finally { + allowControl.get().set(before); + } + + TestLoggerFinder.LoggerImpl sysSink = null; + before = allowControl.get().get(); + try { + allowControl.get().set(true); + sysSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class)); + } finally { + allowControl.get().set(before); + } + + if (hasRequiredPermissions && appSink == sysSink) { + throw new RuntimeException("identical loggers"); + } + + if (provider.system.contains(appSink)) { + throw new RuntimeException("app logger in system map"); + } + if (!provider.user.contains(appSink)) { + throw new RuntimeException("app logger not in appplication map"); + } + if (hasRequiredPermissions && provider.user.contains(sysSink)) { + throw new RuntimeException("sys logger in appplication map"); + } + if (hasRequiredPermissions && !provider.system.contains(sysSink)) { + throw new RuntimeException("sys logger not in system map"); + } + + PlatformLogger platform = getPlatformLogger("foo"); + loggerDescMap.put(platform, "PlatformLogger.getLogger(\"foo\")"); + + testLogger(provider, loggerDescMap, "foo", null, platform, sysSink); + } + + public static class Foo { + + } + + static void verbose(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + + static void checkLogEvent(TestLoggerFinder provider, String desc, + TestLoggerFinder.LogEvent expected) { + TestLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + + static void checkLogEvent(TestLoggerFinder provider, String desc, + TestLoggerFinder.LogEvent expected, boolean expectNotNull) { + TestLoggerFinder.LogEvent actual = provider.eventQueue.poll(); + if (actual == null && !expectNotNull) return; + if (actual != null && !expectNotNull) { + throw new RuntimeException("Unexpected log event found for " + desc + + "\n\tgot: " + actual); + } + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + + // Calls the methods defined on LogProducer and verify the + // parameters received by the underlying TestLoggerFinder.LoggerImpl + // logger. + private static void testLogger(TestLoggerFinder provider, + Map loggerDescMap, + String name, + ResourceBundle loggerBundle, + PlatformLogger logger, + TestLoggerFinder.LoggerImpl sink) { + + System.out.println("Testing " + loggerDescMap.get(logger)); + + Foo foo = new Foo(); + String fooMsg = foo.toString(); + System.out.println("\tlogger.(fooMsg)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (PlatformLogger.Level messageLevel :julLevels) { + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0, + name, expectedMessageLevel, loggerBundle, + fooMsg, null, (Throwable)null, (Object[])null); + String desc2 = "logger." + messageLevel.toString().toLowerCase() + + "(fooMsg): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + if (messageLevel == PlatformLogger.Level.FINEST) { + logger.finest(fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.FINER) { + logger.finer(fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.FINE) { + logger.fine(fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.CONFIG) { + logger.config(fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.INFO) { + logger.info(fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.WARNING) { + logger.warning(fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.SEVERE) { + logger.severe(fooMsg); + checkLogEvent(provider, desc2, expected); + } + } + } + + Throwable thrown = new Exception("OK: log me!"); + System.out.println("\tlogger.(msg, thrown)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (PlatformLogger.Level messageLevel :julLevels) { + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0, + name, expectedMessageLevel, (ResourceBundle) null, + fooMsg, null, (Throwable)thrown, (Object[])null); + String desc2 = "logger." + messageLevel.toString().toLowerCase() + + "(msg, thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + if (messageLevel == PlatformLogger.Level.FINEST) { + logger.finest(fooMsg, thrown); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.FINER) { + logger.finer(fooMsg, thrown); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.FINE) { + logger.fine(fooMsg, thrown); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.CONFIG) { + logger.config(fooMsg, thrown); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.INFO) { + logger.info(fooMsg, thrown); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.WARNING) { + logger.warning(fooMsg, thrown); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.SEVERE) { + logger.severe(fooMsg, thrown); + checkLogEvent(provider, desc2, expected); + } + } + } + + String format = "two params [{1} {2}]"; + Object arg1 = foo; + Object arg2 = fooMsg; + System.out.println("\tlogger.(format, arg1, arg2)"); + for (Level loggerLevel : Level.values()) { + sink.level = loggerLevel; + for (PlatformLogger.Level messageLevel :julLevels) { + Level expectedMessageLevel = julToSpiMap.get(messageLevel); + TestLoggerFinder.LogEvent expected = + TestLoggerFinder.LogEvent.of( + sequencer.get(), + loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0, + name, expectedMessageLevel, (ResourceBundle) null, + format, null, (Throwable)null, foo, fooMsg); + String desc2 = "logger." + messageLevel.toString().toLowerCase() + + "(format, foo, fooMsg): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + if (messageLevel == PlatformLogger.Level.FINEST) { + logger.finest(format, foo, fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.FINER) { + logger.finer(format, foo, fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.FINE) { + logger.fine(format, foo, fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.CONFIG) { + logger.config(format, foo, fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.INFO) { + logger.info(format, foo, fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.WARNING) { + logger.warning(format, foo, fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == PlatformLogger.Level.SEVERE) { + logger.severe(format, foo, fooMsg); + checkLogEvent(provider, desc2, expected); + } + } + } + + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION; + final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging"); + + final Permissions permissions; + final Permissions allPermissions; + final ThreadLocal allowControl; + final ThreadLocal allowAccess; + final ThreadLocal allowAll; + public SimplePolicy(ThreadLocal allowControl, + ThreadLocal allowAccess, + ThreadLocal allowAll) { + this.allowControl = allowControl; + this.allowAccess = allowAccess; + this.allowAll = allowAll; + permissions = new Permissions(); + allPermissions = new PermissionsBuilder() + .add(new java.security.AllPermission()) + .toPermissions(); + } + + Permissions getPermissions() { + if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) { + PermissionsBuilder builder = new PermissionsBuilder() + .addAll(permissions); + if (allowControl.get().get()) { + builder.add(CONTROL); + } + if (allowAccess.get().get()) { + builder.add(ACCESS_LOGGING); + } + if (allowAll.get().get()) { + builder.addAll(allPermissions); + } + return builder.toPermissions(); + } + return permissions; + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + return getPermissions().implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + } +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/BasePlatformLoggerTest/CustomSystemClassLoader.java b/jdk/test/java/lang/System/LoggerFinder/internal/BasePlatformLoggerTest/CustomSystemClassLoader.java new file mode 100644 index 00000000000..f903b43dba2 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/BasePlatformLoggerTest/CustomSystemClassLoader.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.security.AllPermission; +import java.security.Permissions; +import java.security.ProtectionDomain; + + +/** + * A custom ClassLoader to load the concrete LoggerFinder class + * with all permissions. + * + * @author danielfuchs + */ +public class CustomSystemClassLoader extends ClassLoader { + + + Class finderClass = null; + + public CustomSystemClassLoader() { + super(); + } + public CustomSystemClassLoader(ClassLoader parent) { + super(parent); + } + + private Class defineFinderClass(String name) + throws ClassNotFoundException { + final Object obj = getClassLoadingLock(name); + synchronized(obj) { + if (finderClass != null) return finderClass; + + URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation(); + File file = new File(url.getPath(), name+".class"); + if (file.canRead()) { + try { + byte[] b = Files.readAllBytes(file.toPath()); + Permissions perms = new Permissions(); + perms.add(new AllPermission()); + finderClass = defineClass( + name, b, 0, b.length, new ProtectionDomain( + this.getClass().getProtectionDomain().getCodeSource(), + perms)); + System.out.println("Loaded " + name); + return finderClass; + } catch (Throwable ex) { + ex.printStackTrace(); + throw new ClassNotFoundException(name, ex); + } + } else { + throw new ClassNotFoundException(name, + new IOException(file.toPath() + ": can't read")); + } + } + } + + @Override + public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (name.endsWith("$BaseLoggerFinder")) { + Class c = defineFinderClass(name); + if (resolve) { + resolveClass(c); + } + return c; + } + return super.loadClass(name, resolve); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + if (name.endsWith("$BaseLoggerFinder")) { + return defineFinderClass(name); + } + return super.findClass(name); + } + +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/BasePlatformLoggerTest/META-INF/services/java.lang.System$LoggerFinder b/jdk/test/java/lang/System/LoggerFinder/internal/BasePlatformLoggerTest/META-INF/services/java.lang.System$LoggerFinder new file mode 100644 index 00000000000..7ce6e966935 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/BasePlatformLoggerTest/META-INF/services/java.lang.System$LoggerFinder @@ -0,0 +1 @@ +BasePlatformLoggerTest$BaseLoggerFinder diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/BootstrapLogger/BootstrapLoggerTest.java b/jdk/test/java/lang/System/LoggerFinder/internal/BootstrapLogger/BootstrapLoggerTest.java new file mode 100644 index 00000000000..4bbfe1e1f10 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/BootstrapLogger/BootstrapLoggerTest.java @@ -0,0 +1,430 @@ +/* + * 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. + * + * 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. + */ + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BooleanSupplier; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.AllPermission; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import jdk.internal.logger.BootstrapLogger; +import jdk.internal.logger.LazyLoggers; + +/* + * @test + * @bug 8140364 + * @author danielfuchs + * @summary JDK implementation specific unit test for JDK internal artifacts. + Tests the behavior of bootstrap loggers (and SimpleConsoleLoggers + * too). + * @modules java.base/jdk.internal.logger + * @run main/othervm BootstrapLoggerTest NO_SECURITY + * @run main/othervm BootstrapLoggerTest SECURE + * @run main/othervm/timeout=120 BootstrapLoggerTest SECURE_AND_WAIT + */ +public class BootstrapLoggerTest { + + static final Method awaitPending; + static final Method isAlive; + static final Field isBooted; + static final Field logManagerInitialized; + static { + try { + isBooted = BootstrapLogger.class.getDeclaredField("isBooted"); + isBooted.setAccessible(true); + // private reflection hook that allows us to test wait until all + // the tasks pending in the BootstrapExecutor are finished. + awaitPending = BootstrapLogger.class + .getDeclaredMethod("awaitPendingTasks"); + awaitPending.setAccessible(true); + // private reflection hook that allows us to test whether + // the BootstrapExecutor is alive. + isAlive = BootstrapLogger.class + .getDeclaredMethod("isAlive"); + isAlive.setAccessible(true); + // private reflection hook that allows us to test whether the LogManager + // has initialized and registered with the BootstrapLogger class + logManagerInitialized = BootstrapLogger.class + .getDeclaredField("logManagerConfigured"); + logManagerInitialized.setAccessible(true); + } catch (Exception ex) { + throw new ExceptionInInitializerError(ex); + } + } + + static void awaitPending() { + try { + awaitPending.invoke(null); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + ex.printStackTrace(LogStream.err); + } + } + + /** + * We use an instance of this class to check what the logging system has + * printed on System.err. + */ + public static class LogStream extends OutputStream { + + final static PrintStream err = System.err; + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + public LogStream() { + super(); + } + + @Override + public synchronized void write(int b) { + baos.write(b); + err.write(b); + } + + public String drain() { + awaitPending(); + synchronized(this) { + String txt = baos.toString(); + baos.reset(); + return txt; + } + } + } + + static enum TestCase { + NO_SECURITY, SECURE, SECURE_AND_WAIT + } + + public static void main(String[] args) throws Exception { + if (args == null || args.length == 0) { + args = new String[] { TestCase.SECURE_AND_WAIT.name() }; + } + if (args.length > 1) throw new RuntimeException("Only one argument allowed"); + TestCase test = TestCase.valueOf(args[0]); + System.err.println("Testing: " + test); + + + // private reflection hook that allows us to simulate a non booted VM + final AtomicBoolean vmBooted = new AtomicBoolean(false); + isBooted.set(null,(BooleanSupplier) () -> vmBooted.get()); + + // We replace System.err to check the messages that have been logged + // by the JUL ConsoleHandler and default SimpleConsoleLogger + // implementaion + final LogStream err = new LogStream(); + System.setErr(new PrintStream(err)); + + if (BootstrapLogger.isBooted()) { + throw new RuntimeException("VM should not be booted!"); + } + Logger logger = LazyLoggers.getLogger("foo.bar", Thread.class); + + if (test != TestCase.NO_SECURITY) { + LogStream.err.println("Setting security manager"); + Policy.setPolicy(new SimplePolicy()); + System.setSecurityManager(new SecurityManager()); + } + + Level[] levels = {Level.INFO, Level.WARNING, Level.INFO}; + int index = 0; + logger.log(levels[index], "Early message #" + (index+1)); index++; + logger.log(levels[index], "Early message #" + (index+1)); index++; + LogStream.err.println("VM Booted: " + vmBooted.get()); + LogStream.err.println("LogManager initialized: " + logManagerInitialized.get(null)); + logger.log(levels[index], "Early message #" + (index+1)); index++; + if (err.drain().contains("Early message")) { + // We're expecting that logger will be a LazyLogger wrapping a + // BootstrapLogger. The Bootstrap logger will stack the log messages + // it receives until the VM is booted. + // Since our private hook pretend that the VM is not booted yet, + // the logged messages shouldn't have reached System.err yet. + throw new RuntimeException("Early message logged while VM is not booted!"); + } + + // Now pretend that the VM is booted. Nothing should happen yet, until + // we try to log a new message. + vmBooted.getAndSet(true); + LogStream.err.println("VM Booted: " + vmBooted.get()); + LogStream.err.println("LogManager initialized: " + logManagerInitialized.get(null)); + if (!BootstrapLogger.isBooted()) { + throw new RuntimeException("VM should now be booted!"); + } + if (((Boolean)logManagerInitialized.get(null)).booleanValue()) { + throw new RuntimeException("LogManager shouldn't be initialized yet!"); + } + + // Logging a message should cause the BootstrapLogger to replace itself + // by a 'real' logger in the LazyLogger. But since the LogManager isn't + // initialized yet, this should be a SimpleConsoleLogger... + logger.log(Level.INFO, "LOG#4: VM now booted: {0}", vmBooted.get()); + logger.log(Level.DEBUG, "LOG#5: hi!"); + SimplePolicy.allowAll.set(Boolean.TRUE); + WeakReference threadRef = null; + ReferenceQueue queue = new ReferenceQueue<>(); + try { + Set set = Thread.getAllStackTraces().keySet().stream() + .filter((t) -> t.getName().startsWith("BootstrapMessageLoggerTask-")) + .collect(Collectors.toSet()); + set.stream().forEach(t -> LogStream.err.println("Found: " + t)); + if (set.size() > 1) { + throw new RuntimeException("Too many bootsrap threads found"); + } + Optional t = set.stream().findFirst(); + if (t.isPresent()) { + threadRef = new WeakReference<>(t.get(), queue); + } + } finally{ + SimplePolicy.allowAll.set(Boolean.FALSE); + } + if (!BootstrapLogger.isBooted()) { + throw new RuntimeException("VM should still be booted!"); + } + if (((Boolean)logManagerInitialized.get(null)).booleanValue()) { + throw new RuntimeException("LogManager shouldn't be initialized yet!"); + } + + // Now check that the early messages we had printed before the VM was + // booted have appeared on System.err... + String afterBoot = err.drain(); + for (int i=0; i loggerClass = Class.forName("java.util.logging.Logger"); + Class levelClass = Class.forName("java.util.logging.Level"); + Class handlerClass = Class.forName("java.util.logging.Handler"); + + // java.util.logging.Logger.getLogger("foo") + // .setLevel(java.util.logging.Level.FINEST); + Object fooLogger = loggerClass.getMethod("getLogger", String.class) + .invoke(null, "foo"); + loggerClass.getMethod("setLevel", levelClass) + .invoke(fooLogger, levelClass.getField("FINEST").get(null)); + + // java.util.logging.Logger.getLogger("").getHandlers()[0] + // .setLevel(java.util.logging.Level.ALL); + Object rootLogger = loggerClass.getMethod("getLogger", String.class) + .invoke(null, ""); + Object handlers = loggerClass.getMethod("getHandlers"). + invoke(rootLogger); + handlerClass.getMethod("setLevel", levelClass) + .invoke(Array.get(handlers, 0), levelClass.getField("ALL") + .get(null)); + + hasJUL = true; + } catch (ClassNotFoundException x) { + LogStream.err.println("JUL is not present: class " + x.getMessage() + + " not found"); + hasJUL = false; + } finally { + SimplePolicy.allowAll.set(Boolean.FALSE); + } + + logger.log(Level.DEBUG, "hi now!"); + String debug = err.drain(); + if (hasJUL) { + if (!((Boolean)logManagerInitialized.get(null)).booleanValue()) { + throw new RuntimeException("LogManager should be initialized now!"); + } + if (!debug.contains("FINE: hi now!")) { + throw new RuntimeException("System.err does not contain: " + + "FINE: hi now!"); + } + } else { + if (debug.contains("hi now!")) { + throw new RuntimeException("System.err contains: " + "hi now!"); + } + if (((Boolean)logManagerInitialized.get(null)).booleanValue()) { + throw new RuntimeException("LogManager shouldn't be initialized yet!"); + } + Logger baz = System.getLogger("foo.bar.baz"); + if (((Boolean)logManagerInitialized.get(null)).booleanValue()) { + throw new RuntimeException("LogManager shouldn't be initialized yet!"); + } + } + Logger bazbaz = null; + SimplePolicy.allowAll.set(Boolean.TRUE); + try { + bazbaz = java.lang.System.LoggerFinder + .getLoggerFinder().getLogger("foo.bar.baz.baz", BootstrapLoggerTest.class); + } finally { + SimplePolicy.allowAll.set(Boolean.FALSE); + } + if (!((Boolean)logManagerInitialized.get(null)).booleanValue()) { + throw new RuntimeException("LogManager should be initialized now!"); + } + Logger bazbaz2 = System.getLogger("foo.bar.baz.baz"); + if (bazbaz2.getClass() != bazbaz.getClass()) { + throw new RuntimeException("bazbaz2.class != bazbaz.class [" + + bazbaz2.getClass() + " != " + + bazbaz.getClass() + "]"); + } + if (hasJUL != bazbaz2.getClass().getName() + .equals("sun.util.logging.internal.LoggingProviderImpl$JULWrapper")) { + throw new RuntimeException("Unexpected class for bazbaz: " + + bazbaz.getClass().getName() + + "\n\t expected: " + + "sun.util.logging.internal.LoggingProviderImpl$JULWrapper"); + } + + // Now we're going to check that the thread of the BootstrapLogger + // executor terminates, and that the Executor is GC'ed after that. + // This will involve a bit of waiting, hence the timeout=120 in + // the @run line. + // If this test fails in timeout - we could envisage skipping this part, + // or adding some System property to configure the keep alive delay + // of the executor. + SimplePolicy.allowAll.set(Boolean.TRUE); + try { + Stream stream = Thread.getAllStackTraces().keySet().stream(); + stream.filter((t) -> t.getName().startsWith("BootstrapMessageLoggerTask-")) + .forEach(t -> LogStream.err.println(t)); + stream = null; + if (threadRef != null && test == TestCase.SECURE_AND_WAIT) { + Thread t = threadRef.get(); + if (t != null) { + if (!(Boolean)isAlive.invoke(null)) { + throw new RuntimeException("Executor already terminated"); + } else { + LogStream.err.println("Executor still alive as expected."); + } + LogStream.err.println("Waiting for " + t.getName() + " to terminate (join)"); + t.join(60_000); + t = null; + } + LogStream.err.println("Calling System.gc()"); + System.gc(); + LogStream.err.println("Waiting for BootstrapMessageLoggerTask to be gc'ed"); + while (queue.remove(1000) == null) { + LogStream.err.println("Calling System.gc()"); + System.gc(); + } + + // Call the reference here to make sure threadRef will not be + // eagerly garbage collected before the thread it references. + // otherwise, it might not be enqueued, resulting in the + // queue.remove() call above to always return null.... + if (threadRef.get() != null) { + throw new RuntimeException("Reference should have been cleared"); + } + + LogStream.err.println("BootstrapMessageLoggerTask has been gc'ed"); + // Wait for the executor to be gc'ed... + for (int i=0; i<10; i++) { + LogStream.err.println("Calling System.gc()"); + System.gc(); + if (!(Boolean)isAlive.invoke(null)) break; + // It would be unexpected that we reach here... + Thread.sleep(1000); + } + + if ((Boolean)isAlive.invoke(null)) { + throw new RuntimeException("Executor still alive"); + } else { + LogStream.err.println("Executor terminated as expected."); + } + } else { + LogStream.err.println("Not checking executor termination for " + test); + } + } finally { + SimplePolicy.allowAll.set(Boolean.FALSE); + } + LogStream.err.println(test.name() + ": PASSED"); + } + + final static class SimplePolicy extends Policy { + static final ThreadLocal allowAll = new ThreadLocal() { + @Override + protected Boolean initialValue() { + return Boolean.FALSE; + } + }; + + Permissions getPermissions() { + Permissions perms = new Permissions(); + if (allowAll.get()) { + perms.add(new AllPermission()); + } + return perms; + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + return getPermissions(domain).implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return getPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return getPermissions(); + } + + } +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/LoggerBridgeTest/CustomSystemClassLoader.java b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerBridgeTest/CustomSystemClassLoader.java new file mode 100644 index 00000000000..0c68ff89e93 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerBridgeTest/CustomSystemClassLoader.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.security.AllPermission; +import java.security.Permissions; +import java.security.ProtectionDomain; + + +/** + * A custom ClassLoader to load the concrete LoggerFinder class + * with all permissions. + * + * @author danielfuchs + */ +public class CustomSystemClassLoader extends ClassLoader { + + + Class loggerFinderClass = null; +// Class loggerImplClass = null; + + public CustomSystemClassLoader() { + super(); + } + public CustomSystemClassLoader(ClassLoader parent) { + super(parent); + } + + private Class defineFinderClass(String name) + throws ClassNotFoundException { + final Object obj = getClassLoadingLock(name); + synchronized(obj) { + if (loggerFinderClass != null) return loggerFinderClass; + + URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation(); + File file = new File(url.getPath(), name+".class"); + if (file.canRead()) { + try { + byte[] b = Files.readAllBytes(file.toPath()); + Permissions perms = new Permissions(); + perms.add(new AllPermission()); + loggerFinderClass = defineClass( + name, b, 0, b.length, new ProtectionDomain( + this.getClass().getProtectionDomain().getCodeSource(), + perms)); + System.out.println("Loaded " + name); + return loggerFinderClass; + } catch (Throwable ex) { + ex.printStackTrace(); + throw new ClassNotFoundException(name, ex); + } + } else { + throw new ClassNotFoundException(name, + new IOException(file.toPath() + ": can't read")); + } + } + } +// private Class defineLoggerImplClass(String name) +// throws ClassNotFoundException { +// final Object obj = getClassLoadingLock(name); +// synchronized(obj) { +// if (loggerImplClass != null) return loggerImplClass; +// +// URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation(); +// File file = new File(url.getPath(), name+".class"); +// if (file.canRead()) { +// try { +// byte[] b = Files.readAllBytes(file.toPath()); +// Permissions perms = new Permissions(); +// perms.add(new AllPermission()); +// loggerImplClass = defineClass( +// name, b, 0, b.length, new ProtectionDomain( +// this.getClass().getProtectionDomain().getCodeSource(), +// perms)); +// System.out.println("Loaded " + name); +// return loggerImplClass; +// } catch (Throwable ex) { +// ex.printStackTrace(); +// throw new ClassNotFoundException(name, ex); +// } +// } else { +// throw new ClassNotFoundException(name, +// new IOException(file.toPath() + ": can't read")); +// } +// } +// } +// + @Override + public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (name.endsWith("$LogProducerFinder")) { + Class c = defineFinderClass(name); + if (resolve) { + resolveClass(c); + } + return c; + } +// if (name.endsWith("$LogProducerFinder$LoggerImpl")) { +// Class c = defineLoggerImplClass(name); +// if (resolve) { +// resolveClass(c); +// } +// return c; +// } + return super.loadClass(name, resolve); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { +// if (name.endsWith("$LogProducerFinder$LoggerImpl")) { +// return defineLoggerImplClass(name); +// } + if (name.endsWith("$$LogProducerFinder")) { + return defineFinderClass(name); + } + return super.findClass(name); + } + +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/LoggerBridgeTest/LoggerBridgeTest.java b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerBridgeTest/LoggerBridgeTest.java new file mode 100644 index 00000000000..10e480fc363 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerBridgeTest/LoggerBridgeTest.java @@ -0,0 +1,1087 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Queue; +import java.util.ResourceBundle; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.lang.System.LoggerFinder; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.util.stream.Stream; +import sun.util.logging.PlatformLogger; + +/** + * @test + * @bug 8140364 + * @summary JDK implementation specific unit test for JDK internal artifacts. + * Tests all bridge methods with the a custom backend whose + * loggers implement PlatformLogger.Bridge. + * @modules java.base/sun.util.logging java.base/jdk.internal.logger + * @build CustomSystemClassLoader LoggerBridgeTest + * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader LoggerBridgeTest NOSECURITY + * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader LoggerBridgeTest NOPERMISSIONS + * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader LoggerBridgeTest WITHPERMISSIONS + * @author danielfuchs + */ +public class LoggerBridgeTest { + + public static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + + final static AtomicLong sequencer = new AtomicLong(); + final static boolean VERBOSE = false; + static final ThreadLocal allowControl = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAccess = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAll = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + + public static final Queue eventQueue = new ArrayBlockingQueue<>(128); + + public static final class LogEvent implements Cloneable { + + public LogEvent() { + this(sequencer.getAndIncrement()); + } + + LogEvent(long sequenceNumber) { + this.sequenceNumber = sequenceNumber; + } + + long sequenceNumber; + boolean isLoggable; + String loggerName; + sun.util.logging.PlatformLogger.Level level; + ResourceBundle bundle; + Throwable thrown; + Object[] args; + String msg; + Supplier supplier; + String className; + String methodName; + + Object[] toArray() { + return new Object[] { + sequenceNumber, + loggerName, + level, + isLoggable, + bundle, + msg, + supplier, + thrown, + args, + className, + methodName, + }; + } + + @Override + public String toString() { + return Arrays.deepToString(toArray()); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof LogEvent + && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray()); + } + + @Override + public int hashCode() { + return Objects.hash(toArray()); + } + + public LogEvent cloneWith(long sequenceNumber) + throws CloneNotSupportedException { + LogEvent cloned = (LogEvent)super.clone(); + cloned.sequenceNumber = sequenceNumber; + return cloned; + } + + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + String key, Throwable thrown, Object... params) { + return LogEvent.of(sequenceNumber, isLoggable, name, + null, null, level, bundle, key, + thrown, params); + } + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + Supplier supplier, Throwable thrown, Object... params) { + return LogEvent.of(sequenceNumber, isLoggable, name, + null, null, level, bundle, supplier, + thrown, params); + } + + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + String className, String methodName, + sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + String key, Throwable thrown, Object... params) { + LogEvent evt = new LogEvent(sequenceNumber); + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = thrown; + evt.msg = key; + evt.isLoggable = isLoggable; + evt.className = className; + evt.methodName = methodName; + return evt; + } + + public static LogEvent of(boolean isLoggable, String name, + String className, String methodName, + sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + String key, Throwable thrown, Object... params) { + return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name, + className, methodName, level, bundle, key, thrown, params); + } + + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + String className, String methodName, + sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + Supplier supplier, Throwable thrown, Object... params) { + LogEvent evt = new LogEvent(sequenceNumber); + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = thrown; + evt.supplier = supplier; + evt.isLoggable = isLoggable; + evt.className = className; + evt.methodName = methodName; + return evt; + } + + public static LogEvent of(boolean isLoggable, String name, + String className, String methodName, + sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + Supplier supplier, Throwable thrown, Object... params) { + return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name, + className, methodName, level, bundle, supplier, thrown, params); + } + + } + static final Class providerClass; + static { + try { + // Preload classes before the security manager is on. + providerClass = ClassLoader.getSystemClassLoader().loadClass("LoggerBridgeTest$LogProducerFinder"); + ((LoggerFinder)providerClass.newInstance()).getLogger("foo", providerClass); + } catch (Exception ex) { + throw new ExceptionInInitializerError(ex); + } + } + + public static class LogProducerFinder extends LoggerFinder { + final ConcurrentHashMap system = new ConcurrentHashMap<>(); + final ConcurrentHashMap user = new ConcurrentHashMap<>(); + + public class LoggerImpl implements Logger, PlatformLogger.Bridge { + private final String name; + private sun.util.logging.PlatformLogger.Level level = sun.util.logging.PlatformLogger.Level.INFO; + private sun.util.logging.PlatformLogger.Level OFF = sun.util.logging.PlatformLogger.Level.OFF; + private sun.util.logging.PlatformLogger.Level FINE = sun.util.logging.PlatformLogger.Level.FINE; + private sun.util.logging.PlatformLogger.Level FINER = sun.util.logging.PlatformLogger.Level.FINER; + private sun.util.logging.PlatformLogger.Level FINEST = sun.util.logging.PlatformLogger.Level.FINEST; + private sun.util.logging.PlatformLogger.Level CONFIG = sun.util.logging.PlatformLogger.Level.CONFIG; + private sun.util.logging.PlatformLogger.Level INFO = sun.util.logging.PlatformLogger.Level.INFO; + private sun.util.logging.PlatformLogger.Level WARNING = sun.util.logging.PlatformLogger.Level.WARNING; + private sun.util.logging.PlatformLogger.Level SEVERE = sun.util.logging.PlatformLogger.Level.SEVERE; + + public LoggerImpl(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isLoggable(Level level) { + return this.level != OFF && this.level.intValue() <= level.getSeverity(); + } + + @Override + public void log(Level level, ResourceBundle bundle, + String key, Throwable thrown) { + throw new UnsupportedOperationException(); + } + + @Override + public void log(Level level, ResourceBundle bundle, + String format, Object... params) { + throw new UnsupportedOperationException(); + } + + void log(LogEvent event) { + eventQueue.add(event); + } + + @Override + public void log(Level level, Supplier msgSupplier) { + throw new UnsupportedOperationException(); + } + + @Override + public void log(Level level, Supplier msgSupplier, + Throwable thrown) { + throw new UnsupportedOperationException(); + } + + @Override + public void log(sun.util.logging.PlatformLogger.Level level, String msg) { + log(LogEvent.of(isLoggable(level), name, null, null, + level, null, msg, null, (Object[])null)); + } + + @Override + public void log(sun.util.logging.PlatformLogger.Level level, + Supplier msgSupplier) { + log(LogEvent.of(isLoggable(level), name, null, null, + level, null, msgSupplier, null, (Object[])null)); + } + + @Override + public void log(sun.util.logging.PlatformLogger.Level level, String msg, + Object... params) { + log(LogEvent.of(isLoggable(level), name, null, null, + level, null, msg, null, params)); + } + + @Override + public void log(sun.util.logging.PlatformLogger.Level level, String msg, + Throwable thrown) { + log(LogEvent.of(isLoggable(level), name, null, null, + level, null, msg, thrown, (Object[])null)); + } + + @Override + public void log(sun.util.logging.PlatformLogger.Level level, Throwable thrown, + Supplier msgSupplier) { + log(LogEvent.of(isLoggable(level), name, null, null, + level, null, msgSupplier, thrown, (Object[])null)); + } + + @Override + public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, + String sourceMethod, String msg) { + log(LogEvent.of(isLoggable(level), name, + sourceClass, sourceMethod, + level, null, msg, null, (Object[])null)); + } + + @Override + public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, + String sourceMethod, Supplier msgSupplier) { + log(LogEvent.of(isLoggable(level), name, + sourceClass, sourceMethod, + level, null, msgSupplier, null, (Object[])null)); + } + + @Override + public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, + String sourceMethod, String msg, Object... params) { + log(LogEvent.of(isLoggable(level), name, + sourceClass, sourceMethod, + level, null, msg, null, params)); + } + + @Override + public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, + String sourceMethod, String msg, Throwable thrown) { + log(LogEvent.of(isLoggable(level), name, + sourceClass, sourceMethod, + level, null, msg, thrown, (Object[])null)); + } + + @Override + public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, + String sourceMethod, Throwable thrown, + Supplier msgSupplier) { + log(LogEvent.of(isLoggable(level), name, + sourceClass, sourceMethod, + level, null, msgSupplier, thrown, (Object[])null)); + } + + @Override + public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, + String sourceMethod, ResourceBundle bundle, String msg, + Object... params) { + log(LogEvent.of(isLoggable(level), name, + sourceClass, sourceMethod, + level, bundle, msg, null, params)); + } + + @Override + public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + String msg, Object... params) { + log(LogEvent.of(isLoggable(level), name, null, null, + level, bundle, msg, null, params)); + } + + @Override + public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, + String sourceMethod, ResourceBundle bundle, String msg, + Throwable thrown) { + log(LogEvent.of(isLoggable(level), name, + sourceClass, sourceMethod, + level, bundle, msg, thrown, (Object[])null)); + } + + @Override + public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + String msg, Throwable thrown) { + log(LogEvent.of(isLoggable(level), name, null, null, + level, bundle, msg, thrown, (Object[])null)); + } + + @Override + public boolean isLoggable(sun.util.logging.PlatformLogger.Level level) { + return this.level != OFF && level.intValue() + >= this.level.intValue(); + } + + @Override + public boolean isEnabled() { + return this.level != OFF; + } + + } + + @Override + public Logger getLogger(String name, Class caller) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(LOGGERFINDER_PERMISSION); + } + PrivilegedAction pa = () -> caller.getClassLoader(); + ClassLoader callerLoader = AccessController.doPrivileged(pa); + if (callerLoader == null) { + return system.computeIfAbsent(name, (n) -> new LoggerImpl(n)); + } else { + return user.computeIfAbsent(name, (n) -> new LoggerImpl(n)); + } + } + } + + static final sun.util.logging.PlatformLogger.Level[] julLevels = { + sun.util.logging.PlatformLogger.Level.ALL, + sun.util.logging.PlatformLogger.Level.FINEST, + sun.util.logging.PlatformLogger.Level.FINER, + sun.util.logging.PlatformLogger.Level.FINE, + sun.util.logging.PlatformLogger.Level.CONFIG, + sun.util.logging.PlatformLogger.Level.INFO, + sun.util.logging.PlatformLogger.Level.WARNING, + sun.util.logging.PlatformLogger.Level.SEVERE, + sun.util.logging.PlatformLogger.Level.OFF, + }; + + public static class MyBundle extends ResourceBundle { + + final ConcurrentHashMap map = new ConcurrentHashMap<>(); + + @Override + protected Object handleGetObject(String key) { + if (key.contains(" (translated)")) { + throw new RuntimeException("Unexpected key: " + key); + } + return map.computeIfAbsent(key, k -> k + " (translated)"); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(map.keySet()); + } + + } + + public static class MyHandler extends Handler { + + @Override + public java.util.logging.Level getLevel() { + return java.util.logging.Level.ALL; + } + + @Override + public void publish(LogRecord record) { + eventQueue.add(LogEvent.of(sequencer.getAndIncrement(), + true, record.getLoggerName(), + record.getSourceClassName(), + record.getSourceMethodName(), + PlatformLogger.Level.valueOf(record.getLevel().getName()), + record.getResourceBundle(), record.getMessage(), + record.getThrown(), record.getParameters())); + } + @Override + public void flush() { + } + @Override + public void close() throws SecurityException { + } + + } + + public static class MyLoggerBundle extends MyBundle { + + } + + final static Method lazyGetLogger; + static { + // jdk.internal.logging.LoggerBridge.getLogger(name, caller) + try { + Class bridgeClass = Class.forName("jdk.internal.logger.LazyLoggers"); + lazyGetLogger = bridgeClass.getDeclaredMethod("getLogger", + String.class, Class.class); + lazyGetLogger.setAccessible(true); + } catch (Throwable ex) { + throw new ExceptionInInitializerError(ex); + } + } + + static Logger getLogger(LoggerFinder provider, String name, Class caller) { + Logger logger; + try { + logger = Logger.class.cast(lazyGetLogger.invoke(null, name, caller)); + } catch (Throwable x) { + Throwable t = (x instanceof InvocationTargetException) ? + ((InvocationTargetException)x).getTargetException() : x; + if (t instanceof RuntimeException) { + throw (RuntimeException)t; + } else if (t instanceof Exception) { + throw new RuntimeException(t); + } else { + throw (Error)t; + } + } + // The method above does not throw exception... + // call the provider here to verify that an exception would have + // been thrown by the provider. + if (logger != null && caller == Thread.class) { + Logger log = provider.getLogger(name, caller); + } + return logger; + } + + static Logger getLogger(LoggerFinder provider, String name, ResourceBundle bundle, Class caller) { + if (caller.getClassLoader() != null) { + return System.getLogger(name,bundle); + } else { + return provider.getLocalizedLogger(name, bundle, caller); + } + } + + static PlatformLogger.Bridge convert(Logger logger) { + return PlatformLogger.Bridge.convert(logger); + } + + static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS}; + + static void setSecurityManager() { + if (System.getSecurityManager() == null) { + Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll)); + System.setSecurityManager(new SecurityManager()); + } + } + + public static void main(String[] args) { + if (args.length == 0) + args = new String[] { + //"NOSECURITY", + "NOPERMISSIONS", + "WITHPERMISSIONS" + }; + + + Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> { + LoggerFinder provider; + switch (testCase) { + case NOSECURITY: + System.out.println("\n*** Without Security Manager\n"); + provider = LoggerFinder.getLoggerFinder(); + test(provider, true); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case NOPERMISSIONS: + System.out.println("\n*** With Security Manager, without permissions\n"); + setSecurityManager(); + try { + provider = LoggerFinder.getLoggerFinder(); + throw new RuntimeException("Expected exception not raised"); + } catch (AccessControlException x) { + if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) { + throw new RuntimeException("Unexpected permission check", x); + } + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = LoggerFinder.getLoggerFinder(); + } finally { + allowControl.get().set(control); + } + } + test(provider, false); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case WITHPERMISSIONS: + System.out.println("\n*** With Security Manager, with control permission\n"); + setSecurityManager(); + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = LoggerFinder.getLoggerFinder(); + test(provider, true); + } finally { + allowControl.get().set(control); + } + break; + default: + throw new RuntimeException("Unknown test case: " + testCase); + } + }); + System.out.println("\nPASSED: Tested " + sequencer.get() + " cases."); + } + + public static void test(LoggerFinder provider, boolean hasRequiredPermissions) { + + ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName()); + final Map loggerDescMap = new HashMap<>(); + + + Logger appLogger1 = System.getLogger("foo"); + loggerDescMap.put(appLogger1, "LogProducer.getApplicationLogger(\"foo\")"); + + Logger sysLogger1 = null; + try { + sysLogger1 = getLogger(provider, "foo", Thread.class); + loggerDescMap.put(sysLogger1, "LogProducer.getSystemLogger(\"foo\")"); + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a system logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for system logger: " + acx); + } + + + Logger appLogger2 = + System.getLogger("foo", loggerBundle); + loggerDescMap.put(appLogger2, "LogProducer.getApplicationLogger(\"foo\", loggerBundle)"); + + Logger sysLogger2 = null; + try { + sysLogger2 = getLogger(provider, "foo", loggerBundle, Thread.class); + loggerDescMap.put(sysLogger2, "provider.getSystemLogger(\"foo\", loggerBundle)"); + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a system logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for localized system logger: " + acx); + } + if (hasRequiredPermissions && appLogger2 == sysLogger2) { + throw new RuntimeException("identical loggers"); + } + if (appLogger2 == appLogger1) { + throw new RuntimeException("identical loggers"); + } + if (hasRequiredPermissions && sysLogger2 == sysLogger1) { + throw new RuntimeException("identical loggers"); + } + + + final LogProducerFinder.LoggerImpl appSink; + final LogProducerFinder.LoggerImpl sysSink; + boolean old = allowControl.get().get(); + allowControl.get().set(true); + try { + appSink = LogProducerFinder.LoggerImpl.class.cast( + provider.getLogger("foo", LoggerBridgeTest.class)); + sysSink = LogProducerFinder.LoggerImpl.class.cast( + provider.getLogger("foo", Thread.class)); + } finally { + allowControl.get().set(old); + } + + testLogger(provider, loggerDescMap, "foo", null, convert(appLogger1), appSink); + if (hasRequiredPermissions) { + testLogger(provider, loggerDescMap, "foo", null, convert(sysLogger1), sysSink); + } + testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(appLogger2), appSink); + if (hasRequiredPermissions) { + testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(sysLogger2), sysSink); + } + } + + public static class Foo { + + } + + static void verbose(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + + static void checkLogEvent(LoggerFinder provider, String desc, + LogEvent expected) { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + + static void checkLogEvent(LoggerFinder provider, String desc, + LogEvent expected, boolean expectNotNull) { + LogEvent actual = eventQueue.poll(); + if (actual == null && !expectNotNull) return; + if (actual != null && !expectNotNull) { + throw new RuntimeException("Unexpected log event found for " + desc + + "\n\tgot: " + actual); + } + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + + static void setLevel( LogProducerFinder.LoggerImpl sink, + sun.util.logging.PlatformLogger.Level loggerLevel) { + sink.level = loggerLevel; + } + + // Calls the methods defined on LogProducer and verify the + // parameters received by the underlying LogProducerFinder.LoggerImpl + // logger. + private static void testLogger(LoggerFinder provider, + Map loggerDescMap, + String name, + ResourceBundle loggerBundle, + PlatformLogger.Bridge logger, + LogProducerFinder.LoggerImpl sink) { + + System.out.println("Testing " + loggerDescMap.get(logger) + "[" + logger + "]"); + final sun.util.logging.PlatformLogger.Level OFF = sun.util.logging.PlatformLogger.Level.OFF; + + Foo foo = new Foo(); + String fooMsg = foo.toString(); + System.out.println("\tlogger.log(messageLevel, fooMsg)"); + System.out.println("\tlogger.(fooMsg)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.log(messageLevel, fooMsg): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, loggerBundle, + fooMsg, (Throwable)null, (Object[])null); + logger.log(messageLevel, fooMsg); + checkLogEvent(provider, desc, expected); + } + } + + Supplier supplier = new Supplier() { + @Override + public String get() { + return this.toString(); + } + }; + System.out.println("\tlogger.log(messageLevel, supplier)"); + System.out.println("\tlogger.(supplier)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.log(messageLevel, supplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, null, + supplier, (Throwable)null, (Object[])null); + logger.log(messageLevel, supplier); + checkLogEvent(provider, desc, expected); + } + } + + String format = "two params [{1} {2}]"; + Object arg1 = foo; + Object arg2 = fooMsg; + System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, loggerBundle, + format, (Throwable)null, arg1, arg2); + logger.log(messageLevel, format, arg1, arg2); + checkLogEvent(provider, desc, expected); + } + } + + Throwable thrown = new Exception("OK: log me!"); + System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, loggerBundle, + fooMsg, thrown, (Object[])null); + logger.log(messageLevel, fooMsg, thrown); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.log(messageLevel, thrown, supplier)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, null, + supplier, thrown, (Object[])null); + logger.log(messageLevel, thrown, supplier); + checkLogEvent(provider, desc, expected); + } + } + + String sourceClass = "blah.Blah"; + String sourceMethod = "blih"; + System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, sourceClass, sourceMethod, messageLevel, loggerBundle, + fooMsg, (Throwable)null, (Object[])null); + logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, sourceClass, sourceMethod, messageLevel, null, + supplier, (Throwable)null, (Object[])null); + logger.logp(messageLevel, sourceClass, sourceMethod, supplier); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, sourceClass, sourceMethod, messageLevel, loggerBundle, + format, (Throwable)null, arg1, arg2); + logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, sourceClass, sourceMethod, messageLevel, loggerBundle, + fooMsg, thrown, (Object[])null); + logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, sourceClass, sourceMethod, messageLevel, null, + supplier, thrown, (Object[])null); + logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier); + checkLogEvent(provider, desc, expected); + } + } + + ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName()); + System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, bundle, + format, (Throwable)null, arg1, arg2); + logger.logrb(messageLevel, bundle, format, arg1, arg2); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, bundle, + fooMsg, thrown, (Object[])null); + logger.logrb(messageLevel, bundle, fooMsg, thrown); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, sourceClass, sourceMethod, messageLevel, bundle, + format, (Throwable)null, arg1, arg2); + logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2); + checkLogEvent(provider, desc, expected); + } + } + + System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, sourceClass, sourceMethod, messageLevel, bundle, + fooMsg, thrown, (Object[])null); + logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, fooMsg, thrown); + checkLogEvent(provider, desc, expected); + } + } + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION; + final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger"); + final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging"); + + final Permissions permissions; + final Permissions allPermissions; + final ThreadLocal allowControl; + final ThreadLocal allowAccess; + final ThreadLocal allowAll; + public SimplePolicy(ThreadLocal allowControl, + ThreadLocal allowAccess, + ThreadLocal allowAll) { + this.allowControl = allowControl; + this.allowAccess = allowAccess; + this.allowAll = allowAll; + permissions = new Permissions(); + allPermissions = new PermissionsBuilder() + .add(new java.security.AllPermission()) + .toPermissions(); + } + + Permissions getPermissions() { + if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) { + PermissionsBuilder builder = new PermissionsBuilder() + .addAll(permissions); + if (allowControl.get().get()) { + builder.add(CONTROL); + } + if (allowAccess.get().get()) { + builder.add(ACCESS_LOGGER); + builder.add(ACCESS_LOGGING); + } + if (allowAll.get().get()) { + builder.addAll(allPermissions); + } + return builder.toPermissions(); + } + return permissions; + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + return getPermissions().implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + } +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/LoggerBridgeTest/META-INF/services/java.lang.System$LoggerFinder b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerBridgeTest/META-INF/services/java.lang.System$LoggerFinder new file mode 100644 index 00000000000..8abf57e2bac --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerBridgeTest/META-INF/services/java.lang.System$LoggerFinder @@ -0,0 +1 @@ +LoggerBridgeTest$LogProducerFinder diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/AccessSystemLogger.java b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/AccessSystemLogger.java new file mode 100644 index 00000000000..928dc97987e --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/AccessSystemLogger.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.io.IOException; +import java.lang.System.Logger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ResourceBundle; + +/** + * + * @author danielfuchs + */ +public final class AccessSystemLogger { + + public AccessSystemLogger() { + this(check()); + } + + private AccessSystemLogger(Void unused) { + } + + private static Void check() { + if (AccessSystemLogger.class.getClassLoader() != null) { + throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader"); + } + return null; + } + + public Logger getLogger(String name) { + Logger logger = System.getLogger(name); + System.out.println("System.getLogger(\"" + name + "\"): " + logger); + return logger; + } + + public Logger getLogger(String name, ResourceBundle bundle) { + Logger logger = System.getLogger(name, bundle); + System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger); + return logger; + } + + static final Class[] toCopy = { AccessSystemLogger.class, CustomSystemClassLoader.class }; + + // copy AccessSystemLogger.class to ./boot + public static void main(String[] args) throws IOException { + Path testDir = Paths.get(System.getProperty("user.dir", ".")); + Path bootDir = Paths.get(testDir.toString(), "boot"); + Path classes = Paths.get(System.getProperty("test.classes", "build/classes")); + if (Files.notExists(bootDir)) { + Files.createDirectory(bootDir); + } + for (Class c : toCopy) { + Path thisClass = Paths.get(classes.toString(), + c.getSimpleName()+".class"); + Path dest = Paths.get(bootDir.toString(), + c.getSimpleName()+".class"); + Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING); + } + } + +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/CustomSystemClassLoader.java b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/CustomSystemClassLoader.java new file mode 100644 index 00000000000..7e3db8b7dab --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/CustomSystemClassLoader.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.security.AllPermission; +import java.security.Permissions; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * A custom ClassLoader to load the concrete LoggerFinder class + * with all permissions. The CustomSystemClassLoader class must be + * in the BCL, otherwise when system classes - such as + * ZoneDateTime try to load their resource bundle a MissingResourceBundle + * caused by a SecurityException may be thrown, as the CustomSystemClassLoader + * code base will be found in the stack called by doPrivileged. + * + * @author danielfuchs + */ +public class CustomSystemClassLoader extends ClassLoader { + + + final List finderClassNames = + Arrays.asList("LoggerFinderLoaderTest$BaseLoggerFinder", + "LoggerFinderLoaderTest$BaseLoggerFinder2"); + final Map> finderClasses = new HashMap<>(); + Class testLoggerFinderClass; + + public CustomSystemClassLoader() { + super(); + } + public CustomSystemClassLoader(ClassLoader parent) { + super(parent); + } + + private Class defineFinderClass(String name) + throws ClassNotFoundException { + final Object obj = getClassLoadingLock(name); + synchronized(obj) { + if (finderClasses.get(name) != null) return finderClasses.get(name); + if (testLoggerFinderClass == null) { + // Hack: we load testLoggerFinderClass to get its code source. + // we can't use this.getClass() since we are in the boot. + testLoggerFinderClass = super.loadClass("LoggerFinderLoaderTest$TestLoggerFinder"); + } + URL url = testLoggerFinderClass.getProtectionDomain().getCodeSource().getLocation(); + File file = new File(url.getPath(), name+".class"); + if (file.canRead()) { + try { + byte[] b = Files.readAllBytes(file.toPath()); + Permissions perms = new Permissions(); + perms.add(new AllPermission()); + Class finderClass = defineClass( + name, b, 0, b.length, new ProtectionDomain( + this.getClass().getProtectionDomain().getCodeSource(), + perms)); + System.out.println("Loaded " + name); + finderClasses.put(name, finderClass); + return finderClass; + } catch (Throwable ex) { + ex.printStackTrace(); + throw new ClassNotFoundException(name, ex); + } + } else { + throw new ClassNotFoundException(name, + new IOException(file.toPath() + ": can't read")); + } + } + } + + @Override + public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (finderClassNames.contains(name)) { + Class c = defineFinderClass(name); + if (resolve) { + resolveClass(c); + } + return c; + } + return super.loadClass(name, resolve); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + if (finderClassNames.contains(name)) { + return defineFinderClass(name); + } + return super.findClass(name); + } + +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/LoggerFinderLoaderTest.java b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/LoggerFinderLoaderTest.java new file mode 100644 index 00000000000..3756956edb8 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/LoggerFinderLoaderTest.java @@ -0,0 +1,882 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UncheckedIOException; +import java.security.AccessControlException; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.stream.Stream; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; +import java.lang.System.LoggerFinder; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.Locale; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.concurrent.atomic.AtomicReference; +import jdk.internal.logger.SimpleConsoleLogger; + +/** + * @test + * @bug 8140364 + * @summary JDK implementation specific unit test for LoggerFinderLoader. + * Tests the behavior of LoggerFinderLoader with respect to the + * value of the internal diagnosability switches. Also test the + * DefaultLoggerFinder and SimpleConsoleLogger implementation. + * @modules java.base/sun.util.logging + * java.base/jdk.internal.logger + * @build AccessSystemLogger LoggerFinderLoaderTest CustomSystemClassLoader + * @run driver AccessSystemLogger + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader LoggerFinderLoaderTest NOSECURITY + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader LoggerFinderLoaderTest NOPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader LoggerFinderLoaderTest WITHPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true LoggerFinderLoaderTest NOSECURITY + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true LoggerFinderLoaderTest NOPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true LoggerFinderLoaderTest WITHPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOSECURITY + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest WITHPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOSECURITY + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest WITHPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOSECURITY + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest WITHPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true LoggerFinderLoaderTest NOSECURITY + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true LoggerFinderLoaderTest NOPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true LoggerFinderLoaderTest WITHPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOSECURITY + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest WITHPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOSECURITY + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest WITHPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOSECURITY + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOPERMISSIONS + * @run main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest WITHPERMISSIONS + * @author danielfuchs + */ +public class LoggerFinderLoaderTest { + + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + final static boolean VERBOSE = false; + static final ThreadLocal allowControl = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAccess = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + + final static AccessSystemLogger accessSystemLogger = new AccessSystemLogger(); + static final Class[] providerClass; + static { + try { + providerClass = new Class[] { + ClassLoader.getSystemClassLoader().loadClass("LoggerFinderLoaderTest$BaseLoggerFinder"), + ClassLoader.getSystemClassLoader().loadClass("LoggerFinderLoaderTest$BaseLoggerFinder2") + }; + } catch (ClassNotFoundException ex) { + throw new ExceptionInInitializerError(ex); + } + } + + /** + * What our test provider needs to implement. + */ + public static interface TestLoggerFinder { + public final static AtomicBoolean fails = new AtomicBoolean(); + public final static AtomicReference conf = new AtomicReference<>(""); + public final static AtomicLong sequencer = new AtomicLong(); + public final ConcurrentHashMap system = new ConcurrentHashMap<>(); + public final ConcurrentHashMap user = new ConcurrentHashMap<>(); + + public class LoggerImpl implements System.Logger { + final String name; + final Logger logger; + + public LoggerImpl(String name, Logger logger) { + this.name = name; + this.logger = logger; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isLoggable(Logger.Level level) { + return logger.isLoggable(level); + } + + @Override + public void log(Logger.Level level, ResourceBundle bundle, String key, Throwable thrown) { + logger.log(level, bundle, key, thrown); + } + + @Override + public void log(Logger.Level level, ResourceBundle bundle, String format, Object... params) { + logger.log(level, bundle, format, params); + } + + } + + public Logger getLogger(String name, Class caller); + public Logger getLocalizedLogger(String name, ResourceBundle bundle, Class caller); + } + + public static class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder { + + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + public BaseLoggerFinder() { + if (fails.get()) { + throw new RuntimeException("Simulate exception while loading provider"); + } + } + + System.Logger createSimpleLogger(String name) { + PrivilegedAction pa = () -> SimpleConsoleLogger.makeSimpleLogger(name, false); + return AccessController.doPrivileged(pa); + } + + + @Override + public Logger getLogger(String name, Class caller) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(LOGGERFINDER_PERMISSION); + } + PrivilegedAction pa = () -> caller.getClassLoader(); + ClassLoader callerLoader = AccessController.doPrivileged(pa); + if (callerLoader == null) { + return system.computeIfAbsent(name, (n) -> new LoggerImpl(n, createSimpleLogger(name))); + } else { + return user.computeIfAbsent(name, (n) -> new LoggerImpl(n, createSimpleLogger(name))); + } + } + } + + public static class BaseLoggerFinder2 extends LoggerFinder implements TestLoggerFinder { + + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + public BaseLoggerFinder2() { + throw new ServiceConfigurationError("Should not come here"); + } + @Override + public Logger getLogger(String name, Class caller) { + throw new ServiceConfigurationError("Should not come here"); + } + } + + public static class MyBundle extends ResourceBundle { + + final ConcurrentHashMap map = new ConcurrentHashMap<>(); + + @Override + protected Object handleGetObject(String key) { + if (key.contains(" (translated)")) { + throw new RuntimeException("Unexpected key: " + key); + } + return map.computeIfAbsent(key, k -> k.toUpperCase(Locale.ROOT) + " (translated)"); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(map.keySet()); + } + + } + public static class MyLoggerBundle extends MyBundle { + + } + + static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS}; + + static void setSecurityManager() { + if (System.getSecurityManager() == null) { + Policy.setPolicy(new SimplePolicy(allowControl, allowAccess)); + System.setSecurityManager(new SecurityManager()); + } + } + + static LoggerFinder getLoggerFinder(Class expectedClass, + String errorPolicy, boolean singleton) { + LoggerFinder provider = null; + try { + TestLoggerFinder.sequencer.incrementAndGet(); + provider = LoggerFinder.getLoggerFinder(); + if (TestLoggerFinder.fails.get() || singleton) { + if ("ERROR".equals(errorPolicy.toUpperCase(Locale.ROOT))) { + throw new RuntimeException("Expected exception not thrown"); + } else if ("WARNING".equals(errorPolicy.toUpperCase(Locale.ROOT))) { + String warning = ErrorStream.errorStream.peek(); + if (!warning.contains("WARNING: Failed to instantiate LoggerFinder provider; Using default.")) { + throw new RuntimeException("Expected message not found. Error stream contained: " + warning); + } + } else if ("DEBUG".equals(errorPolicy.toUpperCase(Locale.ROOT))) { + String warning = ErrorStream.errorStream.peek(); + if (!warning.contains("WARNING: Failed to instantiate LoggerFinder provider; Using default.")) { + throw new RuntimeException("Expected message not found. Error stream contained: " + warning); + } + if (!warning.contains("WARNING: Exception raised trying to instantiate LoggerFinder")) { + throw new RuntimeException("Expected message not found. Error stream contained: " + warning); + } + if (TestLoggerFinder.fails.get()) { + if (!warning.contains("java.util.ServiceConfigurationError: java.lang.System$LoggerFinder: Provider LoggerFinderLoaderTest$BaseLoggerFinder could not be instantiated")) { + throw new RuntimeException("Expected message not found. Error stream contained: " + warning); + } + } else if (singleton) { + if (!warning.contains("java.util.ServiceConfigurationError: More than on LoggerFinder implementation")) { + throw new RuntimeException("Expected message not found. Error stream contained: " + warning); + } + } + } else if ("QUIET".equals(errorPolicy.toUpperCase(Locale.ROOT))) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("Unexpected error message found: " + + ErrorStream.errorStream.peek()); + } + } + } + } catch(AccessControlException a) { + throw a; + } catch(Throwable t) { + if (TestLoggerFinder.fails.get() || singleton) { + // must check System.err + if ("ERROR".equals(errorPolicy.toUpperCase(Locale.ROOT))) { + provider = LoggerFinder.getLoggerFinder(); + } else { + Throwable orig = t.getCause(); + while (orig != null && orig.getCause() != null) orig = orig.getCause(); + if (orig != null) orig.printStackTrace(ErrorStream.err); + throw new RuntimeException("Unexpected exception: " + t, t); + } + } else { + throw new RuntimeException("Unexpected exception: " + t, t); + } + } + expectedClass.cast(provider); + ErrorStream.errorStream.store(); + System.out.println("*** Actual LoggerFinder class is: " + provider.getClass().getName()); + return provider; + } + + + static class ErrorStream extends PrintStream { + + static AtomicBoolean forward = new AtomicBoolean(); + ByteArrayOutputStream out; + String saved = ""; + public ErrorStream(ByteArrayOutputStream out) { + super(out); + this.out = out; + } + + @Override + public void write(int b) { + super.write(b); + if (forward.get()) err.write(b); + } + + @Override + public void write(byte[] b) throws IOException { + super.write(b); + if (forward.get()) err.write(b); + } + + @Override + public void write(byte[] buf, int off, int len) { + super.write(buf, off, len); + if (forward.get()) err.write(buf, off, len); + } + + public String peek() { + flush(); + return out.toString(); + } + + public String drain() { + flush(); + String res = out.toString(); + out.reset(); + return res; + } + + public void store() { + flush(); + saved = out.toString(); + out.reset(); + } + + public void restore() { + out.reset(); + try { + out.write(saved.getBytes()); + } catch(IOException io) { + throw new UncheckedIOException(io); + } + } + + static final PrintStream err = System.err; + static final ErrorStream errorStream = new ErrorStream(new ByteArrayOutputStream()); + } + + private static StringBuilder appendProperty(StringBuilder b, String name) { + String value = System.getProperty(name); + if (value == null) return b; + return b.append(name).append("=").append(value).append('\n'); + } + + public static void main(String[] args) { + if (args.length == 0) { + args = new String[] { + "NOSECURITY", + "NOPERMISSIONS", + "WITHPERMISSIONS" + }; + } + Locale.setDefault(Locale.ENGLISH); + System.setErr(ErrorStream.errorStream); + System.setProperty("jdk.logger.packages", TestLoggerFinder.LoggerImpl.class.getName()); + //System.setProperty("jdk.logger.finder.error", "ERROR"); + //System.setProperty("jdk.logger.finder.singleton", "true"); + //System.setProperty("test.fails", "true"); + TestLoggerFinder.fails.set(Boolean.getBoolean("test.fails")); + StringBuilder c = new StringBuilder(); + appendProperty(c, "jdk.logger.packages"); + appendProperty(c, "jdk.logger.finder.error"); + appendProperty(c, "jdk.logger.finder.singleton"); + appendProperty(c, "test.fails"); + TestLoggerFinder.conf.set(c.toString()); + try { + test(args); + } finally { + try { + System.setErr(ErrorStream.err); + } catch (Error | RuntimeException x) { + x.printStackTrace(ErrorStream.err); + } + } + } + + + public static void test(String[] args) { + + final String errorPolicy = System.getProperty("jdk.logger.finder.error", "WARNING"); + final Boolean ensureSingleton = Boolean.getBoolean("jdk.logger.finder.singleton"); + + final Class expectedClass = + TestLoggerFinder.fails.get() || ensureSingleton + ? jdk.internal.logger.DefaultLoggerFinder.class + : TestLoggerFinder.class; + + System.out.println("Declared provider class: " + providerClass[0] + + "[" + providerClass[0].getClassLoader() + "]"); + + if (!TestLoggerFinder.fails.get()) { + ServiceLoader serviceLoader = + ServiceLoader.load(LoggerFinder.class, ClassLoader.getSystemClassLoader()); + Iterator iterator = serviceLoader.iterator(); + Object firstProvider = iterator.next(); + if (!firstProvider.getClass().getName().equals("LoggerFinderLoaderTest$BaseLoggerFinder")) { + throw new RuntimeException("Unexpected provider: " + firstProvider.getClass().getName()); + } + if (!iterator.hasNext()) { + throw new RuntimeException("Expected two providers"); + } + } + + Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> { + LoggerFinder provider; + ErrorStream.errorStream.restore(); + switch (testCase) { + case NOSECURITY: + System.out.println("\n*** Without Security Manager\n"); + System.out.println(TestLoggerFinder.conf.get()); + provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton); + test(provider, true); + System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get()); + break; + case NOPERMISSIONS: + System.out.println("\n*** With Security Manager, without permissions\n"); + System.out.println(TestLoggerFinder.conf.get()); + setSecurityManager(); + try { + provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton); + throw new RuntimeException("Expected exception not raised"); + } catch (AccessControlException x) { + if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) { + throw new RuntimeException("Unexpected permission check", x); + } + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton); + } finally { + allowControl.get().set(control); + } + } + test(provider, false); + System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get()); + break; + case WITHPERMISSIONS: + System.out.println("\n*** With Security Manager, with control permission\n"); + System.out.println(TestLoggerFinder.conf.get()); + setSecurityManager(); + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton); + test(provider, true); + } finally { + allowControl.get().set(control); + } + break; + default: + throw new RuntimeException("Unknown test case: " + testCase); + } + }); + System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases."); + } + + public static void test(LoggerFinder provider, boolean hasRequiredPermissions) { + + ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName()); + final Map loggerDescMap = new HashMap<>(); + + System.Logger sysLogger = accessSystemLogger.getLogger("foo"); + loggerDescMap.put(sysLogger, "accessSystemLogger.getLogger(\"foo\")"); + System.Logger localizedSysLogger = accessSystemLogger.getLogger("fox", loggerBundle); + loggerDescMap.put(localizedSysLogger, "accessSystemLogger.getLogger(\"fox\", loggerBundle)"); + System.Logger appLogger = System.getLogger("bar"); + loggerDescMap.put(appLogger,"System.getLogger(\"bar\")"); + System.Logger localizedAppLogger = System.getLogger("baz", loggerBundle); + loggerDescMap.put(localizedAppLogger,"System.getLogger(\"baz\", loggerBundle)"); + + testLogger(provider, loggerDescMap, "foo", null, sysLogger); + testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedSysLogger); + testLogger(provider, loggerDescMap, "foo", null, appLogger); + testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedAppLogger); + } + + public static class Foo { + + } + + static void verbose(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + + // Calls the 8 methods defined on Logger and verify the + // parameters received by the underlying TestProvider.LoggerImpl + // logger. + private static void testLogger(LoggerFinder provider, + Map loggerDescMap, + String name, + ResourceBundle loggerBundle, + Logger logger) { + + System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]"); + AtomicLong sequencer = TestLoggerFinder.sequencer; + + Foo foo = new Foo(); + String fooMsg = foo.toString(); + for (Level loggerLevel : EnumSet.of(Level.INFO)) { + for (Level messageLevel : Level.values()) { + ErrorStream.errorStream.drain(); + String desc = "logger.log(messageLevel, foo): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, foo); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + if (!logged.contains("LoggerFinderLoaderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + fooMsg)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] LoggerFinderLoaderTest testLogger\n" + + messageLevel.getName() + " " + fooMsg + + "\n>>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + String msg = "blah"; + for (Level loggerLevel : EnumSet.of(Level.INFO)) { + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, \"blah\"): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, msg); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg); + if (!logged.contains("LoggerFinderLoaderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + msgText)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] LoggerFinderLoaderTest testLogger\n" + + messageLevel.getName() + " " + msgText + + "\n>>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + Supplier fooSupplier = new Supplier() { + @Override + public String get() { + return this.toString(); + } + }; + + for (Level loggerLevel : EnumSet.of(Level.INFO)) { + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, fooSupplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, fooSupplier); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + if (!logged.contains("LoggerFinderLoaderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] LoggerFinderLoaderTest testLogger\n" + + messageLevel.getName() + " " + fooSupplier.get() + + "\n>>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + + String format = "two params [{1} {2}]"; + Object arg1 = foo; + Object arg2 = msg; + for (Level loggerLevel : EnumSet.of(Level.INFO)) { + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, format, params...): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, format, foo, msg); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + String msgFormat = loggerBundle == null ? format : loggerBundle.getString(format); + String text = java.text.MessageFormat.format(msgFormat, foo, msg); + if (!logged.contains("LoggerFinderLoaderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + text)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] LoggerFinderLoaderTest testLogger\n" + + messageLevel.getName() + " " + text + + "\n>>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + Throwable thrown = new Exception("OK: log me!"); + for (Level loggerLevel : EnumSet.of(Level.INFO)) { + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, msg, thrown); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + thrown.printStackTrace(new PrintStream(baos)); + String text = baos.toString(); + String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg); + if (!logged.contains("LoggerFinderLoaderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + msgText) + || !logged.contains(text)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] LoggerFinderLoaderTest testLogger\n" + + messageLevel.getName() + " " + msgText +"\n" + + text + + ">>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + + for (Level loggerLevel : EnumSet.of(Level.INFO)) { + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, fooSupplier, thrown); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + thrown.printStackTrace(new PrintStream(baos)); + String text = baos.toString(); + if (!logged.contains("LoggerFinderLoaderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get()) + || !logged.contains(text)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] LoggerFinderLoaderTest testLogger\n" + + messageLevel.getName() + " " + fooSupplier.get() +"\n" + + text + + ">>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName()); + for (Level loggerLevel : EnumSet.of(Level.INFO)) { + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, bundle, format, foo, msg); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + String text = java.text.MessageFormat.format(bundle.getString(format), foo, msg); + if (!logged.contains("LoggerFinderLoaderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + text)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] LoggerFinderLoaderTest testLogger\n" + + messageLevel.getName() + " " + text + + "\n>>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + for (Level loggerLevel : EnumSet.of(Level.INFO)) { + for (Level messageLevel : Level.values()) { + String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + sequencer.incrementAndGet(); + logger.log(messageLevel, bundle, msg, thrown); + if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) { + if (!ErrorStream.errorStream.peek().isEmpty()) { + throw new RuntimeException("unexpected event in queue for " + + desc +": " + "\n\t" + ErrorStream.errorStream.drain()); + } + } else { + String logged = ErrorStream.errorStream.drain(); + String textMsg = bundle.getString(msg); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + thrown.printStackTrace(new PrintStream(baos)); + String text = baos.toString(); + if (!logged.contains("LoggerFinderLoaderTest testLogger") + || !logged.contains(messageLevel.getName() + ": " + textMsg) + || !logged.contains(text)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected:" + "\n<<<<\n" + + "[date] LoggerFinderLoaderTest testLogger\n" + + messageLevel.getName() + " " + textMsg +"\n" + + text + + ">>>>" + + "\n\t actual:" + + "\n<<<<\n" + logged + ">>>>\n"); + } else { + verbose("Got expected results for " + + desc + "\n<<<<\n" + logged + ">>>>\n"); + } + } + } + } + + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION; + final static RuntimePermission ACCESS = new RuntimePermission("accessClassInPackage.jdk.internal.logger"); + + final Permissions permissions; + final ThreadLocal allowControl; + final ThreadLocal allowAccess; + public SimplePolicy(ThreadLocal allowControl, ThreadLocal allowAccess) { + this.allowControl = allowControl; + this.allowAccess = allowAccess; + permissions = new Permissions(); + permissions.add(new RuntimePermission("setIO")); + } + + Permissions getPermissions() { + if (allowControl.get().get() || allowAccess.get().get()) { + PermissionsBuilder builder = new PermissionsBuilder() + .addAll(permissions); + if (allowControl.get().get()) { + builder.add(CONTROL); + } + if (allowAccess.get().get()) { + builder.add(ACCESS); + } + return builder.toPermissions(); + } + return permissions; + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + return getPermissions().implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + } +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/META-INF/services/java.lang.System$LoggerFinder b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/META-INF/services/java.lang.System$LoggerFinder new file mode 100644 index 00000000000..39a1084640c --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/META-INF/services/java.lang.System$LoggerFinder @@ -0,0 +1,3 @@ +LoggerFinderLoaderTest$BaseLoggerFinder +LoggerFinderLoaderTest$BaseLoggerFinder2 + diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/PlatformLoggerBridgeTest/CustomSystemClassLoader.java b/jdk/test/java/lang/System/LoggerFinder/internal/PlatformLoggerBridgeTest/CustomSystemClassLoader.java new file mode 100644 index 00000000000..b0730fb0828 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/PlatformLoggerBridgeTest/CustomSystemClassLoader.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.security.AllPermission; +import java.security.Permissions; +import java.security.ProtectionDomain; + + +/** + * A custom ClassLoader to load the concrete LoggerFinder class + * with all permissions. + * + * @author danielfuchs + */ +public class CustomSystemClassLoader extends ClassLoader { + + + Class loggerFinderClass = null; + + public CustomSystemClassLoader() { + super(); + } + public CustomSystemClassLoader(ClassLoader parent) { + super(parent); + } + + private Class defineFinderClass(String name) + throws ClassNotFoundException { + final Object obj = getClassLoadingLock(name); + synchronized(obj) { + if (loggerFinderClass != null) return loggerFinderClass; + + URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation(); + File file = new File(url.getPath(), name+".class"); + if (file.canRead()) { + try { + byte[] b = Files.readAllBytes(file.toPath()); + Permissions perms = new Permissions(); + perms.add(new AllPermission()); + loggerFinderClass = defineClass( + name, b, 0, b.length, new ProtectionDomain( + this.getClass().getProtectionDomain().getCodeSource(), + perms)); + System.out.println("Loaded " + name); + return loggerFinderClass; + } catch (Throwable ex) { + ex.printStackTrace(); + throw new ClassNotFoundException(name, ex); + } + } else { + throw new ClassNotFoundException(name, + new IOException(file.toPath() + ": can't read")); + } + } + } + + @Override + public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (name.endsWith("$LogProducerFinder")) { + Class c = defineFinderClass(name); + if (resolve) { + resolveClass(c); + } + return c; + } + return super.loadClass(name, resolve); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + if (name.endsWith("$$LogProducerFinder")) { + return defineFinderClass(name); + } + return super.findClass(name); + } + +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/PlatformLoggerBridgeTest/META-INF/services/java.lang.System$LoggerFinder b/jdk/test/java/lang/System/LoggerFinder/internal/PlatformLoggerBridgeTest/META-INF/services/java.lang.System$LoggerFinder new file mode 100644 index 00000000000..a686921e3be --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/PlatformLoggerBridgeTest/META-INF/services/java.lang.System$LoggerFinder @@ -0,0 +1 @@ +PlatformLoggerBridgeTest$LogProducerFinder diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/PlatformLoggerBridgeTest/PlatformLoggerBridgeTest.java b/jdk/test/java/lang/System/LoggerFinder/internal/PlatformLoggerBridgeTest/PlatformLoggerBridgeTest.java new file mode 100644 index 00000000000..f9b8eabba40 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/PlatformLoggerBridgeTest/PlatformLoggerBridgeTest.java @@ -0,0 +1,876 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Queue; +import java.util.ResourceBundle; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.lang.System.LoggerFinder; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.util.stream.Stream; +import sun.util.logging.PlatformLogger; + +/** + * @test + * @bug 8140364 + * @summary JDK implementation specific unit test for JDK internal artifacts. + * Tests all bridge methods from PlatformLogger with the a custom + * backend whose loggers implement PlatformLogger.Bridge. + * @modules java.base/sun.util.logging + * @build CustomSystemClassLoader PlatformLoggerBridgeTest + * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader PlatformLoggerBridgeTest NOSECURITY + * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader PlatformLoggerBridgeTest NOPERMISSIONS + * @run main/othervm -Djava.system.class.loader=CustomSystemClassLoader PlatformLoggerBridgeTest WITHPERMISSIONS + * @author danielfuchs + */ +public class PlatformLoggerBridgeTest { + + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + final static AtomicLong sequencer = new AtomicLong(); + final static boolean VERBOSE = false; + static final ThreadLocal allowControl = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAccess = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAll = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + + static final Class providerClass; + static { + try { + // Preload classes before the security manager is on. + providerClass = ClassLoader.getSystemClassLoader().loadClass("PlatformLoggerBridgeTest$LogProducerFinder"); + ((LoggerFinder)providerClass.newInstance()).getLogger("foo", providerClass); + } catch (Exception ex) { + throw new ExceptionInInitializerError(ex); + } + } + + public static final Queue eventQueue = new ArrayBlockingQueue<>(128); + + public static final class LogEvent implements Cloneable { + + public LogEvent() { + this(sequencer.getAndIncrement()); + } + + LogEvent(long sequenceNumber) { + this.sequenceNumber = sequenceNumber; + } + + long sequenceNumber; + boolean isLoggable; + String loggerName; + sun.util.logging.PlatformLogger.Level level; + ResourceBundle bundle; + Throwable thrown; + Object[] args; + String msg; + Supplier supplier; + String className; + String methodName; + + Object[] toArray() { + return new Object[] { + sequenceNumber, + loggerName, + level, + isLoggable, + bundle, + msg, + supplier, + thrown, + args, + className, + methodName, + }; + } + + @Override + public String toString() { + return Arrays.deepToString(toArray()); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof LogEvent + && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray()); + } + + @Override + public int hashCode() { + return Objects.hash(toArray()); + } + + public LogEvent cloneWith(long sequenceNumber) + throws CloneNotSupportedException { + LogEvent cloned = (LogEvent)super.clone(); + cloned.sequenceNumber = sequenceNumber; + return cloned; + } + + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + String key, Throwable thrown, Object... params) { + return LogEvent.of(sequenceNumber, isLoggable, name, + null, null, level, bundle, key, + thrown, params); + } + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + Supplier supplier, Throwable thrown, Object... params) { + return LogEvent.of(sequenceNumber, isLoggable, name, + null, null, level, bundle, supplier, + thrown, params); + } + + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + String className, String methodName, + sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + String key, Throwable thrown, Object... params) { + LogEvent evt = new LogEvent(sequenceNumber); + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = thrown; + evt.msg = key; + evt.isLoggable = isLoggable; + evt.className = className; + evt.methodName = methodName; + return evt; + } + + public static LogEvent of(boolean isLoggable, String name, + String className, String methodName, + sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + String key, Throwable thrown, Object... params) { + return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name, + className, methodName, level, bundle, key, thrown, params); + } + + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + String className, String methodName, + sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + Supplier supplier, Throwable thrown, Object... params) { + LogEvent evt = new LogEvent(sequenceNumber); + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = thrown; + evt.supplier = supplier; + evt.isLoggable = isLoggable; + evt.className = className; + evt.methodName = methodName; + return evt; + } + + public static LogEvent of(boolean isLoggable, String name, + String className, String methodName, + sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + Supplier supplier, Throwable thrown, Object... params) { + return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name, + className, methodName, level, bundle, supplier, thrown, params); + } + + } + + public static class LogProducerFinder extends LoggerFinder { + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + final ConcurrentHashMap system = new ConcurrentHashMap<>(); + final ConcurrentHashMap user = new ConcurrentHashMap<>(); + + public class LoggerImpl implements Logger, PlatformLogger.Bridge { + private final String name; + private sun.util.logging.PlatformLogger.Level level = sun.util.logging.PlatformLogger.Level.INFO; + private sun.util.logging.PlatformLogger.Level OFF = sun.util.logging.PlatformLogger.Level.OFF; + private sun.util.logging.PlatformLogger.Level FINE = sun.util.logging.PlatformLogger.Level.FINE; + private sun.util.logging.PlatformLogger.Level FINER = sun.util.logging.PlatformLogger.Level.FINER; + private sun.util.logging.PlatformLogger.Level FINEST = sun.util.logging.PlatformLogger.Level.FINEST; + private sun.util.logging.PlatformLogger.Level CONFIG = sun.util.logging.PlatformLogger.Level.CONFIG; + private sun.util.logging.PlatformLogger.Level INFO = sun.util.logging.PlatformLogger.Level.INFO; + private sun.util.logging.PlatformLogger.Level WARNING = sun.util.logging.PlatformLogger.Level.WARNING; + private sun.util.logging.PlatformLogger.Level SEVERE = sun.util.logging.PlatformLogger.Level.SEVERE; + + public LoggerImpl(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isLoggable(Level level) { + return this.level != OFF && this.level.intValue() <= level.getSeverity(); + } + + @Override + public void log(Level level, ResourceBundle bundle, + String key, Throwable thrown) { + throw new UnsupportedOperationException(); + } + + @Override + public void log(Level level, ResourceBundle bundle, + String format, Object... params) { + throw new UnsupportedOperationException(); + } + + void log(LogEvent event) { + eventQueue.add(event); + } + + @Override + public void log(Level level, Supplier msgSupplier) { + throw new UnsupportedOperationException(); + } + + @Override + public void log(Level level, Supplier msgSupplier, + Throwable thrown) { + throw new UnsupportedOperationException(); + } + + @Override + public void log(sun.util.logging.PlatformLogger.Level level, String msg) { + log(LogEvent.of(isLoggable(level), name, null, null, + level, null, msg, null, (Object[])null)); + } + + @Override + public void log(sun.util.logging.PlatformLogger.Level level, + Supplier msgSupplier) { + log(LogEvent.of(isLoggable(level), name, null, null, + level, null, msgSupplier, null, (Object[])null)); + } + + @Override + public void log(sun.util.logging.PlatformLogger.Level level, String msg, + Object... params) { + log(LogEvent.of(isLoggable(level), name, null, null, + level, null, msg, null, params)); + } + + @Override + public void log(sun.util.logging.PlatformLogger.Level level, String msg, + Throwable thrown) { + log(LogEvent.of(isLoggable(level), name, null, null, + level, null, msg, thrown, (Object[])null)); + } + + @Override + public void log(sun.util.logging.PlatformLogger.Level level, Throwable thrown, + Supplier msgSupplier) { + log(LogEvent.of(isLoggable(level), name, null, null, + level, null, msgSupplier, thrown, (Object[])null)); + } + + @Override + public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, + String sourceMethod, String msg) { + log(LogEvent.of(isLoggable(level), name, + sourceClass, sourceMethod, + level, null, msg, null, (Object[])null)); + } + + @Override + public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, + String sourceMethod, Supplier msgSupplier) { + log(LogEvent.of(isLoggable(level), name, + sourceClass, sourceMethod, + level, null, msgSupplier, null, (Object[])null)); + } + + @Override + public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, + String sourceMethod, String msg, Object... params) { + log(LogEvent.of(isLoggable(level), name, + sourceClass, sourceMethod, + level, null, msg, null, params)); + } + + @Override + public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, + String sourceMethod, String msg, Throwable thrown) { + log(LogEvent.of(isLoggable(level), name, + sourceClass, sourceMethod, + level, null, msg, thrown, (Object[])null)); + } + + @Override + public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, + String sourceMethod, Throwable thrown, + Supplier msgSupplier) { + log(LogEvent.of(isLoggable(level), name, + sourceClass, sourceMethod, + level, null, msgSupplier, thrown, (Object[])null)); + } + + @Override + public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, + String sourceMethod, ResourceBundle bundle, String msg, + Object... params) { + log(LogEvent.of(isLoggable(level), name, + sourceClass, sourceMethod, + level, bundle, msg, null, params)); + } + + @Override + public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + String msg, Object... params) { + log(LogEvent.of(isLoggable(level), name, null, null, + level, bundle, msg, null, params)); + } + + @Override + public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, + String sourceMethod, ResourceBundle bundle, String msg, + Throwable thrown) { + log(LogEvent.of(isLoggable(level), name, + sourceClass, sourceMethod, + level, bundle, msg, thrown, (Object[])null)); + } + + @Override + public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, + String msg, Throwable thrown) { + log(LogEvent.of(isLoggable(level), name, null, null, + level, bundle, msg, thrown, (Object[])null)); + } + + @Override + public boolean isLoggable(sun.util.logging.PlatformLogger.Level level) { + return this.level != OFF && level.intValue() + >= this.level.intValue(); + } + + @Override + public boolean isEnabled() { + return this.level != OFF; + } + + + } + + @Override + public Logger getLogger(String name, Class caller) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(LOGGERFINDER_PERMISSION); + } + PrivilegedAction pa = () -> caller.getClassLoader(); + ClassLoader callerLoader = AccessController.doPrivileged(pa); + if (callerLoader == null) { + return system.computeIfAbsent(name, (n) -> new LoggerImpl(n)); + } else { + return user.computeIfAbsent(name, (n) -> new LoggerImpl(n)); + } + } + } + + static final sun.util.logging.PlatformLogger.Level[] julLevels = { + sun.util.logging.PlatformLogger.Level.ALL, + sun.util.logging.PlatformLogger.Level.FINEST, + sun.util.logging.PlatformLogger.Level.FINER, + sun.util.logging.PlatformLogger.Level.FINE, + sun.util.logging.PlatformLogger.Level.CONFIG, + sun.util.logging.PlatformLogger.Level.INFO, + sun.util.logging.PlatformLogger.Level.WARNING, + sun.util.logging.PlatformLogger.Level.SEVERE, + sun.util.logging.PlatformLogger.Level.OFF, + }; + + public static class MyBundle extends ResourceBundle { + + final ConcurrentHashMap map = new ConcurrentHashMap(); + + @Override + protected Object handleGetObject(String key) { + if (key.contains(" (translated)")) { + throw new RuntimeException("Unexpected key: " + key); + } + return map.computeIfAbsent(key, k -> k + " (translated)"); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(map.keySet()); + } + + } + + public static class MyHandler extends Handler { + + @Override + public java.util.logging.Level getLevel() { + return java.util.logging.Level.ALL; + } + + @Override + public void publish(LogRecord record) { + eventQueue.add(LogEvent.of(sequencer.getAndIncrement(), + true, record.getLoggerName(), + record.getSourceClassName(), + record.getSourceMethodName(), + PlatformLogger.Level.valueOf(record.getLevel().getName()), + record.getResourceBundle(), record.getMessage(), + record.getThrown(), record.getParameters())); + } + @Override + public void flush() { + } + @Override + public void close() throws SecurityException { + } + + } + + public static class MyLoggerBundle extends MyBundle { + + } + + static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS}; + + static void setSecurityManager() { + if (System.getSecurityManager() == null) { + Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll)); + System.setSecurityManager(new SecurityManager()); + } + } + + public static void main(String[] args) { + if (args.length == 0) + args = new String[] { + //"NOSECURITY", + "NOPERMISSIONS", + "WITHPERMISSIONS" + }; + + + Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> { + LoggerFinder provider; + switch (testCase) { + case NOSECURITY: + System.out.println("\n*** Without Security Manager\n"); + provider = LoggerFinder.getLoggerFinder(); + test(provider, true); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case NOPERMISSIONS: + System.out.println("\n*** With Security Manager, without permissions\n"); + setSecurityManager(); + try { + provider = LoggerFinder.getLoggerFinder(); + throw new RuntimeException("Expected exception not raised"); + } catch (AccessControlException x) { + if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) { + throw new RuntimeException("Unexpected permission check", x); + } + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = LoggerFinder.getLoggerFinder(); + } finally { + allowControl.get().set(control); + } + } + test(provider, false); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case WITHPERMISSIONS: + System.out.println("\n*** With Security Manager, with access permission\n"); + setSecurityManager(); + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = LoggerFinder.getLoggerFinder(); + } finally { + allowControl.get().set(control); + } + final boolean access = allowAccess.get().get(); + try { + allowAccess.get().set(true); + test(provider, true); + } finally { + allowAccess.get().set(access); + } + break; + default: + throw new RuntimeException("Unknown test case: " + testCase); + } + }); + System.out.println("\nPASSED: Tested " + sequencer.get() + " cases."); + } + + public static void test(LoggerFinder provider, boolean hasRequiredPermissions) { + + final Map loggerDescMap = new HashMap<>(); + + PlatformLogger sysLogger1 = null; + try { + sysLogger1 = PlatformLogger.getLogger("foo"); + loggerDescMap.put(sysLogger1, "PlatformLogger.getLogger(\"foo\")"); + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a system logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(SimplePolicy.ACCESS_LOGGING)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + final boolean old = allowAccess.get().get(); + allowAccess.get().set(true); + try { + sysLogger1 = PlatformLogger.getLogger("foo"); + loggerDescMap.put(sysLogger1, "PlatformLogger.getLogger(\"foo\")"); + } finally { + allowAccess.get().set(old); + } + System.out.println("Got expected exception for system logger: " + acx); + } + + final LogProducerFinder.LoggerImpl sysSink; + boolean old = allowControl.get().get(); + allowControl.get().set(true); + try { + sysSink = LogProducerFinder.LoggerImpl.class.cast( + provider.getLogger("foo", Thread.class)); + } finally { + allowControl.get().set(old); + } + + testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink); + } + + public static class Foo { + + } + + static void verbose(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + + static void checkLogEvent(LoggerFinder provider, String desc, + LogEvent expected) { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + + static void checkLogEvent(LoggerFinder provider, String desc, + LogEvent expected, boolean expectNotNull) { + LogEvent actual = eventQueue.poll(); + if (actual == null && !expectNotNull) return; + if (actual != null && !expectNotNull) { + throw new RuntimeException("Unexpected log event found for " + desc + + "\n\tgot: " + actual); + } + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + + static void setLevel( LogProducerFinder.LoggerImpl sink, + sun.util.logging.PlatformLogger.Level loggerLevel) { + sink.level = loggerLevel; + } + + // Calls the methods defined on LogProducer and verify the + // parameters received by the underlying LogProducerFinder.LoggerImpl + // logger. + private static void testLogger(LoggerFinder provider, + Map loggerDescMap, + String name, + ResourceBundle loggerBundle, + PlatformLogger logger, + LogProducerFinder.LoggerImpl sink) { + + System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]"); + final sun.util.logging.PlatformLogger.Level OFF = sun.util.logging.PlatformLogger.Level.OFF; + final sun.util.logging.PlatformLogger.Level ALL = sun.util.logging.PlatformLogger.Level.OFF; + + Foo foo = new Foo(); + String fooMsg = foo.toString(); + System.out.println("\tlogger.log(messageLevel, fooMsg)"); + System.out.println("\tlogger.(fooMsg)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + if (messageLevel == ALL || messageLevel == OFF) continue; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, loggerBundle, + fooMsg, (Throwable)null, (Object[])null); + String desc2 = "logger." + messageLevel.toString().toLowerCase() + + "(fooMsg): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + if (messageLevel == sun.util.logging.PlatformLogger.Level.FINEST) { + logger.finest(fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINER) { + logger.finer(fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINE) { + logger.fine(fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.CONFIG) { + logger.config(fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.INFO) { + logger.info(fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.WARNING) { + logger.warning(fooMsg); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.SEVERE) { + logger.severe(fooMsg); + checkLogEvent(provider, desc2, expected); + } + } + } + + String format = "two params [{1} {2}]"; + Object arg1 = foo; + Object arg2 = fooMsg; + System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + if (messageLevel == ALL || messageLevel == OFF) continue; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, loggerBundle, + format, (Throwable)null, arg1, arg2); + String desc2 = "logger." + messageLevel.toString().toLowerCase() + + "(format, foo, fooMsg): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + if (messageLevel == sun.util.logging.PlatformLogger.Level.FINEST) { + logger.finest(format, arg1, arg2); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINER) { + logger.finer(format, arg1, arg2); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINE) { + logger.fine(format, arg1, arg2); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.CONFIG) { + logger.config(format, arg1, arg2); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.INFO) { + logger.info(format, arg1, arg2); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.WARNING) { + logger.warning(format, arg1, arg2); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.SEVERE) { + logger.severe(format, arg1, arg2); + checkLogEvent(provider, desc2, expected); + } + } + } + + Throwable thrown = new Exception("OK: log me!"); + System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)"); + for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) { + if (messageLevel == ALL || messageLevel == OFF) continue; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, loggerBundle, + fooMsg, thrown, (Object[])null); + String desc2 = "logger." + messageLevel.toString().toLowerCase() + + "(fooMsg, thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + if (messageLevel == sun.util.logging.PlatformLogger.Level.FINEST) { + logger.finest(fooMsg, thrown); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINER) { + logger.finer(fooMsg, thrown); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINE) { + logger.fine(fooMsg, thrown); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.CONFIG) { + logger.config(fooMsg, thrown); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.INFO) { + logger.info(fooMsg, thrown); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.WARNING) { + logger.warning(fooMsg, thrown); + checkLogEvent(provider, desc2, expected); + } else if (messageLevel == sun.util.logging.PlatformLogger.Level.SEVERE) { + logger.severe(fooMsg, thrown); + checkLogEvent(provider, desc2, expected); + } + } + } + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION; + final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger"); + final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging"); + + final Permissions permissions; + final Permissions allPermissions; + final ThreadLocal allowControl; + final ThreadLocal allowAccess; + final ThreadLocal allowAll; + public SimplePolicy(ThreadLocal allowControl, + ThreadLocal allowAccess, + ThreadLocal allowAll) { + this.allowControl = allowControl; + this.allowAccess = allowAccess; + this.allowAll = allowAll; + permissions = new Permissions(); + allPermissions = new PermissionsBuilder() + .add(new java.security.AllPermission()) + .toPermissions(); + } + + Permissions getPermissions() { + if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) { + PermissionsBuilder builder = new PermissionsBuilder() + .addAll(permissions); + if (allowControl.get().get()) { + builder.add(CONTROL); + } + if (allowAccess.get().get()) { + builder.add(ACCESS_LOGGER); + builder.add(ACCESS_LOGGING); + } + if (allowAll.get().get()) { + builder.addAll(allPermissions); + } + return builder.toPermissions(); + } + return permissions; + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + return getPermissions().implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + } +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/api/LoggerFinderAPITest.java b/jdk/test/java/lang/System/LoggerFinder/internal/api/LoggerFinderAPITest.java new file mode 100644 index 00000000000..b616ec81944 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/api/LoggerFinderAPITest.java @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8140364 + * @author danielfuchs + * @summary JDK implementation specific unit test for JDK internal artifacts. + * Tests the consistency of the LoggerFinder and JDK extensions. + * @modules java.base/sun.util.logging + * java.base/jdk.internal.logger + * @run main LoggerFinderAPITest + */ + + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.function.Supplier; +import java.util.logging.ConsoleHandler; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import sun.util.logging.PlatformLogger; + +public class LoggerFinderAPITest { + + static final Class spiLoggerClass + = java.lang.System.Logger.class; + static final Class jdkLoggerClass + = java.lang.System.Logger.class; + static final Class bridgeLoggerClass + = sun.util.logging.PlatformLogger.Bridge.class; + static final Class julLoggerClass + = java.util.logging.Logger.class; + static final Class julLogProducerClass + = PlatformLogger.Bridge.class; + static final Pattern julLogNames = Pattern.compile( + "^((log(p|rb)?)|severe|warning|info|config|fine|finer|finest|isLoggable)$"); + static final Collection julLoggerIgnores; + static { + List ignores = new ArrayList<>(); + try { + ignores.add(julLoggerClass.getDeclaredMethod("log", LogRecord.class)); + } catch (NoSuchMethodException | SecurityException ex) { + throw new ExceptionInInitializerError(ex); + } + julLoggerIgnores = Collections.unmodifiableList(ignores); + } + + + + // Don't require LoggerBridge to have a body for those methods + interface LoggerBridgeMethodsWithNoBody extends + PlatformLogger.Bridge, java.lang.System.Logger { + + @Override + public default String getName() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public default boolean isLoggable(PlatformLogger.Level level) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public default void log(sun.util.logging.PlatformLogger.Level level, + String msg, Throwable thrown) { + } + @Override + public default void log(sun.util.logging.PlatformLogger.Level level, + Throwable thrown, Supplier msgSupplier) { + } + @Override + public default void log(sun.util.logging.PlatformLogger.Level level, + Supplier msgSupplier) { + } + @Override + public default void log(sun.util.logging.PlatformLogger.Level level, String msg) { + } + @Override + public default void log(sun.util.logging.PlatformLogger.Level level, + String format, Object... params) { + } + @Override + public default void logrb(sun.util.logging.PlatformLogger.Level level, + ResourceBundle bundle, String key, Throwable thrown) { + } + @Override + public default void logrb(sun.util.logging.PlatformLogger.Level level, + ResourceBundle bundle, String format, Object... params) { + } + + @Override + public default void logrb(PlatformLogger.Level level, + String sourceClass, String sourceMethod, + ResourceBundle bundle, String msg, Throwable thrown) { + } + + @Override + public default void logrb(PlatformLogger.Level level, String sourceClass, + String sourceMethod, ResourceBundle bundle, String msg, + Object... params) { + } + + @Override + public default void logp(PlatformLogger.Level level, String sourceClass, + String sourceMethod, Supplier msgSupplier) { + } + + @Override + public default void logp(PlatformLogger.Level level, String sourceClass, + String sourceMethod, String msg, Object... params) { + } + + @Override + public default void logp(PlatformLogger.Level level, String sourceClass, + String sourceMethod, String msg, Throwable thrown) { + } + + @Override + public default void logp(PlatformLogger.Level level, String sourceClass, + String sourceMethod, String msg) { + } + + @Override + public default void logp(PlatformLogger.Level level, String sourceClass, + String sourceMethod, Throwable thrown, + Supplier msgSupplier) { + } + + static boolean requiresDefaultBodyFor(Method m) { + try { + Method m2 = LoggerBridgeMethodsWithNoBody.class + .getDeclaredMethod(m.getName(), + m.getParameterTypes()); + return !m2.isDefault(); + } catch (NoSuchMethodException x) { + return true; + } + } + } + + final boolean warnDuplicateMappings; + public LoggerFinderAPITest(boolean verbose) { + this.warnDuplicateMappings = verbose; + for (Handler h : Logger.getLogger("").getHandlers()) { + if (h instanceof ConsoleHandler) { + Logger.getLogger("").removeHandler(h); + } + } + Logger.getLogger("").addHandler( new Handler() { + @Override + public void publish(LogRecord record) { + StringBuilder builder = new StringBuilder(); + builder.append("GOT LogRecord: ") + .append(record.getLevel().getLocalizedName()) + .append(": [").append(record.getLoggerName()) + .append("] ").append(record.getSourceClassName()) + .append('.') + .append(record.getSourceMethodName()).append(" -> ") + .append(record.getMessage()) + .append(' ') + .append(record.getParameters() == null ? "" + : Arrays.toString(record.getParameters())) + ; + System.out.println(builder); + if (record.getThrown() != null) { + record.getThrown().printStackTrace(System.out); + } + } + @Override public void flush() {} + @Override public void close() {} + }); + } + + public Stream getJulLogMethodStream(Class loggerClass) { + + return Stream.of(loggerClass.getMethods()).filter((x) -> { + final Matcher m = julLogNames.matcher(x.getName()); + return m.matches() ? x.getAnnotation(Deprecated.class) == null : false; + }); + } + + /** + * Tells whether a method invocation of 'origin' can be transformed in a + * method invocation of 'target'. + * This method only look at the parameter signatures, it doesn't look at + * the name, nor does it look at the return types. + *

    + * Example: + *

      + *
    • java.util.logging.Logger.log(Level, String, Object) can be invoked as
      + java.util.logging.spi.Logger.log(Level, String, Object...) because the + last parameter in 'target' is a varargs.
    • + *
    • java.util.logging.Logger.log(Level, String) can also be invoked as
      + java.util.logging.spi.Logger.log(Level, String, Object...) for the + same reason.
    • + *
    + *

    + * The algorithm is tailored for our needs: when the last parameter in the + * target is a vararg, and when origin & target have the same number of + * parameters, then we consider that the types of the last parameter *must* + * match. + *

    + * Similarly - we do not consider that o(X x, Y y, Y y) matches t(X x, Y... y) + * although strictly speaking, it should... + * + * @param origin The method in the original class + * @param target The correspondent candidate in the target class + * @return true if a method invocation of 'origin' can be transformed in a + * method invocation of 'target'. + */ + public boolean canBeInvokedAs(Method origin, Method target, + Map,Class> substitutes) { + final Class[] xParams = target.getParameterTypes(); + final Class[] mParams = Stream.of(origin.getParameterTypes()) + .map((x) -> substitutes.getOrDefault(x, x)) + .collect(Collectors.toList()).toArray(new Class[0]); + if (Arrays.deepEquals(xParams, mParams)) return true; + if (target.isVarArgs()) { + if (xParams.length == mParams.length) { + if (xParams[xParams.length-1].isArray()) { + return mParams[mParams.length -1].equals( + xParams[xParams.length -1].getComponentType()); + } + } else if (xParams.length == mParams.length + 1) { + return Arrays.deepEquals( + Arrays.copyOfRange(xParams, 0, xParams.length-1), mParams); + } + } + return false; + } + + /** + * Look whether {@code otherClass} has a public method similar to m + * @param m + * @param otherClass + * @return + */ + public Stream findInvokable(Method m, Class otherClass) { + final Map,Class> substitues = + Collections.singletonMap(java.util.logging.Level.class, + sun.util.logging.PlatformLogger.Level.class); + return Stream.of(otherClass.getMethods()) + .filter((x) -> m.getName().equals(x.getName())) + .filter((x) -> canBeInvokedAs(m, x, substitues)); + } + + /** + * Test that the concrete Logger implementation passed as parameter + * overrides all the methods defined by its interface. + * @param julLogger A concrete implementation of System.Logger + * whose backend is a JUL Logger. + */ + StringBuilder testDefaultJULLogger(java.lang.System.Logger julLogger) { + final StringBuilder errors = new StringBuilder(); + if (!bridgeLoggerClass.isInstance(julLogger)) { + final String errorMsg = + "Logger returned by LoggerFactory.getLogger(\"foo\") is not a " + + bridgeLoggerClass + "\n\t" + julLogger; + System.err.println(errorMsg); + errors.append(errorMsg).append('\n'); + } + final Class xClass = julLogger.getClass(); + List notOverridden = + Stream.of(bridgeLoggerClass.getDeclaredMethods()).filter((m) -> { + try { + Method x = xClass.getDeclaredMethod(m.getName(), m.getParameterTypes()); + return x == null; + } catch (NoSuchMethodException ex) { + return !Modifier.isStatic(m.getModifiers()); + } + }).collect(Collectors.toList()); + notOverridden.stream().filter((x) -> { + boolean shouldOverride = true; + try { + final Method m = xClass.getMethod(x.getName(), x.getParameterTypes()); + Method m2 = null; + try { + m2 = jdkLoggerClass.getDeclaredMethod(x.getName(), x.getParameterTypes()); + } catch (Exception e) { + + } + shouldOverride = m.isDefault() || m2 == null; + } catch (Exception e) { + // should override. + } + return shouldOverride; + }).forEach(x -> { + final String errorMsg = xClass.getName() + " should override\n\t" + x.toString(); + System.err.println(errorMsg); + errors.append(errorMsg).append('\n'); + }); + if (notOverridden.isEmpty()) { + System.out.println(xClass + " overrides all methods from " + bridgeLoggerClass); + } + return errors; + } + + public static class ResourceBundeParam extends ResourceBundle { + Map map = Collections.synchronizedMap(new LinkedHashMap<>()); + @Override + protected Object handleGetObject(String key) { + map.putIfAbsent(key, "${"+key+"}"); + return map.get(key); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(new LinkedHashSet<>(map.keySet())); + } + + } + + final ResourceBundle bundleParam = + ResourceBundle.getBundle(ResourceBundeParam.class.getName()); + + public static class ResourceBundeLocalized extends ResourceBundle { + Map map = Collections.synchronizedMap(new LinkedHashMap<>()); + @Override + protected Object handleGetObject(String key) { + map.putIfAbsent(key, "Localized:${"+key+"}"); + return map.get(key); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(new LinkedHashSet<>(map.keySet())); + } + + } + + final static ResourceBundle bundleLocalized = + ResourceBundle.getBundle(ResourceBundeLocalized.class.getName()); + + final Map, Object> params = new HashMap<>(); + { + params.put(String.class, "TestString"); + params.put(sun.util.logging.PlatformLogger.Level.class, sun.util.logging.PlatformLogger.Level.WARNING); + params.put(java.lang.System.Logger.Level.class, java.lang.System.Logger.Level.WARNING); + params.put(ResourceBundle.class, bundleParam); + params.put(Throwable.class, new Throwable("TestThrowable (Please ignore it!)")); + params.put(Object[].class, new Object[] {"One", "Two"}); + params.put(Object.class, new Object() { + @Override public String toString() { return "I am an object!"; } + }); + } + + public Object[] getParamsFor(Method m) { + final Object[] res = new Object[m.getParameterCount()]; + final Class[] sig = m.getParameterTypes(); + if (res.length == 0) { + return res; + } + for (int i=0; i) () -> msg; + } + if (p instanceof String) { + res[i] = String.valueOf(p)+"["+i+"]"; + } else { + res[i] = p; + } + } + return res; + } + + public void invokeOn(java.lang.System.Logger logger, Method m) { + Object[] p = getParamsFor(m); + try { + m.invoke(logger, p); + } catch (Exception e) { + throw new RuntimeException("Failed to invoke "+m.toString(), e); + } + } + + public void testAllJdkExtensionMethods(java.lang.System.Logger logger) { + Stream.of(jdkLoggerClass.getDeclaredMethods()) + .filter(m -> !Modifier.isStatic(m.getModifiers())) + .forEach((m) -> invokeOn(logger, m)); + } + + public void testAllAPIMethods(java.lang.System.Logger logger) { + Stream.of(spiLoggerClass.getDeclaredMethods()) + .filter(m -> !Modifier.isStatic(m.getModifiers())) + .forEach((m) -> invokeOn(logger, m)); + } + + public void testAllBridgeMethods(java.lang.System.Logger logger) { + Stream.of(bridgeLoggerClass.getDeclaredMethods()) + .filter(m -> !Modifier.isStatic(m.getModifiers())) + .forEach((m) -> invokeOn(logger, m)); + } + + public void testAllLogProducerMethods(java.lang.System.Logger logger) { + Stream.of(julLogProducerClass.getDeclaredMethods()) + .filter(m -> !Modifier.isStatic(m.getModifiers())) + .forEach((m) -> invokeOn(logger, m)); + } + + public StringBuilder testGetLoggerOverriddenOnSpi() { + final StringBuilder errors = new StringBuilder(); + Stream.of(jdkLoggerClass.getDeclaredMethods()) + .filter(m -> Modifier.isStatic(m.getModifiers())) + .filter(m -> Modifier.isPublic(m.getModifiers())) + .filter(m -> !m.getName().equals("getLoggerFinder")) + .filter(m -> { + try { + final Method x = bridgeLoggerClass.getDeclaredMethod(m.getName(), m.getParameterTypes()); + return x == null; + } catch (NoSuchMethodException ex) { + return true; + } + }).forEach(m -> { + final String errorMsg = bridgeLoggerClass.getName() + " should override\n\t" + m.toString(); + System.err.println(errorMsg); + errors.append(errorMsg).append('\n'); + }); + if (errors.length() == 0) { + System.out.println(bridgeLoggerClass + " overrides all static methods from " + jdkLoggerClass); + } else { + if (errors.length() > 0) throw new RuntimeException(errors.toString()); + } + return errors; + } + + public static void main(String argv[]) throws Exception { + final LoggerFinderAPITest test = new LoggerFinderAPITest(false); + final StringBuilder errors = new StringBuilder(); + errors.append(test.testGetLoggerOverriddenOnSpi()); + java.lang.System.Logger julLogger = + java.lang.System.LoggerFinder.getLoggerFinder() + .getLogger("foo", LoggerFinderAPITest.class); + errors.append(test.testDefaultJULLogger(julLogger)); + if (errors.length() > 0) throw new RuntimeException(errors.toString()); + java.lang.System.Logger julSystemLogger = + java.lang.System.LoggerFinder.getLoggerFinder() + .getLogger("bar", Thread.class); + errors.append(test.testDefaultJULLogger(julSystemLogger)); + if (errors.length() > 0) throw new RuntimeException(errors.toString()); + java.lang.System.Logger julLocalizedLogger = + (java.lang.System.Logger) + System.getLogger("baz", bundleLocalized); + java.lang.System.Logger julLocalizedSystemLogger = + java.lang.System.LoggerFinder.getLoggerFinder() + .getLocalizedLogger("oof", bundleLocalized, Thread.class); + final String error = errors.toString(); + if (!error.isEmpty()) throw new RuntimeException(error); + for (java.lang.System.Logger logger : new java.lang.System.Logger[] { + julLogger, julSystemLogger, julLocalizedLogger, julLocalizedSystemLogger + }) { + test.testAllJdkExtensionMethods(logger); + test.testAllAPIMethods(logger); + test.testAllBridgeMethods(logger); + test.testAllLogProducerMethods(logger); + } + } + +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/backend/LoggerFinderBackendTest.java b/jdk/test/java/lang/System/LoggerFinder/internal/backend/LoggerFinderBackendTest.java new file mode 100644 index 00000000000..226f2f7cf30 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/backend/LoggerFinderBackendTest.java @@ -0,0 +1,2316 @@ +/* + * Copyright (c) 2015, 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. + */ + +/** + * @test + * @bug 8140364 + * @author danielfuchs + * @summary JDK implementation specific unit test for JDK internal artifacts. + * This test tests all the public API methods defined in the {@link + * java.lang.System.Logger} interface, as well as all the JDK + * internal methods defined in the + * {@link sun.util.logging.PlatformLogger.Bridge} + * interface, with loggers returned by {@link + * java.lang.System.LoggerFinder#getLogger(java.lang.String, java.lang.Class)} + * and {@link java.lang.System.LoggerFinder#getLocalizedLogger(java.lang.String, + * java.util.ResourceBundle, java.lang.Class)} + * (using both a null resource bundle and a non null resource bundle). + * It calls both the {@link java.lang.System} factory methods and + * {@link jdk.internal.logger.LazyLoggers} to obtains those loggers, + * and configure them with all possible known levels. + * @modules java.base/sun.util.logging + * java.base/jdk.internal.logger + * java.logging/sun.util.logging.internal + * @build LoggerFinderBackendTest SystemClassLoader + * @run main/othervm -Djava.system.class.loader=SystemClassLoader -Dtest.logger.hidesProvider=true LoggerFinderBackendTest + * @run main/othervm -Djava.system.class.loader=SystemClassLoader -Dtest.logger.hidesProvider=false LoggerFinderBackendTest + */ + + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.ResourceBundle; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiFunction; +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.function.Supplier; +import java.lang.System.LoggerFinder; +import java.util.logging.ConsoleHandler; +import java.util.logging.Handler; +import sun.util.logging.PlatformLogger.Level; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import sun.util.logging.internal.LoggingProviderImpl; + +/** + * @author danielfuchs + */ +public class LoggerFinderBackendTest { + + // whether the implementation of Logger try to do a best + // effort for logp... If the provider is not hidden, then + // the logp() implementation comes from LoggerWrapper - which does a + // best effort. Otherwise, it comes from the default provider + // which does support logp. + static final boolean BEST_EFFORT_FOR_LOGP = + !Boolean.getBoolean("test.logger.hidesProvider"); + static final boolean VERBOSE = false; + + static final Class spiLoggerClass = + java.lang.System.Logger.class; + static final Class jdkLoggerClass = + java.lang.System.Logger.class; + static final Class bridgeLoggerClass = + sun.util.logging.PlatformLogger.Bridge.class; + + /** Use to retrieve the log records that were produced by the JUL backend */ + static class LoggerTesterHandler extends Handler { + public final List records = + Collections.synchronizedList(new ArrayList<>()); + + @Override + public void publish(LogRecord record) { + record.getSourceClassName(); record.getSourceMethodName(); + records.add(record); + } + + @Override + public void flush() { + } + + @Override + public void close() throws SecurityException { + records.clear(); + } + + public void reset() { + records.clear(); + } + } + + /** The {@link LoggerTesterHandler} handler is added to the root logger. */ + static final LoggerTesterHandler handler = new LoggerTesterHandler(); + static { + for (Handler h : Logger.getLogger("").getHandlers()) { + if (h instanceof ConsoleHandler) { + Logger.getLogger("").removeHandler(h); + } + } + Logger.getLogger("").addHandler(handler); + } + + /** + * A resource handler parameter that will be used when calling out the + * logrb-like methods - as well as when calling the level-specific + * methods that take a ResourceBundle parameter. + */ + public static class ResourceBundeParam extends ResourceBundle { + Map map = Collections.synchronizedMap(new LinkedHashMap<>()); + @Override + protected Object handleGetObject(String key) { + map.putIfAbsent(key, "${"+key+"}"); + return map.get(key); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(new LinkedHashSet<>(map.keySet())); + } + + } + + final static ResourceBundle bundleParam = + ResourceBundle.getBundle(ResourceBundeParam.class.getName()); + + /** + * A resource handler parameter that will be used when creating localized + * loggers by calling {@link + * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class)}. + */ + public static class ResourceBundeLocalized extends ResourceBundle { + Map map = Collections.synchronizedMap(new LinkedHashMap<>()); + @Override + protected Object handleGetObject(String key) { + map.putIfAbsent(key, "Localized:${"+key+"}"); + return map.get(key); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(new LinkedHashSet<>(map.keySet())); + } + + } + + /** + * The Levels enum is used to call all the level-specific methods on + * a logger instance. To minimize the amount of code it uses reflection + * to do so. + */ + static Lookup lookup = MethodHandles.lookup(); + public enum Levels { + /** Used to call all forms of Logger.log?(SEVERE, ...) */ + SEVERE("severe", bridgeLoggerClass, Level.SEVERE, null, "error", false), + /** Used to call all forms of Logger.log?(WARNING,...) */ + WARNING("warning", bridgeLoggerClass, Level.WARNING, "warning", "warning", false), + /** Used to call all forms of Logger.log?(INFO,...) */ + INFO("info", bridgeLoggerClass, Level.INFO, "info", "info", false), + /** Used to call all forms of Logger.log?(CONFIG,...) */ + CONFIG("config", bridgeLoggerClass, Level.CONFIG, null, "debug", false), + /** Used to call all forms of Logger.log?(FINE,...) */ + FINE("fine", bridgeLoggerClass, Level.FINE, null, "debug", false), + /** Used to call all forms of Logger.log?(FINER,...) */ + FINER("finer", bridgeLoggerClass, Level.FINER, null, "trace", false), + /** Used to call all forms of Logger.log?(FINEST,...) */ + FINEST("finest", bridgeLoggerClass, Level.FINEST, null, "trace", false), + ; + public final String method; // The name of the level-specific method to call + public final Class definingClass; // which interface j.u.logger.Logger or j.u.logging.spi.Logger defines it + public final Level platformLevel; // The platform Level it will be mapped to in Jul when Jul is the backend + public final String jdkExtensionToJUL; // The name of the method called on the JUL logger when JUL is the backend + public final String julToJdkExtension; // The name of the method called in the jdk extension by the default impl in jdk.internal.logging.Logger + public final String enableMethod; // The name of the isXxxxEnabled method + public final boolean hasSpecificIsEnabled; + Levels(String method, Class definingClass, Level defaultMapping, + String jdkExtensionToJUL, String julToJdkExtension, + boolean hasSpecificIsEnabled) { + this.method = method; + this.definingClass = definingClass; + this.platformLevel = defaultMapping; + this.jdkExtensionToJUL = jdkExtensionToJUL; + this.julToJdkExtension = julToJdkExtension; + this.hasSpecificIsEnabled = hasSpecificIsEnabled; + if (hasSpecificIsEnabled) { + this.enableMethod = "is" + method.substring(0,1).toUpperCase() + + method.substring(1) + "Enabled"; + } else { + this.enableMethod = "isLoggable"; + } + } + + /* + * calls this level specific method - e.g. if this==INFO: logger.info(msg); + */ + public void level(Object logger, String msg) { + MethodType mt = MethodType.methodType(void.class, Level.class, String.class); + invoke("log", logger, mt, platformLevel, msg); + } + + /* + * calls this level specific method - e.g. if this==INFO: logger.info(msgSupplier); + */ + public void level(Object logger, Supplier msgSupplier) { + MethodType mt = MethodType.methodType(void.class, Level.class, Supplier.class); + invoke("log", logger, mt, platformLevel, msgSupplier); + } + + /* + * calls this level specific method - e.g. if this==INFO: logger.info(msg, params); + */ + public void level(Object logger, String msg, Object... params) { + MethodType mt = MethodType.methodType(void.class, Level.class, String.class, + Object[].class); + invoke("log", logger, mt, platformLevel, msg, params); + } + + /* + * calls this level specific method - e.g. if this==INFO: logger.info(msg, thrown); + */ + public void level(Object logger, String msg, Throwable thrown) { + MethodType mt = MethodType.methodType(void.class, Level.class, String.class, + Throwable.class); + invoke("log", logger, mt, platformLevel, msg, thrown); + } + + /* + * calls this level specific method - e.g. if this==INFO: logger.info(msgSupplier, thrown); + */ + public void level(Object logger, Supplier msgSupplier, Throwable thrown) { + MethodType mt = MethodType.methodType(void.class, Level.class, + Throwable.class, Supplier.class); + invoke("log", logger, mt, platformLevel, thrown, msgSupplier); + } + + /* + * calls this level specific method - e.g. if this==INFO: logger.info(bundle, msg); + */ + public void level(Object logger, String msg, ResourceBundle bundle) { + MethodType mt = MethodType.methodType(void.class, Level.class, + ResourceBundle.class, String.class, Object[].class); + invoke("logrb", logger, mt, platformLevel, bundle, msg, null); + } + + public void level(Object logger, String msg, ResourceBundle bundle, + Object... params) { + MethodType mt = MethodType.methodType(void.class, Level.class, + ResourceBundle.class, String.class, Object[].class); + invoke("logrb", logger, mt, platformLevel, bundle, msg, params); + } + + public void level(Object logger, String msg, ResourceBundle bundle, + Throwable thrown) { + MethodType mt = MethodType.methodType(void.class, Level.class, + ResourceBundle.class, String.class, Throwable.class); + invoke("logrb", logger, mt, platformLevel, bundle, msg, thrown); + } + + public boolean isEnabled(Object logger) { + try { + if (hasSpecificIsEnabled) { + MethodType mt = MethodType.methodType(boolean.class); + final MethodHandle handle = lookup.findVirtual(definingClass, + enableMethod, mt).bindTo(logger); + return Boolean.class.cast(handle.invoke()); + } else { + MethodType mt = MethodType.methodType(boolean.class, + Level.class); + final MethodHandle handle = lookup.findVirtual(definingClass, + enableMethod, mt).bindTo(logger); + return Boolean.class.cast(handle.invoke(platformLevel)); + } + } catch (Throwable ex) { + throw new RuntimeException(ex); + } + } + + private void invoke(String method, Object logger, MethodType mt, Object... args) { + try { + final int last = mt.parameterCount()-1; + boolean isVarargs = mt.parameterType(last).isArray(); + final MethodHandle handle = lookup.findVirtual(definingClass, + method, mt).bindTo(logger); + + final StringBuilder builder = new StringBuilder(); + builder.append(logger.getClass().getSimpleName()).append('.') + .append(method).append('('); + String sep = ""; + int offset = 0; + Object[] params = args; + for (int i=0; (i-offset) < params.length; i++) { + if (isVarargs && i == last) { + offset = last; + params = (Object[])args[i]; + if (params == null) break; + } + Object p = params[i - offset]; + String quote = (p instanceof String) ? "\"" : ""; + builder.append(sep).append(quote).append(p).append(quote); + sep = ", "; + } + builder.append(')'); + if (verbose) { + System.out.println(builder); + } + handle.invokeWithArguments(args); + } catch (Throwable ex) { + throw new RuntimeException(ex); + } + } + + }; + + static interface Checker extends BiFunction {} + static interface JdkLogTester + extends BiFunction {} + static interface SpiLogTester + extends BiFunction {} + + static interface MethodInvoker { + public void logX(LOGGER logger, LEVEL level, Object... args); + } + + public enum JdkLogMethodInvoker + implements MethodInvoker { + /** + * Tests {@link + * jdk.internal.logging.Logger#log(Level, String, Object...)}; + **/ + LOG_STRING_PARAMS("log", MethodType.methodType(void.class, + Level.class, String.class, Object[].class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#log(Level, String, Throwable)}; + **/ + LOG_STRING_THROWN("log", MethodType.methodType(void.class, + Level.class, String.class, Throwable.class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#log(Level, Supplier)}; + **/ + LOG_SUPPLIER("log", MethodType.methodType(void.class, + Level.class, Supplier.class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#log(Level, Throwable, Supplier)}; + **/ + LOG_SUPPLIER_THROWN("log", MethodType.methodType(void.class, + Level.class, Throwable.class, Supplier.class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#logp(Level, String, String, String)}; + **/ + LOGP_STRING("logp", MethodType.methodType(void.class, + Level.class, String.class, String.class, String.class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#logp(Level, String, String, String, Object...)}; + **/ + LOGP_STRING_PARAMS("logp", MethodType.methodType(void.class, + Level.class, String.class, String.class, String.class, Object[].class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#logp(Level, String, String, String, Throwable)}; + **/ + LOGP_STRING_THROWN("logp", MethodType.methodType(void.class, + Level.class, String.class, String.class, String.class, Throwable.class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#logp(Level, String, String, Supplier)}; + **/ + LOGP_SUPPLIER("logp", MethodType.methodType(void.class, + Level.class, String.class, String.class, Supplier.class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#logp(Level, String, String, Throwable, Supplier)}; + **/ + LOGP_SUPPLIER_THROWN("logp", MethodType.methodType(void.class, + Level.class, String.class, String.class, + Throwable.class, Supplier.class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Object...)}; + **/ + LOGRB_STRING_PARAMS("logrb", MethodType.methodType(void.class, + Level.class, ResourceBundle.class, String.class, Object[].class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Throwable)}; + **/ + LOGRB_STRING_THROWN("logrb", MethodType.methodType(void.class, + Level.class, ResourceBundle.class, String.class, Throwable.class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#logrb(Level, String, String, ResourceBundle, String, Object...)}; + **/ + LOGRBP_STRING_PARAMS("logrb", MethodType.methodType(void.class, + Level.class, String.class, String.class, ResourceBundle.class, + String.class, Object[].class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#logrb(Level, String, String, ResourceBundle, String, Throwable)}; + **/ + LOGRBP_STRING_THROWN("logrb", MethodType.methodType(void.class, + Level.class, String.class, String.class, ResourceBundle.class, + String.class, Throwable.class)), + ; + final MethodType mt; + final String method; + JdkLogMethodInvoker(String method, MethodType mt) { + this.mt = mt; + this.method = method; + } + Object[] makeArgs(Level level, Object... rest) { + List list = new ArrayList<>(rest == null ? 1 : rest.length + 1); + list.add(level); + if (rest != null) { + list.addAll(Arrays.asList(rest)); + } + return list.toArray(new Object[list.size()]); + } + + @Override + public void logX(sun.util.logging.PlatformLogger.Bridge logger, Level level, Object... args) { + try { + MethodHandle handle = lookup.findVirtual(bridgeLoggerClass, + method, mt).bindTo(logger); + final int last = mt.parameterCount()-1; + boolean isVarargs = mt.parameterType(last).isArray(); + + args = makeArgs(level, args); + + final StringBuilder builder = new StringBuilder(); + builder.append(logger.getClass().getSimpleName()).append('.') + .append(this.method).append('('); + String sep = ""; + int offset = 0; + Object[] params = args; + for (int i=0; (i-offset) < params.length; i++) { + if (isVarargs && i == last) { + offset = last; + params = (Object[])args[i]; + if (params == null) break; + } + Object p = params[i - offset]; + String quote = (p instanceof String) ? "\"" : ""; + p = p instanceof Level ? "Level."+p : p; + builder.append(sep).append(quote).append(p).append(quote); + sep = ", "; + } + builder.append(')'); + if (verbose) System.out.println(builder); + handle.invokeWithArguments(args); + } catch (Throwable ex) { + throw new RuntimeException(ex); + } + } + } + + + public enum SpiLogMethodInvoker implements MethodInvoker { + /** + * Tests {@link + * jdk.internal.logging.Logger#log(Level, String, Object...)}; + **/ + LOG_STRING_PARAMS("log", MethodType.methodType(void.class, + java.lang.System.Logger.Level.class, String.class, Object[].class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#log(Level, String, Throwable)}; + **/ + LOG_STRING_THROWN("log", MethodType.methodType(void.class, + java.lang.System.Logger.Level.class, String.class, Throwable.class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#log(Level, Supplier)}; + **/ + LOG_SUPPLIER("log", MethodType.methodType(void.class, + java.lang.System.Logger.Level.class, Supplier.class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#log(Level, Throwable, Supplier)}; + **/ + LOG_SUPPLIER_THROWN("log", MethodType.methodType(void.class, + java.lang.System.Logger.Level.class, Supplier.class, Throwable.class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#log(Level, Supplier)}; + **/ + LOG_OBJECT("log", MethodType.methodType(void.class, + java.lang.System.Logger.Level.class, Object.class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Object...)}; + **/ + LOGRB_STRING_PARAMS("log", MethodType.methodType(void.class, + java.lang.System.Logger.Level.class, ResourceBundle.class, + String.class, Object[].class)), + /** + * Tests {@link + * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Throwable)}; + **/ + LOGRB_STRING_THROWN("log", MethodType.methodType(void.class, + java.lang.System.Logger.Level.class, ResourceBundle.class, + String.class, Throwable.class)), + ; + final MethodType mt; + final String method; + SpiLogMethodInvoker(String method, MethodType mt) { + this.mt = mt; + this.method = method; + } + Object[] makeArgs(java.lang.System.Logger.Level level, Object... rest) { + List list = new ArrayList<>(rest == null ? 1 : rest.length + 1); + list.add(level); + if (rest != null) { + list.addAll(Arrays.asList(rest)); + } + return list.toArray(new Object[list.size()]); + } + + @Override + public void logX(java.lang.System.Logger logger, + java.lang.System.Logger.Level level, Object... args) { + try { + MethodHandle handle = lookup.findVirtual(spiLoggerClass, + method, mt).bindTo(logger); + final int last = mt.parameterCount()-1; + boolean isVarargs = mt.parameterType(last).isArray(); + + args = makeArgs(level, args); + + final StringBuilder builder = new StringBuilder(); + builder.append(logger.getClass().getSimpleName()).append('.') + .append(this.method).append('('); + String sep = ""; + int offset = 0; + Object[] params = args; + for (int i=0; (i-offset) < params.length; i++) { + if (isVarargs && i == last) { + offset = last; + params = (Object[])args[i]; + if (params == null) break; + } + Object p = params[i - offset]; + String quote = (p instanceof String) ? "\"" : ""; + p = p instanceof Level ? "Level."+p : p; + builder.append(sep).append(quote).append(p).append(quote); + sep = ", "; + } + builder.append(')'); + if (verbose) System.out.println(builder); + handle.invokeWithArguments(args); + } catch (Throwable ex) { + throw new RuntimeException(ex); + } + } + } + + + public abstract static class BackendTester { + static final Level[] levelMap = {Level.ALL, Level.FINER, Level.FINE, + Level.INFO, Level.WARNING, Level.SEVERE, Level.OFF}; + + abstract class BackendAdaptor { + public abstract String getLoggerName(BackendRecord res); + public abstract Object getLevel(BackendRecord res); + public abstract String getMessage(BackendRecord res); + public abstract String getSourceClassName(BackendRecord res); + public abstract String getSourceMethodName(BackendRecord res); + public abstract Throwable getThrown(BackendRecord res); + public abstract ResourceBundle getResourceBundle(BackendRecord res); + public abstract void setLevel(java.lang.System.Logger logger, + Level level); + public abstract void setLevel(java.lang.System.Logger logger, + java.lang.System.Logger.Level level); + public abstract List getBackendRecords(); + public abstract void resetBackendRecords(); + public boolean shouldBeLoggable(Levels level, Level loggerLevel) { + final Level logLevel = level.platformLevel; + return shouldBeLoggable(logLevel, loggerLevel); + } + public boolean shouldBeLoggable(Level logLevel, Level loggerLevel) { + return loggerLevel.intValue() != Level.OFF.intValue() + && logLevel.intValue() >= loggerLevel.intValue(); + } + public boolean shouldBeLoggable(java.lang.System.Logger.Level logLevel, + java.lang.System.Logger.Level loggerLevel) { + return loggerLevel != java.lang.System.Logger.Level.OFF + && logLevel.ordinal() >= loggerLevel.ordinal(); + } + public boolean isLoggable(java.lang.System.Logger logger, Level l) { + return bridgeLoggerClass.cast(logger).isLoggable(l); + } + public String getCallerClassName(Levels level, String clazz) { + return clazz != null ? clazz : Levels.class.getName(); + } + public String getCallerClassName(MethodInvoker logMethod, + String clazz) { + return clazz != null ? clazz : logMethod.getClass().getName(); + } + public String getCallerMethodName(Levels level, String method) { + return method != null ? method : "invoke"; + } + public String getCallerMethodName(MethodInvoker logMethod, + String method) { + return method != null ? method : "logX"; + } + public Object getMappedLevel(Object level) { + return level; + } + + public Level toJUL(java.lang.System.Logger.Level level) { + return levelMap[level.ordinal()]; + } + } + + public final boolean isSystem; + public final Class restrictedTo; + public final ResourceBundle localized; + public BackendTester(boolean isSystem) { + this(isSystem,null,null); + } + public BackendTester(boolean isSystem, ResourceBundle localized) { + this(isSystem,null,localized); + } + public BackendTester(boolean isSystem, + Class restrictedTo) { + this(isSystem, restrictedTo, null); + } + public BackendTester(boolean isSystem, + Class restrictedTo, + ResourceBundle localized) { + this.isSystem = isSystem; + this.restrictedTo = restrictedTo; + this.localized = localized; + } + + public java.lang.System.Logger convert(java.lang.System.Logger logger) { + return logger; + } + + public static Level[] LEVELS = { + Level.OFF, + Level.SEVERE, Level.WARNING, Level.INFO, Level.CONFIG, + Level.FINE, Level.FINER, Level.FINEST, + Level.ALL + }; + + abstract BackendAdaptor adaptor(); + + protected void checkRecord(Levels test, BackendRecord res, String loggerName, + Level level, String msg, String className, String methodName, + Throwable thrown, ResourceBundle bundle, Object... params) { + checkRecord(test, res, loggerName, level, ()->msg, className, + methodName, thrown, bundle, params); + + } + protected void checkRecord(Levels test, BackendRecord res, String loggerName, + Level level, Supplier msg, String className, String methodName, + Throwable thrown, ResourceBundle bundle, Object... params) { + checkRecord(test.method, res, loggerName, level, msg, + className, methodName, thrown, bundle, params); + } + protected void checkRecord(String logMethod, BackendRecord res, String loggerName, + L level, Supplier msg, String className, String methodName, + Throwable thrown, ResourceBundle bundle, Object... params) { + final BackendAdaptor analyzer = adaptor(); + if (! Objects.equals(analyzer.getLoggerName(res), loggerName)) { + throw new RuntimeException(logMethod+": expected logger name " + + loggerName + " got " + analyzer.getLoggerName(res)); + } + if (!Objects.equals(analyzer.getLevel(res), analyzer.getMappedLevel(level))) { + throw new RuntimeException(logMethod+": expected level " + + analyzer.getMappedLevel(level) + " got " + analyzer.getLevel(res)); + } + if (!Objects.equals(analyzer.getMessage(res), msg.get())) { + throw new RuntimeException(logMethod+": expected message \"" + + msg.get() + "\" got \"" + analyzer.getMessage(res) +"\""); + } + if (!Objects.equals(analyzer.getSourceClassName(res), className)) { + throw new RuntimeException(logMethod + + ": expected class name \"" + className + + "\" got \"" + analyzer.getSourceClassName(res) +"\""); + } + if (!Objects.equals(analyzer.getSourceMethodName(res), methodName)) { + throw new RuntimeException(logMethod + + ": expected method name \"" + methodName + + "\" got \"" + analyzer.getSourceMethodName(res) +"\""); + } + final Throwable thrownRes = analyzer.getThrown(res); + if (!Objects.equals(thrownRes, thrown)) { + throw new RuntimeException(logMethod + + ": expected throwable \"" + thrown + + "\" got \"" + thrownRes + "\""); + } + if (!Objects.equals(analyzer.getResourceBundle(res), bundle)) { + throw new RuntimeException(logMethod + + ": expected bundle \"" + bundle + + "\" got \"" + analyzer.getResourceBundle(res) +"\""); + } + } + + public void testLevel(Levels level, java.lang.System.Logger logger, + String msg) { + Runnable test = () -> level.level(logger, msg); + Checker check = (res, l) -> { + checkRecord(level, res, logger.getName(), l, msg, + adaptor().getCallerClassName(level, Levels.class.getName()), + adaptor().getCallerMethodName(level, "invoke"), + null, localized); + return null; + }; + test("msg", level, logger, test, check); + } + + public void testLevel(Levels level, java.lang.System.Logger logger, + String msg, Object... params) { + Runnable test = () -> level.level(logger, msg, (Object[])params); + Checker check = (res, l) -> { + checkRecord(level, res, logger.getName(), l, msg, + adaptor().getCallerClassName(level, Levels.class.getName()), + adaptor().getCallerMethodName(level, "invoke"), + null, localized, (Object[])params); + return null; + }; + test("msg, params", level, logger, test, check); + } + + public void testLevel(Levels level, java.lang.System.Logger logger, + String msg, Throwable thrown) { + Runnable test = () -> level.level(logger, msg, thrown); + Checker check = (res, l) -> { + checkRecord(level, res, logger.getName(), l, msg, + adaptor().getCallerClassName(level, Levels.class.getName()), + adaptor().getCallerMethodName(level, "invoke"), + thrown, localized); + return null; + }; + test("msg, thrown", level, logger, test, check); + } + + public void testLevel(Levels level, java.lang.System.Logger logger, + Supplier msg) { + Runnable test = () -> level.level(logger, msg); + Checker check = (res, l) -> { + checkRecord(level, res, logger.getName(), l, msg, + adaptor().getCallerClassName(level, Levels.class.getName()), + adaptor().getCallerMethodName(level, "invoke"), + null, null); + return null; + }; + test("msgSupplier", level, logger, test, check); + } + + public void testLevel(Levels level, java.lang.System.Logger logger, + Supplier msg, Throwable thrown) { + Runnable test = () -> level.level(logger, msg, thrown); + Checker check = (res, l) -> { + checkRecord(level, res, logger.getName(), l, msg, + adaptor().getCallerClassName(level, Levels.class.getName()), + adaptor().getCallerMethodName(level, "invoke"), + thrown, null); + return null; + }; + test("throw, msgSupplier", level, logger, test, check); + } + + public void testLevel(Levels level, java.lang.System.Logger logger, + String msg, ResourceBundle bundle) { + Runnable test = () -> level.level(logger, msg, bundle); + Checker check = (res, l) -> { + checkRecord(level, res, logger.getName(), l, msg, + adaptor().getCallerClassName(level, Levels.class.getName()), + adaptor().getCallerMethodName(level, "invoke"), + null, bundle); + return null; + }; + test("bundle, msg", level, logger, test, check); + } + + public void testLevel(Levels level, java.lang.System.Logger logger, + String msg, ResourceBundle bundle, Object... params) { + Runnable test = () -> level.level(logger, msg, bundle, (Object[])params); + Checker check = (res, l) -> { + checkRecord(level, res, logger.getName(), l, msg, + adaptor().getCallerClassName(level, Levels.class.getName()), + adaptor().getCallerMethodName(level, "invoke"), + null, bundle, (Object[])params); + return null; + }; + test("bundle, msg, params", level, logger, test, check); + } + + public void testLevel(Levels level, java.lang.System.Logger logger, + String msg, ResourceBundle bundle, Throwable thrown) { + Runnable test = () -> level.level(logger, msg, bundle, thrown); + Checker check = (res, l) -> { + checkRecord(level, res, logger.getName(), l, msg, + adaptor().getCallerClassName(level, Levels.class.getName()), + adaptor().getCallerMethodName(level, "invoke"), + thrown, bundle); + return null; + }; + test("bundle, msg, throwable", level, logger, test, check); + } + + // System.Logger + public void testSpiLog(java.lang.System.Logger logger, String msg) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, () -> msg, + adaptor().getCallerClassName( + SpiLogMethodInvoker.LOG_STRING_PARAMS, + SpiLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + SpiLogMethodInvoker.LOG_STRING_PARAMS, + "logX"), null, localized); + return null; + }; + SpiLogTester tester = (x, level) -> { + SpiLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, (Object[])null); + return null; + }; + Function nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\")"; + testSpiLog(logger, tester, check, nameProducer); + } + + public void testSpiLog(java.lang.System.Logger logger, + ResourceBundle bundle, String msg) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, () -> msg, + adaptor().getCallerClassName( + SpiLogMethodInvoker.LOGRB_STRING_PARAMS, + SpiLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + SpiLogMethodInvoker.LOGRB_STRING_PARAMS, + "logX"), null, bundle); + return null; + }; + SpiLogTester tester = (x, level) -> { + SpiLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, (Object[])null); + return null; + }; + Function nameProducer = (l) -> "log(Level." + l + + ", bundle, \"" + msg + "\")"; + testSpiLog(logger, tester, check, nameProducer); + } + + public void testSpiLog(java.lang.System.Logger logger, String msg, Object... params) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, () -> msg, + adaptor().getCallerClassName( + SpiLogMethodInvoker.LOG_STRING_PARAMS, + SpiLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + SpiLogMethodInvoker.LOG_STRING_PARAMS, + "logX"), null, localized, params); + return null; + }; + SpiLogTester tester = (x, level) -> { + SpiLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, params); + return null; + }; + Function nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\", params...)"; + testSpiLog(logger, tester, check, nameProducer); + } + + public void testSpiLog(java.lang.System.Logger logger, + ResourceBundle bundle, String msg, Object... params) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, () -> msg, + adaptor().getCallerClassName( + SpiLogMethodInvoker.LOGRB_STRING_PARAMS, + SpiLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + SpiLogMethodInvoker.LOGRB_STRING_PARAMS, + "logX"), null, bundle, params); + return null; + }; + SpiLogTester tester = (x, level) -> { + SpiLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, params); + return null; + }; + Function nameProducer = (l) -> "log(Level." + l + + ", bundle, \"" + msg + "\", params...)"; + testSpiLog(logger, tester, check, nameProducer); + } + + public void testSpiLog(java.lang.System.Logger logger, String msg, Throwable thrown) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, () -> msg, + adaptor().getCallerClassName( + SpiLogMethodInvoker.LOG_STRING_THROWN, + SpiLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + SpiLogMethodInvoker.LOG_STRING_THROWN, + "logX"), thrown, localized); + return null; + }; + SpiLogTester tester = (x, level) -> { + SpiLogMethodInvoker.LOG_STRING_THROWN.logX(x, level, msg, thrown); + return null; + }; + Function nameProducer = (l) -> + "log(Level." + l + ", \"" + msg + "\", thrown)"; + testSpiLog(logger, tester, check, nameProducer); + } + + public void testSpiLog(java.lang.System.Logger logger, + ResourceBundle bundle, String msg, Throwable thrown) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, () -> msg, + adaptor().getCallerClassName( + SpiLogMethodInvoker.LOGRB_STRING_THROWN, + SpiLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + SpiLogMethodInvoker.LOGRB_STRING_THROWN, + "logX"), thrown, bundle); + return null; + }; + SpiLogTester tester = (x, level) -> { + SpiLogMethodInvoker.LOGRB_STRING_THROWN.logX(x, level, bundle, msg, thrown); + return null; + }; + Function nameProducer = (l) -> + "log(Level." + l + ", bundle, \"" + msg + "\", thrown)"; + testSpiLog(logger, tester, check, nameProducer); + } + + public void testSpiLog(java.lang.System.Logger logger, Supplier msg) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, msg, + adaptor().getCallerClassName( + SpiLogMethodInvoker.LOG_SUPPLIER, + SpiLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + SpiLogMethodInvoker.LOG_SUPPLIER, + "logX"), null, null); + return null; + }; + SpiLogTester tester = (x, level) -> { + SpiLogMethodInvoker.LOG_SUPPLIER.logX(x, level, msg); + return null; + }; + Function nameProducer = (l) -> + "log(Level." + l + ", () -> \"" + msg.get() + "\")"; + testSpiLog(logger, tester, check, nameProducer); + } + + public void testSpiLog(java.lang.System.Logger logger, Object obj) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, () -> obj.toString(), + adaptor().getCallerClassName( + SpiLogMethodInvoker.LOG_OBJECT, + SpiLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + SpiLogMethodInvoker.LOG_OBJECT, + "logX"), null, null); + return null; + }; + SpiLogTester tester = (x, level) -> { + SpiLogMethodInvoker.LOG_OBJECT.logX(x, level, obj); + return null; + }; + Function nameProducer = (l) -> + "log(Level." + l + ", new "+obj.getClass().getSimpleName()+"(\"" + + obj.toString() + "\"))"; + testSpiLog(logger, tester, check, nameProducer); + } + + public void testSpiLog(java.lang.System.Logger logger, Throwable thrown, Supplier msg) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, msg, + adaptor().getCallerClassName( + SpiLogMethodInvoker.LOG_SUPPLIER_THROWN, + SpiLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + SpiLogMethodInvoker.LOG_SUPPLIER_THROWN, + "logX"), thrown, null); + return null; + }; + SpiLogTester tester = (x, level) -> { + SpiLogMethodInvoker.LOG_SUPPLIER_THROWN.logX(x, level, msg, thrown); + return null; + }; + Function nameProducer = (l) -> + "log(Level." + l + ", () -> \"" + msg.get() + "\", thrown)"; + testSpiLog(logger, tester, check, nameProducer); + } + + + // JDK + + public void testLog(java.lang.System.Logger logger, String msg) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, () -> msg, + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOG_STRING_PARAMS, + JdkLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + JdkLogMethodInvoker.LOG_STRING_PARAMS, + "logX"), null, localized); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, (Object[])null); + return null; + }; + Function nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\")"; + testJdkLog(logger, tester, check, nameProducer); + } + + public void testLogrb(java.lang.System.Logger logger, + ResourceBundle bundle, String msg) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, () -> msg, + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGRB_STRING_PARAMS, + JdkLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + JdkLogMethodInvoker.LOGRB_STRING_PARAMS, + "logX"), null, bundle); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, (Object[])null); + return null; + }; + Function nameProducer = (l) -> "logrb(Level." + l + + ", bundle, \"" + msg + "\")"; + testJdkLog(logger, tester, check, nameProducer); + } + + public void testLog(java.lang.System.Logger logger, String msg, Object... params) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, () -> msg, + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOG_STRING_PARAMS, + JdkLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + JdkLogMethodInvoker.LOG_STRING_PARAMS, + "logX"), null, localized, params); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, params); + return null; + }; + Function nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\", params...)"; + testJdkLog(logger, tester, check, nameProducer); + } + + public void testLogrb(java.lang.System.Logger logger, + ResourceBundle bundle, String msg, Object... params) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, () -> msg, + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGRB_STRING_PARAMS, + JdkLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + JdkLogMethodInvoker.LOGRB_STRING_PARAMS, + "logX"), null, bundle, params); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, params); + return null; + }; + Function nameProducer = (l) -> "log(Level." + l + + ", bundle, \"" + msg + "\", params...)"; + testJdkLog(logger, tester, check, nameProducer); + } + + public void testLog(java.lang.System.Logger logger, String msg, Throwable thrown) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, () -> msg, + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOG_STRING_THROWN, + JdkLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + JdkLogMethodInvoker.LOG_STRING_THROWN, + "logX"), thrown, localized); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOG_STRING_THROWN.logX(x, level, msg, thrown); + return null; + }; + Function nameProducer = (l) -> + "log(Level." + l + ", \"" + msg + "\", thrown)"; + testJdkLog(logger, tester, check, nameProducer); + } + + public void testLogrb(java.lang.System.Logger logger, + ResourceBundle bundle, String msg, Throwable thrown) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, () -> msg, + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGRB_STRING_THROWN, + JdkLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + JdkLogMethodInvoker.LOGRB_STRING_THROWN, + "logX"), thrown, bundle); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOGRB_STRING_THROWN.logX(x, level, bundle, msg, thrown); + return null; + }; + Function nameProducer = (l) -> + "log(Level." + l + ", bundle, \"" + msg + "\", thrown)"; + testJdkLog(logger, tester, check, nameProducer); + } + + public void testLog(java.lang.System.Logger logger, Supplier msg) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, msg, + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOG_SUPPLIER, + JdkLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + JdkLogMethodInvoker.LOG_SUPPLIER, + "logX"), null, null); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOG_SUPPLIER.logX(x, level, msg); + return null; + }; + Function nameProducer = (l) -> + "log(Level." + l + ", () -> \"" + msg.get() + "\")"; + testJdkLog(logger, tester, check, nameProducer); + } + + public void testLog(java.lang.System.Logger logger, Throwable thrown, Supplier msg) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, msg, + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOG_SUPPLIER_THROWN, + JdkLogMethodInvoker.class.getName()), + adaptor().getCallerMethodName( + JdkLogMethodInvoker.LOG_SUPPLIER_THROWN, + "logX"), thrown, null); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOG_SUPPLIER_THROWN.logX(x, level, thrown, msg); + return null; + }; + Function nameProducer = (l) -> + "log(Level." + l + ", () -> \"" + msg.get() + "\", thrown)"; + testJdkLog(logger, tester, check, nameProducer); + } + + static Supplier logpMessage(ResourceBundle bundle, + String className, String methodName, Supplier msg) { + if (BEST_EFFORT_FOR_LOGP && bundle == null + && (className != null || methodName != null)) { + final String cName = className == null ? "" : className; + final String mName = methodName == null ? "" : methodName; + return () -> { + String m = msg.get(); + return String.format("[%s %s] %s", cName, mName, m == null ? "" : m); + }; + } else { + return msg; + } + } + + public void testLogp(java.lang.System.Logger logger, String className, + String methodName, String msg) { + Checker check = (res, l) -> { + checkRecord("logp", res, logger.getName(), l, + logpMessage(localized, className, methodName, () -> msg), + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGP_STRING, className), + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGP_STRING, methodName), + null, localized); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOGP_STRING.logX(x, level, + className, methodName, msg); + return null; + }; + Function nameProducer = (l) -> + "logp(Level." + l + ", class, method, \"" + msg + "\")"; + testJdkLog(logger, tester, check, nameProducer); + } + + public void testLogrb(java.lang.System.Logger logger, String className, + String methodName, ResourceBundle bundle, String msg) { + Checker check = (res, l) -> { + checkRecord("logp", res, logger.getName(), l, () -> msg, + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, className), + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, methodName), + null, bundle); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOGRBP_STRING_PARAMS.logX(x, level, + className, methodName, bundle, msg, (Object[])null); + return null; + }; + Function nameProducer = (l) -> + "logp(Level." + l + ", class, method, bundle, \"" + msg + "\")"; + testJdkLog(logger, tester, check, nameProducer); + } + + public void testLogp(java.lang.System.Logger logger, String className, + String methodName, String msg, Object... params) { + Checker check = (res, l) -> { + checkRecord("logp", res, logger.getName(), l, + logpMessage(localized, className, methodName, () -> msg), + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGP_STRING_PARAMS, className), + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGP_STRING_PARAMS, methodName), + null, localized, params); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOGP_STRING_PARAMS.logX(x, level, + className, methodName, msg, params); + return null; + }; + Function nameProducer = (l) -> + "log(Level." + l + ", class, method, \"" + msg + "\", params...)"; + testJdkLog(logger, tester, check, nameProducer); + } + + public void testLogrb(java.lang.System.Logger logger, String className, + String methodName, ResourceBundle bundle, String msg, + Object... params) { + Checker check = (res, l) -> { + checkRecord("logp", res, logger.getName(), l, () -> msg, + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, className), + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, methodName), + null, bundle, params); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOGRBP_STRING_PARAMS.logX(x, level, + className, methodName, bundle, msg, params); + return null; + }; + Function nameProducer = (l) -> + "log(Level." + l + ", class, method, bundle, \"" + + msg + "\", params...)"; + testJdkLog(logger, tester, check, nameProducer); + } + + public void testLogp(java.lang.System.Logger logger, String className, + String methodName, String msg, Throwable thrown) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, + logpMessage(localized, className, methodName, () -> msg), + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGP_STRING_THROWN, className), + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGP_STRING_THROWN, methodName), + thrown, localized); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOGP_STRING_THROWN.logX(x, level, + className, methodName, msg, thrown); + return null; + }; + Function nameProducer = (l) -> + "log(Level." + l + ", class, method, \"" + msg + "\", thrown)"; + testJdkLog(logger, tester, check, nameProducer); + } + + public void testLogrb(java.lang.System.Logger logger, String className, + String methodName, ResourceBundle bundle, + String msg, Throwable thrown) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, () -> msg, + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGRBP_STRING_THROWN, className), + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGRBP_STRING_THROWN, methodName), + thrown, bundle); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOGRBP_STRING_THROWN.logX(x, level, + className, methodName, bundle, msg, thrown); + return null; + }; + Function nameProducer = (l) -> + "log(Level." + l + ", class, method, bundle, \"" + msg + "\", thrown)"; + testJdkLog(logger, tester, check, nameProducer); + + } + + public void testLogp(java.lang.System.Logger logger, String className, + String methodName, Supplier msg) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, + logpMessage(null, className, methodName, msg), + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGP_SUPPLIER, className), + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGP_SUPPLIER, methodName), + null, null); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOGP_SUPPLIER.logX(x, level, + className, methodName, msg); + return null; + }; + Function nameProducer = (l) -> + "log(Level." + l + ", class, method, () -> \"" + msg.get() + "\")"; + testJdkLog(logger, tester, check, nameProducer); + } + + public void testLogp(java.lang.System.Logger logger, String className, + String methodName, Throwable thrown, Supplier msg) { + Checker check = (res, l) -> { + checkRecord("log", res, logger.getName(), l, + logpMessage(null, className, methodName, msg), + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGP_SUPPLIER_THROWN, className), + adaptor().getCallerClassName( + JdkLogMethodInvoker.LOGP_SUPPLIER_THROWN, methodName), + thrown, null); + return null; + }; + JdkLogTester tester = (x, level) -> { + JdkLogMethodInvoker.LOGP_SUPPLIER_THROWN.logX(x, level, + className, methodName, thrown, msg); + return null; + }; + Function nameProducer = (l) -> + "log(Level." + l + ", class, method, () -> \"" + msg.get() + "\", thrown)"; + testJdkLog(logger, tester, check, nameProducer); + } + + private void testJdkLog(java.lang.System.Logger logger, + JdkLogTester log, Checker check, + Function nameProducer) { + if (restrictedTo != null) { + if (!bridgeLoggerClass.isAssignableFrom(restrictedTo)) { + if (VERBOSE) { + System.out.println("Skipping method from " + + bridgeLoggerClass); + } + return; + } + } + System.out.println("Testing Logger." + nameProducer.apply("*") + + " on " + logger); + final BackendAdaptor adaptor = adaptor(); + for (Level loggerLevel : LEVELS) { + adaptor.setLevel(logger, loggerLevel); + for (Level l : LEVELS) { + check(logger, () -> log.apply(bridgeLoggerClass.cast(logger), l), + check, () -> adaptor.isLoggable(logger, l), + () -> adaptor.shouldBeLoggable(l, loggerLevel), + l, loggerLevel, nameProducer.apply(l.toString())); + } + } + } + + private void testSpiLog(java.lang.System.Logger logger, + SpiLogTester log, Checker check, + Function nameProducer) { + System.out.println("Testing System.Logger." + nameProducer.apply("*") + + " on " + logger); + final BackendAdaptor adaptor = adaptor(); + for (java.lang.System.Logger.Level loggerLevel : java.lang.System.Logger.Level.values()) { + + adaptor.setLevel(logger, loggerLevel); + for (java.lang.System.Logger.Level l : java.lang.System.Logger.Level.values()) { + check(logger, () -> log.apply(logger, l), + check, () -> logger.isLoggable(l), + () -> adaptor.shouldBeLoggable(l, loggerLevel), + l, loggerLevel, nameProducer.apply(l.toString())); + } + } + } + + private void test(String args, Levels level, java.lang.System.Logger logger, + Runnable test, Checker check) { + if (restrictedTo != null) { + if (!level.definingClass.isAssignableFrom(restrictedTo)) { + if (VERBOSE) { + System.out.println("Skipping method from " + + level.definingClass); + } + return; + } + } + String method = args.contains("bundle") ? "logrb" : "log"; + System.out.println("Testing Logger." + + method + "(Level." + level.platformLevel + + ", "+ args + ")" + " on " + logger); + final BackendAdaptor adaptor = adaptor(); + for (Level loggerLevel : LEVELS) { + adaptor.setLevel(logger, loggerLevel); + check(logger, test, check, + () -> level.isEnabled(logger), + () -> adaptor.shouldBeLoggable(level, loggerLevel), + level.platformLevel, loggerLevel, level.method); + } + } + + private void check(java.lang.System.Logger logger, + Runnable test, Checker check, + BooleanSupplier checkLevelEnabled, + BooleanSupplier shouldBeLoggable, + L logLevel, L loggerLevel, String logMethod) { + final BackendAdaptor adaptor = adaptor(); + adaptor.resetBackendRecords(); + test.run(); + final List records = adaptor.getBackendRecords(); + if (shouldBeLoggable.getAsBoolean()) { + if (!checkLevelEnabled.getAsBoolean()) { + throw new RuntimeException("Logger is not enabled for " + + logMethod + + " although logger level is " + loggerLevel); + } + if (records.size() != 1) { + throw new RuntimeException(loggerLevel + " [" + + logLevel + "] : Unexpected record sizes: " + + records.toString()); + } + BackendRecord res = records.get(0); + check.apply(res, logLevel); + } else { + if (checkLevelEnabled.getAsBoolean()) { + throw new RuntimeException("Logger is enabled for " + + logMethod + + " although logger level is " + loggerLevel); + } + if (!records.isEmpty()) { + throw new RuntimeException(loggerLevel + " [" + + logLevel + "] : Unexpected record sizes: " + + records.toString()); + } + } + } + } + + public static class JULBackendTester extends BackendTester{ + + public JULBackendTester(boolean isSystem) { + this(isSystem,null,null); + } + public JULBackendTester(boolean isSystem, ResourceBundle localized) { + this(isSystem,null,localized); + } + public JULBackendTester(boolean isSystem, + Class restrictedTo) { + this(isSystem, restrictedTo, null); + } + public JULBackendTester(boolean isSystem, + Class restrictedTo, + ResourceBundle localized) { + super(isSystem, restrictedTo, localized); + } + + Logger getBackendLogger(String name) { + if (isSystem) { + return LoggingProviderImpl.getLogManagerAccess().demandLoggerFor( + LogManager.getLogManager(), name, Thread.class); + } else { + return Logger.getLogger(name); + } + } + + class JULBackendAdaptor extends BackendAdaptor { + @Override + public String getLoggerName(LogRecord res) { + return res.getLoggerName(); + } + @Override + public Level getLevel(LogRecord res) { + return Level.valueOf(res.getLevel().getName()); + } + @Override + public String getMessage(LogRecord res) { + return res.getMessage(); + } + @Override + public String getSourceClassName(LogRecord res) { + return res.getSourceClassName(); + } + @Override + public String getSourceMethodName(LogRecord res) { + return res.getSourceMethodName(); + } + @Override + public Throwable getThrown(LogRecord res) { + return res.getThrown(); + } + @Override + public ResourceBundle getResourceBundle(LogRecord res) { + return res.getResourceBundle(); + } + @Override + public void setLevel(java.lang.System.Logger logger, Level level) { + Logger backend = getBackendLogger(logger.getName()); + backend.setLevel(java.util.logging.Level.parse(level.name())); + } + @Override + public void setLevel(java.lang.System.Logger logger, java.lang.System.Logger.Level level) { + setLevel(logger, toJUL(level)); + } + @Override + public List getBackendRecords() { + return handler.records; + } + @Override + public void resetBackendRecords() { + handler.reset(); + } + @Override + public Level getMappedLevel(Object level) { + if (level instanceof java.lang.System.Logger.Level) { + return toJUL((java.lang.System.Logger.Level)level); + } + return (Level)level; + } + } + + final JULBackendAdaptor julAdaptor = new JULBackendAdaptor(); + + @Override + BackendAdaptor adaptor() { + return julAdaptor; + } + + } + + public abstract static class BackendTesterFactory { + public abstract BackendTester createBackendTester(boolean isSystem); + public abstract BackendTester createBackendTester(boolean isSystem, + Class restrictedTo); + public abstract BackendTester createBackendTester(boolean isSystem, + Class restrictedTo, + ResourceBundle bundle); + public abstract BackendTester createBackendTester(boolean isSystem, + ResourceBundle bundle); + } + + public static class JULBackendTesterFactory extends BackendTesterFactory { + + @Override + public BackendTester createBackendTester(boolean isSystem) { + return new JULBackendTester(isSystem); + } + + @Override + public BackendTester createBackendTester(boolean isSystem, + Class restrictedTo) { + return new JULBackendTester(isSystem, restrictedTo); + } + + @Override + public BackendTester createBackendTester(boolean isSystem, + Class restrictedTo, + ResourceBundle bundle) { + return new JULBackendTester(isSystem, restrictedTo, bundle); + } + + @Override + public BackendTester createBackendTester(boolean isSystem, + ResourceBundle bundle) { + return new JULBackendTester(isSystem, bundle); + } + } + + public static class CustomLoggerFinder extends LoggerFinder { + + static enum CustomLevel { OFF, FATAL, ERROR, WARN, INFO, DEBUG, TRACE, ALL }; + static CustomLevel[] customLevelMap = { CustomLevel.ALL, + CustomLevel.TRACE, CustomLevel.DEBUG, CustomLevel.INFO, + CustomLevel.WARN, CustomLevel.ERROR, CustomLevel.OFF + }; + static class CustomLogRecord { + public final CustomLevel logLevel; + public final java.lang.System.Logger logger; + public final String msg; + public final Object[] params; + public final Throwable thrown; + public final ResourceBundle bundle; + + CustomLogRecord(java.lang.System.Logger producer, + CustomLevel level, String msg) { + this(producer, level, msg, (ResourceBundle)null, (Throwable)null, (Object[])null); + } + + CustomLogRecord(java.lang.System.Logger producer, + CustomLevel level, String msg, ResourceBundle bundle, + Throwable thrown, Object... params) { + this.logger = producer; + this.logLevel = level; + this.msg = msg; + this.params = params; + this.thrown = thrown; + this.bundle = bundle; + } + } + + static final List records = + Collections.synchronizedList(new ArrayList<>()); + + static class CustomLogger implements java.lang.System.Logger { + + final String name; + volatile CustomLevel level; + CustomLogger(String name) { + this.name = name; + this.level = CustomLevel.INFO; + } + + @Override + public String getName() { + return name; + } + + public void setLevel(CustomLevel level) { + this.level = level; + } + + + @Override + public boolean isLoggable(java.lang.System.Logger.Level level) { + + return this.level != CustomLevel.OFF && this.level.ordinal() + >= customLevelMap[level.ordinal()].ordinal(); + } + + @Override + public void log(java.lang.System.Logger.Level level, ResourceBundle bundle, String key, Throwable thrown) { + if (isLoggable(level)) { + records.add(new CustomLogRecord(this, customLevelMap[level.ordinal()], + key, bundle, thrown)); + } + } + + @Override + public void log(java.lang.System.Logger.Level level, ResourceBundle bundle, String format, Object... params) { + if (isLoggable(level)) { + records.add(new CustomLogRecord(this, customLevelMap[level.ordinal()], + format, bundle, null, params)); + } + } + + } + + final Map applicationLoggers = + Collections.synchronizedMap(new HashMap<>()); + final Map systemLoggers = + Collections.synchronizedMap(new HashMap<>()); + + @Override + public java.lang.System.Logger getLogger(String name, Class caller) { + ClassLoader callerLoader = caller.getClassLoader(); + if (callerLoader == null) { + systemLoggers.putIfAbsent(name, new CustomLogger(name)); + return systemLoggers.get(name); + } else { + applicationLoggers.putIfAbsent(name, new CustomLogger(name)); + return applicationLoggers.get(name); + } + } + + CustomLevel fromJul(Level level) { + if (level.intValue() == Level.OFF.intValue()) { + return CustomLevel.OFF; + } else if (level.intValue() > Level.SEVERE.intValue()) { + return CustomLevel.ERROR; + } else if (level.intValue() > Level.WARNING.intValue()) { + return CustomLevel.ERROR; + } else if (level.intValue() > Level.INFO.intValue()) { + return CustomLevel.WARN; + } else if (level.intValue() > Level.CONFIG.intValue()) { + return CustomLevel.INFO; + } else if (level.intValue() > Level.FINER.intValue()) { + return CustomLevel.DEBUG; + } else if (level.intValue() > Level.FINEST.intValue()) { + return CustomLevel.TRACE; + } else if (level.intValue() == Level.ALL.intValue()) { + return CustomLevel.ALL; + } else { + return CustomLevel.TRACE; + } + } + + Level toJul(CustomLevel level) { + switch(level) { + case OFF: return Level.OFF; + case FATAL: return Level.SEVERE; + case ERROR: return Level.SEVERE; + case WARN: return Level.WARNING; + case INFO: return Level.INFO; + case DEBUG: return Level.FINE; + case TRACE: return Level.FINER; + case ALL: return Level.ALL; + default: throw new InternalError("No such level: "+level); + } + } + + } + + public static class CustomBackendTester extends + BackendTester { + + public final CustomLoggerFinder provider; + + public CustomBackendTester(boolean isSystem) { + this(isSystem, null, null); + } + + public CustomBackendTester(boolean isSystem, + Class restrictedTo) { + this(isSystem, restrictedTo, null); + } + + public CustomBackendTester(boolean isSystem, + ResourceBundle localized) { + this(isSystem, null, localized); + } + + public CustomBackendTester(boolean isSystem, + Class restrictedTo, + ResourceBundle localized) { + super(isSystem, restrictedTo, localized); + provider = (CustomLoggerFinder)java.lang.System.LoggerFinder.getLoggerFinder(); + } + + @Override + public java.lang.System.Logger convert(java.lang.System.Logger logger) { + if (restrictedTo != null && restrictedTo.isInstance(logger)) { + return logger; + } else if (restrictedTo == jdkLoggerClass) { + return logger; + } else { + return java.lang.System.Logger.class.cast( + sun.util.logging.PlatformLogger.Bridge.convert(logger)); + } + } + + class CustomBackendAdaptor extends BackendAdaptor { + + @Override + public String getLoggerName(CustomLoggerFinder.CustomLogRecord res) { + return res.logger.getName(); + } + + @Override + public CustomLoggerFinder.CustomLevel getLevel(CustomLoggerFinder.CustomLogRecord res) { + return res.logLevel; + } + + @Override + public String getMessage(CustomLoggerFinder.CustomLogRecord res) { + return res.msg; + } + + @Override // we don't support source class name in our custom provider implementation + public String getSourceClassName(CustomLoggerFinder.CustomLogRecord res) { + return null; + } + + @Override // we don't support source method name in our custom provider implementation + public String getSourceMethodName(CustomLoggerFinder.CustomLogRecord res) { + return null; + } + + @Override + public Throwable getThrown(CustomLoggerFinder.CustomLogRecord res) { + return res.thrown; + } + + @Override + public ResourceBundle getResourceBundle(CustomLoggerFinder.CustomLogRecord res) { + return res.bundle; + } + + @Override + public void setLevel(java.lang.System.Logger logger, Level level) { + final CustomLoggerFinder.CustomLogger l = + (CustomLoggerFinder.CustomLogger) + (isSystem ? provider.getLogger(logger.getName(), Thread.class) : + provider.getLogger(logger.getName(), LoggerFinderBackendTest.class)); + l.setLevel(provider.fromJul(level)); + } + @Override + public void setLevel(java.lang.System.Logger logger, + java.lang.System.Logger.Level level) { + setLevel(logger, toJUL(level)); + } + + CustomLoggerFinder.CustomLevel getLevel(java.lang.System.Logger logger) { + final CustomLoggerFinder.CustomLogger l = + (CustomLoggerFinder.CustomLogger) + (isSystem ? provider.getLogger(logger.getName(), Thread.class) : + provider.getLogger(logger.getName(), LoggerFinderBackendTest.class)); + return l.level; + } + + @Override + public List getBackendRecords() { + return CustomLoggerFinder.records; + } + + @Override + public void resetBackendRecords() { + CustomLoggerFinder.records.clear(); + } + + @Override + public boolean shouldBeLoggable(Levels level, Level loggerLevel) { + return loggerLevel != Level.OFF && + fromLevels(level).ordinal() <= provider.fromJul(loggerLevel).ordinal(); + } + + @Override + public boolean isLoggable(java.lang.System.Logger logger, Level l) { + return super.isLoggable(logger, l); + } + + @Override + public boolean shouldBeLoggable(Level logLevel, Level loggerLevel) { + return loggerLevel != Level.OFF && + provider.fromJul(logLevel).ordinal() <= provider.fromJul(loggerLevel).ordinal(); + } + + @Override // we don't support source class name in our custom provider implementation + public String getCallerClassName(Levels level, String clazz) { + return null; + } + + @Override // we don't support source method name in our custom provider implementation + public String getCallerMethodName(Levels level, String method) { + return null; + } + + @Override // we don't support source class name in our custom provider implementation + public String getCallerClassName(MethodInvoker logMethod, String clazz) { + return null; + } + + @Override // we don't support source method name in our custom provider implementation + public String getCallerMethodName(MethodInvoker logMethod, String method) { + return null; + } + + @Override + public CustomLoggerFinder.CustomLevel getMappedLevel(Object level) { + if (level instanceof java.lang.System.Logger.Level) { + final int index = ((java.lang.System.Logger.Level)level).ordinal(); + return CustomLoggerFinder.customLevelMap[index]; + } else if (level instanceof Level) { + return provider.fromJul((Level)level); + } + return (CustomLoggerFinder.CustomLevel) level; + } + + CustomLoggerFinder.CustomLevel fromLevels(Levels level) { + switch(level) { + case SEVERE: + return CustomLoggerFinder.CustomLevel.ERROR; + case WARNING: + return CustomLoggerFinder.CustomLevel.WARN; + case INFO: + return CustomLoggerFinder.CustomLevel.INFO; + case CONFIG: case FINE: + return CustomLoggerFinder.CustomLevel.DEBUG; + case FINER: case FINEST: + return CustomLoggerFinder.CustomLevel.TRACE; + } + throw new InternalError("No such level "+level); + } + + } + + @Override + BackendAdaptor adaptor() { + return new CustomBackendAdaptor(); + } + + } + + public static class CustomBackendTesterFactory extends BackendTesterFactory { + + @Override + public BackendTester createBackendTester(boolean isSystem) { + return new CustomBackendTester(isSystem); + } + + @Override + public BackendTester createBackendTester(boolean isSystem, + Class restrictedTo) { + return new CustomBackendTester(isSystem, restrictedTo); + } + + @Override + public BackendTester createBackendTester(boolean isSystem, + Class restrictedTo, + ResourceBundle bundle) { + return new CustomBackendTester(isSystem, restrictedTo, bundle); + } + + @Override + public BackendTester createBackendTester(boolean isSystem, + ResourceBundle bundle) { + return new CustomBackendTester(isSystem, bundle); + } + } + + static final Method getLazyLogger; + static final Method accessLoggerFinder; + static { + // jdk.internal.logger.LazyLoggers.getLazyLogger(name, caller); + try { + Class lazyLoggers = jdk.internal.logger.LazyLoggers.class; + getLazyLogger = lazyLoggers.getMethod("getLazyLogger", + String.class, Class.class); + getLazyLogger.setAccessible(true); + Class loggerFinderLoader = + Class.forName("java.lang.System$LoggerFinder"); + accessLoggerFinder = loggerFinderLoader.getDeclaredMethod("accessProvider"); + accessLoggerFinder.setAccessible(true); + } catch (Throwable ex) { + throw new ExceptionInInitializerError(ex); + } + } + + static java.lang.System.Logger getSystemLogger(String name, Class caller) throws Exception { + try { + return java.lang.System.Logger.class.cast(getLazyLogger.invoke(null, name, caller)); + } catch (InvocationTargetException x) { + Throwable t = x.getTargetException(); + if (t instanceof Exception) { + throw (Exception)t; + } else { + throw (Error)t; + } + } + } + static java.lang.System.Logger getSystemLogger(String name, + ResourceBundle bundle, Class caller) throws Exception { + try { + LoggerFinder provider = LoggerFinder.class.cast(accessLoggerFinder.invoke(null)); + return provider.getLocalizedLogger(name, bundle, caller); + } catch (InvocationTargetException x) { + Throwable t = x.getTargetException(); + if (t instanceof Exception) { + throw (Exception)t; + } else { + throw (Error)t; + } + } + } + + // Change this to 'true' to get more traces... + public static boolean verbose = false; + + public static void main(String[] argv) throws Exception { + + final AtomicInteger nb = new AtomicInteger(0); + final boolean hidesProvider = Boolean.getBoolean("test.logger.hidesProvider"); + System.out.println(ClassLoader.getSystemClassLoader()); + final BackendTesterFactory factory; + if (java.lang.System.LoggerFinder.getLoggerFinder() instanceof CustomLoggerFinder) { + if (hidesProvider) { + System.err.println("Custom backend " + + java.lang.System.LoggerFinder.getLoggerFinder() + + " should have been hidden!"); + throw new RuntimeException( + "Custom backend should have been hidden: " + + "check value of java.system.class.loader property"); + } + System.out.println("Using custom backend"); + factory = new CustomBackendTesterFactory(); + } else { + if (!hidesProvider) { + System.err.println("Default JUL backend " + + java.lang.System.LoggerFinder.getLoggerFinder() + + " should have been hidden!"); + throw new RuntimeException( + "Default JUL backend should have been hidden: " + + "check value of java.system.class.loader property"); + } + System.out.println("Using JUL backend"); + factory = new JULBackendTesterFactory(); + } + + testBackend(nb, factory); + } + + public static void testBackend(AtomicInteger nb, BackendTesterFactory factory) throws Exception { + + // Tests all level specifics methods with loggers configured with + // all possible levels and loggers obtained with all possible + // entry points from LoggerFactory and JdkLoggerFactory, with + // JUL as backend. + + // Test a simple application logger with JUL backend + final BackendTester tester = factory.createBackendTester(false); + final java.lang.System.Logger logger = + java.lang.System.LoggerFinder.getLoggerFinder() + .getLogger("foo", LoggerFinderBackendTest.class); + + testLogger(tester, logger, nb); + + // Test a simple system logger with JUL backend + final java.lang.System.Logger system = + java.lang.System.LoggerFinder.getLoggerFinder() + .getLogger("bar", Thread.class); + final BackendTester systemTester = factory.createBackendTester(true); + testLogger(systemTester, system, nb); + + // Test a localized application logger with null resource bundle and + // JUL backend + final java.lang.System.Logger noBundleLogger = + java.lang.System.LoggerFinder.getLoggerFinder() + .getLocalizedLogger("baz", null, LoggerFinderBackendTest.class); + final BackendTester noBundleTester = + factory.createBackendTester(false, spiLoggerClass); + testLogger(noBundleTester, noBundleLogger, nb); + + // Test a localized system logger with null resource bundle and JUL + // backend + final java.lang.System.Logger noBundleSysLogger = + java.lang.System.LoggerFinder.getLoggerFinder() + .getLocalizedLogger("oof", null, Thread.class); + final BackendTester noBundleSysTester = + factory.createBackendTester(true, spiLoggerClass); + testLogger(noBundleSysTester, noBundleSysLogger, nb); + + // Test a localized application logger with null resource bundle and + // JUL backend + try { + System.getLogger("baz", null); + throw new RuntimeException("Expected NullPointerException not thrown"); + } catch (NullPointerException x) { + System.out.println("System.Loggers.getLogger(\"baz\", null): got expected " + x); + } + final java.lang.System.Logger noBundleExtensionLogger = + getSystemLogger("baz", null, LoggerFinderBackendTest.class); + final BackendTester noBundleExtensionTester = + factory.createBackendTester(false, jdkLoggerClass); + testLogger(noBundleExtensionTester, noBundleExtensionLogger, nb); + + // Test a simple system logger with JUL backend + final java.lang.System.Logger sysExtensionLogger = + getSystemLogger("oof", Thread.class); + final BackendTester sysExtensionTester = + factory.createBackendTester(true, jdkLoggerClass); + testLogger(sysExtensionTester, sysExtensionLogger, nb); + + // Test a localized system logger with null resource bundle and JUL + // backend + final java.lang.System.Logger noBundleSysExtensionLogger = + getSystemLogger("oof", null, Thread.class); + final BackendTester noBundleSysExtensionTester = + factory.createBackendTester(true, jdkLoggerClass); + testLogger(noBundleSysExtensionTester, noBundleSysExtensionLogger, nb); + + // Test a localized application logger converted to JDK with null + // resource bundle and JUL backend + final java.lang.System.Logger noBundleConvertedLogger = + (java.lang.System.Logger) + sun.util.logging.PlatformLogger.Bridge.convert(noBundleLogger); + final BackendTester noBundleJdkTester = factory.createBackendTester(false); + testLogger(noBundleJdkTester, noBundleConvertedLogger, nb); + + // Test a localized system logger converted to JDK with null resource + // bundle and JUL backend + final java.lang.System.Logger noBundleConvertedSysLogger = + (java.lang.System.Logger) + sun.util.logging.PlatformLogger.Bridge.convert(noBundleSysLogger); + final BackendTester noBundleJdkSysTester = factory.createBackendTester(true); + testLogger(noBundleJdkSysTester, noBundleConvertedSysLogger, nb); + + // Test a localized application logger with resource bundle and JUL + // backend + final ResourceBundle bundle = + ResourceBundle.getBundle(ResourceBundeLocalized.class.getName()); + final java.lang.System.Logger bundleLogger = + java.lang.System.LoggerFinder.getLoggerFinder() + .getLocalizedLogger("toto", bundle, LoggerFinderBackendTest.class); + final BackendTester bundleTester = + factory.createBackendTester(false, spiLoggerClass, bundle); + testLogger(bundleTester, bundleLogger, nb); + + // Test a localized system logger with resource bundle and JUL backend + final java.lang.System.Logger bundleSysLogger = + java.lang.System.LoggerFinder.getLoggerFinder() + .getLocalizedLogger("titi", bundle, Thread.class); + final BackendTester bundleSysTester = + factory.createBackendTester(true, spiLoggerClass, bundle); + testLogger(bundleSysTester, bundleSysLogger, nb); + + // Test a localized Jdk application logger with resource bundle and JUL + // backend + final java.lang.System.Logger bundleExtensionLogger = + System.getLogger("tita", bundle); + final BackendTester bundleExtensionTester = + factory.createBackendTester(false, jdkLoggerClass, bundle); + testLogger(bundleExtensionTester, bundleExtensionLogger, nb); + + // Test a localized Jdk system logger with resource bundle and JUL + // backend + final java.lang.System.Logger bundleExtensionSysLogger = + getSystemLogger("titu", bundle, Thread.class); + final BackendTester bundleExtensionSysTester = + factory.createBackendTester(true, jdkLoggerClass, bundle); + testLogger(bundleExtensionSysTester, bundleExtensionSysLogger, nb); + + // Test a localized application logger converted to JDK with resource + // bundle and JUL backend + final BackendTester bundleJdkTester = + factory.createBackendTester(false, bundle); + final java.lang.System.Logger bundleConvertedLogger = + (java.lang.System.Logger) + sun.util.logging.PlatformLogger.Bridge.convert(bundleLogger); + testLogger(bundleJdkTester, bundleConvertedLogger, nb); + + // Test a localized Jdk system logger converted to JDK with resource + // bundle and JUL backend + final BackendTester bundleJdkSysTester = + factory.createBackendTester(true, bundle); + final java.lang.System.Logger bundleConvertedSysLogger = + (java.lang.System.Logger) + sun.util.logging.PlatformLogger.Bridge.convert(bundleSysLogger); + testLogger(bundleJdkSysTester, bundleConvertedSysLogger, nb); + + // Now need to add tests for all the log/logp/logrb methods... + + } + + private static class FooObj { + final String s; + FooObj(String s) { + this.s = s; + } + + @Override + public String toString() { + return super.toString() +": "+s; + } + + } + + public static void testLogger(BackendTester tester, + java.lang.System.Logger spiLogger, AtomicInteger nb) { + + // Test all level-specific method forms: + // fatal(...) error(...) severe(...) etc... + java.lang.System.Logger jdkLogger = tester.convert(spiLogger); + for (Levels l : Levels.values()) { + java.lang.System.Logger logger = + l.definingClass.equals(spiLoggerClass) ? spiLogger : jdkLogger; + tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-" + + nb.incrementAndGet()); + tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-" + + nb.incrementAndGet(), + bundleParam); + final int nbb = nb.incrementAndGet(); + tester.testLevel(l, logger, () -> l.method + "[" + logger.getName() + + "]-" + nbb); + } + for (Levels l : Levels.values()) { + java.lang.System.Logger logger = + l.definingClass.equals(spiLoggerClass) ? spiLogger : jdkLogger; + tester.testLevel(l, logger, + l.method + "[" + logger.getName()+ "]({0},{1})-" + + nb.incrementAndGet(), + "One", "Two"); + tester.testLevel(l, logger, + l.method + "[" + logger.getName()+ "]({0},{1})-" + + nb.incrementAndGet(), + bundleParam, "One", "Two"); + } + final Throwable thrown = new RuntimeException("Test"); + for (Levels l : Levels.values()) { + java.lang.System.Logger logger = + l.definingClass.equals(spiLoggerClass) ? spiLogger : jdkLogger; + tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-" + + nb.incrementAndGet(), + thrown); + tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-" + + nb.incrementAndGet(), + bundleParam, thrown); + final int nbb = nb.incrementAndGet(); + tester.testLevel(l, logger, ()->l.method + "[" + logger.getName()+ "]-" + + nbb, thrown); + } + + java.lang.System.Logger logger = jdkLogger; + + // test System.Logger methods + tester.testSpiLog(logger, "[" + logger.getName()+ "]-" + + nb.incrementAndGet()); + tester.testSpiLog(logger, bundleParam, "[" + logger.getName()+ "]-" + + nb.incrementAndGet()); + tester.testSpiLog(logger, "[" + logger.getName()+ "]-({0},{1})" + + nb.incrementAndGet(), "One", "Two"); + tester.testSpiLog(logger, bundleParam, "[" + logger.getName()+ "]-({0},{1})" + + nb.incrementAndGet(), "One", "Two"); + tester.testSpiLog(logger, "[" + logger.getName()+ "]-" + + nb.incrementAndGet(), thrown); + tester.testSpiLog(logger, bundleParam, "[" + logger.getName()+ "]-" + + nb.incrementAndGet(), thrown); + final int nbb01 = nb.incrementAndGet(); + tester.testSpiLog(logger, () -> "[" + logger.getName()+ "]-" + nbb01); + final int nbb02 = nb.incrementAndGet(); + tester.testSpiLog(logger, thrown, () -> "[" + logger.getName()+ "]-" + nbb02); + final int nbb03 = nb.incrementAndGet(); + tester.testSpiLog(logger, new FooObj("[" + logger.getName()+ "]-" + nbb03)); + + // Test all log method forms: + // jdk.internal.logging.Logger.log(...) + tester.testLog(logger, "[" + logger.getName()+ "]-" + + nb.incrementAndGet()); + tester.testLogrb(logger, bundleParam, "[" + logger.getName()+ "]-" + + nb.incrementAndGet()); + tester.testLog(logger, "[" + logger.getName()+ "]-({0},{1})" + + nb.incrementAndGet(), "One", "Two"); + tester.testLogrb(logger, bundleParam, "[" + logger.getName()+ "]-({0},{1})" + + nb.incrementAndGet(), "One", "Two"); + tester.testLog(logger, "[" + logger.getName()+ "]-" + + nb.incrementAndGet(), thrown); + tester.testLogrb(logger, bundleParam, "[" + logger.getName()+ "]-" + + nb.incrementAndGet(), thrown); + final int nbb1 = nb.incrementAndGet(); + tester.testLog(logger, () -> "[" + logger.getName()+ "]-" + nbb1); + final int nbb2 = nb.incrementAndGet(); + tester.testLog(logger, thrown, () -> "[" + logger.getName()+ "]-" + nbb2); + + // Test all logp method forms + // jdk.internal.logging.Logger.logp(...) + tester.testLogp(logger, "clazz" + nb.incrementAndGet(), + "method" + nb.incrementAndGet(), + "[" + logger.getName()+ "]-" + + nb.incrementAndGet()); + tester.testLogrb(logger, "clazz" + nb.incrementAndGet(), + "method" + nb.incrementAndGet(), bundleParam, + "[" + logger.getName()+ "]-" + + nb.incrementAndGet()); + tester.testLogp(logger, "clazz" + nb.incrementAndGet(), + "method" + nb.incrementAndGet(), + "[" + logger.getName()+ "]-({0},{1})" + + nb.incrementAndGet(), "One", "Two"); + tester.testLogrb(logger, "clazz" + nb.incrementAndGet(), + "method" + nb.incrementAndGet(), bundleParam, + "[" + logger.getName()+ "]-({0},{1})" + + nb.incrementAndGet(), "One", "Two"); + tester.testLogp(logger, "clazz" + nb.incrementAndGet(), + "method" + nb.incrementAndGet(), + "[" + logger.getName()+ "]-" + + nb.incrementAndGet(), thrown); + tester.testLogrb(logger, "clazz" + nb.incrementAndGet(), + "method" + nb.incrementAndGet(), bundleParam, + "[" + logger.getName()+ "]-" + + nb.incrementAndGet(), thrown); + final int nbb3 = nb.incrementAndGet(); + tester.testLogp(logger, "clazz" + nb.incrementAndGet(), + "method" + nb.incrementAndGet(), + () -> "[" + logger.getName()+ "]-" + nbb3); + final int nbb4 = nb.incrementAndGet(); + tester.testLogp(logger, "clazz" + nb.incrementAndGet(), + "method" + nb.incrementAndGet(), + thrown, () -> "[" + logger.getName()+ "]-" + nbb4); + } + +} diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/backend/META-INF/services/java.lang.System$LoggerFinder b/jdk/test/java/lang/System/LoggerFinder/internal/backend/META-INF/services/java.lang.System$LoggerFinder new file mode 100644 index 00000000000..c4068ecdd4b --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/backend/META-INF/services/java.lang.System$LoggerFinder @@ -0,0 +1,2 @@ +LoggerFinderBackendTest$CustomLoggerFinder + diff --git a/jdk/test/java/lang/System/LoggerFinder/internal/backend/SystemClassLoader.java b/jdk/test/java/lang/System/LoggerFinder/internal/backend/SystemClassLoader.java new file mode 100644 index 00000000000..54a85911032 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/internal/backend/SystemClassLoader.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; +import java.lang.System.LoggerFinder; + +/** + * A custom class loader which can hide the registered LoggerProvider + * depending on the value of a test.logger.hidesProvider system property. + * @author danielfuchs + */ +public class SystemClassLoader extends ClassLoader { + + final public boolean hidesProvider; + + public SystemClassLoader() { + hidesProvider = Boolean.getBoolean("test.logger.hidesProvider"); + } + public SystemClassLoader(ClassLoader parent) { + super(parent); + hidesProvider = Boolean.getBoolean("test.logger.hidesProvider"); + } + + boolean accept(String name) { + final boolean res = !name.endsWith(LoggerFinder.class.getName()); + if (res == false) { + System.out.println("Hiding " + name); + } + return res; + } + + @Override + public URL getResource(String name) { + if (hidesProvider && !accept(name)) { + return null; + } else { + return super.getResource(name); + } + } + + class Enumerator implements Enumeration { + final Enumeration enumeration; + volatile URL next; + Enumerator(Enumeration enumeration) { + this.enumeration = enumeration; + } + + @Override + public boolean hasMoreElements() { + if (next != null) return true; + if (!enumeration.hasMoreElements()) return false; + if (hidesProvider == false) return true; + next = enumeration.nextElement(); + if (accept(next.getPath())) return true; + next = null; + return hasMoreElements(); + } + + @Override + public URL nextElement() { + final URL res = next == null ? enumeration.nextElement() : next; + next = null; + if (hidesProvider == false || accept(res.getPath())) return res; + return nextElement(); + } + } + + @Override + public Enumeration getResources(String name) throws IOException { + final Enumeration enumeration = super.getResources(name); + return hidesProvider ? new Enumerator(enumeration) : enumeration; + } + + + +} diff --git a/jdk/test/java/lang/System/LoggerFinder/jdk/DefaultLoggerBridgeTest/DefaultLoggerBridgeTest.java b/jdk/test/java/lang/System/LoggerFinder/jdk/DefaultLoggerBridgeTest/DefaultLoggerBridgeTest.java new file mode 100644 index 00000000000..6f15819fcd2 --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/jdk/DefaultLoggerBridgeTest/DefaultLoggerBridgeTest.java @@ -0,0 +1,850 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.security.AccessControlException; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Queue; +import java.util.ResourceBundle; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; +import java.util.logging.Handler; +import java.util.logging.LogManager; +import sun.util.logging.PlatformLogger; +import java.util.logging.LogRecord; +import java.lang.System.LoggerFinder; +import java.lang.System.Logger; +import java.util.stream.Stream; +import sun.util.logging.internal.LoggingProviderImpl; + +/** + * @test + * @bug 8140364 + * @summary JDK implementation specific unit test for JDK internal artifacts. + * Tests all internal bridge methods with the default LoggerFinder + * JUL backend. + * @modules java.base/sun.util.logging + * java.base/jdk.internal.logger + * java.logging/sun.util.logging.internal + * @run main/othervm DefaultLoggerBridgeTest + * @author danielfuchs + */ +public class DefaultLoggerBridgeTest { + + final static AtomicLong sequencer = new AtomicLong(); + final static boolean VERBOSE = false; + static final ThreadLocal allowControl = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAccess = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAll = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + + public static final Queue eventQueue = new ArrayBlockingQueue<>(128); + + public static final class LogEvent implements Cloneable { + + public LogEvent() { + this(sequencer.getAndIncrement()); + } + + LogEvent(long sequenceNumber) { + this.sequenceNumber = sequenceNumber; + } + + long sequenceNumber; + boolean isLoggable; + String loggerName; + java.util.logging.Level level; + ResourceBundle bundle; + Throwable thrown; + Object[] args; + String msg; + String className; + String methodName; + + Object[] toArray() { + return new Object[] { + sequenceNumber, + isLoggable, + loggerName, + level, + bundle, + thrown, + args, + msg, + className, + methodName, + }; + } + + @Override + public String toString() { + return Arrays.deepToString(toArray()); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof LogEvent + && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray()); + } + + @Override + public int hashCode() { + return Objects.hash(toArray()); + } + + public LogEvent cloneWith(long sequenceNumber) + throws CloneNotSupportedException { + LogEvent cloned = (LogEvent)super.clone(); + cloned.sequenceNumber = sequenceNumber; + return cloned; + } + + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + java.util.logging.Level level, ResourceBundle bundle, + String key, Throwable thrown, Object... params) { + return LogEvent.of(sequenceNumber, isLoggable, name, + DefaultLoggerBridgeTest.class.getName(), + "testLogger", level, bundle, key, + thrown, params); + } + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + String className, String methodName, + java.util.logging.Level level, ResourceBundle bundle, + String key, Throwable thrown, Object... params) { + LogEvent evt = new LogEvent(sequenceNumber); + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = thrown; + evt.msg = key; + evt.isLoggable = isLoggable; + evt.className = className; + evt.methodName = methodName; + return evt; + } + + } + + static final java.util.logging.Level[] julLevels = { + java.util.logging.Level.ALL, + java.util.logging.Level.FINEST, + java.util.logging.Level.FINER, + java.util.logging.Level.FINE, + java.util.logging.Level.CONFIG, + java.util.logging.Level.INFO, + java.util.logging.Level.WARNING, + java.util.logging.Level.SEVERE, + java.util.logging.Level.OFF, + }; + + + public static class MyBundle extends ResourceBundle { + + final ConcurrentHashMap map = new ConcurrentHashMap<>(); + + @Override + protected Object handleGetObject(String key) { + if (key.contains(" (translated)")) { + throw new RuntimeException("Unexpected key: " + key); + } + return map.computeIfAbsent(key, k -> k + " (translated)"); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(map.keySet()); + } + + } + + public static class MyHandler extends Handler { + + @Override + public java.util.logging.Level getLevel() { + return java.util.logging.Level.ALL; + } + + @Override + public void publish(LogRecord record) { + eventQueue.add(LogEvent.of(sequencer.getAndIncrement(), + true, record.getLoggerName(), + record.getSourceClassName(), + record.getSourceMethodName(), + record.getLevel(), + record.getResourceBundle(), record.getMessage(), + record.getThrown(), record.getParameters())); + } + @Override + public void flush() { + } + @Override + public void close() throws SecurityException { + } + + } + + public static class MyLoggerBundle extends MyBundle { + + } + + static PlatformLogger.Bridge convert(Logger logger) { + boolean old = allowAccess.get().get(); + allowAccess.get().set(true); + try { + return PlatformLogger.Bridge.convert(logger); + } finally { + allowAccess.get().set(old); + } + } + + static Logger getLogger(String name, Class caller) { + boolean old = allowAccess.get().get(); + allowAccess.get().set(true); + try { + return jdk.internal.logger.LazyLoggers.getLogger(name, caller); + } finally { + allowAccess.get().set(old); + } + } + + static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS}; + + static void setSecurityManager() { + if (System.getSecurityManager() == null) { + Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll)); + System.setSecurityManager(new SecurityManager()); + } + } + + public static void main(String[] args) { + if (args.length == 0) + args = new String[] { + "NOSECURITY", + "NOPERMISSIONS", + "WITHPERMISSIONS" + }; + + Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> { + LoggerFinder provider; + switch (testCase) { + case NOSECURITY: + System.out.println("\n*** Without Security Manager\n"); + test(true); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case NOPERMISSIONS: + System.out.println("\n*** With Security Manager, without permissions\n"); + setSecurityManager(); + test(false); + System.out.println("Tetscase count: " + sequencer.get()); + break; + case WITHPERMISSIONS: + System.out.println("\n*** With Security Manager, with control permission\n"); + setSecurityManager(); + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + test(true); + } finally { + allowControl.get().set(control); + } + break; + default: + throw new RuntimeException("Unknown test case: " + testCase); + } + }); + System.out.println("\nPASSED: Tested " + sequencer.get() + " cases."); + } + + public static void test(boolean hasRequiredPermissions) { + + ResourceBundle loggerBundle = + ResourceBundle.getBundle(MyLoggerBundle.class.getName()); + final Map loggerDescMap = new HashMap<>(); + + Logger sysLogger1a = getLogger("foo", Thread.class); + loggerDescMap.put(sysLogger1a, "jdk.internal.logger.LazyLoggers.getLogger(\"foo\", Thread.class)"); + + Logger appLogger1 = System.getLogger("foo"); + loggerDescMap.put(appLogger1, "System.getLogger(\"foo\")"); + + LoggerFinder provider; + try { + provider = LoggerFinder.getLoggerFinder(); + if (!hasRequiredPermissions) { + throw new RuntimeException("Expected exception not raised"); + } + } catch (AccessControlException x) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected permission check", x); + } + if (!SimplePolicy.LOGGERFINDER_PERMISSION.equals(x.getPermission())) { + throw new RuntimeException("Unexpected permission in exception: " + x, x); + } + final boolean control = allowControl.get().get(); + try { + allowControl.get().set(true); + provider = LoggerFinder.getLoggerFinder(); + } finally { + allowControl.get().set(control); + } + } + + Logger sysLogger1b = null; + try { + sysLogger1b = provider.getLogger("foo", Thread.class); + if (sysLogger1b != sysLogger1a) { + loggerDescMap.put(sysLogger1b, "provider.getLogger(\"foo\", Thread.class)"); + } + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a system logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for system logger: " + acx); + } + + Logger appLogger2 = System.getLogger("foo", loggerBundle); + loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)"); + + if (appLogger2 == appLogger1) { + throw new RuntimeException("identical loggers"); + } + + Logger sysLogger2 = null; + try { + sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class); + loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class)"); + if (!hasRequiredPermissions) { + throw new RuntimeException("Managed to obtain a system logger without permission"); + } + } catch (AccessControlException acx) { + if (hasRequiredPermissions) { + throw new RuntimeException("Unexpected security exception: ", acx); + } + if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) { + throw new RuntimeException("Unexpected permission in exception: " + acx, acx); + } + System.out.println("Got expected exception for localized system logger: " + acx); + } + if (hasRequiredPermissions && appLogger2 == sysLogger2) { + throw new RuntimeException("identical loggers"); + } + if (hasRequiredPermissions && sysLogger2 == sysLogger1a) { + throw new RuntimeException("identical loggers"); + } + + final java.util.logging.Logger appSink; + final java.util.logging.Logger sysSink; + final MyHandler appHandler; + final MyHandler sysHandler; + final boolean old = allowAll.get().get(); + allowAll.get().set(true); + try { + sysSink = LoggingProviderImpl.getLogManagerAccess().demandLoggerFor( + LogManager.getLogManager(), "foo", Thread.class); + appSink = LoggingProviderImpl.getLogManagerAccess().demandLoggerFor( + LogManager.getLogManager(), "foo", DefaultLoggerBridgeTest.class); + if (appSink == sysSink) { + throw new RuntimeException("identical backend loggers"); + } + appSink.addHandler(appHandler = new MyHandler()); + sysSink.addHandler(sysHandler = new MyHandler()); + appSink.setUseParentHandlers(VERBOSE); + sysSink.setUseParentHandlers(VERBOSE); + } finally { + allowAll.get().set(old); + } + + try { + testLogger(provider, loggerDescMap, "foo", null, convert(sysLogger1a), sysSink); + testLogger(provider, loggerDescMap, "foo", null, convert(appLogger1), appSink); + testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(appLogger2), appSink); + if (sysLogger1b != null && sysLogger1b != sysLogger1a) { + testLogger(provider, loggerDescMap, "foo", null, convert(sysLogger1b), sysSink); + } + if (sysLogger2 != null) { + testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(sysLogger2), sysSink); + } + } finally { + allowAll.get().set(true); + try { + appSink.removeHandler(appHandler); + sysSink.removeHandler(sysHandler); + } finally { + allowAll.get().set(old); + } + } + } + + public static class Foo { + + } + + static void verbose(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + + static void checkLogEvent(LoggerFinder provider, String desc, + LogEvent expected) { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + + static void checkLogEvent(LoggerFinder provider, String desc, + LogEvent expected, boolean expectNotNull) { + LogEvent actual = eventQueue.poll(); + if (actual == null && !expectNotNull) return; + if (actual != null && !expectNotNull) { + throw new RuntimeException("Unexpected log event found for " + desc + + "\n\tgot: " + actual); + } + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + + static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) { + boolean before = allowAll.get().get(); + try { + allowAll.get().set(true); + sink.setLevel(loggerLevel); + } finally { + allowAll.get().set(before); + } + } + + static sun.util.logging.PlatformLogger.Level toPlatformLevel(java.util.logging.Level level) { + boolean old = allowAccess.get().get(); + allowAccess.get().set(true); + try { + return sun.util.logging.PlatformLogger.Level.valueOf(level.getName()); + } finally { + allowAccess.get().set(old); + } + } + + // Calls the methods defined on LogProducer and verify the + // parameters received by the underlying logger. + private static void testLogger(LoggerFinder provider, + Map loggerDescMap, + String name, + ResourceBundle loggerBundle, + PlatformLogger.Bridge logger, + java.util.logging.Logger sink) { + + if (loggerDescMap.get(logger) == null) { + throw new RuntimeException("Missing description for " + logger); + } + System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger + "]"); + final java.util.logging.Level OFF = java.util.logging.Level.OFF; + + Foo foo = new Foo(); + String fooMsg = foo.toString(); + System.out.println("\tlogger.log(messageLevel, fooMsg)"); + System.out.println("\tlogger.(fooMsg)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel :julLevels) { + String desc = "logger.log(messageLevel, fooMsg): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, loggerBundle, + fooMsg, (Throwable)null, (Object[])null); + logger.log(toPlatformLevel(messageLevel), fooMsg); + checkLogEvent(provider, desc, expected, expected.isLoggable); + } + } + + Supplier supplier = new Supplier() { + @Override + public String get() { + return this.toString(); + } + }; + System.out.println("\tlogger.log(messageLevel, supplier)"); + System.out.println("\tlogger.(supplier)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel :julLevels) { + String desc = "logger.log(messageLevel, supplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, null, + supplier.get(), (Throwable)null, (Object[])null); + logger.log(toPlatformLevel(messageLevel), supplier); + checkLogEvent(provider, desc, expected, expected.isLoggable); + } + } + + String format = "two params [{1} {2}]"; + Object arg1 = foo; + Object arg2 = fooMsg; + System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel :julLevels) { + String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, loggerBundle, + format, (Throwable)null, arg1, arg2); + logger.log(toPlatformLevel(messageLevel), format, arg1, arg2); + checkLogEvent(provider, desc, expected, expected.isLoggable); + } + } + + Throwable thrown = new Exception("OK: log me!"); + System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel :julLevels) { + String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, loggerBundle, + fooMsg, thrown, (Object[])null); + logger.log(toPlatformLevel(messageLevel), fooMsg, thrown); + checkLogEvent(provider, desc, expected, expected.isLoggable); + } + } + + System.out.println("\tlogger.log(messageLevel, thrown, supplier)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel :julLevels) { + String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, null, + supplier.get(), thrown, (Object[])null); + logger.log(toPlatformLevel(messageLevel), thrown, supplier); + checkLogEvent(provider, desc, expected, expected.isLoggable); + } + } + + String sourceClass = "blah.Blah"; + String sourceMethod = "blih"; + System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel :julLevels) { + String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, sourceClass, sourceMethod, messageLevel, loggerBundle, + fooMsg, (Throwable)null, (Object[])null); + logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, fooMsg); + checkLogEvent(provider, desc, expected, expected.isLoggable); + } + } + + System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel :julLevels) { + String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, sourceClass, sourceMethod, messageLevel, null, + supplier.get(), (Throwable)null, (Object[])null); + logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, supplier); + checkLogEvent(provider, desc, expected, expected.isLoggable); + } + } + + System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel :julLevels) { + String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, sourceClass, sourceMethod, messageLevel, loggerBundle, + format, (Throwable)null, arg1, arg2); + logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, format, arg1, arg2); + checkLogEvent(provider, desc, expected, expected.isLoggable); + } + } + + System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel :julLevels) { + String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, sourceClass, sourceMethod, messageLevel, loggerBundle, + fooMsg, thrown, (Object[])null); + logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, fooMsg, thrown); + checkLogEvent(provider, desc, expected, expected.isLoggable); + } + } + + System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel :julLevels) { + String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, sourceClass, sourceMethod, messageLevel, null, + supplier.get(), thrown, (Object[])null); + logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, thrown, supplier); + checkLogEvent(provider, desc, expected, expected.isLoggable); + } + } + + ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName()); + System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel :julLevels) { + String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, bundle, + format, (Throwable)null, arg1, arg2); + logger.logrb(toPlatformLevel(messageLevel), bundle, format, arg1, arg2); + checkLogEvent(provider, desc, expected, expected.isLoggable); + } + } + + System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel :julLevels) { + String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, bundle, + fooMsg, thrown, (Object[])null); + logger.logrb(toPlatformLevel(messageLevel), bundle, fooMsg, thrown); + checkLogEvent(provider, desc, expected, expected.isLoggable); + } + } + + System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel :julLevels) { + String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, sourceClass, sourceMethod, messageLevel, bundle, + format, (Throwable)null, arg1, arg2); + logger.logrb(toPlatformLevel(messageLevel), sourceClass, sourceMethod, bundle, format, arg1, arg2); + checkLogEvent(provider, desc, expected, expected.isLoggable); + } + } + + System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel :julLevels) { + String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, sourceClass, sourceMethod, messageLevel, bundle, + fooMsg, thrown, (Object[])null); + logger.logrb(toPlatformLevel(messageLevel), sourceClass, sourceMethod, bundle, fooMsg, thrown); + checkLogEvent(provider, desc, expected, expected.isLoggable); + } + } + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + public static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger"); + final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging"); + + final Permissions permissions; + final Permissions allPermissions; + final ThreadLocal allowControl; + final ThreadLocal allowAccess; + final ThreadLocal allowAll; + public SimplePolicy(ThreadLocal allowControl, + ThreadLocal allowAccess, + ThreadLocal allowAll) { + this.allowControl = allowControl; + this.allowAccess = allowAccess; + this.allowAll = allowAll; + permissions = new Permissions(); + allPermissions = new PermissionsBuilder() + .add(new java.security.AllPermission()) + .toPermissions(); + } + + Permissions getPermissions() { + if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) { + PermissionsBuilder builder = new PermissionsBuilder() + .addAll(permissions); + if (allowControl.get().get()) { + builder.add(LOGGERFINDER_PERMISSION); + } + if (allowAccess.get().get()) { + builder.add(ACCESS_LOGGER); + builder.add(ACCESS_LOGGING); + } + if (allowAll.get().get()) { + builder.addAll(allPermissions); + } + return builder.toPermissions(); + } + return permissions; + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + return getPermissions().implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return new PermissionsBuilder().addAll(getPermissions()).toPermissions(); + } + } +} diff --git a/jdk/test/java/lang/System/LoggerFinder/jdk/DefaultPlatformLoggerTest/DefaultPlatformLoggerTest.java b/jdk/test/java/lang/System/LoggerFinder/jdk/DefaultPlatformLoggerTest/DefaultPlatformLoggerTest.java new file mode 100644 index 00000000000..2eb64d0743d --- /dev/null +++ b/jdk/test/java/lang/System/LoggerFinder/jdk/DefaultPlatformLoggerTest/DefaultPlatformLoggerTest.java @@ -0,0 +1,544 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Queue; +import java.util.ResourceBundle; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Handler; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import java.lang.System.LoggerFinder; +import sun.util.logging.PlatformLogger; +import sun.util.logging.internal.LoggingProviderImpl; + +/** + * @test + * @bug 8140364 + * @summary Tests all PlatformLogger methods with the default LoggerFinder JUL backend. + * @modules java.base/sun.util.logging java.logging/sun.util.logging.internal + * @run main/othervm DefaultPlatformLoggerTest + * @author danielfuchs + */ +public class DefaultPlatformLoggerTest { + + final static AtomicLong sequencer = new AtomicLong(); + final static boolean VERBOSE = false; + static final ThreadLocal allowControl = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + static final ThreadLocal allowAll = new ThreadLocal() { + @Override + protected AtomicBoolean initialValue() { + return new AtomicBoolean(false); + } + }; + + public static final Queue eventQueue = new ArrayBlockingQueue<>(128); + + public static final class LogEvent implements Cloneable { + + public LogEvent() { + this(sequencer.getAndIncrement()); + } + + LogEvent(long sequenceNumber) { + this.sequenceNumber = sequenceNumber; + } + + long sequenceNumber; + boolean isLoggable; + String loggerName; + java.util.logging.Level level; + ResourceBundle bundle; + Throwable thrown; + Object[] args; + String msg; + String className; + String methodName; + + Object[] toArray() { + return new Object[] { + sequenceNumber, + isLoggable, + loggerName, + level, + bundle, + thrown, + args, + msg, + className, + methodName, + }; + } + + @Override + public String toString() { + return Arrays.deepToString(toArray()); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof LogEvent + && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray()); + } + + @Override + public int hashCode() { + return Objects.hash(toArray()); + } + + public LogEvent cloneWith(long sequenceNumber) + throws CloneNotSupportedException { + LogEvent cloned = (LogEvent)super.clone(); + cloned.sequenceNumber = sequenceNumber; + return cloned; + } + + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + java.util.logging.Level level, ResourceBundle bundle, + String key, Throwable thrown, Object... params) { + return LogEvent.of(sequenceNumber, isLoggable, name, + DefaultPlatformLoggerTest.class.getName(), + "testLogger", level, bundle, key, + thrown, params); + } + public static LogEvent of(long sequenceNumber, + boolean isLoggable, String name, + String className, String methodName, + java.util.logging.Level level, ResourceBundle bundle, + String key, Throwable thrown, Object... params) { + LogEvent evt = new LogEvent(sequenceNumber); + evt.loggerName = name; + evt.level = level; + evt.args = params; + evt.bundle = bundle; + evt.thrown = thrown; + evt.msg = key; + evt.isLoggable = isLoggable; + evt.className = className; + evt.methodName = methodName; + return evt; + } + + } + + static final java.util.logging.Level[] julLevels = { + java.util.logging.Level.ALL, + new java.util.logging.Level("FINER_THAN_FINEST", java.util.logging.Level.FINEST.intValue() - 10) {}, + java.util.logging.Level.FINEST, + new java.util.logging.Level("FINER_THAN_FINER", java.util.logging.Level.FINER.intValue() - 10) {}, + java.util.logging.Level.FINER, + new java.util.logging.Level("FINER_THAN_FINE", java.util.logging.Level.FINE.intValue() - 10) {}, + java.util.logging.Level.FINE, + new java.util.logging.Level("FINER_THAN_CONFIG", java.util.logging.Level.FINE.intValue() + 10) {}, + java.util.logging.Level.CONFIG, + new java.util.logging.Level("FINER_THAN_INFO", java.util.logging.Level.INFO.intValue() - 10) {}, + java.util.logging.Level.INFO, + new java.util.logging.Level("FINER_THAN_WARNING", java.util.logging.Level.INFO.intValue() + 10) {}, + java.util.logging.Level.WARNING, + new java.util.logging.Level("FINER_THAN_SEVERE", java.util.logging.Level.SEVERE.intValue() - 10) {}, + java.util.logging.Level.SEVERE, + new java.util.logging.Level("FATAL", java.util.logging.Level.SEVERE.intValue() + 10) {}, + java.util.logging.Level.OFF, + }; + + static final java.util.logging.Level[] julPlatformLevels = { + java.util.logging.Level.FINEST, + java.util.logging.Level.FINER, + java.util.logging.Level.FINE, + java.util.logging.Level.CONFIG, + java.util.logging.Level.INFO, + java.util.logging.Level.WARNING, + java.util.logging.Level.SEVERE, + }; + + + public static class MyBundle extends ResourceBundle { + + final ConcurrentHashMap map = new ConcurrentHashMap<>(); + + @Override + protected Object handleGetObject(String key) { + if (key.contains(" (translated)")) { + throw new RuntimeException("Unexpected key: " + key); + } + return map.computeIfAbsent(key, k -> k + " (translated)"); + } + + @Override + public Enumeration getKeys() { + return Collections.enumeration(map.keySet()); + } + + } + + public static class MyHandler extends Handler { + + @Override + public java.util.logging.Level getLevel() { + return java.util.logging.Level.ALL; + } + + @Override + public void publish(LogRecord record) { + eventQueue.add(LogEvent.of(sequencer.getAndIncrement(), + true, record.getLoggerName(), + record.getSourceClassName(), + record.getSourceMethodName(), + record.getLevel(), + record.getResourceBundle(), record.getMessage(), + record.getThrown(), record.getParameters())); + } + @Override + public void flush() { + } + @Override + public void close() throws SecurityException { + } + + } + + public static class MyLoggerBundle extends MyBundle { + + } + + public static void main(String[] args) throws Exception { + LoggerFinder provider = LoggerFinder.getLoggerFinder(); + java.util.logging.Logger appSink = LoggingProviderImpl.getLogManagerAccess() + .demandLoggerFor(LogManager.getLogManager(), "foo", + DefaultPlatformLoggerTest.class); + java.util.logging.Logger sysSink = LoggingProviderImpl.getLogManagerAccess() + .demandLoggerFor(LogManager.getLogManager(),"foo", Thread.class); + appSink.addHandler(new MyHandler()); + sysSink.addHandler(new MyHandler()); + appSink.setUseParentHandlers(VERBOSE); + sysSink.setUseParentHandlers(VERBOSE); + + System.out.println("\n*** Without Security Manager\n"); + test(provider, true, appSink, sysSink); + System.out.println("Tetscase count: " + sequencer.get()); + + Policy.setPolicy(new SimplePolicy(allowAll, allowControl)); + System.setSecurityManager(new SecurityManager()); + + System.out.println("\n*** With Security Manager, without permissions\n"); + test(provider, false, appSink, sysSink); + System.out.println("Tetscase count: " + sequencer.get()); + + System.out.println("\n*** With Security Manager, with control permission\n"); + allowControl.get().set(true); + test(provider, true, appSink, sysSink); + + System.out.println("\nPASSED: Tested " + sequencer.get() + " cases."); + } + + public static void test(LoggerFinder provider, boolean hasRequiredPermissions, + java.util.logging.Logger appSink, java.util.logging.Logger sysSink) throws Exception { + + // No way to giva a resource bundle to a platform logger. + // ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName()); + final Map loggerDescMap = new HashMap<>(); + + PlatformLogger platform = PlatformLogger.getLogger("foo"); + loggerDescMap.put(platform, "PlatformLogger.getLogger(\"foo\")"); + + testLogger(provider, loggerDescMap, "foo", null, platform, sysSink); + } + + public static class Foo { + + } + + static void verbose(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + + static void checkLogEvent(LoggerFinder provider, String desc, + LogEvent expected) { + LogEvent actual = eventQueue.poll(); + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + + static void checkLogEvent(LoggerFinder provider, String desc, + LogEvent expected, boolean expectNotNull) { + LogEvent actual = eventQueue.poll(); + if (actual == null && !expectNotNull) return; + if (actual != null && !expectNotNull) { + throw new RuntimeException("Unexpected log event found for " + desc + + "\n\tgot: " + actual); + } + if (!expected.equals(actual)) { + throw new RuntimeException("mismatch for " + desc + + "\n\texpected=" + expected + + "\n\t actual=" + actual); + } else { + verbose("Got expected results for " + + desc + "\n\t" + expected); + } + } + + static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) { + boolean before = allowAll.get().get(); + try { + allowAll.get().set(true); + sink.setLevel(loggerLevel); + } finally { + allowAll.get().set(before); + } + } + + // Calls the methods defined on LogProducer and verify the + // parameters received by the underlying logger. + private static void testLogger(LoggerFinder provider, + Map loggerDescMap, + String name, + ResourceBundle loggerBundle, + PlatformLogger logger, + java.util.logging.Logger sink) throws Exception { + + System.out.println("Testing " + loggerDescMap.get(logger)); + final java.util.logging.Level OFF = java.util.logging.Level.OFF; + + Foo foo = new Foo(); + String fooMsg = foo.toString(); + System.out.println("\tlogger.(fooMsg)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel : julPlatformLevels) { + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, loggerBundle, + fooMsg, (Throwable)null, (Object[])null); + String desc2 = "logger." + messageLevel.toString().toLowerCase() + + "(fooMsg): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + if (messageLevel == java.util.logging.Level.FINEST) { + logger.finest(fooMsg); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.FINER) { + logger.finer(fooMsg); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.FINE) { + logger.fine(fooMsg); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.CONFIG) { + logger.config(fooMsg); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.INFO) { + logger.info(fooMsg); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.WARNING) { + logger.warning(fooMsg); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.SEVERE) { + logger.severe(fooMsg); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } + } + } + + Throwable thrown = new Exception("OK: log me!"); + System.out.println("\tlogger.(msg, thrown)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel :julPlatformLevels) { + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, loggerBundle, + fooMsg, thrown, (Object[])null); + String desc2 = "logger." + messageLevel.toString().toLowerCase() + + "(msg, thrown): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + if (messageLevel == java.util.logging.Level.FINEST) { + logger.finest(fooMsg, thrown); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.FINER) { + logger.finer(fooMsg, thrown); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.FINE) { + logger.fine(fooMsg, thrown); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.CONFIG) { + logger.config(fooMsg, thrown); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.INFO) { + logger.info(fooMsg, thrown); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.WARNING) { + logger.warning(fooMsg, thrown); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.SEVERE) { + logger.severe(fooMsg, thrown); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } + } + } + + String format = "two params [{1} {2}]"; + Object arg1 = foo; + Object arg2 = fooMsg; + System.out.println("\tlogger.(format, arg1, arg2)"); + for (java.util.logging.Level loggerLevel : julLevels) { + setLevel(sink, loggerLevel); + for (java.util.logging.Level messageLevel : julPlatformLevels) { + LogEvent expected = + LogEvent.of( + sequencer.get(), + loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(), + name, messageLevel, loggerBundle, + format, (Throwable)null, foo, fooMsg); + String desc2 = "logger." + messageLevel.toString().toLowerCase() + + "(format, foo, fooMsg): loggerLevel=" + + loggerLevel+", messageLevel="+messageLevel; + if (messageLevel == java.util.logging.Level.FINEST) { + logger.finest(format, foo, fooMsg); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.FINER) { + logger.finer(format, foo, fooMsg); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.FINE) { + logger.fine(format, foo, fooMsg); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.CONFIG) { + logger.config(format, foo, fooMsg); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.INFO) { + logger.info(format, foo, fooMsg); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.WARNING) { + logger.warning(format, foo, fooMsg); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } else if (messageLevel == java.util.logging.Level.SEVERE) { + logger.severe(format, foo, fooMsg); + checkLogEvent(provider, desc2, expected, expected.isLoggable); + } + } + } + + } + + final static class PermissionsBuilder { + final Permissions perms; + public PermissionsBuilder() { + this(new Permissions()); + } + public PermissionsBuilder(Permissions perms) { + this.perms = perms; + } + public PermissionsBuilder add(Permission p) { + perms.add(p); + return this; + } + public PermissionsBuilder addAll(PermissionCollection col) { + if (col != null) { + for (Enumeration e = col.elements(); e.hasMoreElements(); ) { + perms.add(e.nextElement()); + } + } + return this; + } + public Permissions toPermissions() { + final PermissionsBuilder builder = new PermissionsBuilder(); + builder.addAll(perms); + return builder.perms; + } + } + + public static class SimplePolicy extends Policy { + public static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + + final Permissions permissions; + final Permissions withControlPermissions; + final Permissions allPermissions; + final ThreadLocal allowAll; + final ThreadLocal allowControl; + public SimplePolicy(ThreadLocal allowAll, + ThreadLocal allowControl) { + this.allowAll = allowAll; + this.allowControl = allowControl; + permissions = new Permissions(); + + withControlPermissions = new Permissions(); + withControlPermissions.add(LOGGERFINDER_PERMISSION); + + // these are used for configuring the test itself... + allPermissions = new Permissions(); + allPermissions.add(new java.security.AllPermission()); + } + + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + if (allowAll.get().get()) return allPermissions.implies(permission); + if (allowControl.get().get()) return withControlPermissions.implies(permission); + return permissions.implies(permission); + } + + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return new PermissionsBuilder().addAll( + allowAll.get().get() ? allPermissions : + allowControl.get().get() + ? withControlPermissions : permissions).toPermissions(); + } + + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + return new PermissionsBuilder().addAll( + allowAll.get().get() ? allPermissions : + allowControl.get().get() + ? withControlPermissions : permissions).toPermissions(); + } + } +} diff --git a/jdk/test/java/lang/invoke/AccessControlTest.java b/jdk/test/java/lang/invoke/AccessControlTest.java index e6c38804867..f10a2300745 100644 --- a/jdk/test/java/lang/invoke/AccessControlTest.java +++ b/jdk/test/java/lang/invoke/AccessControlTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, 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 @@ -225,6 +225,31 @@ public class AccessControlTest { System.out.println(this+" willAccess "+lc+" m1="+m1+" m2="+m2+" => "+((m2 & m1) != 0)); return (m2 & m1) != 0; } + + /** Predict the success or failure of accessing this class. */ + public boolean willAccessClass(Class c2, boolean load) { + Class c1 = lookupClass(); + if (load && c1.getClassLoader() == null) { + return false; + } + LookupCase lc = this.in(c2); + int m1 = lc.lookupModes(); + boolean r = false; + if (m1 == 0) { + r = false; + } else { + int m2 = fixMods(c2.getModifiers()); + if ((m2 & PUBLIC) != 0) { + r = true; + } else if ((m1 & PACKAGE) != 0 && c1.getPackage() == c2.getPackage()) { + r = true; + } + } + if (verbosity >= 2) { + System.out.println(this+" willAccessClass "+lc+" c1="+c1+" c2="+c2+" => "+r); + } + return r; + } } private static Class topLevelClass(Class cls) { @@ -342,6 +367,8 @@ public class AccessControlTest { Method method = targetMethod(targetClass, targetAccess, methodType); // Try to access target method from various contexts. for (LookupCase sourceCase : CASES) { + testOneAccess(sourceCase, method, "findClass"); + testOneAccess(sourceCase, method, "accessClass"); testOneAccess(sourceCase, method, "find"); testOneAccess(sourceCase, method, "unreflect"); } @@ -356,11 +383,19 @@ public class AccessControlTest { Class targetClass = method.getDeclaringClass(); String methodName = method.getName(); MethodType methodType = methodType(method.getReturnType(), method.getParameterTypes()); - boolean willAccess = sourceCase.willAccess(method); + boolean isFindOrAccessClass = "findClass".equals(kind) || "accessClass".equals(kind); + boolean willAccess = isFindOrAccessClass ? + sourceCase.willAccessClass(targetClass, "findClass".equals(kind)) : sourceCase.willAccess(method); boolean didAccess = false; ReflectiveOperationException accessError = null; try { switch (kind) { + case "accessClass": + sourceCase.lookup().accessClass(targetClass); + break; + case "findClass": + sourceCase.lookup().findClass(targetClass.getName()); + break; case "find": if ((method.getModifiers() & Modifier.STATIC) != 0) sourceCase.lookup().findStatic(targetClass, methodName, methodType); @@ -378,8 +413,8 @@ public class AccessControlTest { accessError = ex; } if (willAccess != didAccess) { - System.out.println(sourceCase+" => "+targetClass.getSimpleName()+"."+methodName+methodType); - System.out.println("fail on "+method+" ex="+accessError); + System.out.println(sourceCase+" => "+targetClass.getSimpleName()+(isFindOrAccessClass?"":"."+methodName+methodType)); + System.out.println("fail "+(isFindOrAccessClass?kind:"on "+method)+" ex="+accessError); assertEquals(willAccess, didAccess); } testCount++; diff --git a/jdk/test/java/lang/invoke/BigArityTest.java b/jdk/test/java/lang/invoke/BigArityTest.java index 2a767b05f98..3ee9401c6ff 100644 --- a/jdk/test/java/lang/invoke/BigArityTest.java +++ b/jdk/test/java/lang/invoke/BigArityTest.java @@ -58,6 +58,8 @@ public class BigArityTest { return x == null ? dflt : x; } + static final MethodType MT_A = MethodType.methodType(Object.class, Object.class, Object[].class, Object.class); + static Object hashArguments(Object... args) { return Objects.hash(args); } @@ -108,9 +110,36 @@ public class BigArityTest { } } // Sizes not in the above array are good: - target.asCollector(Object[].class, minbig-1); + target.asCollector(Object[].class, minbig - 1); for (int i = 2; i <= 10; i++) - target.asCollector(Object[].class, minbig-i); + target.asCollector(Object[].class, minbig - i); + } + + static void asciae02target(Object[] a, Object b) { + // naught + } + + @Test + public void asCollectorIAE02() throws ReflectiveOperationException { + final int[] INVALID_ARRAY_LENGTHS = { + Integer.MIN_VALUE, Integer.MIN_VALUE + 1, -2, -1, 254, 255, Integer.MAX_VALUE - 1, Integer.MAX_VALUE + }; + MethodHandle target = MethodHandles.lookup().findStatic(BigArityTest.class, "asciae02target", + MethodType.methodType(void.class, Object[].class, Object.class)); + int minbig = Integer.MAX_VALUE; + for (int invalidLength : INVALID_ARRAY_LENGTHS) { + if (minbig > invalidLength && invalidLength > 100) minbig = invalidLength; + try { + target.asCollector(0, Object[].class, invalidLength); + assert(false) : invalidLength; + } catch (IllegalArgumentException ex) { + System.out.println("OK: "+ex); + } + } + // Sizes not in the above array are good: + for (int i = 1; i <= 10; ++i) { + target.asCollector(0, Object[].class, minbig - i); + } } @Test @@ -216,51 +245,86 @@ public class BigArityTest { Class cls = (Class) cls0; //Class cls = Object[].class.asSubclass(cls0); int nargs = args.length, skip; + Object hr; MethodHandle smh = mh.asSpreader(cls, nargs - (skip = 0)); + MethodHandle hsmh = mh.asSpreader(0, cls, nargs - skip); Object[] tail = Arrays.copyOfRange(args, skip, nargs, cls); - if (cls == Object[].class) + Object[] head = Arrays.copyOfRange(args, 0, nargs - skip, cls); + if (cls == Object[].class) { r = smh.invokeExact(tail); - else if (cls == Integer[].class) + hr = hsmh.invokeExact(head); + } else if (cls == Integer[].class) { r = smh.invokeExact((Integer[]) tail); //warning OK, see 8019340 - else + hr = hsmh.invokeExact((Integer[]) head); + } else { r = smh.invoke(tail); + hr = hsmh.invoke(head); + } assertEquals(r0, r); + assertEquals(r0, hr); smh = mh.asSpreader(cls, nargs - (skip = 1)); + hsmh = mh.asSpreader(0, cls, nargs - skip); tail = Arrays.copyOfRange(args, skip, nargs, cls); - if (cls == Object[].class) + head = Arrays.copyOfRange(args, 0, nargs - skip, cls); + if (cls == Object[].class) { r = smh.invokeExact(args[0], tail); - else if (cls == Integer[].class) + hr = hsmh.invokeExact(head, args[2]); + } else if (cls == Integer[].class) { r = smh.invokeExact(args[0], (Integer[]) tail); - else + hr = hsmh.invokeExact((Integer[]) head, args[2]); + } else { r = smh.invoke(args[0], tail); + hr = hsmh.invoke(head, args[2]); + } assertEquals(r0, r); + assertEquals(r0, hr); smh = mh.asSpreader(cls, nargs - (skip = 2)); + hsmh = mh.asSpreader(0, cls, nargs - skip); tail = Arrays.copyOfRange(args, skip, nargs, cls); - if (cls == Object[].class) + head = Arrays.copyOfRange(args, 0, nargs - skip, cls); + if (cls == Object[].class) { r = smh.invokeExact(args[0], args[1], tail); - else if (cls == Integer[].class) + hr = hsmh.invokeExact(head, args[1], args[2]); + } else if (cls == Integer[].class) { r = smh.invokeExact(args[0], args[1], (Integer[]) tail); - else + hr = hsmh.invokeExact((Integer[]) head, args[1], args[2]); + } else { r = smh.invoke(args[0], args[1], tail); + hr = hsmh.invoke(head, args[1], args[2]); + } assertEquals(r0, r); + assertEquals(r0, hr); smh = mh.asSpreader(cls, nargs - (skip = 3)); + hsmh = mh.asSpreader(0, cls, nargs - skip); tail = Arrays.copyOfRange(args, skip, nargs, cls); - if (cls == Object[].class) + head = Arrays.copyOfRange(args, 0, nargs - skip, cls); + if (cls == Object[].class) { r = smh.invokeExact(args[0], args[1], args[2], tail); - else if (cls == Integer[].class) + hr = hsmh.invokeExact(head, args[0], args[1], args[2]); + } else if (cls == Integer[].class) { r = smh.invokeExact(args[0], args[1], args[2], (Integer[]) tail); - else + hr = hsmh.invokeExact((Integer[]) head, args[0], args[1], args[2]); + } else { r = smh.invoke(args[0], args[1], args[2], tail); + hr = hsmh.invoke(head, args[0], args[1], args[2]); + } assertEquals(r0, r); + assertEquals(r0, hr); // Try null array in addition to zero-length array: tail = null; - if (cls == Object[].class) + head = null; + if (cls == Object[].class) { r = smh.invokeExact(args[0], args[1], args[2], tail); - else if (cls == Integer[].class) + hr = hsmh.invokeExact(head, args[0], args[1], args[2]); + } else if (cls == Integer[].class) { r = smh.invokeExact(args[0], args[1], args[2], (Integer[]) tail); - else + hr = hsmh.invokeExact((Integer[]) head, args[0], args[1], args[2]); + } else { r = smh.invoke(args[0], args[1], args[2], tail); + hr = hsmh.invoke(head, args[0], args[1], args[2]); + } assertEquals(r0, r); + assertEquals(r0, hr); } } @@ -292,7 +356,7 @@ public class BigArityTest { @Test public void testArities() throws Throwable { System.out.println("testing spreaders and collectors on high arities..."); - int iterations = ITERATION_COUNT; + int iterations = ITERATION_COUNT; testArities(Object[].class, MIN_ARITY-10, MIN_ARITY-1, iterations / 1000); testArities(Object[].class, MIN_ARITY, SLOW_ARITY-1, iterations); testArities(Object[].class, SLOW_ARITY, MAX_ARITY, iterations / 1000); @@ -307,8 +371,13 @@ public class BigArityTest { Class cls = (Class) cls0; System.out.println("array class: "+cls.getSimpleName()); int iterations = ITERATION_COUNT / 1000; - testArities(cls, MIN_ARITY, SLOW_ARITY-1, iterations); - testArities(cls, SLOW_ARITY, MAX_ARITY, iterations / 100); + try { + testArities(cls, MIN_ARITY, SLOW_ARITY - 1, iterations); + testArities(cls, SLOW_ARITY, MAX_ARITY, iterations / 100); + } catch (Throwable t) { + t.printStackTrace(); + throw t; + } } } @@ -321,11 +390,14 @@ public class BigArityTest { if (verbose) System.out.println("arity="+arity); MethodHandle mh = MH_hashArguments(cls, arity); MethodHandle mh_VA = mh.asSpreader(cls, arity); + MethodHandle mh_VA_h = mh.asSpreader(0, cls, arity-1); assert(mh_VA.type().parameterType(0) == cls); - testArities(cls, arity, iterations, verbose, mh, mh_VA); + assert(mh_VA_h.type().parameterType(0) == cls); + testArities(cls, arity, iterations, verbose, mh, mh_VA, mh_VA_h); // mh_CA will collect arguments of a particular type and pass them to mh_VA MethodHandle mh_CA = mh_VA.asCollector(cls, arity); MethodHandle mh_VA2 = mh_CA.asSpreader(cls, arity); + MethodHandle mh_VA2_h = mh_CA.asSpreader(0, cls, arity-1); assert(mh_CA.type().equals(mh.type())); assert(mh_VA2.type().equals(mh_VA.type())); if (cls != Object[].class) { @@ -336,7 +408,7 @@ public class BigArityTest { } } int iterations_VA = iterations / 100; - testArities(cls, arity, iterations_VA, false, mh_CA, mh_VA2); + testArities(cls, arity, iterations_VA, false, mh_CA, mh_VA2, mh_VA2_h); } } @@ -357,13 +429,16 @@ public class BigArityTest { * @param verbose are we printing extra output? * @param mh a fixed-arity version of {@code hashArguments} * @param mh_VA a variable-arity version of {@code hashArguments}, accepting the given array type {@code cls} + * @param mh_VA_h a version of {@code hashArguments} that has a leading {@code cls} array and one final {@code cls} + * argument */ private void testArities(Class cls, int arity, int iterations, boolean verbose, MethodHandle mh, - MethodHandle mh_VA + MethodHandle mh_VA, + MethodHandle mh_VA_h ) throws Throwable { if (iterations < 4) iterations = 4; final int MAX_MH_ARITY = MAX_JVM_ARITY - 1; // mh.invoke(arg*[N]) @@ -373,6 +448,7 @@ public class BigArityTest { args = Arrays.copyOf(args, arity, cls); Object r0 = Objects.hash(args); Object r; + Object hr; MethodHandle ximh = null; MethodHandle gimh = null; if (arity <= MAX_INVOKER_ARITY) { @@ -397,13 +473,18 @@ public class BigArityTest { Object[] mh_args = cat(mh, args); assert(arity <= MAX_MH_ARITY); for (int i = 0; i < iterations; ++i) { - if (cls == Object[].class) + if (cls == Object[].class) { r = mh_VA.invokeExact(args); - else if (cls == Integer[].class) - r = mh_VA.invokeExact((Integer[])args); //warning OK, see 8019340 - else + hr = mh_VA_h.invokeExact(Arrays.copyOfRange(args, 0, arity - 1), args[arity - 1]); + } else if (cls == Integer[].class) { + r = mh_VA.invokeExact((Integer[]) args); //warning OK, see 8019340 + hr = mh_VA_h.invokeExact((Integer[]) Arrays.copyOfRange(args, 0, arity - 1), (Integer) args[arity - 1]); + } else { r = mh_VA.invoke(args); + hr = mh_VA_h.invoke(Arrays.copyOfRange(args, 0, arity - 1), args[arity - 1]); + } assertEquals(r0, r); + assertEquals(r0, hr); r = mh.invokeWithArguments(args); assertEquals(r0, r); if (ximh != null) { @@ -473,6 +554,43 @@ public class BigArityTest { // xF8, xF9, xFA, xFB); } + static Object hashArguments_252_a(Object x00, Object[] x01_FA, Object xFB) { + return Objects.hash( + // + x00, x01_FA[0], x01_FA[1], x01_FA[2], x01_FA[3], x01_FA[4], x01_FA[5], x01_FA[6], x01_FA[7], x01_FA[8], + x01_FA[9], x01_FA[10], x01_FA[11], x01_FA[12], x01_FA[13], x01_FA[14], x01_FA[15], x01_FA[16], + x01_FA[17], x01_FA[18], x01_FA[19], x01_FA[20], x01_FA[21], x01_FA[22], x01_FA[23], x01_FA[24], + x01_FA[25], x01_FA[26], x01_FA[27], x01_FA[28], x01_FA[29], x01_FA[30], x01_FA[31], x01_FA[32], + x01_FA[33], x01_FA[34], x01_FA[35], x01_FA[36], x01_FA[37], x01_FA[38], x01_FA[39], x01_FA[40], + x01_FA[41], x01_FA[42], x01_FA[43], x01_FA[44], x01_FA[45], x01_FA[46], x01_FA[47], x01_FA[48], + x01_FA[49], x01_FA[50], x01_FA[51], x01_FA[52], x01_FA[53], x01_FA[54], x01_FA[55], x01_FA[56], + x01_FA[57], x01_FA[58], x01_FA[59], x01_FA[60], x01_FA[61], x01_FA[62], x01_FA[63], x01_FA[64], + x01_FA[65], x01_FA[66], x01_FA[67], x01_FA[68], x01_FA[69], x01_FA[70], x01_FA[71], x01_FA[72], + x01_FA[73], x01_FA[74], x01_FA[75], x01_FA[76], x01_FA[77], x01_FA[78], x01_FA[79], x01_FA[80], + x01_FA[81], x01_FA[82], x01_FA[83], x01_FA[84], x01_FA[85], x01_FA[86], x01_FA[87], x01_FA[88], + x01_FA[89], x01_FA[90], x01_FA[91], x01_FA[92], x01_FA[93], x01_FA[94], x01_FA[95], x01_FA[96], + x01_FA[97], x01_FA[98], x01_FA[99], x01_FA[100], x01_FA[101], x01_FA[102], x01_FA[103], x01_FA[104], + x01_FA[105], x01_FA[106], x01_FA[107], x01_FA[108], x01_FA[109], x01_FA[110], x01_FA[111], x01_FA[112], + x01_FA[113], x01_FA[114], x01_FA[115], x01_FA[116], x01_FA[117], x01_FA[118], x01_FA[119], x01_FA[120], + x01_FA[121], x01_FA[122], x01_FA[123], x01_FA[124], x01_FA[125], x01_FA[126], x01_FA[127], x01_FA[128], + x01_FA[129], x01_FA[130], x01_FA[131], x01_FA[132], x01_FA[133], x01_FA[134], x01_FA[135], x01_FA[136], + x01_FA[137], x01_FA[138], x01_FA[139], x01_FA[140], x01_FA[141], x01_FA[142], x01_FA[143], x01_FA[144], + x01_FA[145], x01_FA[146], x01_FA[147], x01_FA[148], x01_FA[149], x01_FA[150], x01_FA[151], x01_FA[152], + x01_FA[153], x01_FA[154], x01_FA[155], x01_FA[156], x01_FA[157], x01_FA[158], x01_FA[159], x01_FA[160], + x01_FA[161], x01_FA[162], x01_FA[163], x01_FA[164], x01_FA[165], x01_FA[166], x01_FA[167], x01_FA[168], + x01_FA[169], x01_FA[170], x01_FA[171], x01_FA[172], x01_FA[173], x01_FA[174], x01_FA[175], x01_FA[176], + x01_FA[177], x01_FA[178], x01_FA[179], x01_FA[180], x01_FA[181], x01_FA[182], x01_FA[183], x01_FA[184], + x01_FA[185], x01_FA[186], x01_FA[187], x01_FA[188], x01_FA[189], x01_FA[190], x01_FA[191], x01_FA[192], + x01_FA[193], x01_FA[194], x01_FA[195], x01_FA[196], x01_FA[197], x01_FA[198], x01_FA[199], x01_FA[200], + x01_FA[201], x01_FA[202], x01_FA[203], x01_FA[204], x01_FA[205], x01_FA[206], x01_FA[207], x01_FA[208], + x01_FA[209], x01_FA[210], x01_FA[211], x01_FA[212], x01_FA[213], x01_FA[214], x01_FA[215], x01_FA[216], + x01_FA[217], x01_FA[218], x01_FA[219], x01_FA[220], x01_FA[221], x01_FA[222], x01_FA[223], x01_FA[224], + x01_FA[225], x01_FA[226], x01_FA[227], x01_FA[228], x01_FA[229], x01_FA[230], x01_FA[231], x01_FA[232], + x01_FA[233], x01_FA[234], x01_FA[235], x01_FA[236], x01_FA[237], x01_FA[238], x01_FA[239], x01_FA[240], + x01_FA[241], x01_FA[242], x01_FA[243], x01_FA[244], x01_FA[245], x01_FA[246], x01_FA[247], x01_FA[248], + // + x01_FA[249], xFB); + } @Test public void test252() throws Throwable { @@ -507,6 +625,8 @@ public class BigArityTest { test252(mh, a, r0); MethodHandle mh_CA = MH_hashArguments_VA.asFixedArity().asCollector(Object[].class, ARITY); test252(mh_CA, a, r0); + MethodHandle mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2); + test252(mh_a, a, r0); } public void test252(MethodHandle mh, Object[] a, Object r0) throws Throwable { Object r; @@ -686,6 +806,43 @@ public class BigArityTest { // xF8, xF9, xFA, xFB, xFC); } + static Object hashArguments_253_a(Object x00, Object[] x01_FB, Object xFC) { + return Objects.hash( + // + x00, x01_FB[0], x01_FB[1], x01_FB[2], x01_FB[3], x01_FB[4], x01_FB[5], x01_FB[6], x01_FB[7], x01_FB[8], + x01_FB[9], x01_FB[10], x01_FB[11], x01_FB[12], x01_FB[13], x01_FB[14], x01_FB[15], x01_FB[16], + x01_FB[17], x01_FB[18], x01_FB[19], x01_FB[20], x01_FB[21], x01_FB[22], x01_FB[23], x01_FB[24], + x01_FB[25], x01_FB[26], x01_FB[27], x01_FB[28], x01_FB[29], x01_FB[30], x01_FB[31], x01_FB[32], + x01_FB[33], x01_FB[34], x01_FB[35], x01_FB[36], x01_FB[37], x01_FB[38], x01_FB[39], x01_FB[40], + x01_FB[41], x01_FB[42], x01_FB[43], x01_FB[44], x01_FB[45], x01_FB[46], x01_FB[47], x01_FB[48], + x01_FB[49], x01_FB[50], x01_FB[51], x01_FB[52], x01_FB[53], x01_FB[54], x01_FB[55], x01_FB[56], + x01_FB[57], x01_FB[58], x01_FB[59], x01_FB[60], x01_FB[61], x01_FB[62], x01_FB[63], x01_FB[64], + x01_FB[65], x01_FB[66], x01_FB[67], x01_FB[68], x01_FB[69], x01_FB[70], x01_FB[71], x01_FB[72], + x01_FB[73], x01_FB[74], x01_FB[75], x01_FB[76], x01_FB[77], x01_FB[78], x01_FB[79], x01_FB[80], + x01_FB[81], x01_FB[82], x01_FB[83], x01_FB[84], x01_FB[85], x01_FB[86], x01_FB[87], x01_FB[88], + x01_FB[89], x01_FB[90], x01_FB[91], x01_FB[92], x01_FB[93], x01_FB[94], x01_FB[95], x01_FB[96], + x01_FB[97], x01_FB[98], x01_FB[99], x01_FB[100], x01_FB[101], x01_FB[102], x01_FB[103], x01_FB[104], + x01_FB[105], x01_FB[106], x01_FB[107], x01_FB[108], x01_FB[109], x01_FB[110], x01_FB[111], x01_FB[112], + x01_FB[113], x01_FB[114], x01_FB[115], x01_FB[116], x01_FB[117], x01_FB[118], x01_FB[119], x01_FB[120], + x01_FB[121], x01_FB[122], x01_FB[123], x01_FB[124], x01_FB[125], x01_FB[126], x01_FB[127], x01_FB[128], + x01_FB[129], x01_FB[130], x01_FB[131], x01_FB[132], x01_FB[133], x01_FB[134], x01_FB[135], x01_FB[136], + x01_FB[137], x01_FB[138], x01_FB[139], x01_FB[140], x01_FB[141], x01_FB[142], x01_FB[143], x01_FB[144], + x01_FB[145], x01_FB[146], x01_FB[147], x01_FB[148], x01_FB[149], x01_FB[150], x01_FB[151], x01_FB[152], + x01_FB[153], x01_FB[154], x01_FB[155], x01_FB[156], x01_FB[157], x01_FB[158], x01_FB[159], x01_FB[160], + x01_FB[161], x01_FB[162], x01_FB[163], x01_FB[164], x01_FB[165], x01_FB[166], x01_FB[167], x01_FB[168], + x01_FB[169], x01_FB[170], x01_FB[171], x01_FB[172], x01_FB[173], x01_FB[174], x01_FB[175], x01_FB[176], + x01_FB[177], x01_FB[178], x01_FB[179], x01_FB[180], x01_FB[181], x01_FB[182], x01_FB[183], x01_FB[184], + x01_FB[185], x01_FB[186], x01_FB[187], x01_FB[188], x01_FB[189], x01_FB[190], x01_FB[191], x01_FB[192], + x01_FB[193], x01_FB[194], x01_FB[195], x01_FB[196], x01_FB[197], x01_FB[198], x01_FB[199], x01_FB[200], + x01_FB[201], x01_FB[202], x01_FB[203], x01_FB[204], x01_FB[205], x01_FB[206], x01_FB[207], x01_FB[208], + x01_FB[209], x01_FB[210], x01_FB[211], x01_FB[212], x01_FB[213], x01_FB[214], x01_FB[215], x01_FB[216], + x01_FB[217], x01_FB[218], x01_FB[219], x01_FB[220], x01_FB[221], x01_FB[222], x01_FB[223], x01_FB[224], + x01_FB[225], x01_FB[226], x01_FB[227], x01_FB[228], x01_FB[229], x01_FB[230], x01_FB[231], x01_FB[232], + x01_FB[233], x01_FB[234], x01_FB[235], x01_FB[236], x01_FB[237], x01_FB[238], x01_FB[239], x01_FB[240], + x01_FB[241], x01_FB[242], x01_FB[243], x01_FB[244], x01_FB[245], x01_FB[246], x01_FB[247], x01_FB[248], + // + x01_FB[249], x01_FB[250], xFC); + } @Test public void test253() throws Throwable { @@ -720,6 +877,8 @@ public class BigArityTest { test253(mh, a, r0); MethodHandle mh_CA = MH_hashArguments_VA.asFixedArity().asCollector(Object[].class, ARITY); test253(mh_CA, a, r0); + MethodHandle mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2); + test253(mh_a, a, r0); } public void test253(MethodHandle mh, Object[] a, Object r0) throws Throwable { Object r; @@ -899,6 +1058,43 @@ public class BigArityTest { // xF8, xF9, xFA, xFB, xFC, xFD); } + static Object hashArguments_254_a(Object x00, Object[] x01_FC, Object xFD) { + return Objects.hash( + // + x00, x01_FC[0], x01_FC[1], x01_FC[2], x01_FC[3], x01_FC[4], x01_FC[5], x01_FC[6], x01_FC[7], x01_FC[8], + x01_FC[9], x01_FC[10], x01_FC[11], x01_FC[12], x01_FC[13], x01_FC[14], x01_FC[15], x01_FC[16], + x01_FC[17], x01_FC[18], x01_FC[19], x01_FC[20], x01_FC[21], x01_FC[22], x01_FC[23], x01_FC[24], + x01_FC[25], x01_FC[26], x01_FC[27], x01_FC[28], x01_FC[29], x01_FC[30], x01_FC[31], x01_FC[32], + x01_FC[33], x01_FC[34], x01_FC[35], x01_FC[36], x01_FC[37], x01_FC[38], x01_FC[39], x01_FC[40], + x01_FC[41], x01_FC[42], x01_FC[43], x01_FC[44], x01_FC[45], x01_FC[46], x01_FC[47], x01_FC[48], + x01_FC[49], x01_FC[50], x01_FC[51], x01_FC[52], x01_FC[53], x01_FC[54], x01_FC[55], x01_FC[56], + x01_FC[57], x01_FC[58], x01_FC[59], x01_FC[60], x01_FC[61], x01_FC[62], x01_FC[63], x01_FC[64], + x01_FC[65], x01_FC[66], x01_FC[67], x01_FC[68], x01_FC[69], x01_FC[70], x01_FC[71], x01_FC[72], + x01_FC[73], x01_FC[74], x01_FC[75], x01_FC[76], x01_FC[77], x01_FC[78], x01_FC[79], x01_FC[80], + x01_FC[81], x01_FC[82], x01_FC[83], x01_FC[84], x01_FC[85], x01_FC[86], x01_FC[87], x01_FC[88], + x01_FC[89], x01_FC[90], x01_FC[91], x01_FC[92], x01_FC[93], x01_FC[94], x01_FC[95], x01_FC[96], + x01_FC[97], x01_FC[98], x01_FC[99], x01_FC[100], x01_FC[101], x01_FC[102], x01_FC[103], x01_FC[104], + x01_FC[105], x01_FC[106], x01_FC[107], x01_FC[108], x01_FC[109], x01_FC[110], x01_FC[111], x01_FC[112], + x01_FC[113], x01_FC[114], x01_FC[115], x01_FC[116], x01_FC[117], x01_FC[118], x01_FC[119], x01_FC[120], + x01_FC[121], x01_FC[122], x01_FC[123], x01_FC[124], x01_FC[125], x01_FC[126], x01_FC[127], x01_FC[128], + x01_FC[129], x01_FC[130], x01_FC[131], x01_FC[132], x01_FC[133], x01_FC[134], x01_FC[135], x01_FC[136], + x01_FC[137], x01_FC[138], x01_FC[139], x01_FC[140], x01_FC[141], x01_FC[142], x01_FC[143], x01_FC[144], + x01_FC[145], x01_FC[146], x01_FC[147], x01_FC[148], x01_FC[149], x01_FC[150], x01_FC[151], x01_FC[152], + x01_FC[153], x01_FC[154], x01_FC[155], x01_FC[156], x01_FC[157], x01_FC[158], x01_FC[159], x01_FC[160], + x01_FC[161], x01_FC[162], x01_FC[163], x01_FC[164], x01_FC[165], x01_FC[166], x01_FC[167], x01_FC[168], + x01_FC[169], x01_FC[170], x01_FC[171], x01_FC[172], x01_FC[173], x01_FC[174], x01_FC[175], x01_FC[176], + x01_FC[177], x01_FC[178], x01_FC[179], x01_FC[180], x01_FC[181], x01_FC[182], x01_FC[183], x01_FC[184], + x01_FC[185], x01_FC[186], x01_FC[187], x01_FC[188], x01_FC[189], x01_FC[190], x01_FC[191], x01_FC[192], + x01_FC[193], x01_FC[194], x01_FC[195], x01_FC[196], x01_FC[197], x01_FC[198], x01_FC[199], x01_FC[200], + x01_FC[201], x01_FC[202], x01_FC[203], x01_FC[204], x01_FC[205], x01_FC[206], x01_FC[207], x01_FC[208], + x01_FC[209], x01_FC[210], x01_FC[211], x01_FC[212], x01_FC[213], x01_FC[214], x01_FC[215], x01_FC[216], + x01_FC[217], x01_FC[218], x01_FC[219], x01_FC[220], x01_FC[221], x01_FC[222], x01_FC[223], x01_FC[224], + x01_FC[225], x01_FC[226], x01_FC[227], x01_FC[228], x01_FC[229], x01_FC[230], x01_FC[231], x01_FC[232], + x01_FC[233], x01_FC[234], x01_FC[235], x01_FC[236], x01_FC[237], x01_FC[238], x01_FC[239], x01_FC[240], + x01_FC[241], x01_FC[242], x01_FC[243], x01_FC[244], x01_FC[245], x01_FC[246], x01_FC[247], x01_FC[248], + // + x01_FC[249], x01_FC[250], x01_FC[251], xFD); + } @Test public void test254() throws Throwable { @@ -933,6 +1129,8 @@ public class BigArityTest { test254(mh, a, r0); MethodHandle mh_CA = MH_hashArguments_VA.asFixedArity().asCollector(Object[].class, ARITY); test254(mh_CA, a, r0); + MethodHandle mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2); + test254(mh_a, a, r0); } public void test254(MethodHandle mh, Object[] a, Object r0) throws Throwable { Object r; @@ -1094,6 +1292,43 @@ public class BigArityTest { // xF8, xF9, xFA, xFB, xFC, xFD, xFE); } + static Object hashArguments_255_a(Object x00, Object[] x01_FD, Object xFE) { + return Objects.hash( + // + x00, x01_FD[0], x01_FD[1], x01_FD[2], x01_FD[3], x01_FD[4], x01_FD[5], x01_FD[6], x01_FD[7], x01_FD[8], + x01_FD[9], x01_FD[10], x01_FD[11], x01_FD[12], x01_FD[13], x01_FD[14], x01_FD[15], x01_FD[16], + x01_FD[17], x01_FD[18], x01_FD[19], x01_FD[20], x01_FD[21], x01_FD[22], x01_FD[23], x01_FD[24], + x01_FD[25], x01_FD[26], x01_FD[27], x01_FD[28], x01_FD[29], x01_FD[30], x01_FD[31], x01_FD[32], + x01_FD[33], x01_FD[34], x01_FD[35], x01_FD[36], x01_FD[37], x01_FD[38], x01_FD[39], x01_FD[40], + x01_FD[41], x01_FD[42], x01_FD[43], x01_FD[44], x01_FD[45], x01_FD[46], x01_FD[47], x01_FD[48], + x01_FD[49], x01_FD[50], x01_FD[51], x01_FD[52], x01_FD[53], x01_FD[54], x01_FD[55], x01_FD[56], + x01_FD[57], x01_FD[58], x01_FD[59], x01_FD[60], x01_FD[61], x01_FD[62], x01_FD[63], x01_FD[64], + x01_FD[65], x01_FD[66], x01_FD[67], x01_FD[68], x01_FD[69], x01_FD[70], x01_FD[71], x01_FD[72], + x01_FD[73], x01_FD[74], x01_FD[75], x01_FD[76], x01_FD[77], x01_FD[78], x01_FD[79], x01_FD[80], + x01_FD[81], x01_FD[82], x01_FD[83], x01_FD[84], x01_FD[85], x01_FD[86], x01_FD[87], x01_FD[88], + x01_FD[89], x01_FD[90], x01_FD[91], x01_FD[92], x01_FD[93], x01_FD[94], x01_FD[95], x01_FD[96], + x01_FD[97], x01_FD[98], x01_FD[99], x01_FD[100], x01_FD[101], x01_FD[102], x01_FD[103], x01_FD[104], + x01_FD[105], x01_FD[106], x01_FD[107], x01_FD[108], x01_FD[109], x01_FD[110], x01_FD[111], x01_FD[112], + x01_FD[113], x01_FD[114], x01_FD[115], x01_FD[116], x01_FD[117], x01_FD[118], x01_FD[119], x01_FD[120], + x01_FD[121], x01_FD[122], x01_FD[123], x01_FD[124], x01_FD[125], x01_FD[126], x01_FD[127], x01_FD[128], + x01_FD[129], x01_FD[130], x01_FD[131], x01_FD[132], x01_FD[133], x01_FD[134], x01_FD[135], x01_FD[136], + x01_FD[137], x01_FD[138], x01_FD[139], x01_FD[140], x01_FD[141], x01_FD[142], x01_FD[143], x01_FD[144], + x01_FD[145], x01_FD[146], x01_FD[147], x01_FD[148], x01_FD[149], x01_FD[150], x01_FD[151], x01_FD[152], + x01_FD[153], x01_FD[154], x01_FD[155], x01_FD[156], x01_FD[157], x01_FD[158], x01_FD[159], x01_FD[160], + x01_FD[161], x01_FD[162], x01_FD[163], x01_FD[164], x01_FD[165], x01_FD[166], x01_FD[167], x01_FD[168], + x01_FD[169], x01_FD[170], x01_FD[171], x01_FD[172], x01_FD[173], x01_FD[174], x01_FD[175], x01_FD[176], + x01_FD[177], x01_FD[178], x01_FD[179], x01_FD[180], x01_FD[181], x01_FD[182], x01_FD[183], x01_FD[184], + x01_FD[185], x01_FD[186], x01_FD[187], x01_FD[188], x01_FD[189], x01_FD[190], x01_FD[191], x01_FD[192], + x01_FD[193], x01_FD[194], x01_FD[195], x01_FD[196], x01_FD[197], x01_FD[198], x01_FD[199], x01_FD[200], + x01_FD[201], x01_FD[202], x01_FD[203], x01_FD[204], x01_FD[205], x01_FD[206], x01_FD[207], x01_FD[208], + x01_FD[209], x01_FD[210], x01_FD[211], x01_FD[212], x01_FD[213], x01_FD[214], x01_FD[215], x01_FD[216], + x01_FD[217], x01_FD[218], x01_FD[219], x01_FD[220], x01_FD[221], x01_FD[222], x01_FD[223], x01_FD[224], + x01_FD[225], x01_FD[226], x01_FD[227], x01_FD[228], x01_FD[229], x01_FD[230], x01_FD[231], x01_FD[232], + x01_FD[233], x01_FD[234], x01_FD[235], x01_FD[236], x01_FD[237], x01_FD[238], x01_FD[239], x01_FD[240], + x01_FD[241], x01_FD[242], x01_FD[243], x01_FD[244], x01_FD[245], x01_FD[246], x01_FD[247], x01_FD[248], + // + x01_FD[249], x01_FD[250], x01_FD[251], x01_FD[252], xFE); + } @Test public void test255() throws Throwable { @@ -1163,5 +1398,38 @@ public class BigArityTest { } catch (IllegalArgumentException ex) { System.out.println("OK: "+ex); } + MethodHandle mh_a; + try { + mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2); + throw new AssertionError("should not create an arity 255 collector method handle"); + } catch (IllegalArgumentException ex) { + System.out.println("OK: "+ex); + mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-3); + } + try { + r = mh_a.invokeExact( + // + a[0x00], a[0x01], a[0x02], a[0x03], a[0x04], a[0x05], a[0x06], a[0x07], a[0x08], a[0x09], a[0x0A], a[0x0B], a[0x0C], a[0x0D], a[0x0E], a[0x0F], + a[0x10], a[0x11], a[0x12], a[0x13], a[0x14], a[0x15], a[0x16], a[0x17], a[0x18], a[0x19], a[0x1A], a[0x1B], a[0x1C], a[0x1D], a[0x1E], a[0x1F], + a[0x20], a[0x21], a[0x22], a[0x23], a[0x24], a[0x25], a[0x26], a[0x27], a[0x28], a[0x29], a[0x2A], a[0x2B], a[0x2C], a[0x2D], a[0x2E], a[0x2F], + a[0x30], a[0x31], a[0x32], a[0x33], a[0x34], a[0x35], a[0x36], a[0x37], a[0x38], a[0x39], a[0x3A], a[0x3B], a[0x3C], a[0x3D], a[0x3E], a[0x3F], + a[0x40], a[0x41], a[0x42], a[0x43], a[0x44], a[0x45], a[0x46], a[0x47], a[0x48], a[0x49], a[0x4A], a[0x4B], a[0x4C], a[0x4D], a[0x4E], a[0x4F], + a[0x50], a[0x51], a[0x52], a[0x53], a[0x54], a[0x55], a[0x56], a[0x57], a[0x58], a[0x59], a[0x5A], a[0x5B], a[0x5C], a[0x5D], a[0x5E], a[0x5F], + a[0x60], a[0x61], a[0x62], a[0x63], a[0x64], a[0x65], a[0x66], a[0x67], a[0x68], a[0x69], a[0x6A], a[0x6B], a[0x6C], a[0x6D], a[0x6E], a[0x6F], + a[0x70], a[0x71], a[0x72], a[0x73], a[0x74], a[0x75], a[0x76], a[0x77], a[0x78], a[0x79], a[0x7A], a[0x7B], a[0x7C], a[0x7D], a[0x7E], a[0x7F], + a[0x80], a[0x81], a[0x82], a[0x83], a[0x84], a[0x85], a[0x86], a[0x87], a[0x88], a[0x89], a[0x8A], a[0x8B], a[0x8C], a[0x8D], a[0x8E], a[0x8F], + a[0x90], a[0x91], a[0x92], a[0x93], a[0x94], a[0x95], a[0x96], a[0x97], a[0x98], a[0x99], a[0x9A], a[0x9B], a[0x9C], a[0x9D], a[0x9E], a[0x9F], + a[0xA0], a[0xA1], a[0xA2], a[0xA3], a[0xA4], a[0xA5], a[0xA6], a[0xA7], a[0xA8], a[0xA9], a[0xAA], a[0xAB], a[0xAC], a[0xAD], a[0xAE], a[0xAF], + a[0xB0], a[0xB1], a[0xB2], a[0xB3], a[0xB4], a[0xB5], a[0xB6], a[0xB7], a[0xB8], a[0xB9], a[0xBA], a[0xBB], a[0xBC], a[0xBD], a[0xBE], a[0xBF], + a[0xC0], a[0xC1], a[0xC2], a[0xC3], a[0xC4], a[0xC5], a[0xC6], a[0xC7], a[0xC8], a[0xC9], a[0xCA], a[0xCB], a[0xCC], a[0xCD], a[0xCE], a[0xCF], + a[0xD0], a[0xD1], a[0xD2], a[0xD3], a[0xD4], a[0xD5], a[0xD6], a[0xD7], a[0xD8], a[0xD9], a[0xDA], a[0xDB], a[0xDC], a[0xDD], a[0xDE], a[0xDF], + a[0xE0], a[0xE1], a[0xE2], a[0xE3], a[0xE4], a[0xE5], a[0xE6], a[0xE7], a[0xE8], a[0xE9], a[0xEA], a[0xEB], a[0xEC], a[0xED], a[0xEE], a[0xEF], + a[0xF0], a[0xF1], a[0xF2], a[0xF3], a[0xF4], a[0xF5], a[0xF6], a[0xF7], + // + a[0xF8], a[0xF9], a[0xFA], a[0xFB], a[0xFC], a[0xFD], a[0xFE]); + throw new AssertionError("should not call an arity 255 collector method handle"); + } catch (LinkageError ex) { + System.out.println("OK: "+ex); + } } } diff --git a/jdk/test/java/lang/invoke/CompileThresholdBootstrapTest.java b/jdk/test/java/lang/invoke/CompileThresholdBootstrapTest.java new file mode 100644 index 00000000000..f7e0e0004e8 --- /dev/null +++ b/jdk/test/java/lang/invoke/CompileThresholdBootstrapTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8143232 + * @summary Test verifies that LF bootstraps properly when run with COMPILE_THRESHOLD set + * @compile CompileThresholdBootstrapTest.java + * @run testng/othervm -Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=30 test.java.lang.invoke.CompileThresholdBootstrapTest + */ +package test.java.lang.invoke; + +import java.lang.invoke.MethodHandles; +import org.testng.*; +import org.testng.annotations.*; + +public final class CompileThresholdBootstrapTest { + + @Test + public void testBootstrap() throws Throwable { + Assert.assertEquals(0, (int)MethodHandles.constant(int.class, (int)0).invokeExact()); + } + + public static void main(String ... args) { + try { + CompileThresholdBootstrapTest test = new CompileThresholdBootstrapTest(); + test.testBootstrap(); + } catch (Throwable t) { + t.printStackTrace(); + } + } +} diff --git a/jdk/test/java/lang/invoke/FindClassSecurityManager.java b/jdk/test/java/lang/invoke/FindClassSecurityManager.java new file mode 100644 index 00000000000..b877e885364 --- /dev/null +++ b/jdk/test/java/lang/invoke/FindClassSecurityManager.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* @test + * @run main/othervm/policy=findclass.security.policy/secure=java.lang.SecurityManager -ea -esa test.java.lang.invoke.FindClassSecurityManager + */ + +package test.java.lang.invoke; + +import java.lang.invoke.MethodHandles; + +public class FindClassSecurityManager { + public static void main(String[] args) throws Throwable { + assert null != System.getSecurityManager(); + Class thisClass = FindClassSecurityManager.class; + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Class lookedUp = lookup.findClass(thisClass.getName()); + assert thisClass == lookedUp; + Class accessed = lookup.accessClass(thisClass); + assert thisClass == accessed; + } +} diff --git a/jdk/test/java/lang/invoke/MethodHandlesTest.java b/jdk/test/java/lang/invoke/MethodHandlesTest.java index 90137024be4..7b578a4bf3f 100644 --- a/jdk/test/java/lang/invoke/MethodHandlesTest.java +++ b/jdk/test/java/lang/invoke/MethodHandlesTest.java @@ -32,6 +32,7 @@ package test.java.lang.invoke; import test.java.lang.invoke.remote.RemoteExample; import java.lang.invoke.*; +import static java.lang.invoke.MethodType.methodType; import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.*; import java.util.*; @@ -448,6 +449,7 @@ public class MethodHandlesTest { } public static interface IntExample { public void v0(); + public default void vd() { called("vd", this); } public static class Impl implements IntExample { public void v0() { called("Int/v0", this); } final String name; @@ -719,9 +721,10 @@ public class MethodHandlesTest { public void testFindSpecial0() throws Throwable { if (CAN_SKIP_WORKING) return; startTest("findSpecial"); - testFindSpecial(SubExample.class, Example.class, void.class, "v0"); - testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0"); - testFindSpecial(RemoteExample.class, PubExample.class, void.class, "Pub/pro_v0"); + testFindSpecial(SubExample.class, Example.class, void.class, false, "v0"); + testFindSpecial(SubExample.class, Example.class, void.class, false, "pkg_v0"); + testFindSpecial(RemoteExample.class, PubExample.class, void.class, false, "Pub/pro_v0"); + testFindSpecial(Example.class, IntExample.class, void.class, true, "vd"); // Do some negative testing: for (Lookup lookup : new Lookup[]{ PRIVATE, EXAMPLE, PACKAGE, PUBLIC }) { testFindSpecial(false, lookup, Object.class, Example.class, void.class, "v0"); @@ -729,11 +732,12 @@ public class MethodHandlesTest { testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "", int.class); testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "", Void.class); testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "s0"); + testFindSpecial(false, lookup, Example.class, IntExample.class, void.class, "v0"); } } void testFindSpecial(Class specialCaller, - Class defc, Class ret, String name, Class... params) throws Throwable { + Class defc, Class ret, boolean dflt, String name, Class... params) throws Throwable { if (specialCaller == RemoteExample.class) { testFindSpecial(false, EXAMPLE, specialCaller, defc, ret, name, params); testFindSpecial(false, PRIVATE, specialCaller, defc, ret, name, params); @@ -742,11 +746,11 @@ public class MethodHandlesTest { testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params); return; } - testFindSpecial(true, EXAMPLE, specialCaller, defc, ret, name, params); - testFindSpecial(true, PRIVATE, specialCaller, defc, ret, name, params); - testFindSpecial(false, PACKAGE, specialCaller, defc, ret, name, params); - testFindSpecial(false, SUBCLASS, specialCaller, defc, ret, name, params); - testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params); + testFindSpecial(true, EXAMPLE, specialCaller, defc, ret, name, params); + testFindSpecial(true, PRIVATE, specialCaller, defc, ret, name, params); + testFindSpecial(false || dflt, PACKAGE, specialCaller, defc, ret, name, params); + testFindSpecial(false, SUBCLASS, specialCaller, defc, ret, name, params); + testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params); } void testFindSpecial(boolean positive, Lookup lookup, Class specialCaller, Class defc, Class ret, String name, Class... params) throws Throwable { @@ -1834,6 +1838,7 @@ public class MethodHandlesTest { @Test // SLOW public void testSpreadArguments() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments0); + CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments1); } public void testSpreadArguments0() throws Throwable { @@ -1842,44 +1847,27 @@ public class MethodHandlesTest { for (Class argType : new Class[]{Object.class, Integer.class, int.class}) { if (verbosity >= 3) System.out.println("spreadArguments "+argType); + Class arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass(); for (int nargs = 0; nargs < 50; nargs++) { if (CAN_TEST_LIGHTLY && nargs > 11) break; for (int pos = 0; pos <= nargs; pos++) { if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue; if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3) continue; - testSpreadArguments(argType, pos, nargs); + testSpreadArguments(argType, arrayType, pos, nargs); } } } } - public void testSpreadArguments(Class argType, int pos, int nargs) throws Throwable { + public void testSpreadArguments(Class argType, Class arrayType, int pos, int nargs) throws Throwable { countTest(); - Class arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass(); MethodHandle target2 = varargsArray(arrayType, nargs); MethodHandle target = target2.asType(target2.type().generic()); if (verbosity >= 3) System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]"); Object[] args = randomArgs(target2.type().parameterArray()); // make sure the target does what we think it does: - if (pos == 0 && nargs < 5 && !argType.isPrimitive()) { - Object[] check = (Object[]) target.invokeWithArguments(args); - assertArrayEquals(args, check); - switch (nargs) { - case 0: - check = (Object[]) (Object) target.invokeExact(); - assertArrayEquals(args, check); - break; - case 1: - check = (Object[]) (Object) target.invokeExact(args[0]); - assertArrayEquals(args, check); - break; - case 2: - check = (Object[]) (Object) target.invokeExact(args[0], args[1]); - assertArrayEquals(args, check); - break; - } - } + checkTarget(argType, pos, nargs, target, args); List> newParams = new ArrayList<>(target2.type().parameterList()); { // modify newParams in place List> spreadParams = newParams.subList(pos, nargs); @@ -1898,6 +1886,78 @@ public class MethodHandlesTest { args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length)); returnValue = result.invokeWithArguments(args1); } + checkReturnValue(argType, args, result, returnValue); + } + public void testSpreadArguments1() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("spreadArguments/pos"); + for (Class argType : new Class[]{Object.class, Integer.class, int.class}) { + if (verbosity >= 3) + System.out.println("spreadArguments "+argType); + Class arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass(); + for (int nargs = 0; nargs < 50; nargs++) { + if (CAN_TEST_LIGHTLY && nargs > 11) break; + for (int pos = 0; pos <= nargs; pos++) { + if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue; + if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3) + continue; + for (int spr = 1; spr < nargs - pos; ++spr) { + if (spr > 4 && spr != 7 && spr != 11 && spr != 20 && spr < nargs - pos - 4) continue; + testSpreadArguments(argType, arrayType, pos, spr, nargs); + } + } + } + } + } + public void testSpreadArguments(Class argType, Class arrayType, int pos, int spread, int nargs) throws Throwable { + countTest(); + MethodHandle target2 = varargsArray(arrayType, nargs); + MethodHandle target = target2.asType(target2.type().generic()); + if (verbosity >= 3) + System.out.println("spread into " + target2 + " [" + pos + ".." + (pos + spread) + "["); + Object[] args = randomArgs(target2.type().parameterArray()); + // make sure the target does what we think it does: + checkTarget(argType, pos, nargs, target, args); + List> newParams = new ArrayList<>(target2.type().parameterList()); + { // modify newParams in place + List> spreadParams = newParams.subList(pos, pos + spread); + spreadParams.clear(); + spreadParams.add(arrayType); + } + MethodType newType = MethodType.methodType(arrayType, newParams); + MethodHandle result = target2.asSpreader(pos, arrayType, spread); + assert (result.type() == newType) : Arrays.asList(result, newType); + result = result.asType(newType.generic()); + // args1 has nargs-spread entries, plus one for the to-be-spread array + int args1Length = nargs - (spread - 1); + Object[] args1 = new Object[args1Length]; + System.arraycopy(args, 0, args1, 0, pos); + args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, pos + spread)); + System.arraycopy(args, pos + spread, args1, pos + 1, nargs - spread - pos); + Object returnValue = result.invokeWithArguments(args1); + checkReturnValue(argType, args, result, returnValue); + } + private static void checkTarget(Class argType, int pos, int nargs, MethodHandle target, Object[] args) throws Throwable { + if (pos == 0 && nargs < 5 && !argType.isPrimitive()) { + Object[] check = (Object[]) target.invokeWithArguments(args); + assertArrayEquals(args, check); + switch (nargs) { + case 0: + check = (Object[]) (Object) target.invokeExact(); + assertArrayEquals(args, check); + break; + case 1: + check = (Object[]) (Object) target.invokeExact(args[0]); + assertArrayEquals(args, check); + break; + case 2: + check = (Object[]) (Object) target.invokeExact(args[0], args[1]); + assertArrayEquals(args, check); + break; + } + } + } + private static void checkReturnValue(Class argType, Object[] args, MethodHandle result, Object returnValue) { String argstr = Arrays.toString(args); if (!argType.isPrimitive()) { Object[] rv = (Object[]) returnValue; @@ -1932,6 +1992,7 @@ public class MethodHandlesTest { @Test // SLOW public void testAsCollector() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testAsCollector0); + CodeCacheOverflowProcessor.runMHTest(this::testAsCollector1); } public void testAsCollector0() throws Throwable { @@ -1974,6 +2035,51 @@ public class MethodHandlesTest { // collectedArgs[pos] = Arrays.asList((Object[]) collectedArgs[pos]); assertArrayEquals(collectedArgs, returnValue); } + public void testAsCollector1() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("asCollector/pos"); + for (Class argType : new Class[]{Object.class, Integer.class, int.class}) { + if (verbosity >= 3) + System.out.println("asCollector/pos "+argType); + for (int nargs = 0; nargs < 50; nargs++) { + if (CAN_TEST_LIGHTLY && nargs > 11) break; + for (int pos = 0; pos <= nargs; pos++) { + if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2) continue; + if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3) + continue; + for (int coll = 1; coll < nargs - pos; ++coll) { + if (coll > 4 && coll != 7 && coll != 11 && coll != 20 && coll < nargs - pos - 4) continue; + testAsCollector(argType, pos, coll, nargs); + } + } + } + } + } + public void testAsCollector(Class argType, int pos, int collect, int nargs) throws Throwable { + countTest(); + // fake up a MH with the same type as the desired adapter: + MethodHandle fake = varargsArray(nargs); + fake = changeArgTypes(fake, argType); + MethodType newType = fake.type(); + Object[] args = randomArgs(newType.parameterArray()); + // here is what should happen: + // new arg list has "collect" less arguments, but one extra for collected arguments array + int collectedLength = nargs-(collect-1); + Object[] collectedArgs = new Object[collectedLength]; + System.arraycopy(args, 0, collectedArgs, 0, pos); + collectedArgs[pos] = Arrays.copyOfRange(args, pos, pos+collect); + System.arraycopy(args, pos+collect, collectedArgs, pos+1, args.length-(pos+collect)); + // here is the MH which will witness the collected argument part (not tail!): + MethodHandle target = varargsArray(collectedLength); + target = changeArgTypes(target, 0, pos, argType); + target = changeArgTypes(target, pos, pos+1, Object[].class); + target = changeArgTypes(target, pos+1, collectedLength, argType); + if (verbosity >= 3) + System.out.println("collect "+collect+" from "+Arrays.asList(args)+" ["+pos+".."+(pos+collect)+"["); + MethodHandle result = target.asCollector(pos, Object[].class, collect).asType(newType); + Object[] returnValue = (Object[]) result.invokeWithArguments(args); + assertArrayEquals(collectedArgs, returnValue); + } @Test // SLOW public void testInsertArguments() throws Throwable { @@ -2117,21 +2223,29 @@ public class MethodHandlesTest { public void testCollectArguments0() throws Throwable { if (CAN_SKIP_WORKING) return; startTest("collectArguments"); - testFoldOrCollectArguments(true); + testFoldOrCollectArguments(true, false); } @Test public void testFoldArguments() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testFoldArguments0); + CodeCacheOverflowProcessor.runMHTest(this::testFoldArguments1); } public void testFoldArguments0() throws Throwable { if (CAN_SKIP_WORKING) return; startTest("foldArguments"); - testFoldOrCollectArguments(false); + testFoldOrCollectArguments(false, false); } - void testFoldOrCollectArguments(boolean isCollect) throws Throwable { + public void testFoldArguments1() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("foldArguments/pos"); + testFoldOrCollectArguments(false, true); + } + + void testFoldOrCollectArguments(boolean isCollect, boolean withFoldPos) throws Throwable { + assert !(isCollect && withFoldPos); // exclude illegal argument combination for (Class lastType : new Class[]{ Object.class, String.class, int.class }) { for (Class collectType : new Class[]{ Object.class, String.class, int.class, void.class }) { int maxArity = 10; @@ -2146,7 +2260,7 @@ public class MethodHandlesTest { if (!mixArgs(argTypes, mix, argTypesSeen)) continue; for (int collect = 0; collect <= nargs; collect++) { for (int pos = 0; pos <= nargs - collect; pos++) { - testFoldOrCollectArguments(argTypes, pos, collect, collectType, lastType, isCollect); + testFoldOrCollectArguments(argTypes, pos, collect, collectType, lastType, isCollect, withFoldPos); } } } @@ -2186,13 +2300,14 @@ public class MethodHandlesTest { int pos, int fold, // position and length of the folded arguments Class combineType, // type returned from the combiner Class lastType, // type returned from the target - boolean isCollect) throws Throwable { + boolean isCollect, + boolean withFoldPos) throws Throwable { int nargs = argTypes.size(); - if (pos != 0 && !isCollect) return; // can fold only at pos=0 for now + if (pos != 0 && !isCollect && !withFoldPos) return; // test MethodHandles.foldArguments(MH,MH) only for pos=0 countTest(); List> combineArgTypes = argTypes.subList(pos, pos + fold); List> targetArgTypes = new ArrayList<>(argTypes); - if (isCollect) // does targret see arg[pos..pos+cc-1]? + if (isCollect) // does target see arg[pos..pos+cc-1]? targetArgTypes.subList(pos, pos + fold).clear(); if (combineType != void.class) targetArgTypes.add(pos, combineType); @@ -2205,7 +2320,7 @@ public class MethodHandlesTest { if (isCollect) target2 = MethodHandles.collectArguments(target, pos, combine); else - target2 = MethodHandles.foldArguments(target, combine); + target2 = withFoldPos ? MethodHandles.foldArguments(target, pos, combine) : MethodHandles.foldArguments(target, combine); // Simulate expected effect of combiner on arglist: List expectedList = new ArrayList<>(argsToPass); List argsToFold = expectedList.subList(pos, pos + fold); @@ -2540,6 +2655,203 @@ public class MethodHandlesTest { } } + @Test + public void testGenericLoopCombinator() throws Throwable { + CodeCacheOverflowProcessor.runMHTest(this::testGenericLoopCombinator0); + } + public void testGenericLoopCombinator0() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("loop"); + // Test as follows: + // * Have an increasing number of loop-local state. Local state type diversity grows with the number. + // * Initializers set the starting value of loop-local state from the corresponding loop argument. + // * For each local state element, there is a predicate - for all state combinations, exercise all predicates. + // * Steps modify each local state element in each iteration. + // * Finalizers group all local state elements into a resulting array. Verify end values. + // * Exercise both pre- and post-checked loops. + // Local state types, start values, predicates, and steps: + // * int a, 0, a < 7, a = a + 1 + // * double b, 7.0, b > 0.5, b = b / 2.0 + // * String c, "start", c.length <= 9, c = c + a + final Class[] argTypes = new Class[] {int.class, double.class, String.class}; + final Object[][] args = new Object[][] { + new Object[]{0 }, + new Object[]{0, 7.0 }, + new Object[]{0, 7.0, "start"} + }; + // These are the expected final state tuples for argument type tuple / predicate combinations, for pre- and + // post-checked loops: + final Object[][] preCheckedResults = new Object[][] { + new Object[]{7 }, // (int) / int + new Object[]{7, 0.0546875 }, // (int,double) / int + new Object[]{5, 0.4375 }, // (int,double) / double + new Object[]{7, 0.0546875, "start1234567"}, // (int,double,String) / int + new Object[]{5, 0.4375, "start1234" }, // (int,double,String) / double + new Object[]{6, 0.109375, "start12345" } // (int,double,String) / String + }; + final Object[][] postCheckedResults = new Object[][] { + new Object[]{7 }, // (int) / int + new Object[]{7, 0.109375 }, // (int,double) / int + new Object[]{4, 0.4375 }, // (int,double) / double + new Object[]{7, 0.109375, "start123456"}, // (int,double,String) / int + new Object[]{4, 0.4375, "start123" }, // (int,double,String) / double + new Object[]{5, 0.21875, "start12345" } // (int,double,String) / String + }; + final Lookup l = MethodHandles.lookup(); + final Class MHT = MethodHandlesTest.class; + final Class B = boolean.class; + final Class I = int.class; + final Class D = double.class; + final Class S = String.class; + final MethodHandle hip = l.findStatic(MHT, "loopIntPred", methodType(B, I)); + final MethodHandle hdp = l.findStatic(MHT, "loopDoublePred", methodType(B, I, D)); + final MethodHandle hsp = l.findStatic(MHT, "loopStringPred", methodType(B, I, D, S)); + final MethodHandle his = l.findStatic(MHT, "loopIntStep", methodType(I, I)); + final MethodHandle hds = l.findStatic(MHT, "loopDoubleStep", methodType(D, I, D)); + final MethodHandle hss = l.findStatic(MHT, "loopStringStep", methodType(S, I, D, S)); + final MethodHandle[] preds = new MethodHandle[] {hip, hdp, hsp}; + final MethodHandle[] steps = new MethodHandle[] {his, hds, hss}; + for (int nargs = 1, useResultsStart = 0; nargs <= argTypes.length; useResultsStart += nargs++) { + Class[] useArgTypes = Arrays.copyOf(argTypes, nargs, Class[].class); + MethodHandle[] usePreds = Arrays.copyOf(preds, nargs, MethodHandle[].class); + MethodHandle[] useSteps = Arrays.copyOf(steps, nargs, MethodHandle[].class); + Object[] useArgs = args[nargs - 1]; + Object[][] usePreCheckedResults = new Object[nargs][]; + Object[][] usePostCheckedResults = new Object[nargs][]; + System.arraycopy(preCheckedResults, useResultsStart, usePreCheckedResults, 0, nargs); + System.arraycopy(postCheckedResults, useResultsStart, usePostCheckedResults, 0, nargs); + testGenericLoopCombinator(nargs, useArgTypes, usePreds, useSteps, useArgs, usePreCheckedResults, + usePostCheckedResults); + } + } + void testGenericLoopCombinator(int nargs, Class[] argTypes, MethodHandle[] preds, MethodHandle[] steps, + Object[] args, Object[][] preCheckedResults, Object[][] postCheckedResults) + throws Throwable { + List> lArgTypes = Arrays.asList(argTypes); + // Predicate and step handles are passed in as arguments, initializer and finalizer handles are constructed here + // from the available information. + MethodHandle[] inits = new MethodHandle[nargs]; + for (int i = 0; i < nargs; ++i) { + MethodHandle h; + // Initializers are meant to return whatever they are passed at a given argument position. This means that + // additional arguments may have to be appended and prepended. + h = MethodHandles.identity(argTypes[i]); + if (i < nargs - 1) { + h = MethodHandles.dropArguments(h, 1, lArgTypes.subList(i + 1, nargs)); + } + if (i > 0) { + h = MethodHandles.dropArguments(h, 0, lArgTypes.subList(0, i)); + } + inits[i] = h; + } + // Finalizers are all meant to collect all of the loop-local state in a single array and return that. Local + // state is passed before the loop args. Construct such a finalizer by first taking a varargsArray collector for + // the number of local state arguments, and then appending the loop args as to-be-dropped arguments. + MethodHandle[] finis = new MethodHandle[nargs]; + MethodHandle genericFini = MethodHandles.dropArguments( + varargsArray(nargs).asType(methodType(Object[].class, lArgTypes)), nargs, lArgTypes); + Arrays.fill(finis, genericFini); + // The predicate and step handles' signatures need to be extended. They currently just accept local state args; + // append possibly missing local state args and loop args using dropArguments. + for (int i = 0; i < nargs; ++i) { + List> additionalLocalStateArgTypes = lArgTypes.subList(i + 1, nargs); + preds[i] = MethodHandles.dropArguments( + MethodHandles.dropArguments(preds[i], i + 1, additionalLocalStateArgTypes), nargs, lArgTypes); + steps[i] = MethodHandles.dropArguments( + MethodHandles.dropArguments(steps[i], i + 1, additionalLocalStateArgTypes), nargs, lArgTypes); + } + // Iterate over all of the predicates, using only one of them at a time. + for (int i = 0; i < nargs; ++i) { + MethodHandle[] usePreds; + if (nargs == 1) { + usePreds = preds; + } else { + // Create an all-null preds array, and only use one predicate in this iteration. The null entries will + // be substituted with true predicates by the loop combinator. + usePreds = new MethodHandle[nargs]; + usePreds[i] = preds[i]; + } + // Go for it. + if (verbosity >= 3) { + System.out.println("calling loop for argument types " + lArgTypes + " with predicate at index " + i); + if (verbosity >= 5) { + System.out.println("predicates: " + Arrays.asList(usePreds)); + } + } + MethodHandle[] preInits = new MethodHandle[nargs + 1]; + MethodHandle[] prePreds = new MethodHandle[nargs + 1]; + MethodHandle[] preSteps = new MethodHandle[nargs + 1]; + MethodHandle[] preFinis = new MethodHandle[nargs + 1]; + System.arraycopy(inits, 0, preInits, 1, nargs); + System.arraycopy(usePreds, 0, prePreds, 0, nargs); // preds are offset by 1 for pre-checked loops + System.arraycopy(steps, 0, preSteps, 1, nargs); + System.arraycopy(finis, 0, preFinis, 0, nargs); // finis are also offset by 1 for pre-checked loops + // Convert to clause-major form. + MethodHandle[][] preClauses = new MethodHandle[nargs+1][4]; + MethodHandle[][] postClauses = new MethodHandle[nargs][4]; + toClauseMajor(preClauses, preInits, preSteps, prePreds, preFinis); + toClauseMajor(postClauses, inits, steps, usePreds, finis); + MethodHandle pre = MethodHandles.loop(preClauses); + MethodHandle post = MethodHandles.loop(postClauses); + Object[] preResults = (Object[]) pre.invokeWithArguments(args); + if (verbosity >= 4) { + System.out.println("pre-checked: expected " + Arrays.asList(preCheckedResults[i]) + ", actual " + + Arrays.asList(preResults)); + } + Object[] postResults = (Object[]) post.invokeWithArguments(args); + if (verbosity >= 4) { + System.out.println("post-checked: expected " + Arrays.asList(postCheckedResults[i]) + ", actual " + + Arrays.asList(postResults)); + } + assertArrayEquals(preCheckedResults[i], preResults); + assertArrayEquals(postCheckedResults[i], postResults); + } + } + static void toClauseMajor(MethodHandle[][] clauses, MethodHandle[] init, MethodHandle[] step, MethodHandle[] pred, MethodHandle[] fini) { + for (int i = 0; i < clauses.length; ++i) { + clauses[i][0] = init[i]; + clauses[i][1] = step[i]; + clauses[i][2] = pred[i]; + clauses[i][3] = fini[i]; + } + } + static boolean loopIntPred(int a) { + if (verbosity >= 5) { + System.out.println("int pred " + a + " -> " + (a < 7)); + } + return a < 7; + } + static boolean loopDoublePred(int a, double b) { + if (verbosity >= 5) { + System.out.println("double pred (a=" + a + ") " + b + " -> " + (b > 0.5)); + } + return b > 0.5; + } + static boolean loopStringPred(int a, double b, String c) { + if (verbosity >= 5) { + System.out.println("String pred (a=" + a + ",b=" + b + ") " + c + " -> " + (c.length() <= 9)); + } + return c.length() <= 9; + } + static int loopIntStep(int a) { + if (verbosity >= 5) { + System.out.println("int step " + a + " -> " + (a + 1)); + } + return a + 1; + } + static double loopDoubleStep(int a, double b) { + if (verbosity >= 5) { + System.out.println("double step (a=" + a + ") " + b + " -> " + (b / 2.0)); + } + return b / 2.0; + } + static String loopStringStep(int a, double b, String c) { + if (verbosity >= 5) { + System.out.println("String step (a=" + a + ",b=" + b + ") " + c + " -> " + (c + a)); + } + return c + a; + } + @Test public void testThrowException() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testThrowException0); @@ -2575,13 +2887,108 @@ public class MethodHandlesTest { assertSame(thrown, caught); } + @Test + public void testTryFinally() throws Throwable { + CodeCacheOverflowProcessor.runMHTest(this::testTryFinally0); + } + public void testTryFinally0() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("tryFinally"); + String inputMessage = "returned"; + String augmentedMessage = "augmented"; + String thrownMessage = "thrown"; + String rethrownMessage = "rethrown"; + // Test these cases: + // * target returns, cleanup passes through + // * target returns, cleanup augments + // * target throws, cleanup augments and returns + // * target throws, cleanup augments and rethrows + MethodHandle target = MethodHandles.identity(String.class); + MethodHandle targetThrow = MethodHandles.dropArguments( + MethodHandles.throwException(String.class, Exception.class).bindTo(new Exception(thrownMessage)), 0, String.class); + MethodHandle cleanupPassThrough = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, + Throwable.class, String.class); + MethodHandle cleanupAugment = MethodHandles.dropArguments(MethodHandles.constant(String.class, augmentedMessage), + 0, Throwable.class, String.class, String.class); + MethodHandle cleanupCatch = MethodHandles.dropArguments(MethodHandles.constant(String.class, thrownMessage), 0, + Throwable.class, String.class, String.class); + MethodHandle cleanupThrow = MethodHandles.dropArguments(MethodHandles.throwException(String.class, Exception.class). + bindTo(new Exception(rethrownMessage)), 0, Throwable.class, String.class, String.class); + testTryFinally(target, cleanupPassThrough, inputMessage, inputMessage, false); + testTryFinally(target, cleanupAugment, inputMessage, augmentedMessage, false); + testTryFinally(targetThrow, cleanupCatch, inputMessage, thrownMessage, true); + testTryFinally(targetThrow, cleanupThrow, inputMessage, rethrownMessage, true); + // Test the same cases as above for void targets and cleanups. + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Class C = this.getClass(); + MethodType targetType = methodType(void.class, String[].class); + MethodType cleanupType = methodType(void.class, Throwable.class, String[].class); + MethodHandle vtarget = lookup.findStatic(C, "vtarget", targetType); + MethodHandle vtargetThrow = lookup.findStatic(C, "vtargetThrow", targetType); + MethodHandle vcleanupPassThrough = lookup.findStatic(C, "vcleanupPassThrough", cleanupType); + MethodHandle vcleanupAugment = lookup.findStatic(C, "vcleanupAugment", cleanupType); + MethodHandle vcleanupCatch = lookup.findStatic(C, "vcleanupCatch", cleanupType); + MethodHandle vcleanupThrow = lookup.findStatic(C, "vcleanupThrow", cleanupType); + testTryFinally(vtarget, vcleanupPassThrough, inputMessage, inputMessage, false); + testTryFinally(vtarget, vcleanupAugment, inputMessage, augmentedMessage, false); + testTryFinally(vtargetThrow, vcleanupCatch, inputMessage, thrownMessage, true); + testTryFinally(vtargetThrow, vcleanupThrow, inputMessage, rethrownMessage, true); + } + void testTryFinally(MethodHandle target, MethodHandle cleanup, String input, String msg, boolean mustCatch) + throws Throwable { + countTest(); + MethodHandle tf = MethodHandles.tryFinally(target, cleanup); + String result = null; + boolean isVoid = target.type().returnType() == void.class; + String[] argArray = new String[]{input}; + try { + if (isVoid) { + tf.invoke(argArray); + } else { + result = (String) tf.invoke(input); + } + } catch (Throwable t) { + assertTrue(mustCatch); + assertEquals(msg, t.getMessage()); + return; + } + assertFalse(mustCatch); + if (isVoid) { + assertEquals(msg, argArray[0]); + } else { + assertEquals(msg, result); + } + } + static void vtarget(String[] a) { + // naught, akin to identity + } + static void vtargetThrow(String[] a) throws Exception { + throw new Exception("thrown"); + } + static void vcleanupPassThrough(Throwable t, String[] a) { + assertNull(t); + // naught, akin to identity + } + static void vcleanupAugment(Throwable t, String[] a) { + assertNull(t); + a[0] = "augmented"; + } + static void vcleanupCatch(Throwable t, String[] a) { + assertNotNull(t); + a[0] = "caught"; + } + static void vcleanupThrow(Throwable t, String[] a) throws Exception { + assertNotNull(t); + throw new Exception("rethrown"); + } + @Test public void testInterfaceCast() throws Throwable { CodeCacheOverflowProcessor.runMHTest(this::testInterfaceCast0); } public void testInterfaceCast0() throws Throwable { - //if (CAN_SKIP_WORKING) return; + if (CAN_SKIP_WORKING) return; startTest("interfaceCast"); assert( (((Object)"foo") instanceof CharSequence)); assert(!(((Object)"foo") instanceof Iterable)); diff --git a/jdk/test/java/lang/invoke/T8139885.java b/jdk/test/java/lang/invoke/T8139885.java new file mode 100644 index 00000000000..913111c9efe --- /dev/null +++ b/jdk/test/java/lang/invoke/T8139885.java @@ -0,0 +1,1082 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* @test + * @run testng/othervm -ea -esa test.java.lang.invoke.T8139885 + */ + +package test.java.lang.invoke; + +import java.io.StringWriter; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.util.*; + +import static java.lang.invoke.MethodType.methodType; + +import static org.testng.AssertJUnit.*; + +import org.testng.annotations.*; + +/** + * Example-scale and negative tests for JEP 274 extensions. + */ +public class T8139885 { + + static final Lookup LOOKUP = MethodHandles.lookup(); + + // + // Tests. + // + + @Test + public static void testLoopFac() throws Throwable { + MethodHandle[] counterClause = new MethodHandle[]{Fac.MH_zero, Fac.MH_inc}; + MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin}; + MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause); + assertEquals(Fac.MT_fac, loop.type()); + assertEquals(120, loop.invoke(5)); + } + + @Test + public static void testLoopFacNullInit() throws Throwable { + // null initializer for counter, should initialize to 0 + MethodHandle[] counterClause = new MethodHandle[]{null, Fac.MH_inc}; + MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin}; + MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause); + assertEquals(Fac.MT_fac, loop.type()); + assertEquals(120, loop.invoke(5)); + } + + @Test + public static void testLoopVoid1() throws Throwable { + // construct a post-checked loop that only does one iteration and has a void body and void local state + MethodHandle loop = MethodHandles.loop(new MethodHandle[]{Empty.MH_f, Empty.MH_f, Empty.MH_pred, null}); + assertEquals(MethodType.methodType(void.class), loop.type()); + loop.invoke(); + } + + @Test + public static void testLoopVoid2() throws Throwable { + // construct a post-checked loop that only does one iteration and has a void body and void local state, + // initialized implicitly from the step type + MethodHandle loop = MethodHandles.loop(new MethodHandle[]{null, Empty.MH_f, Empty.MH_pred, null}); + assertEquals(MethodType.methodType(void.class), loop.type()); + loop.invoke(); + } + + @Test + public static void testLoopFacWithVoidState() throws Throwable { + // like testLoopFac, but with additional void state that outputs a dot + MethodHandle[] counterClause = new MethodHandle[]{Fac.MH_zero, Fac.MH_inc}; + MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin}; + MethodHandle[] dotClause = new MethodHandle[]{null, Fac.MH_dot}; + MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause, dotClause); + assertEquals(Fac.MT_fac, loop.type()); + assertEquals(120, loop.invoke(5)); + } + + @Test + public static void testLoopNegative() throws Throwable { + MethodHandle mh_loop = + LOOKUP.findStatic(MethodHandles.class, "loop", methodType(MethodHandle.class, MethodHandle[][].class)); + MethodHandle i0 = MethodHandles.constant(int.class, 0); + MethodHandle ii = MethodHandles.dropArguments(i0, 0, int.class, int.class); + MethodHandle id = MethodHandles.dropArguments(i0, 0, int.class, double.class); + MethodHandle i3 = MethodHandles.dropArguments(i0, 0, int.class, int.class, int.class); + List inits = Arrays.asList(ii, id, i3); + List> ints = Arrays.asList(int.class, int.class, int.class); + List finis = Arrays.asList(Fac.MH_fin, Fac.MH_inc, Counted.MH_step); + List preds1 = Arrays.asList(null, null, null); + List preds2 = Arrays.asList(null, Fac.MH_fin, null); + MethodHandle eek = MethodHandles.dropArguments(i0, 0, int.class, int.class, double.class); + List nesteps = Arrays.asList(Fac.MH_inc, eek, Fac.MH_dot); + List nepreds = Arrays.asList(null, Fac.MH_pred, null); + List nefinis = Arrays.asList(null, Fac.MH_fin, null); + MethodHandle[][][] cases = { + null, + {}, + {{null, Fac.MH_inc}, {Fac.MH_one, null, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin}}, + {{null, Fac.MH_inc}, null}, + {{Fac.MH_zero, Fac.MH_dot}}, + {{ii}, {id}, {i3}}, + {{null, Fac.MH_inc, null, Fac.MH_fin}, {null, Fac.MH_inc, null, Fac.MH_inc}, + {null, Counted.MH_start, null, Counted.MH_step}}, + {{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, Fac.MH_mult, null, Fac.MH_fin}, {null, Fac.MH_dot}}, + {{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, Fac.MH_mult, Fac.MH_fin, Fac.MH_fin}, {null, Fac.MH_dot}}, + {{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, eek, Fac.MH_pred, Fac.MH_fin}, {null, Fac.MH_dot}} + }; + String[] messages = { + "null or no clauses passed", + "null or no clauses passed", + "All loop clauses must be represented as MethodHandle arrays with at most 4 elements.", + "null clauses are not allowed", + "clause 0: init and step return types must match: int != void", + "found non-effectively identical init parameter type lists: " + inits + " (common suffix: " + ints + ")", + "found non-identical finalizer return types: " + finis + " (return type: int)", + "no predicate found: " + preds1, + "predicates must have boolean return type: " + preds2, + "found non-effectively identical parameter type lists:\nstep: " + nesteps + "\npred: " + nepreds + + "\nfini: " + nefinis + " (common parameter sequence: " + ints + ")" + }; + for (int i = 0; i < cases.length; ++i) { + boolean caught = false; + try { + mh_loop.invokeWithArguments(cases[i]); + } catch (IllegalArgumentException iae) { + assertEquals(messages[i], iae.getMessage()); + caught = true; + } + assertTrue(caught); + } + } + + @Test + public static void testWhileLoop() throws Throwable { + // int i = 0; while (i < limit) { ++i; } return i; => limit + MethodHandle loop = MethodHandles.whileLoop(While.MH_zero, While.MH_pred, While.MH_step); + assertEquals(While.MT_while, loop.type()); + assertEquals(23, loop.invoke(23)); + } + + @Test + public static void testWhileLoopNoIteration() throws Throwable { + // a while loop that never executes its body because the predicate evaluates to false immediately + MethodHandle loop = MethodHandles.whileLoop(While.MH_initString, While.MH_predString, While.MH_stepString); + assertEquals(While.MT_string, loop.type()); + assertEquals("a", loop.invoke()); + } + + @Test + public static void testDoWhileLoop() throws Throwable { + // int i = 0; do { ++i; } while (i < limit); return i; => limit + MethodHandle loop = MethodHandles.doWhileLoop(While.MH_zero, While.MH_step, While.MH_pred); + assertEquals(While.MT_while, loop.type()); + assertEquals(23, loop.invoke(23)); + } + + @Test + public static void testWhileZip() throws Throwable { + MethodHandle loop = MethodHandles.doWhileLoop(While.MH_zipInitZip, While.MH_zipStep, While.MH_zipPred); + assertEquals(While.MT_zip, loop.type()); + List a = Arrays.asList("a", "b", "c", "d"); + List b = Arrays.asList("e", "f", "g", "h"); + List zipped = Arrays.asList("a", "e", "b", "f", "c", "g", "d", "h"); + assertEquals(zipped, (List) loop.invoke(a.iterator(), b.iterator())); + } + + @Test + public static void testCountedLoop() throws Throwable { + // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s; => a variation on a well known theme + MethodHandle fit13 = MethodHandles.constant(int.class, 13); + MethodHandle loop = MethodHandles.countedLoop(fit13, Counted.MH_start, Counted.MH_step); + assertEquals(Counted.MT_counted, loop.type()); + assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!")); + } + + @Test + public static void testCountedArrayLoop() throws Throwable { + // int[] a = new int[]{0}; for (int i = 0; i < 13; ++i) { ++a[0]; } => a[0] == 13 + MethodHandle fit13 = MethodHandles.dropArguments(MethodHandles.constant(int.class, 13), 0, int[].class); + MethodHandle loop = MethodHandles.countedLoop(fit13, null, Counted.MH_stepUpdateArray); + assertEquals(Counted.MT_arrayCounted, loop.type()); + int[] a = new int[]{0}; + loop.invoke(a); + assertEquals(13, a[0]); + } + + @Test + public static void testCountedPrintingLoop() throws Throwable { + MethodHandle fit5 = MethodHandles.constant(int.class, 5); + MethodHandle loop = MethodHandles.countedLoop(fit5, null, Counted.MH_printHello); + assertEquals(Counted.MT_countedPrinting, loop.type()); + loop.invoke(); + } + + @Test + public static void testCountedRangeLoop() throws Throwable { + // String s = "Lambdaman!"; for (int i = -5; i < 8; ++i) { s = "na " + s; } return s; => a well known theme + MethodHandle fitm5 = MethodHandles.dropArguments(Counted.MH_m5, 0, String.class); + MethodHandle fit8 = MethodHandles.dropArguments(Counted.MH_8, 0, String.class); + MethodHandle loop = MethodHandles.countedLoop(fitm5, fit8, Counted.MH_start, Counted.MH_step); + assertEquals(Counted.MT_counted, loop.type()); + assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!")); + } + + @Test + public static void testIterateSum() throws Throwable { + // Integer[] a = new Integer[]{1,2,3,4,5,6}; int sum = 0; for (int e : a) { sum += e; } return sum; => 21 + MethodHandle loop = MethodHandles.iteratedLoop(Iterate.MH_sumIterator, Iterate.MH_sumInit, Iterate.MH_sumStep); + assertEquals(Iterate.MT_sum, loop.type()); + assertEquals(21, loop.invoke(new Integer[]{1, 2, 3, 4, 5, 6})); + } + + @Test + public static void testIterateReverse() throws Throwable { + MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_reverseInit, Iterate.MH_reverseStep); + assertEquals(Iterate.MT_reverse, loop.type()); + List list = Arrays.asList("a", "b", "c", "d", "e"); + List reversedList = Arrays.asList("e", "d", "c", "b", "a"); + assertEquals(reversedList, (List) loop.invoke(list)); + } + + @Test + public static void testIterateLength() throws Throwable { + MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_lengthInit, Iterate.MH_lengthStep); + assertEquals(Iterate.MT_length, loop.type()); + List list = Arrays.asList(23.0, 148.0, 42.0); + assertEquals(list.size(), (int) loop.invoke(list)); + } + + @Test + public static void testIterateMap() throws Throwable { + MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_mapInit, Iterate.MH_mapStep); + assertEquals(Iterate.MT_map, loop.type()); + List list = Arrays.asList("Hello", "world", "!"); + List upList = Arrays.asList("HELLO", "WORLD", "!"); + assertEquals(upList, (List) loop.invoke(list)); + } + + @Test + public static void testIteratePrint() throws Throwable { + MethodHandle loop = MethodHandles.iteratedLoop(null, null, Iterate.MH_printStep); + assertEquals(Iterate.MT_print, loop.type()); + loop.invoke(Arrays.asList("hello", "world")); + } + + @Test + public static void testIterateNullBody() { + boolean caught = false; + try { + MethodHandles.iteratedLoop(MethodHandles.identity(int.class), MethodHandles.identity(int.class), null); + } catch (IllegalArgumentException iae) { + assertEquals("iterated loop body must not be null", iae.getMessage()); + caught = true; + } + assertTrue(caught); + } + + @Test + public static void testTryFinally() throws Throwable { + MethodHandle hello = MethodHandles.tryFinally(TryFinally.MH_greet, TryFinally.MH_exclaim); + assertEquals(TryFinally.MT_hello, hello.type()); + assertEquals("Hello, world!", hello.invoke("world")); + } + + @Test + public static void testTryFinallyVoid() throws Throwable { + MethodHandle tfVoid = MethodHandles.tryFinally(TryFinally.MH_print, TryFinally.MH_printMore); + assertEquals(TryFinally.MT_printHello, tfVoid.type()); + tfVoid.invoke("world"); + } + + @Test + public static void testTryFinallySublist() throws Throwable { + MethodHandle helloMore = MethodHandles.tryFinally(TryFinally.MH_greetMore, TryFinally.MH_exclaimMore); + assertEquals(TryFinally.MT_moreHello, helloMore.type()); + assertEquals("Hello, world and universe (but world first)!", helloMore.invoke("world", "universe")); + } + + @Test + public static void testTryFinallyNegative() { + MethodHandle intid = MethodHandles.identity(int.class); + MethodHandle intco = MethodHandles.constant(int.class, 0); + MethodHandle errTarget = MethodHandles.dropArguments(intco, 0, int.class, double.class, String.class, int.class); + MethodHandle errCleanup = MethodHandles.dropArguments(MethodHandles.constant(int.class, 0), 0, Throwable.class, + int.class, double.class, Object.class); + MethodHandle[][] cases = { + {intid, MethodHandles.identity(double.class)}, + {intid, MethodHandles.dropArguments(intid, 0, String.class)}, + {intid, MethodHandles.dropArguments(intid, 0, Throwable.class, double.class)}, + {errTarget, errCleanup} + }; + String[] messages = { + "target and return types must match: double != int", + "cleanup first argument and Throwable must match: (String,int)int != class java.lang.Throwable", + "cleanup second argument and target return type must match: (Throwable,double,int)int != int", + "cleanup parameters after (Throwable,result) and target parameter list prefix must match: " + + errCleanup.type() + " != " + errTarget.type() + }; + for (int i = 0; i < cases.length; ++i) { + boolean caught = false; + try { + MethodHandles.tryFinally(cases[i][0], cases[i][1]); + } catch (IllegalArgumentException iae) { + assertEquals(messages[i], iae.getMessage()); + caught = true; + } + assertTrue(caught); + } + } + + @Test + public static void testFold0a() throws Throwable { + // equivalence to foldArguments(MethodHandle,MethodHandle) + MethodHandle fold = MethodHandles.foldArguments(Fold.MH_multer, 0, Fold.MH_adder); + assertEquals(Fold.MT_folded1, fold.type()); + assertEquals(720, (int) fold.invoke(3, 4, 5)); + } + + @Test + public static void testFold1a() throws Throwable { + // test foldArguments for folding position 1 + MethodHandle fold = MethodHandles.foldArguments(Fold.MH_multer, 1, Fold.MH_adder1); + assertEquals(Fold.MT_folded1, fold.type()); + assertEquals(540, (int) fold.invoke(3, 4, 5)); + } + + @Test + public static void testFold0b() throws Throwable { + // test foldArguments equivalence with multiple types + MethodHandle fold = MethodHandles.foldArguments(Fold.MH_str, 0, Fold.MH_comb); + assertEquals(Fold.MT_folded2, fold.type()); + assertEquals(23, (int) fold.invoke("true", true, 23)); + } + + @Test + public static void testFold1b() throws Throwable { + // test folgArguments for folding position 1, with multiple types + MethodHandle fold = MethodHandles.foldArguments(Fold.MH_str, 1, Fold.MH_comb2); + assertEquals(Fold.MT_folded3, fold.type()); + assertEquals(1, (int) fold.invoke(true, true, 1)); + assertEquals(-1, (int) fold.invoke(true, false, -1)); + } + + @Test + public static void testFoldArgumentsExample() throws Throwable { + // test the JavaDoc foldArguments-with-pos example + StringWriter swr = new StringWriter(); + MethodHandle trace = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, String.class)).bindTo(swr); + MethodHandle cat = LOOKUP.findVirtual(String.class, "concat", methodType(String.class, String.class)); + assertEquals("boojum", (String) cat.invokeExact("boo", "jum")); + MethodHandle catTrace = MethodHandles.foldArguments(cat, 1, trace); + assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); + assertEquals("jum", swr.toString()); + } + + @Test + public static void testAsSpreader() throws Throwable { + MethodHandle spreader = SpreadCollect.MH_forSpreading.asSpreader(1, int[].class, 3); + assertEquals(SpreadCollect.MT_spreader, spreader.type()); + assertEquals("A456B", (String) spreader.invoke("A", new int[]{4, 5, 6}, "B")); + } + + @Test + public static void testAsSpreaderExample() throws Throwable { + // test the JavaDoc asSpreader-with-pos example + MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class)); + MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2); + Object[] ints = new Object[]{3, 9, 7, 7}; + Comparator cmp = (a, b) -> a - b; + assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0); + assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0); + assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0); + } + + @Test + public static void testAsSpreaderIllegalPos() throws Throwable { + int[] illegalPos = {-7, 3, 19}; + int caught = 0; + for (int p : illegalPos) { + try { + SpreadCollect.MH_forSpreading.asSpreader(p, Object[].class, 3); + } catch (IllegalArgumentException iae) { + assertEquals("bad spread position", iae.getMessage()); + ++caught; + } + } + assertEquals(illegalPos.length, caught); + } + + @Test + public static void testAsCollector() throws Throwable { + MethodHandle collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 1); + assertEquals(SpreadCollect.MT_collector1, collector.type()); + assertEquals("A4B", (String) collector.invoke("A", 4, "B")); + collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 2); + assertEquals(SpreadCollect.MT_collector2, collector.type()); + assertEquals("A45B", (String) collector.invoke("A", 4, 5, "B")); + collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 3); + assertEquals(SpreadCollect.MT_collector3, collector.type()); + assertEquals("A456B", (String) collector.invoke("A", 4, 5, 6, "B")); + } + + @Test + public static void testAsCollectorInvokeWithArguments() throws Throwable { + MethodHandle collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 1); + assertEquals(SpreadCollect.MT_collector1, collector.type()); + assertEquals("A4B", (String) collector.invokeWithArguments("A", 4, "B")); + collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 2); + assertEquals(SpreadCollect.MT_collector2, collector.type()); + assertEquals("A45B", (String) collector.invokeWithArguments("A", 4, 5, "B")); + collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 3); + assertEquals(SpreadCollect.MT_collector3, collector.type()); + assertEquals("A456B", (String) collector.invokeWithArguments("A", 4, 5, 6, "B")); + } + + @Test + public static void testAsCollectorLeading() throws Throwable { + MethodHandle collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 1); + assertEquals(SpreadCollect.MT_collectorLeading1, collector.type()); + assertEquals("7Q", (String) collector.invoke(7, "Q")); + collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 2); + assertEquals(SpreadCollect.MT_collectorLeading2, collector.type()); + assertEquals("78Q", (String) collector.invoke(7, 8, "Q")); + collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 3); + assertEquals(SpreadCollect.MT_collectorLeading3, collector.type()); + assertEquals("789Q", (String) collector.invoke(7, 8, 9, "Q")); + } + + @Test + public static void testAsCollectorLeadingInvokeWithArguments() throws Throwable { + MethodHandle collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 1); + assertEquals(SpreadCollect.MT_collectorLeading1, collector.type()); + assertEquals("7Q", (String) collector.invokeWithArguments(7, "Q")); + collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 2); + assertEquals(SpreadCollect.MT_collectorLeading2, collector.type()); + assertEquals("78Q", (String) collector.invokeWithArguments(7, 8, "Q")); + collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 3); + assertEquals(SpreadCollect.MT_collectorLeading3, collector.type()); + assertEquals("789Q", (String) collector.invokeWithArguments(7, 8, 9, "Q")); + } + + @Test + public static void testAsCollectorNone() throws Throwable { + MethodHandle collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 0); + assertEquals(SpreadCollect.MT_collector0, collector.type()); + assertEquals("AB", (String) collector.invoke("A", "B")); + } + + @Test + public static void testAsCollectorIllegalPos() throws Throwable { + int[] illegalPos = {-1, 17}; + int caught = 0; + for (int p : illegalPos) { + try { + SpreadCollect.MH_forCollecting.asCollector(p, int[].class, 0); + } catch (IllegalArgumentException iae) { + assertEquals("bad collect position", iae.getMessage()); + ++caught; + } + } + assertEquals(illegalPos.length, caught); + } + + @Test + public static void testAsCollectorExample() throws Throwable { + // test the JavaDoc asCollector-with-pos example + StringWriter swr = new StringWriter(); + MethodHandle swWrite = LOOKUP. + findVirtual(StringWriter.class, "write", methodType(void.class, char[].class, int.class, int.class)). + bindTo(swr); + MethodHandle swWrite4 = swWrite.asCollector(0, char[].class, 4); + swWrite4.invoke('A', 'B', 'C', 'D', 1, 2); + assertEquals("BC", swr.toString()); + swWrite4.invoke('P', 'Q', 'R', 'S', 0, 4); + assertEquals("BCPQRS", swr.toString()); + swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1); + assertEquals("BCPQRSZ", swr.toString()); + } + + @Test + public static void testFindSpecial() throws Throwable { + FindSpecial.C c = new FindSpecial.C(); + assertEquals("I1.m", c.m()); + MethodType t = MethodType.methodType(String.class); + MethodHandle ci1m = LOOKUP.findSpecial(FindSpecial.I1.class, "m", t, FindSpecial.C.class); + assertEquals("I1.m", (String) ci1m.invoke(c)); + } + + @Test + public static void testFindSpecialAbstract() throws Throwable { + FindSpecial.C c = new FindSpecial.C(); + assertEquals("q", c.q()); + MethodType t = MethodType.methodType(String.class); + boolean caught = false; + try { + MethodHandle ci3q = LOOKUP.findSpecial(FindSpecial.I3.class, "q", t, FindSpecial.C.class); + } catch (Throwable thrown) { + if (!(thrown instanceof IllegalAccessException) || !FindSpecial.ABSTRACT_ERROR.equals(thrown.getMessage())) { + throw new AssertionError(thrown.getMessage(), thrown); + } + caught = true; + } + assertTrue(caught); + } + + @Test + public static void testFindClassCNFE() throws Throwable { + boolean caught = false; + try { + LOOKUP.findClass("does.not.Exist"); + } catch (ClassNotFoundException cnfe) { + caught = true; + } + assertTrue(caught); + } + + // + // Methods used to assemble tests. + // + + static class Empty { + + static void f() { } + + static boolean pred() { + return false; + } + + static final Class EMPTY = Empty.class; + + static final MethodType MT_f = methodType(void.class); + static final MethodType MT_pred = methodType(boolean.class); + + static final MethodHandle MH_f; + static final MethodHandle MH_pred; + + static { + try { + MH_f = LOOKUP.findStatic(EMPTY, "f", MT_f); + MH_pred = LOOKUP.findStatic(EMPTY, "pred", MT_pred); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + } + + static class Fac { + + static int zero(int k) { + return 0; + } + + static int one(int k) { + return 1; + } + + static boolean pred(int i, int acc, int k) { + return i < k; + } + + static int inc(int i, int acc, int k) { + return i + 1; + } + + static int mult(int i, int acc, int k) { + return i * acc; + } + + static void dot(int i, int acc, int k) { + System.out.print('.'); + } + + static int fin(int i, int acc, int k) { + return acc; + } + + static final Class FAC = Fac.class; + + static final MethodType MT_init = methodType(int.class, int.class); + static final MethodType MT_fn = methodType(int.class, int.class, int.class, int.class); + static final MethodType MT_dot = methodType(void.class, int.class, int.class, int.class); + static final MethodType MT_pred = methodType(boolean.class, int.class, int.class, int.class); + + static final MethodHandle MH_zero; + static final MethodHandle MH_one; + static final MethodHandle MH_pred; + static final MethodHandle MH_inc; + static final MethodHandle MH_mult; + static final MethodHandle MH_dot; + static final MethodHandle MH_fin; + + static final MethodType MT_fac = methodType(int.class, int.class); + + static { + try { + MH_zero = LOOKUP.findStatic(FAC, "zero", MT_init); + MH_one = LOOKUP.findStatic(FAC, "one", MT_init); + MH_pred = LOOKUP.findStatic(FAC, "pred", MT_pred); + MH_inc = LOOKUP.findStatic(FAC, "inc", MT_fn); + MH_mult = LOOKUP.findStatic(FAC, "mult", MT_fn); + MH_dot = LOOKUP.findStatic(FAC, "dot", MT_dot); + MH_fin = LOOKUP.findStatic(FAC, "fin", MT_fn); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + } + + static class While { + + static int zero(int limit) { + return 0; + } + + static boolean pred(int i, int limit) { + return i < limit; + } + + static int step(int i, int limit) { + return i + 1; + } + + static String initString() { + return "a"; + } + + static boolean predString(String s) { + return s.length() != 1; + } + + static String stepString(String s) { + return s + "a"; + } + + static List zipInitZip(Iterator a, Iterator b) { + return new ArrayList<>(); + } + + static boolean zipPred(List zip, Iterator a, Iterator b) { + return a.hasNext() && b.hasNext(); + } + + static List zipStep(List zip, Iterator a, Iterator b) { + zip.add(a.next()); + zip.add(b.next()); + return zip; + } + + static final Class WHILE = While.class; + + static final MethodType MT_zero = methodType(int.class, int.class); + static final MethodType MT_pred = methodType(boolean.class, int.class, int.class); + static final MethodType MT_fn = methodType(int.class, int.class, int.class); + static final MethodType MT_initString = methodType(String.class); + static final MethodType MT_predString = methodType(boolean.class, String.class); + static final MethodType MT_stepString = methodType(String.class, String.class); + static final MethodType MT_zipInitZip = methodType(List.class, Iterator.class, Iterator.class); + static final MethodType MT_zipPred = methodType(boolean.class, List.class, Iterator.class, Iterator.class); + static final MethodType MT_zipStep = methodType(List.class, List.class, Iterator.class, Iterator.class); + + static final MethodHandle MH_zero; + static final MethodHandle MH_pred; + static final MethodHandle MH_step; + static final MethodHandle MH_initString; + static final MethodHandle MH_predString; + static final MethodHandle MH_stepString; + static final MethodHandle MH_zipInitZip; + static final MethodHandle MH_zipPred; + static final MethodHandle MH_zipStep; + + static final MethodType MT_while = methodType(int.class, int.class); + static final MethodType MT_string = methodType(String.class); + static final MethodType MT_zip = methodType(List.class, Iterator.class, Iterator.class); + + static { + try { + MH_zero = LOOKUP.findStatic(WHILE, "zero", MT_zero); + MH_pred = LOOKUP.findStatic(WHILE, "pred", MT_pred); + MH_step = LOOKUP.findStatic(WHILE, "step", MT_fn); + MH_initString = LOOKUP.findStatic(WHILE, "initString", MT_initString); + MH_predString = LOOKUP.findStatic(WHILE, "predString", MT_predString); + MH_stepString = LOOKUP.findStatic(WHILE, "stepString", MT_stepString); + MH_zipInitZip = LOOKUP.findStatic(WHILE, "zipInitZip", MT_zipInitZip); + MH_zipPred = LOOKUP.findStatic(WHILE, "zipPred", MT_zipPred); + MH_zipStep = LOOKUP.findStatic(WHILE, "zipStep", MT_zipStep); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + } + + static class Counted { + + static String start(String arg) { + return arg; + } + + static String step(int counter, String v, String arg) { + return "na " + v; + } + + static void stepUpdateArray(int counter, int[] a) { + ++a[0]; + } + + static void printHello(int counter) { + System.out.print("hello"); + } + + static final Class COUNTED = Counted.class; + + static final MethodType MT_start = methodType(String.class, String.class); + static final MethodType MT_step = methodType(String.class, int.class, String.class, String.class); + static final MethodType MT_stepUpdateArray = methodType(void.class, int.class, int[].class); + static final MethodType MT_printHello = methodType(void.class, int.class); + + static final MethodHandle MH_13; + static final MethodHandle MH_m5; + static final MethodHandle MH_8; + static final MethodHandle MH_start; + static final MethodHandle MH_step; + static final MethodHandle MH_stepUpdateArray; + static final MethodHandle MH_printHello; + + static final MethodType MT_counted = methodType(String.class, String.class); + static final MethodType MT_arrayCounted = methodType(void.class, int[].class); + static final MethodType MT_countedPrinting = methodType(void.class); + + static { + try { + MH_13 = MethodHandles.constant(int.class, 13); + MH_m5 = MethodHandles.constant(int.class, -5); + MH_8 = MethodHandles.constant(int.class, 8); + MH_start = LOOKUP.findStatic(COUNTED, "start", MT_start); + MH_step = LOOKUP.findStatic(COUNTED, "step", MT_step); + MH_stepUpdateArray = LOOKUP.findStatic(COUNTED, "stepUpdateArray", MT_stepUpdateArray); + MH_printHello = LOOKUP.findStatic(COUNTED, "printHello", MT_printHello); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + } + + static class Iterate { + + static Iterator sumIterator(Integer[] a) { + return Arrays.asList(a).iterator(); + } + + static int sumInit(Integer[] a) { + return 0; + } + + static int sumStep(int s, int e, Integer[] a) { + return s + e; + } + + static List reverseInit(List l) { + return new ArrayList<>(); + } + + static List reverseStep(String e, List r, List l) { + r.add(0, e); + return r; + } + + static int lengthInit(List l) { + return 0; + } + + static int lengthStep(Object o, int len, List l) { + return len + 1; + } + + static List mapInit(List l) { + return new ArrayList<>(); + } + + static List mapStep(String e, List r, List l) { + r.add(e.toUpperCase()); + return r; + } + + static void printStep(String s, List l) { + System.out.print(s); + } + + static final Class ITERATE = Iterate.class; + + static final MethodType MT_sumIterator = methodType(Iterator.class, Integer[].class); + + static final MethodType MT_sumInit = methodType(int.class, Integer[].class); + static final MethodType MT_reverseInit = methodType(List.class, List.class); + static final MethodType MT_lenghInit = methodType(int.class, List.class); + static final MethodType MT_mapInit = methodType(List.class, List.class); + + static final MethodType MT_sumStep = methodType(int.class, int.class, int.class, Integer[].class); + static final MethodType MT_reverseStep = methodType(List.class, String.class, List.class, List.class); + static final MethodType MT_lengthStep = methodType(int.class, Object.class, int.class, List.class); + static final MethodType MT_mapStep = methodType(List.class, String.class, List.class, List.class); + static final MethodType MT_printStep = methodType(void.class, String.class, List.class); + + static final MethodHandle MH_sumIterator; + static final MethodHandle MH_sumInit; + static final MethodHandle MH_sumStep; + static final MethodHandle MH_printStep; + + static final MethodHandle MH_reverseInit; + static final MethodHandle MH_reverseStep; + + static final MethodHandle MH_lengthInit; + static final MethodHandle MH_lengthStep; + + static final MethodHandle MH_mapInit; + static final MethodHandle MH_mapStep; + + static final MethodType MT_sum = methodType(int.class, Integer[].class); + static final MethodType MT_reverse = methodType(List.class, List.class); + static final MethodType MT_length = methodType(int.class, List.class); + static final MethodType MT_map = methodType(List.class, List.class); + static final MethodType MT_print = methodType(void.class, List.class); + + static { + try { + MH_sumIterator = LOOKUP.findStatic(ITERATE, "sumIterator", MT_sumIterator); + MH_sumInit = LOOKUP.findStatic(ITERATE, "sumInit", MT_sumInit); + MH_sumStep = LOOKUP.findStatic(ITERATE, "sumStep", MT_sumStep); + MH_reverseInit = LOOKUP.findStatic(ITERATE, "reverseInit", MT_reverseInit); + MH_reverseStep = LOOKUP.findStatic(ITERATE, "reverseStep", MT_reverseStep); + MH_lengthInit = LOOKUP.findStatic(ITERATE, "lengthInit", MT_lenghInit); + MH_lengthStep = LOOKUP.findStatic(ITERATE, "lengthStep", MT_lengthStep); + MH_mapInit = LOOKUP.findStatic(ITERATE, "mapInit", MT_mapInit); + MH_mapStep = LOOKUP.findStatic(ITERATE, "mapStep", MT_mapStep); + MH_printStep = LOOKUP.findStatic(ITERATE, "printStep", MT_printStep); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + } + + static class TryFinally { + + static String greet(String whom) { + return "Hello, " + whom; + } + + static String exclaim(Throwable t, String r, String whom) { + return r + "!"; + } + + static void print(String what) { + System.out.print("Hello, " + what); + } + + static void printMore(Throwable t, String what) { + System.out.println("!"); + } + + static String greetMore(String first, String second) { + return "Hello, " + first + " and " + second; + } + + static String exclaimMore(Throwable t, String r, String first) { + return r + " (but " + first + " first)!"; + } + + static final Class TRY_FINALLY = TryFinally.class; + + static final MethodType MT_greet = methodType(String.class, String.class); + static final MethodType MT_exclaim = methodType(String.class, Throwable.class, String.class, String.class); + static final MethodType MT_print = methodType(void.class, String.class); + static final MethodType MT_printMore = methodType(void.class, Throwable.class, String.class); + static final MethodType MT_greetMore = methodType(String.class, String.class, String.class); + static final MethodType MT_exclaimMore = methodType(String.class, Throwable.class, String.class, String.class); + + static final MethodHandle MH_greet; + static final MethodHandle MH_exclaim; + static final MethodHandle MH_print; + static final MethodHandle MH_printMore; + static final MethodHandle MH_greetMore; + static final MethodHandle MH_exclaimMore; + + static final MethodType MT_hello = methodType(String.class, String.class); + static final MethodType MT_printHello = methodType(void.class, String.class); + static final MethodType MT_moreHello = methodType(String.class, String.class, String.class); + + static { + try { + MH_greet = LOOKUP.findStatic(TRY_FINALLY, "greet", MT_greet); + MH_exclaim = LOOKUP.findStatic(TRY_FINALLY, "exclaim", MT_exclaim); + MH_print = LOOKUP.findStatic(TRY_FINALLY, "print", MT_print); + MH_printMore = LOOKUP.findStatic(TRY_FINALLY, "printMore", MT_printMore); + MH_greetMore = LOOKUP.findStatic(TRY_FINALLY, "greetMore", MT_greetMore); + MH_exclaimMore = LOOKUP.findStatic(TRY_FINALLY, "exclaimMore", MT_exclaimMore); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + } + + static class Fold { + + static int adder(int a, int b, int c) { + return a + b + c; + } + + static int adder1(int a, int b) { + return a + b; + } + + static int multer(int x, int q, int r, int s) { + return x * q * r * s; + } + + static int str(boolean b1, String s, boolean b2, int x) { + return b1 && s.equals(String.valueOf(b2)) ? x : -x; + } + + static boolean comb(String s, boolean b2) { + return !s.equals(b2); + } + + static String comb2(boolean b2, int x) { + int ib = b2 ? 1 : 0; + return ib == x ? "true" : "false"; + } + + static final Class FOLD = Fold.class; + + static final MethodType MT_adder = methodType(int.class, int.class, int.class, int.class); + static final MethodType MT_adder1 = methodType(int.class, int.class, int.class); + static final MethodType MT_multer = methodType(int.class, int.class, int.class, int.class, int.class); + static final MethodType MT_str = methodType(int.class, boolean.class, String.class, boolean.class, int.class); + static final MethodType MT_comb = methodType(boolean.class, String.class, boolean.class); + static final MethodType MT_comb2 = methodType(String.class, boolean.class, int.class); + + static final MethodHandle MH_adder; + static final MethodHandle MH_adder1; + static final MethodHandle MH_multer; + static final MethodHandle MH_str; + static final MethodHandle MH_comb; + static final MethodHandle MH_comb2; + + static final MethodType MT_folded1 = methodType(int.class, int.class, int.class, int.class); + static final MethodType MT_folded2 = methodType(int.class, String.class, boolean.class, int.class); + static final MethodType MT_folded3 = methodType(int.class, boolean.class, boolean.class, int.class); + + static { + try { + MH_adder = LOOKUP.findStatic(FOLD, "adder", MT_adder); + MH_adder1 = LOOKUP.findStatic(FOLD, "adder1", MT_adder1); + MH_multer = LOOKUP.findStatic(FOLD, "multer", MT_multer); + MH_str = LOOKUP.findStatic(FOLD, "str", MT_str); + MH_comb = LOOKUP.findStatic(FOLD, "comb", MT_comb); + MH_comb2 = LOOKUP.findStatic(FOLD, "comb2", MT_comb2); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + } + + static class SpreadCollect { + + static String forSpreading(String s1, int i1, int i2, int i3, String s2) { + return s1 + i1 + i2 + i3 + s2; + } + + static String forCollecting(String s1, int[] is, String s2) { + StringBuilder sb = new StringBuilder(s1); + for (int i : is) { + sb.append(i); + } + return sb.append(s2).toString(); + } + + static String forCollectingLeading(int[] is, String s) { + return forCollecting("", is, s); + } + + static final Class SPREAD_COLLECT = SpreadCollect.class; + + static final MethodType MT_forSpreading = methodType(String.class, String.class, int.class, int.class, int.class, String.class); + static final MethodType MT_forCollecting = methodType(String.class, String.class, int[].class, String.class); + static final MethodType MT_forCollectingLeading = methodType(String.class, int[].class, String.class); + + static final MethodHandle MH_forSpreading; + static final MethodHandle MH_forCollecting; + static final MethodHandle MH_forCollectingLeading; + + static final MethodType MT_spreader = methodType(String.class, String.class, int[].class, String.class); + static final MethodType MT_collector0 = methodType(String.class, String.class, String.class); + static final MethodType MT_collector1 = methodType(String.class, String.class, int.class, String.class); + static final MethodType MT_collector2 = methodType(String.class, String.class, int.class, int.class, String.class); + static final MethodType MT_collector3 = methodType(String.class, String.class, int.class, int.class, int.class, String.class); + static final MethodType MT_collectorLeading1 = methodType(String.class, int.class, String.class); + static final MethodType MT_collectorLeading2 = methodType(String.class, int.class, int.class, String.class); + static final MethodType MT_collectorLeading3 = methodType(String.class, int.class, int.class, int.class, String.class); + + static final String NONE_ERROR = "zero array length in MethodHandle.asCollector"; + + static { + try { + MH_forSpreading = LOOKUP.findStatic(SPREAD_COLLECT, "forSpreading", MT_forSpreading); + MH_forCollecting = LOOKUP.findStatic(SPREAD_COLLECT, "forCollecting", MT_forCollecting); + MH_forCollectingLeading = LOOKUP.findStatic(SPREAD_COLLECT, "forCollectingLeading", MT_forCollectingLeading); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + } + + static class FindSpecial { + + interface I1 { + default String m() { + return "I1.m"; + } + } + + interface I2 { + default String m() { + return "I2.m"; + } + } + + interface I3 { + String q(); + } + + static class C implements I1, I2, I3 { + public String m() { + return I1.super.m(); + } + public String q() { + return "q"; + } + } + + static final String ABSTRACT_ERROR = "no such method: test.java.lang.invoke.T8139885$FindSpecial$I3.q()String/invokeSpecial"; + + } + + // + // Auxiliary methods. + // + + static MethodHandle[] mha(MethodHandle... mhs) { + return mhs; + } + +} diff --git a/jdk/test/java/lang/invoke/findclass.security.policy b/jdk/test/java/lang/invoke/findclass.security.policy new file mode 100644 index 00000000000..7bf9d7a99c1 --- /dev/null +++ b/jdk/test/java/lang/invoke/findclass.security.policy @@ -0,0 +1,9 @@ +/* + * Security policy used by the FindClassSecurityManager test. + * Must allow file reads so that jtreg itself can run, and getting class loaders. + */ + +grant { + permission java.io.FilePermission "*", "read"; + permission java.lang.RuntimePermission "getClassLoader"; +}; diff --git a/jdk/test/java/net/NetworkInterface/NetworkInterfaceStreamTest.java b/jdk/test/java/net/NetworkInterface/NetworkInterfaceStreamTest.java index 73b8266dff3..2cb62dc5435 100644 --- a/jdk/test/java/net/NetworkInterface/NetworkInterfaceStreamTest.java +++ b/jdk/test/java/net/NetworkInterface/NetworkInterfaceStreamTest.java @@ -24,7 +24,7 @@ /* @test * @bug 8081678 * @summary Tests for stream returning methods - * @library ../../util/stream/bootlib + * @library ../../util/stream/bootlib/java.base * @build java.util.stream.OpTestCase * @run testng/othervm NetworkInterfaceStreamTest * @run testng/othervm -Djava.net.preferIPv4Stack=true NetworkInterfaceStreamTest diff --git a/jdk/test/java/nio/file/Files/StreamLinesTest.java b/jdk/test/java/nio/file/Files/StreamLinesTest.java index 4c11d2a8a83..4f45026004d 100644 --- a/jdk/test/java/nio/file/Files/StreamLinesTest.java +++ b/jdk/test/java/nio/file/Files/StreamLinesTest.java @@ -23,7 +23,7 @@ /* @test * @bug 8072773 - * @library /lib/testlibrary/ ../../../util/stream/bootlib + * @library /lib/testlibrary/ ../../../util/stream/bootlib/java.base * @build java.util.stream.OpTestCase * @build jdk.testlibrary.RandomFactory * @run testng/othervm StreamLinesTest diff --git a/jdk/test/java/security/PermissionCollection/PermissionCollectionStreamTest.java b/jdk/test/java/security/PermissionCollection/PermissionCollectionStreamTest.java index 5744398e266..e472ee36397 100644 --- a/jdk/test/java/security/PermissionCollection/PermissionCollectionStreamTest.java +++ b/jdk/test/java/security/PermissionCollection/PermissionCollectionStreamTest.java @@ -24,7 +24,7 @@ /* @test * @bug 8081678 * @summary Tests for stream returning methods - * @library ../../util/stream/bootlib + * @library ../../util/stream/bootlib/java.base * @build java.util.stream.OpTestCase * @run testng/othervm PermissionCollectionStreamTest */ diff --git a/jdk/test/java/security/SecureRandom/DefaultProvider.java b/jdk/test/java/security/SecureRandom/DefaultProvider.java new file mode 100644 index 00000000000..50b4719acc6 --- /dev/null +++ b/jdk/test/java/security/SecureRandom/DefaultProvider.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2015, 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. + */ + +import static java.lang.System.out; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +/** + * @test + * @bug 8048356 + * @summary Assert default provider used on all OS for SecureRandom + */ +public class DefaultProvider { + + private static final String OS_NAME = System.getProperty("os.name"); + private static final String SUNOS = "SunOS"; + private static final String WINDOWS = "Windows"; + + public static void main(String[] args) throws NoSuchAlgorithmException { + out.println("Operating System: " + OS_NAME); + + /* Test default provider used with constructor */ + out.println("TEST: Default provider with constructor"); + SecureRandom secureRandom = new SecureRandom(); + String provider = secureRandom.getProvider().getName(); + if (OS_NAME.startsWith(SUNOS)) { + if (!provider.startsWith("SunPKCS11-")) { + throw new RuntimeException("Unexpected provider name: " + + provider); + } + } else if (!provider.equals("SUN")) { + throw new RuntimeException("Unexpected provider name: " + + provider); + } + out.println("Passed, default provider with constructor: " + provider); + + /* Test default provider with getInstance(String algorithm) */ + out.println("TEST: SHA1PRNG supported on all platforms by SUN provider"); + String algorithm = "SHA1PRNG"; + provider = "SUN"; + + SecureRandom instance = SecureRandom.getInstance(algorithm); + assertInstance(instance, algorithm, provider); + out.println("Passed."); + + if (!OS_NAME.startsWith(WINDOWS)) { + out.println("TEST: NativePRNG supported on all platforms" + + "(except Windows), by SUN provider"); + algorithm = "NativePRNG"; + provider = "SUN"; + } else { + out.println( + "TEST: Windows-PRNG supported on windows by SunMSCAPI provider"); + algorithm = "Windows-PRNG"; + provider = "SunMSCAPI"; + } + instance = SecureRandom.getInstance(algorithm); + assertInstance(instance, algorithm, provider); + out.println("Passed."); + + if (OS_NAME.startsWith(SUNOS)) { + out.println( + "TEST: PKCS11 is supported on Solaris by SunPKCS11 provider"); + algorithm = "PKCS11"; + provider = "SunPKCS11-Solaris"; + instance = SecureRandom.getInstance(algorithm); + assertInstance(instance, algorithm, provider); + out.println("Passed."); + } + } + + private static void assertInstance(SecureRandom instance, + String expectedAlgorithm, + String expectedProvider) { + if (instance != null) { + if (!expectedAlgorithm.equalsIgnoreCase(instance.getAlgorithm())) { + throw new RuntimeException("Expected algorithm:" + + expectedAlgorithm + " actual: " + instance.getAlgorithm()); + } + + if (!expectedProvider.equalsIgnoreCase(instance.getProvider().getName())) { + throw new RuntimeException("Expected provider: " + + expectedProvider + " actual: " + + instance.getProvider().getName()); + } + } else { + throw new RuntimeException("Secure instance is not created"); + } + } +} diff --git a/jdk/test/java/time/tck/java/time/TCKClock_Tick.java b/jdk/test/java/time/tck/java/time/TCKClock_Tick.java index c2212373cd8..08b0a524828 100644 --- a/jdk/test/java/time/tck/java/time/TCKClock_Tick.java +++ b/jdk/test/java/time/tck/java/time/TCKClock_Tick.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, 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 @@ -159,6 +159,17 @@ public class TCKClock_Tick extends AbstractTCKTest { Clock.tick(Clock.systemUTC(), null); } + //----------------------------------------------------------------------- + public void test_tickMillis_ZoneId() throws Exception { + Clock test = Clock.tickMillis(PARIS); + assertEquals(test.getZone(), PARIS); + assertEquals(test.instant().getNano() % 1000_000, 0); + } + + @Test(expectedExceptions = NullPointerException.class) + public void test_tickMillis_ZoneId_nullZoneId() { + Clock.tickMillis(null); + } //----------------------------------------------------------------------- public void test_tickSeconds_ZoneId() throws Exception { Clock test = Clock.tickSeconds(PARIS); diff --git a/jdk/test/java/time/tck/java/time/TCKDuration.java b/jdk/test/java/time/tck/java/time/TCKDuration.java index c12e914b91f..fe6a9f98088 100644 --- a/jdk/test/java/time/tck/java/time/TCKDuration.java +++ b/jdk/test/java/time/tck/java/time/TCKDuration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, 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 @@ -521,6 +521,8 @@ public class TCKDuration extends AbstractTCKTest { {"PT-123456789S", -123456789, 0}, {"PT" + Long.MIN_VALUE + "S", Long.MIN_VALUE, 0}, + + {"PT0.1S", 0, 100000000}, {"PT1.1S", 1, 100000000}, {"PT1.12S", 1, 120000000}, {"PT1.123S", 1, 123000000}, @@ -531,6 +533,7 @@ public class TCKDuration extends AbstractTCKTest { {"PT1.12345678S", 1, 123456780}, {"PT1.123456789S", 1, 123456789}, + {"PT-0.1S", -1, 1000000000 - 100000000}, {"PT-1.1S", -2, 1000000000 - 100000000}, {"PT-1.12S", -2, 1000000000 - 120000000}, {"PT-1.123S", -2, 1000000000 - 123000000}, @@ -544,6 +547,24 @@ public class TCKDuration extends AbstractTCKTest { {"PT" + Long.MAX_VALUE + ".123456789S", Long.MAX_VALUE, 123456789}, {"PT" + Long.MIN_VALUE + ".000000000S", Long.MIN_VALUE, 0}, + {"PT12M", 12 * 60, 0}, + {"PT12M0.35S", 12 * 60, 350000000}, + {"PT12M1.35S", 12 * 60 + 1, 350000000}, + {"PT12M-0.35S", 12 * 60 - 1, 1000000000 - 350000000}, + {"PT12M-1.35S", 12 * 60 - 2, 1000000000 - 350000000}, + + {"PT12H", 12 * 3600, 0}, + {"PT12H0.35S", 12 * 3600, 350000000}, + {"PT12H1.35S", 12 * 3600 + 1, 350000000}, + {"PT12H-0.35S", 12 * 3600 - 1, 1000000000 - 350000000}, + {"PT12H-1.35S", 12 * 3600 - 2, 1000000000 - 350000000}, + + {"P12D", 12 * 24 * 3600, 0}, + {"P12DT0.35S", 12 * 24 * 3600, 350000000}, + {"P12DT1.35S", 12 * 24 * 3600 + 1, 350000000}, + {"P12DT-0.35S", 12 * 24 * 3600 - 1, 1000000000 - 350000000}, + {"P12DT-1.35S", 12 * 24 * 3600 - 2, 1000000000 - 350000000}, + {"PT01S", 1, 0}, {"PT001S", 1, 0}, {"PT000S", 0, 0}, diff --git a/jdk/test/java/time/tck/java/time/TCKLocalDate.java b/jdk/test/java/time/tck/java/time/TCKLocalDate.java index 02e2f0789f6..6218d150241 100644 --- a/jdk/test/java/time/tck/java/time/TCKLocalDate.java +++ b/jdk/test/java/time/tck/java/time/TCKLocalDate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, 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 @@ -86,8 +86,6 @@ import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertSame; import static org.testng.Assert.assertTrue; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; import java.time.Clock; import java.time.DateTimeException; import java.time.DayOfWeek; @@ -104,6 +102,7 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.chrono.IsoChronology; +import java.time.chrono.IsoEra; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoField; @@ -135,6 +134,7 @@ public class TCKLocalDate extends AbstractDateTimeTest { private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1); private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2); + private static final ZoneOffset OFFSET_MTWO = ZoneOffset.ofHours(-2); private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris"); private static final ZoneId ZONE_GAZA = ZoneId.of("Asia/Gaza"); @@ -475,6 +475,48 @@ public class TCKLocalDate extends AbstractDateTimeTest { return date.withDayOfMonth(date.getMonth().length(isIsoLeap(date.getYear()))); } + //----------------------------------------------------------------------- + // ofInstant() + //----------------------------------------------------------------------- + @DataProvider(name="instantFactory") + Object[][] data_instantFactory() { + return new Object[][] { + {Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), ZONE_PARIS, LocalDate.of(1970, 1, 2)}, + {Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), OFFSET_MTWO, LocalDate.of(1970, 1, 1)}, + {Instant.ofEpochSecond(-86400 + 4, 500), OFFSET_PTWO, LocalDate.of(1969, 12, 31)}, + {OffsetDateTime.of(LocalDateTime.of(Year.MIN_VALUE, 1, 1, 0, 0), ZoneOffset.UTC).toInstant(), + ZoneOffset.UTC, LocalDate.MIN}, + {OffsetDateTime.of(LocalDateTime.of(Year.MAX_VALUE, 12, 31, 23, 59, 59, 999_999_999), ZoneOffset.UTC).toInstant(), + ZoneOffset.UTC, LocalDate.MAX}, + }; + } + + @Test(dataProvider="instantFactory") + public void factory_ofInstant(Instant instant, ZoneId zone, LocalDate expected) { + LocalDate test = LocalDate.ofInstant(instant, zone); + assertEquals(test, expected); + } + + @Test(expectedExceptions=DateTimeException.class) + public void factory_ofInstant_instantTooBig() { + LocalDate.ofInstant(Instant.MAX, OFFSET_PONE); + } + + @Test(expectedExceptions=DateTimeException.class) + public void factory_ofInstant_instantTooSmall() { + LocalDate.ofInstant(Instant.MIN, OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class) + public void factory_ofInstant_nullInstant() { + LocalDate.ofInstant((Instant) null, ZONE_GAZA); + } + + @Test(expectedExceptions=NullPointerException.class) + public void factory_ofInstant_nullZone() { + LocalDate.ofInstant(Instant.EPOCH, (ZoneId) null); + } + //----------------------------------------------------------------------- // ofEpochDay() //----------------------------------------------------------------------- @@ -2285,4 +2327,13 @@ public class TCKLocalDate extends AbstractDateTimeTest { return LocalDate.of(year, month, day); } + //----------------------------------------------------------------- + // getEra() + // ---------------------------------------------------------------- + @Test + public void test_getEra() { + IsoEra isoEra = LocalDate.MAX.getEra(); + assertSame(isoEra,IsoEra.CE); + assertSame(LocalDate.MIN.getEra(),IsoEra.BCE); + } } diff --git a/jdk/test/java/time/tck/java/time/TCKLocalTime.java b/jdk/test/java/time/tck/java/time/TCKLocalTime.java index 88f99ca8c65..1736aca7cc4 100644 --- a/jdk/test/java/time/tck/java/time/TCKLocalTime.java +++ b/jdk/test/java/time/tck/java/time/TCKLocalTime.java @@ -102,6 +102,7 @@ import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.OffsetTime; import java.time.Period; +import java.time.Year; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -137,6 +138,7 @@ import org.testng.annotations.Test; public class TCKLocalTime extends AbstractDateTimeTest { private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2); + private static final ZoneOffset OFFSET_MTWO = ZoneOffset.ofHours(-2); private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris"); private LocalTime TEST_12_30_40_987654321; @@ -420,6 +422,38 @@ public class TCKLocalTime extends AbstractDateTimeTest { LocalTime.of(0, 0, 0, 1000000000); } + //----------------------------------------------------------------------- + // ofInstant() + //----------------------------------------------------------------------- + @DataProvider(name="instantFactory") + Object[][] data_instantFactory() { + return new Object[][] { + {Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), ZONE_PARIS, LocalTime.of(2, 2, 4, 500)}, + {Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), OFFSET_MTWO, LocalTime.of(23, 2, 4, 500)}, + {Instant.ofEpochSecond(-86400 + 4, 500), OFFSET_PTWO, LocalTime.of(2, 0, 4, 500)}, + {OffsetDateTime.of(LocalDateTime.of(Year.MIN_VALUE, 1, 1, 0, 0), ZoneOffset.UTC).toInstant(), + ZoneOffset.UTC, LocalTime.MIN}, + {OffsetDateTime.of(LocalDateTime.of(Year.MAX_VALUE, 12, 31, 23, 59, 59, 999_999_999), ZoneOffset.UTC).toInstant(), + ZoneOffset.UTC, LocalTime.MAX}, + }; + } + + @Test(dataProvider="instantFactory") + public void factory_ofInstant(Instant instant, ZoneId zone, LocalTime expected) { + LocalTime test = LocalTime.ofInstant(instant, zone); + assertEquals(test, expected); + } + + @Test(expectedExceptions=NullPointerException.class) + public void factory_ofInstant_nullInstant() { + LocalTime.ofInstant((Instant) null, ZONE_PARIS); + } + + @Test(expectedExceptions=NullPointerException.class) + public void factory_ofInstant_nullZone() { + LocalTime.ofInstant(Instant.EPOCH, (ZoneId) null); + } + //----------------------------------------------------------------------- // ofSecondOfDay(long) //----------------------------------------------------------------------- diff --git a/jdk/test/java/time/tck/java/time/format/TCKZoneIdPrinterParser.java b/jdk/test/java/time/tck/java/time/format/TCKZoneIdPrinterParser.java index 11e2dcd9620..c1656885c07 100644 --- a/jdk/test/java/time/tck/java/time/format/TCKZoneIdPrinterParser.java +++ b/jdk/test/java/time/tck/java/time/format/TCKZoneIdPrinterParser.java @@ -156,6 +156,7 @@ public class TCKZoneIdPrinterParser { {"UTC", 3, -1, ZoneId.of("UTC"), false}, {"UT", 2, -1, ZoneId.of("UT"), false}, {"GMT", 3, -1, ZoneId.of("GMT"), false}, + {"GMT0", 4, -1, ZoneId.of("GMT0"), false}, {"+00:00", 6, -1, ZoneOffset.UTC, true}, {"UTC+00:00", 9, -1, ZoneId.of("UTC"), false}, diff --git a/jdk/test/java/time/test/java/time/temporal/TestIsoWeekFields.java b/jdk/test/java/time/test/java/time/temporal/TestIsoWeekFields.java index d79d13f89a3..672295e4def 100644 --- a/jdk/test/java/time/test/java/time/temporal/TestIsoWeekFields.java +++ b/jdk/test/java/time/test/java/time/temporal/TestIsoWeekFields.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014,2015, 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 @@ -121,6 +121,14 @@ public class TestIsoWeekFields { assertEquals(IsoFields.WEEK_BASED_YEAR.isSupportedBy(ThaiBuddhistDate.now()), false); } + @Test + public void test_Unit_isSupportedBy_ISO() { + assertEquals(IsoFields.WEEK_BASED_YEARS.isSupportedBy(LocalDate.now()),true); + assertEquals(IsoFields.WEEK_BASED_YEARS.isSupportedBy(ThaiBuddhistDate.now()),false); + assertEquals(IsoFields.QUARTER_YEARS.isSupportedBy(LocalDate.now()),true); + assertEquals(IsoFields.QUARTER_YEARS.isSupportedBy(ThaiBuddhistDate.now()),false); + } + @Test(dataProvider = "fields") public void test_WBY_range(TemporalField weekField, TemporalField yearField) { assertEquals(yearField.range(), ValueRange.of(Year.MIN_VALUE, Year.MAX_VALUE)); diff --git a/jdk/test/java/util/Arrays/ArraysEqCmpTest.java b/jdk/test/java/util/Arrays/ArraysEqCmpTest.java index 590b71668b4..a2bb9ce6a79 100644 --- a/jdk/test/java/util/Arrays/ArraysEqCmpTest.java +++ b/jdk/test/java/util/Arrays/ArraysEqCmpTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8033148 + * @bug 8033148 8141409 * @summary tests for array equals and compare * @run testng ArraysEqCmpTest */ @@ -312,6 +312,8 @@ public class ArraysEqCmpTest { return Integer.compare(b, a); }; + final MethodHandle eqc; + final MethodHandle eqcr; final MethodHandle cmpc; final MethodHandle cmpcr; final MethodHandle mismatchc; @@ -327,6 +329,8 @@ public class ArraysEqCmpTest { int.class, Object[].class, int.class, int.class, Object[].class, int.class, int.class, Comparator.class); + eqc = l.findStatic(Arrays.class, "equals", cmpt.changeReturnType(boolean.class)); + eqcr = l.findStatic(Arrays.class, "equals", cmprt.changeReturnType(boolean.class)); cmpc = l.findStatic(Arrays.class, "compare", cmpt); cmpcr = l.findStatic(Arrays.class, "compare", cmprt); mismatchc = l.findStatic(Arrays.class, "mismatch", cmpt); @@ -337,6 +341,33 @@ public class ArraysEqCmpTest { } } + @Override + boolean equals(Object a, Object b) { + try { + return (boolean) eqc.invoke(a, b, c); + } + catch (RuntimeException | Error e) { + throw e; + } + catch (Throwable t) { + throw new Error(t); + } + } + + @Override + boolean equals(Object a, int aFromIndex, int aToIndex, + Object b, int bFromIndex, int bToIndex) { + try { + return (boolean) eqcr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex, c); + } + catch (RuntimeException | Error e) { + throw e; + } + catch (Throwable t) { + throw new Error(t); + } + } + @Override int compare(Object a, Object b) { try { @@ -1002,10 +1033,12 @@ public class ArraysEqCmpTest { continue; if (o3 == null) { + testNPE(() -> Arrays.equals(o1, o2, o3)); testNPE(() -> Arrays.compare(o1, o2, o3)); testNPE(() -> Arrays.mismatch(o1, o2, o3)); } + testNPE(() -> Arrays.equals(o1, 0, 0, o2, 0, 0, o3)); testNPE(() -> Arrays.compare(o1, 0, 0, o2, 0, 0, o3)); testNPE(() -> Arrays.mismatch(o1, 0, 0, o2, 0, 0, o3)); } diff --git a/jdk/test/java/util/Arrays/TimSortStackSize2.java b/jdk/test/java/util/Arrays/TimSortStackSize2.java index 9e212fd00bd..420d6bda9b8 100644 --- a/jdk/test/java/util/Arrays/TimSortStackSize2.java +++ b/jdk/test/java/util/Arrays/TimSortStackSize2.java @@ -24,7 +24,7 @@ /* * @test * @bug 8072909 - * @library /lib/testlibrary /../../test/lib + * @library /lib/testlibrary /test/lib * @build jdk.testlibrary.* * @build TimSortStackSize2 * @run main ClassFileInstaller sun.hotspot.WhiteBox diff --git a/jdk/test/java/util/Objects/BasicObjectsTest.java b/jdk/test/java/util/Objects/BasicObjectsTest.java index 878bb825019..a7636ef11d5 100644 --- a/jdk/test/java/util/Objects/BasicObjectsTest.java +++ b/jdk/test/java/util/Objects/BasicObjectsTest.java @@ -241,12 +241,12 @@ public class BasicObjectsTest { String nonNullString = "non-null"; // Confirm the compile time return type matches - String result = Objects.nonNullElse(nullString, defString); + String result = Objects.requireNonNullElse(nullString, defString); errors += (result == defString) ? 0 : 1; - errors += (Objects.nonNullElse(nonNullString, defString) == nonNullString) ? 0 : 1; - errors += (Objects.nonNullElse(nonNullString, null) == nonNullString) ? 0 : 1; + errors += (Objects.requireNonNullElse(nonNullString, defString) == nonNullString) ? 0 : 1; + errors += (Objects.requireNonNullElse(nonNullString, null) == nonNullString) ? 0 : 1; try { - Objects.nonNullElse(null, null); + Objects.requireNonNullElse(null, null); errors += 1; } catch (NullPointerException npe) { // expected @@ -254,20 +254,20 @@ public class BasicObjectsTest { } - // Test nonNullElseGet with a supplier - errors += (Objects.nonNullElseGet(nullString, () -> defString) == defString) ? 0 : 1; - errors += (Objects.nonNullElseGet(nonNullString, () -> defString) == nonNullString) ? 0 : 1; - errors += (Objects.nonNullElseGet(nonNullString, () -> null) == nonNullString) ? 0 : 1; + // Test requireNonNullElseGet with a supplier + errors += (Objects.requireNonNullElseGet(nullString, () -> defString) == defString) ? 0 : 1; + errors += (Objects.requireNonNullElseGet(nonNullString, () -> defString) == nonNullString) ? 0 : 1; + errors += (Objects.requireNonNullElseGet(nonNullString, () -> null) == nonNullString) ? 0 : 1; try { - Objects.nonNullElseGet(null, () -> null); + Objects.requireNonNullElseGet(null, () -> null); errors += 1; } catch (NullPointerException npe) { // expected errors += npe.getMessage().equals("supplier.get()") ? 0 : 1; } try { // supplier is null - Objects.nonNullElseGet(null, null); + Objects.requireNonNullElseGet(null, null); errors += 1; } catch (NullPointerException npe) { // expected diff --git a/jdk/test/java/util/Objects/CheckIndex.java b/jdk/test/java/util/Objects/CheckIndex.java index d225d29e0fb..d809bf4fe50 100644 --- a/jdk/test/java/util/Objects/CheckIndex.java +++ b/jdk/test/java/util/Objects/CheckIndex.java @@ -25,7 +25,7 @@ * @test * @summary IndexOutOfBoundsException check index tests * @run testng CheckIndex - * @bug 8135248 + * @bug 8135248 8142493 */ import org.testng.annotations.DataProvider; @@ -54,6 +54,15 @@ public class CheckIndex { }; } + static BiFunction assertingOutOfBoundsReturnNull( + int expFromIndex, int expToIndexOrSizeOrLength) { + return (fromIndex, toIndexOrSizeorLength) -> { + assertEquals(fromIndex, Integer.valueOf(expFromIndex)); + assertEquals(toIndexOrSizeorLength, Integer.valueOf(expToIndexOrSizeOrLength)); + return null; + }; + } + static final int[] VALUES = {0, 1, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, -1, Integer.MIN_VALUE + 1, Integer.MIN_VALUE}; @DataProvider @@ -94,6 +103,8 @@ public class CheckIndex { check.accept(AssertingOutOfBoundsException.class, () -> Objects.checkIndex(index, length, assertingOutOfBounds(index, length))); + check.accept(IndexOutOfBoundsException.class, + () -> Objects.checkIndex(index, length, assertingOutOfBoundsReturnNull(index, length))); check.accept(IndexOutOfBoundsException.class, () -> Objects.checkIndex(index, length, null)); check.accept(IndexOutOfBoundsException.class, @@ -139,6 +150,8 @@ public class CheckIndex { check.accept(AssertingOutOfBoundsException.class, () -> Objects.checkFromToIndex(fromIndex, toIndex, length, assertingOutOfBounds(fromIndex, toIndex))); + check.accept(IndexOutOfBoundsException.class, + () -> Objects.checkFromToIndex(fromIndex, toIndex, length, assertingOutOfBoundsReturnNull(fromIndex, toIndex))); check.accept(IndexOutOfBoundsException.class, () -> Objects.checkFromToIndex(fromIndex, toIndex, length, null)); check.accept(IndexOutOfBoundsException.class, @@ -191,6 +204,8 @@ public class CheckIndex { check.accept(AssertingOutOfBoundsException.class, () -> Objects.checkFromIndexSize(fromIndex, size, length, assertingOutOfBounds(fromIndex, size))); + check.accept(IndexOutOfBoundsException.class, + () -> Objects.checkFromIndexSize(fromIndex, size, length, assertingOutOfBoundsReturnNull(fromIndex, size))); check.accept(IndexOutOfBoundsException.class, () -> Objects.checkFromIndexSize(fromIndex, size, length, null)); check.accept(IndexOutOfBoundsException.class, diff --git a/jdk/test/java/util/Scanner/ScannerStreamTest.java b/jdk/test/java/util/Scanner/ScannerStreamTest.java index 9fe54a6dabe..0f555c50139 100644 --- a/jdk/test/java/util/Scanner/ScannerStreamTest.java +++ b/jdk/test/java/util/Scanner/ScannerStreamTest.java @@ -46,7 +46,7 @@ import static org.testng.Assert.*; * @test * @bug 8072722 * @summary Tests of stream support in java.util.Scanner - * @library ../stream/bootlib + * @library ../stream/bootlib/java.base * @build java.util.stream.OpTestCase * @run testng/othervm ScannerStreamTest */ diff --git a/jdk/test/java/util/concurrent/Phaser/Basic.java b/jdk/test/java/util/concurrent/Phaser/Basic.java index 401b983e189..c516a969b39 100644 --- a/jdk/test/java/util/concurrent/Phaser/Basic.java +++ b/jdk/test/java/util/concurrent/Phaser/Basic.java @@ -34,6 +34,7 @@ /* * @test * @bug 6445158 + * @key intermittent * @summary Basic tests for Phaser * @author Chris Hegarty */ diff --git a/jdk/test/java/util/logging/LoggerSubclass.java b/jdk/test/java/util/logging/LoggerSubclass.java index ae3d4af0db0..e1a989ee6f9 100644 --- a/jdk/test/java/util/logging/LoggerSubclass.java +++ b/jdk/test/java/util/logging/LoggerSubclass.java @@ -92,7 +92,7 @@ public class LoggerSubclass { else fail(x + " not equal to " + y);} public static void main(String[] args) throws Throwable { try {new LoggerSubclass().instanceMain(args);} - catch (Throwable e) {throw e.getCause();}} + catch (Throwable e) {throw e.getCause() == null ? e : e.getCause();}} public void instanceMain(String[] args) throws Throwable { try {test(args);} catch (Throwable t) {unexpected(t);} System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); diff --git a/jdk/test/java/util/regex/PatternStreamTest.java b/jdk/test/java/util/regex/PatternStreamTest.java index 4ce7bb1b50a..349faad034a 100644 --- a/jdk/test/java/util/regex/PatternStreamTest.java +++ b/jdk/test/java/util/regex/PatternStreamTest.java @@ -25,7 +25,7 @@ * @test * @bug 8016846 8024341 8071479 * @summary Unit tests stream and lambda-based methods on Pattern and Matcher - * @library ../stream/bootlib + * @library ../stream/bootlib/java.base * @build java.util.stream.OpTestCase * @run testng/othervm PatternStreamTest */ diff --git a/jdk/test/java/util/stream/bootlib/TEST.properties b/jdk/test/java/util/stream/bootlib/TEST.properties index a50c2e89a8d..317080b0b37 100644 --- a/jdk/test/java/util/stream/bootlib/TEST.properties +++ b/jdk/test/java/util/stream/bootlib/TEST.properties @@ -1,3 +1,3 @@ # This file identifies root(s) of the test-ng hierarchy. -bootclasspath.dirs = . +bootclasspath.dirs = java.base diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/CollectorOps.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/CollectorOps.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/CollectorOps.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/CollectorOps.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/DefaultMethodStreams.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DefaultMethodStreams.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/DefaultMethodStreams.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DefaultMethodStreams.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/DoubleStreamTestDataProvider.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DoubleStreamTestDataProvider.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/DoubleStreamTestDataProvider.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DoubleStreamTestDataProvider.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/DoubleStreamTestScenario.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DoubleStreamTestScenario.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/DoubleStreamTestScenario.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DoubleStreamTestScenario.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/FlagDeclaringOp.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/FlagDeclaringOp.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/FlagDeclaringOp.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/FlagDeclaringOp.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/IntStreamTestDataProvider.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntStreamTestDataProvider.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/IntStreamTestDataProvider.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntStreamTestDataProvider.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/IntStreamTestScenario.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntStreamTestScenario.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/IntStreamTestScenario.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntStreamTestScenario.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/IntermediateTestOp.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntermediateTestOp.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/IntermediateTestOp.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntermediateTestOp.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/LambdaTestHelpers.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LambdaTestHelpers.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/LambdaTestHelpers.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LambdaTestHelpers.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/LambdaTestMode.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LambdaTestMode.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/LambdaTestMode.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LambdaTestMode.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/LoggingTestCase.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LoggingTestCase.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/LoggingTestCase.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LoggingTestCase.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/LongStreamTestDataProvider.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LongStreamTestDataProvider.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/LongStreamTestDataProvider.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LongStreamTestDataProvider.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/LongStreamTestScenario.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LongStreamTestScenario.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/LongStreamTestScenario.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LongStreamTestScenario.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/OpTestCase.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/OpTestCase.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/OpTestCase.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/OpTestCase.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/SpliteratorTestHelper.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/SpliteratorTestHelper.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/SpliteratorTestHelper.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/SpliteratorTestHelper.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/StatefulTestOp.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StatefulTestOp.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/StatefulTestOp.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StatefulTestOp.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/StatelessTestOp.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StatelessTestOp.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/StatelessTestOp.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StatelessTestOp.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/StreamOpFlagTestHelper.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamOpFlagTestHelper.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/StreamOpFlagTestHelper.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamOpFlagTestHelper.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/StreamTestDataProvider.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamTestDataProvider.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/StreamTestDataProvider.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamTestDataProvider.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/StreamTestScenario.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamTestScenario.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/StreamTestScenario.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamTestScenario.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/TestData.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/TestData.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/TestData.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/TestData.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/TestFlagExpectedOp.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/TestFlagExpectedOp.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/TestFlagExpectedOp.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/TestFlagExpectedOp.java diff --git a/jdk/test/java/util/stream/bootlib/java/util/stream/ThowableHelper.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/ThowableHelper.java similarity index 100% rename from jdk/test/java/util/stream/bootlib/java/util/stream/ThowableHelper.java rename to jdk/test/java/util/stream/bootlib/java.base/java/util/stream/ThowableHelper.java diff --git a/jdk/test/java/util/stream/boottest/TEST.properties b/jdk/test/java/util/stream/boottest/TEST.properties index d51ddf7889a..980a89fecc8 100644 --- a/jdk/test/java/util/stream/boottest/TEST.properties +++ b/jdk/test/java/util/stream/boottest/TEST.properties @@ -1,5 +1,5 @@ # This file identifies root(s) of the test-ng hierarchy. -TestNG.dirs = . -bootclasspath.dirs = . -lib.dirs = /java/util/stream/bootlib +TestNG.dirs = java.base +bootclasspath.dirs = java.base +lib.dirs = /java/util/stream/bootlib/java.base diff --git a/jdk/test/java/util/stream/boottest/java/util/stream/DoubleNodeTest.java b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/DoubleNodeTest.java similarity index 100% rename from jdk/test/java/util/stream/boottest/java/util/stream/DoubleNodeTest.java rename to jdk/test/java/util/stream/boottest/java.base/java/util/stream/DoubleNodeTest.java diff --git a/jdk/test/java/util/stream/boottest/java/util/stream/FlagOpTest.java b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/FlagOpTest.java similarity index 100% rename from jdk/test/java/util/stream/boottest/java/util/stream/FlagOpTest.java rename to jdk/test/java/util/stream/boottest/java.base/java/util/stream/FlagOpTest.java diff --git a/jdk/test/java/util/stream/boottest/java/util/stream/IntNodeTest.java b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/IntNodeTest.java similarity index 100% rename from jdk/test/java/util/stream/boottest/java/util/stream/IntNodeTest.java rename to jdk/test/java/util/stream/boottest/java.base/java/util/stream/IntNodeTest.java diff --git a/jdk/test/java/util/stream/boottest/java/util/stream/LongNodeTest.java b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/LongNodeTest.java similarity index 100% rename from jdk/test/java/util/stream/boottest/java/util/stream/LongNodeTest.java rename to jdk/test/java/util/stream/boottest/java.base/java/util/stream/LongNodeTest.java diff --git a/jdk/test/java/util/stream/boottest/java/util/stream/NodeBuilderTest.java b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/NodeBuilderTest.java similarity index 100% rename from jdk/test/java/util/stream/boottest/java/util/stream/NodeBuilderTest.java rename to jdk/test/java/util/stream/boottest/java.base/java/util/stream/NodeBuilderTest.java diff --git a/jdk/test/java/util/stream/boottest/java/util/stream/NodeTest.java b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/NodeTest.java similarity index 100% rename from jdk/test/java/util/stream/boottest/java/util/stream/NodeTest.java rename to jdk/test/java/util/stream/boottest/java.base/java/util/stream/NodeTest.java diff --git a/jdk/test/java/util/stream/boottest/java/util/stream/SliceSpliteratorTest.java b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/SliceSpliteratorTest.java similarity index 100% rename from jdk/test/java/util/stream/boottest/java/util/stream/SliceSpliteratorTest.java rename to jdk/test/java/util/stream/boottest/java.base/java/util/stream/SliceSpliteratorTest.java diff --git a/jdk/test/java/util/stream/boottest/java/util/stream/SpinedBufferTest.java b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/SpinedBufferTest.java similarity index 100% rename from jdk/test/java/util/stream/boottest/java/util/stream/SpinedBufferTest.java rename to jdk/test/java/util/stream/boottest/java.base/java/util/stream/SpinedBufferTest.java diff --git a/jdk/test/java/util/stream/boottest/java/util/stream/StreamFlagsTest.java b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/StreamFlagsTest.java similarity index 100% rename from jdk/test/java/util/stream/boottest/java/util/stream/StreamFlagsTest.java rename to jdk/test/java/util/stream/boottest/java.base/java/util/stream/StreamFlagsTest.java diff --git a/jdk/test/java/util/stream/boottest/java/util/stream/StreamOpFlagsTest.java b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/StreamOpFlagsTest.java similarity index 100% rename from jdk/test/java/util/stream/boottest/java/util/stream/StreamOpFlagsTest.java rename to jdk/test/java/util/stream/boottest/java.base/java/util/stream/StreamOpFlagsTest.java diff --git a/jdk/test/java/util/stream/boottest/java/util/stream/StreamReuseTest.java b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/StreamReuseTest.java similarity index 100% rename from jdk/test/java/util/stream/boottest/java/util/stream/StreamReuseTest.java rename to jdk/test/java/util/stream/boottest/java.base/java/util/stream/StreamReuseTest.java diff --git a/jdk/test/java/util/stream/test/TEST.properties b/jdk/test/java/util/stream/test/TEST.properties index 4128f6afd75..706ed7c570e 100644 --- a/jdk/test/java/util/stream/test/TEST.properties +++ b/jdk/test/java/util/stream/test/TEST.properties @@ -2,7 +2,7 @@ TestNG.dirs = . -lib.dirs = /java/util/stream/bootlib +lib.dirs = /java/util/stream/bootlib/java.base # Tests that must run in othervm mode othervm.dirs= /java/util/stream diff --git a/jdk/test/java/util/zip/TestLocalTime.java b/jdk/test/java/util/zip/TestLocalTime.java index 47d3e81d226..d1909afba54 100644 --- a/jdk/test/java/util/zip/TestLocalTime.java +++ b/jdk/test/java/util/zip/TestLocalTime.java @@ -21,9 +21,10 @@ * questions. */ -/** +/* * @test * @bug 8075526 + * @key intermittent * @summary Test timestamp via ZipEntry.get/setTimeLocal() */ diff --git a/jdk/test/javax/swing/LookAndFeel/8138881/TestOSVersion.java b/jdk/test/javax/swing/LookAndFeel/8138881/TestOSVersion.java new file mode 100644 index 00000000000..59a86aafa72 --- /dev/null +++ b/jdk/test/javax/swing/LookAndFeel/8138881/TestOSVersion.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8138881 + * @summary typo in OSInfo.java + * @modules java.desktop/sun.awt + * @requires (os.family == "windows") + * @run main TestOSVersion + */ + +import sun.awt.OSInfo; + +public class TestOSVersion { + + private static final String WIN_VISTA_VERSION = "6.0"; + + public static void main(String[] arg) { + + String oSVersion = System.getProperty("os.version"); + if (WIN_VISTA_VERSION.equals(oSVersion)) { + if (OSInfo.getWindowsVersion().toString().equals("6.1") ) { + throw new RuntimeException("Incorrect Windows VISTA OS version " + + "in OSInfo"); + } + } + } +} diff --git a/jdk/test/javax/swing/plaf/metal/MetalUtils/bug6190373.java b/jdk/test/javax/swing/plaf/metal/MetalUtils/bug6190373.java new file mode 100644 index 00000000000..ad484fb6518 --- /dev/null +++ b/jdk/test/javax/swing/plaf/metal/MetalUtils/bug6190373.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2005, 2015, 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. + */ + +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.util.concurrent.CyclicBarrier; + +import javax.swing.JButton; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +import sun.awt.AppContext; +import sun.awt.SunToolkit; + +import static javax.swing.UIManager.getInstalledLookAndFeels; + +/** + * @test + * @bug 6190373 + * @summary Tests 6190373 + * @author Scott Violet + * @modules java.desktop/sun.awt + */ +public final class bug6190373 { + + private static AppContext app1; + private static AppContext app2; + private static final int LOOP_COUNT = 10000; + private static final CyclicBarrier barrier = new CyclicBarrier(2); + + public static void main(final String[] args) throws Exception { + final Thread t1 = new Thread(new ThreadGroup("firstGroup"), () -> { + app1 = SunToolkit.createNewAppContext(); + test(true); + }); + final Thread t2 = new Thread(new ThreadGroup("secondGroup"), () -> { + app2 = SunToolkit.createNewAppContext(); + test(false); + }); + + t1.start(); + t2.start(); + t1.join(); + t2.join(); + app1.dispose(); + app2.dispose(); + } + + private static void test(final boolean lock) { + for (final UIManager.LookAndFeelInfo laf : getInstalledLookAndFeels()) { + try { + SwingUtilities.invokeAndWait(() -> setLookAndFeel(laf)); + barrier.await(); + SwingUtilities.invokeAndWait(() -> slam(lock)); + barrier.await(); + } catch (final Exception e) { + throw new RuntimeException(e); + } + } + } + + private static void slam(final boolean lock) { + JButton button = new JButton("HI"); + button.setSize(100, 100); + BufferedImage image = new BufferedImage(100, 100, + BufferedImage.TYPE_INT_RGB); + for (int i = 0; i < LOOP_COUNT; i++) { + Graphics g = image.getGraphics(); + if (lock) { + synchronized (button.getTreeLock()) { + button.paint(g); + } + } else { + button.paint(g); + } + g.dispose(); + } + } + + private static void setLookAndFeel(final UIManager.LookAndFeelInfo laf) { + try { + UIManager.setLookAndFeel(laf.getClassName()); + System.out.println("LookAndFeel: " + laf.getClassName()); + } catch (ClassNotFoundException | InstantiationException | + UnsupportedLookAndFeelException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } +} diff --git a/jdk/test/javax/swing/plaf/nimbus/8041642/ScrollBarThumbVisibleTest.java b/jdk/test/javax/swing/plaf/nimbus/8041642/ScrollBarThumbVisibleTest.java new file mode 100644 index 00000000000..4d147ac8bd5 --- /dev/null +++ b/jdk/test/javax/swing/plaf/nimbus/8041642/ScrollBarThumbVisibleTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* @test + @bug 8134828 + @summary Scrollbar thumb disappears with Nimbus L&F + @author Semyon Sadetsky +*/ + +import javax.swing.*; +import java.awt.*; + +public class ScrollBarThumbVisibleTest +{ + private static JFrame frame; + private static Point point; + private static JScrollBar bar; + + public static void main(String[] args) throws Exception { + for (UIManager.LookAndFeelInfo info : UIManager + .getInstalledLookAndFeels()) { + if ("Nimbus".equals(info.getName())) { + try { + UIManager.setLookAndFeel(info.getClassName()); + } catch (Exception ex) { + } + break; + } + } + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + frame = new JFrame(); + frame.setUndecorated(true); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setup(frame); + } + }); + final Robot robot = new Robot(); + robot.delay(200); + robot.waitForIdle(); + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + point = bar.getLocationOnScreen(); + } + }); + Color color1 = robot.getPixelColor(point.x + 48, point.y + 55); + Color color2 = robot.getPixelColor(point.x + 48, point.y + 125); + System.out.println(color1); + System.out.println(color2); + if (color1.equals(color2)) { + throw new RuntimeException("Thump is not visible"); + } + } finally { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + frame.dispose(); + } + }); + } + System.out.println("ok"); + } + + static void setup(JFrame frame) { + bar = new JScrollBar(Adjustable.VERTICAL, 500, 0, 0, 1000); + frame.getContentPane().add(bar); + frame.setSize(50, 250); + frame.setLocation(100, 100); + frame.setVisible(true); + } +} diff --git a/jdk/test/jdk/security/jarsigner/Function.java b/jdk/test/jdk/security/jarsigner/Function.java new file mode 100644 index 00000000000..eead632be87 --- /dev/null +++ b/jdk/test/jdk/security/jarsigner/Function.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8056174 + * @summary test the functions of JarSigner API + * @modules java.base/sun.security.tools.keytool + * jdk.jartool + */ + +import jdk.security.jarsigner.JarSigner; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.KeyStore; +import java.security.MessageDigest; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +public class Function { + public static void main(String[] args) throws Exception { + + try (FileOutputStream fout =new FileOutputStream("src.zip"); + ZipOutputStream zout = new ZipOutputStream(fout)) { + zout.putNextEntry(new ZipEntry("x")); + zout.write(new byte[10]); + zout.closeEntry(); + } + + sun.security.tools.keytool.Main.main( + ("-storetype jks -keystore ks -storepass changeit" + + " -keypass changeit -dname" + + " CN=RSA -alias r -genkeypair -keyalg rsa").split(" ")); + + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(new FileInputStream("ks"), "changeit".toCharArray()); + PrivateKey key = (PrivateKey)ks.getKey("r", "changeit".toCharArray()); + Certificate cert = ks.getCertificate("r"); + JarSigner.Builder jsb = new JarSigner.Builder(key, + CertificateFactory.getInstance("X.509").generateCertPath( + Collections.singletonList(cert))); + + jsb.digestAlgorithm("SHA1"); + jsb.signatureAlgorithm("SHA1withRSA"); + + AtomicInteger counter = new AtomicInteger(0); + StringBuilder sb = new StringBuilder(); + jsb.eventHandler( + (a, f)->{ + counter.incrementAndGet(); + sb.append(a).append(' ').append(f).append('\n'); + }); + + OutputStream blackHole = new OutputStream() { + @Override + public void write(int b) throws IOException { } + }; + + try (ZipFile src = new ZipFile("src.zip")) { + jsb.build().sign(src, blackHole); + } + + if (counter.get() != 4) { + throw new Exception("Event number is " + counter.get() + + ":\n" + sb.toString()); + } + + // Provider test. + Provider p = new MyProvider(); + jsb.digestAlgorithm("Five", p); + jsb.signatureAlgorithm("SHA1WithRSA", p); + try (ZipFile src = new ZipFile("src.zip"); + FileOutputStream out = new FileOutputStream("out.jar")) { + jsb.build().sign(src, out); + } + + try (JarFile signed = new JarFile("out.jar")) { + Manifest man = signed.getManifest(); + assertTrue(man.getAttributes("x").getValue("Five-Digest").equals("FAKE")); + + Manifest sf = new Manifest(signed.getInputStream( + signed.getJarEntry("META-INF/SIGNER.SF"))); + assertTrue(sf.getMainAttributes().getValue("Five-Digest-Manifest") + .equals("FAKE")); + assertTrue(sf.getAttributes("x").getValue("Five-Digest").equals("FAKE")); + + try (InputStream sig = signed.getInputStream( + signed.getJarEntry("META-INF/SIGNER.RSA"))) { + byte[] data = sig.readAllBytes(); + assertTrue(Arrays.equals( + Arrays.copyOfRange(data, data.length-8, data.length), + "FAKEFAKE".getBytes())); + } + } + } + + private static void assertTrue(boolean v) { + if (!v) { + throw new AssertionError(); + } + } + + public static class MyProvider extends Provider { + MyProvider() { + super("MY", 1.0d, null); + put("MessageDigest.Five", Five.class.getName()); + put("Signature.SHA1WithRSA", SHA1WithRSA.class.getName()); + } + } + + // "Five" is a MessageDigest always returns the same value + public static class Five extends MessageDigest { + static final byte[] dig = {0x14, 0x02, (byte)0x84}; //base64 -> FAKE + public Five() { super("Five"); } + protected void engineUpdate(byte input) { } + protected void engineUpdate(byte[] input, int offset, int len) { } + protected byte[] engineDigest() { return dig; } + protected void engineReset() { } + } + + // This fake "SHA1withRSA" is a Signature always returns the same value. + // An existing name must be used otherwise PKCS7 does not which OID to use. + public static class SHA1WithRSA extends Signature { + static final byte[] sig = "FAKEFAKE".getBytes(); + public SHA1WithRSA() { super("SHA1WithRSA"); } + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException { } + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException { } + protected void engineUpdate(byte b) throws SignatureException { } + protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException { } + protected byte[] engineSign() throws SignatureException { return sig; } + protected boolean engineVerify(byte[] sigBytes) + throws SignatureException { + return Arrays.equals(sigBytes, sig); + } + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { } + protected Object engineGetParameter(String param) + throws InvalidParameterException { return null; } + } +} diff --git a/jdk/test/jdk/security/jarsigner/Spec.java b/jdk/test/jdk/security/jarsigner/Spec.java new file mode 100644 index 00000000000..a4853bf08c2 --- /dev/null +++ b/jdk/test/jdk/security/jarsigner/Spec.java @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2015, 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. + */ + +/** + * @test + * @bug 8056174 + * @summary Make sure JarSigner impl conforms to spec + * @library /lib/testlibrary + * @modules java.base/sun.security.tools.keytool + * java.base/sun.security.provider.certpath + * jdk.jartool + */ + +import com.sun.jarsigner.ContentSigner; +import com.sun.jarsigner.ContentSignerParameters; +import jdk.security.jarsigner.JarSigner; +import jdk.testlibrary.JarUtils; +import sun.security.provider.certpath.X509CertPath; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.*; +import java.security.cert.CertPath; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.Arrays; +import java.util.Collections; +import java.util.function.BiConsumer; + +public class Spec { + + public static void main(String[] args) throws Exception { + + // Prepares raw file + Files.write(Paths.get("a"), "a".getBytes()); + + // Pack + JarUtils.createJar("a.jar", "a"); + + // Prepare a keystore + sun.security.tools.keytool.Main.main( + ("-keystore ks -storepass changeit -keypass changeit -dname" + + " CN=RSA -alias r -genkeypair -keyalg rsa").split(" ")); + sun.security.tools.keytool.Main.main( + ("-keystore ks -storepass changeit -keypass changeit -dname" + + " CN=DSA -alias d -genkeypair -keyalg dsa").split(" ")); + + char[] pass = "changeit".toCharArray(); + + KeyStore ks = KeyStore.getInstance( + new File("ks"), pass); + PrivateKey pkr = (PrivateKey)ks.getKey("r", pass); + PrivateKey pkd = (PrivateKey)ks.getKey("d", pass); + CertPath cp = CertificateFactory.getInstance("X.509") + .generateCertPath(Arrays.asList(ks.getCertificateChain("r"))); + + Provider sun = Security.getProvider("SUN"); + + // throws + npe(()->new JarSigner.Builder(null)); + npe(()->new JarSigner.Builder(null, cp)); + iae(()->new JarSigner.Builder( + pkr, new X509CertPath(Collections.emptyList()))); + iae(()->new JarSigner.Builder(pkd, cp)); // unmatched certs alg + + JarSigner.Builder b1 = new JarSigner.Builder(pkr, cp); + + npe(()->b1.digestAlgorithm(null)); + nsae(()->b1.digestAlgorithm("HAHA")); + b1.digestAlgorithm("SHA-256"); + + npe(()->b1.digestAlgorithm("SHA-256", null)); + npe(()->b1.digestAlgorithm(null, sun)); + nsae(()->b1.digestAlgorithm("HAHA", sun)); + b1.digestAlgorithm("SHA-256", sun); + + npe(()->b1.signatureAlgorithm(null)); + nsae(()->b1.signatureAlgorithm("HAHAwithHEHE")); + iae(()->b1.signatureAlgorithm("SHA256withECDSA")); + + npe(()->b1.signatureAlgorithm(null, sun)); + npe(()->b1.signatureAlgorithm("SHA256withRSA", null)); + nsae(()->b1.signatureAlgorithm("HAHAwithHEHE", sun)); + iae(()->b1.signatureAlgorithm("SHA256withDSA", sun)); + + npe(()->b1.tsa(null)); + + npe(()->b1.signerName(null)); + iae(()->b1.signerName("")); + iae(()->b1.signerName("123456789")); + iae(()->b1.signerName("a+b")); + + npe(()->b1.setProperty(null, "")); + uoe(()->b1.setProperty("what", "")); + npe(()->b1.setProperty("tsadigestalg", null)); + iae(()->b1.setProperty("tsadigestalg", "HAHA")); + npe(()->b1.setProperty("tsapolicyid", null)); + npe(()->b1.setProperty("internalsf", null)); + iae(()->b1.setProperty("internalsf", "Hello")); + npe(()->b1.setProperty("sectionsonly", null)); + iae(()->b1.setProperty("sectionsonly", "OK")); + npe(()->b1.setProperty("altsigner", null)); + npe(()->b1.eventHandler(null)); + + // default values + JarSigner.Builder b2 = new JarSigner.Builder(pkr, cp); + JarSigner js2 = b2.build(); + + assertTrue(js2.getDigestAlgorithm().equals( + JarSigner.Builder.getDefaultDigestAlgorithm())); + assertTrue(js2.getSignatureAlgorithm().equals( + JarSigner.Builder.getDefaultSignatureAlgorithm(pkr))); + assertTrue(js2.getTsa() == null); + assertTrue(js2.getSignerName().equals("SIGNER")); + assertTrue(js2.getProperty("tsadigestalg").equals( + JarSigner.Builder.getDefaultDigestAlgorithm())); + assertTrue(js2.getProperty("tsapolicyid") == null); + assertTrue(js2.getProperty("internalsf").equals("false")); + assertTrue(js2.getProperty("sectionsonly").equals("false")); + assertTrue(js2.getProperty("altsigner") == null); + uoe(()->js2.getProperty("invalid")); + + // default values + BiConsumer myeh = (a,s)->{}; + URI tsa = new URI("https://tsa.com"); + + JarSigner.Builder b3 = new JarSigner.Builder(pkr, cp) + .digestAlgorithm("SHA-1") + .signatureAlgorithm("SHA1withRSA") + .signerName("Duke") + .tsa(tsa) + .setProperty("tsadigestalg", "SHA-512") + .setProperty("tsapolicyid", "1.2.3.4") + .setProperty("internalsf", "true") + .setProperty("sectionsonly", "true") + .setProperty("altsigner", "MyContentSigner") + .eventHandler(myeh); + JarSigner js3 = b3.build(); + + assertTrue(js3.getDigestAlgorithm().equals("SHA-1")); + assertTrue(js3.getSignatureAlgorithm().equals("SHA1withRSA")); + assertTrue(js3.getTsa().equals(tsa)); + assertTrue(js3.getSignerName().equals("DUKE")); + assertTrue(js3.getProperty("tsadigestalg").equals("SHA-512")); + assertTrue(js3.getProperty("tsapolicyid").equals("1.2.3.4")); + assertTrue(js3.getProperty("internalsf").equals("true")); + assertTrue(js3.getProperty("sectionsonly").equals("true")); + assertTrue(js3.getProperty("altsigner").equals("MyContentSigner")); + assertTrue(js3.getProperty("altsignerpath") == null); + + assertTrue(JarSigner.Builder.getDefaultDigestAlgorithm().equals("SHA-256")); + + // Calculating large DSA and RSA keys are too slow. + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(1024); + assertTrue(JarSigner.Builder + .getDefaultSignatureAlgorithm(kpg.generateKeyPair().getPrivate()) + .equals("SHA256withRSA")); + + kpg = KeyPairGenerator.getInstance("DSA"); + kpg.initialize(1024); + assertTrue(JarSigner.Builder + .getDefaultSignatureAlgorithm(kpg.generateKeyPair().getPrivate()) + .equals("SHA256withDSA")); + + kpg = KeyPairGenerator.getInstance("EC"); + kpg.initialize(192); + assertTrue(JarSigner.Builder + .getDefaultSignatureAlgorithm(kpg.generateKeyPair().getPrivate()) + .equals("SHA256withECDSA")); + kpg.initialize(384); + assertTrue(JarSigner.Builder + .getDefaultSignatureAlgorithm(kpg.generateKeyPair().getPrivate()) + .equals("SHA384withECDSA")); + kpg.initialize(571); + assertTrue(JarSigner.Builder + .getDefaultSignatureAlgorithm(kpg.generateKeyPair().getPrivate()) + .equals("SHA512withECDSA")); + } + + interface RunnableWithException { + void run() throws Exception; + } + + static void uoe(RunnableWithException r) throws Exception { + checkException(r, UnsupportedOperationException.class); + } + + static void nsae(RunnableWithException r) throws Exception { + checkException(r, NoSuchAlgorithmException.class); + } + + static void npe(RunnableWithException r) throws Exception { + checkException(r, NullPointerException.class); + } + + static void iae(RunnableWithException r) throws Exception { + checkException(r, IllegalArgumentException.class); + } + + static void checkException(RunnableWithException r, Class ex) + throws Exception { + try { + r.run(); + } catch (Exception e) { + if (ex.isAssignableFrom(e.getClass())) { + return; + } + throw e; + } + throw new Exception("No exception thrown"); + } + + static void assertTrue(boolean x) throws Exception { + if (!x) throw new Exception("Not true"); + } + + static class MyContentSigner extends ContentSigner { + @Override + public byte[] generateSignedData( + ContentSignerParameters parameters, + boolean omitContent, + boolean applyTimestamp) throws NoSuchAlgorithmException, + CertificateException, IOException { + return new byte[0]; + } + } +} diff --git a/jdk/test/lib/testlibrary/jdk/testlibrary/OutputAnalyzer.java b/jdk/test/lib/testlibrary/jdk/testlibrary/OutputAnalyzer.java index 88819e4d594..523f61c58e3 100644 --- a/jdk/test/lib/testlibrary/jdk/testlibrary/OutputAnalyzer.java +++ b/jdk/test/lib/testlibrary/jdk/testlibrary/OutputAnalyzer.java @@ -38,14 +38,14 @@ public final class OutputAnalyzer { private final OutputBuffer output; private final String stdout; private final String stderr; - private final int exitValue; + private final int exitValue; // useless now. output contains exit value. /** * Create an OutputAnalyzer, a utility class for verifying output and exit * value from a Process. *

    * OutputAnalyzer should never be instantiated directly - - * use {@linkplain ProcessTools#executeProcess(p)} instead + * use {@linkplain ProcessTools#executeProcess(ProcessBuilder)} instead * * @param process * Process to analyze @@ -93,13 +93,14 @@ public final class OutputAnalyzer { * @throws RuntimeException * If the string was not found */ - public void shouldContain(String expectedString) { + public OutputAnalyzer shouldContain(String expectedString) { if (!getStdout().contains(expectedString) && !getStderr().contains(expectedString)) { reportDiagnosticSummary(); throw new RuntimeException("'" + expectedString + "' missing from stdout/stderr \n"); } + return this; } /** @@ -110,12 +111,13 @@ public final class OutputAnalyzer { * @throws RuntimeException * If the string was not found */ - public void stdoutShouldContain(String expectedString) { + public OutputAnalyzer stdoutShouldContain(String expectedString) { if (!getStdout().contains(expectedString)) { reportDiagnosticSummary(); throw new RuntimeException("'" + expectedString + "' missing from stdout \n"); } + return this; } /** @@ -126,24 +128,25 @@ public final class OutputAnalyzer { * @throws RuntimeException * If the string was not found */ - public void stderrShouldContain(String expectedString) { + public OutputAnalyzer stderrShouldContain(String expectedString) { if (!getStderr().contains(expectedString)) { reportDiagnosticSummary(); throw new RuntimeException("'" + expectedString + "' missing from stderr \n"); } + return this; } /** * Verify that the stdout and stderr contents of output buffer does not * contain the string * - * @param expectedString + * @param notExpectedString * String that the buffer should not contain * @throws RuntimeException * If the string was found */ - public void shouldNotContain(String notExpectedString) { + public OutputAnalyzer shouldNotContain(String notExpectedString) { if (getStdout().contains(notExpectedString)) { reportDiagnosticSummary(); throw new RuntimeException("'" + notExpectedString @@ -154,40 +157,43 @@ public final class OutputAnalyzer { throw new RuntimeException("'" + notExpectedString + "' found in stderr \n"); } + return this; } /** * Verify that the stdout contents of output buffer does not contain the * string * - * @param expectedString + * @param notExpectedString * String that the buffer should not contain * @throws RuntimeException * If the string was found */ - public void stdoutShouldNotContain(String notExpectedString) { + public OutputAnalyzer stdoutShouldNotContain(String notExpectedString) { if (getStdout().contains(notExpectedString)) { reportDiagnosticSummary(); throw new RuntimeException("'" + notExpectedString + "' found in stdout \n"); } + return this; } /** * Verify that the stderr contents of output buffer does not contain the * string * - * @param expectedString + * @param notExpectedString * String that the buffer should not contain * @throws RuntimeException * If the string was found */ - public void stderrShouldNotContain(String notExpectedString) { + public OutputAnalyzer stderrShouldNotContain(String notExpectedString) { if (getStderr().contains(notExpectedString)) { reportDiagnosticSummary(); throw new RuntimeException("'" + notExpectedString + "' found in stderr \n"); } + return this; } /** @@ -198,7 +204,7 @@ public final class OutputAnalyzer { * @throws RuntimeException * If the pattern was not found */ - public void shouldMatch(String pattern) { + public OutputAnalyzer shouldMatch(String pattern) { Matcher stdoutMatcher = Pattern.compile(pattern, Pattern.MULTILINE) .matcher(getStdout()); Matcher stderrMatcher = Pattern.compile(pattern, Pattern.MULTILINE) @@ -208,6 +214,7 @@ public final class OutputAnalyzer { throw new RuntimeException("'" + pattern + "' missing from stdout/stderr \n"); } + return this; } /** @@ -217,7 +224,7 @@ public final class OutputAnalyzer { * @throws RuntimeException * If the pattern was not found */ - public void stdoutShouldMatch(String pattern) { + public OutputAnalyzer stdoutShouldMatch(String pattern) { Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher( getStdout()); if (!matcher.find()) { @@ -225,6 +232,7 @@ public final class OutputAnalyzer { throw new RuntimeException("'" + pattern + "' missing from stdout \n"); } + return this; } /** @@ -234,7 +242,7 @@ public final class OutputAnalyzer { * @throws RuntimeException * If the pattern was not found */ - public void stderrShouldMatch(String pattern) { + public OutputAnalyzer stderrShouldMatch(String pattern) { Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher( getStderr()); if (!matcher.find()) { @@ -242,6 +250,7 @@ public final class OutputAnalyzer { throw new RuntimeException("'" + pattern + "' missing from stderr \n"); } + return this; } /** @@ -252,7 +261,7 @@ public final class OutputAnalyzer { * @throws RuntimeException * If the pattern was found */ - public void shouldNotMatch(String pattern) { + public OutputAnalyzer shouldNotMatch(String pattern) { Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher( getStdout()); if (matcher.find()) { @@ -266,6 +275,7 @@ public final class OutputAnalyzer { throw new RuntimeException("'" + pattern + "' found in stderr: '" + matcher.group() + "' \n"); } + return this; } /** @@ -276,13 +286,14 @@ public final class OutputAnalyzer { * @throws RuntimeException * If the pattern was found */ - public void stdoutShouldNotMatch(String pattern) { + public OutputAnalyzer stdoutShouldNotMatch(String pattern) { Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher( getStdout()); if (matcher.find()) { reportDiagnosticSummary(); throw new RuntimeException("'" + pattern + "' found in stdout \n"); } + return this; } /** @@ -293,13 +304,14 @@ public final class OutputAnalyzer { * @throws RuntimeException * If the pattern was found */ - public void stderrShouldNotMatch(String pattern) { + public OutputAnalyzer stderrShouldNotMatch(String pattern) { Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher( getStderr()); if (matcher.find()) { reportDiagnosticSummary(); throw new RuntimeException("'" + pattern + "' found in stderr \n"); } + return this; } /** @@ -347,12 +359,13 @@ public final class OutputAnalyzer { * If the exit value from the process did not match the expected * value */ - public void shouldHaveExitValue(int expectedExitValue) { + public OutputAnalyzer shouldHaveExitValue(int expectedExitValue) { if (getExitValue() != expectedExitValue) { reportDiagnosticSummary(); throw new RuntimeException("Expected to get exit value of [" + expectedExitValue + "]\n"); } + return this; } /** @@ -360,11 +373,12 @@ public final class OutputAnalyzer { * - standard input produced by the process under test - standard output - * exit code Note: the command line is printed by the ProcessTools */ - private void reportDiagnosticSummary() { + private OutputAnalyzer reportDiagnosticSummary() { String msg = " stdout: [" + getStdout() + "];\n" + " stderr: [" + getStderr() + "]\n" + " exitValue = " + getExitValue() + "\n"; System.err.println(msg); + return this; } /** diff --git a/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java b/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java index bcf829254a0..b861cf60778 100644 --- a/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java +++ b/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java @@ -365,11 +365,31 @@ public final class ProcessTools { * @return The {@linkplain OutputAnalyzer} instance wrapping the process. */ public static OutputAnalyzer executeProcess(ProcessBuilder pb) throws Exception { + return executeProcess(pb, null); + } + + /** + * Executes a process, pipe some text into its STDIN, waits for it + * to finish and returns the process output. The process will have exited + * before this method returns. + * @param pb The ProcessBuilder to execute. + * @param input The text to pipe into STDIN. Can be null. + * @return The {@linkplain OutputAnalyzer} instance wrapping the process. + */ + public static OutputAnalyzer executeProcess(ProcessBuilder pb, String input) + throws Exception { OutputAnalyzer output = null; Process p = null; boolean failed = false; try { p = pb.start(); + if (input != null) { + try (OutputStream os = p.getOutputStream(); + PrintStream ps = new PrintStream(os)) { + ps.print(input); + ps.flush(); + } + } output = new OutputAnalyzer(p); p.waitFor(); diff --git a/jdk/test/lib/testlibrary/jdk/testlibrary/SerializationUtils.java b/jdk/test/lib/testlibrary/jdk/testlibrary/SerializationUtils.java new file mode 100644 index 00000000000..3dbc66692db --- /dev/null +++ b/jdk/test/lib/testlibrary/jdk/testlibrary/SerializationUtils.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015, 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. + */ + +package jdk.testlibrary; + +import java.io.*; + +/** + * Common library for various test serialization utility functions. + */ +public final class SerializationUtils { + /* + * Serialize an object into byte array. + */ + public static byte[] serialize(Object obj) throws Exception { + try (ByteArrayOutputStream bs = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bs);) { + out.writeObject(obj); + return bs.toByteArray(); + } + } + + /* + * Deserialize an object from byte array. + */ + public static Object deserialize(byte[] ba) throws Exception { + try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(ba));) { + return in.readObject(); + } + } +} diff --git a/jdk/test/sun/invoke/util/WrapperTest.java b/jdk/test/sun/invoke/util/WrapperTest.java new file mode 100644 index 00000000000..4ca3e98ef42 --- /dev/null +++ b/jdk/test/sun/invoke/util/WrapperTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2015, 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. + */ + +package test.sun.invoke.util; + +import sun.invoke.util.ValueConversions; +import sun.invoke.util.Wrapper; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.MethodHandle; +import java.io.Serializable; +import java.util.Arrays; +import org.junit.Test; +import static org.junit.Assert.*; + +/* @test + * @summary unit tests to assert Wrapper zero identities and conversion behave correctly + * @modules java.base/sun.invoke.util + * @compile -XDignore.symbol.file WrapperTest.java + * @run junit test.sun.invoke.util.WrapperTest + */ +public class WrapperTest { + + @Test + public void testShortZeroConversion() throws Throwable { + MethodHandle h1 = MethodHandles.constant(Short.class, (short)42); + MethodHandle h2 = h1.asType(MethodType.methodType(void.class)); // drop 42 + MethodHandle h3 = h2.asType(MethodType.methodType(short.class)); // add 0 + MethodHandle h4 = h3.asType(MethodType.methodType(Object.class)); // box + + Object x = h4.invokeExact(); + assertEquals(x, (short)0); + assertTrue(x == Short.valueOf((short)0)); + assertTrue(x == Wrapper.SHORT.zero()); + } + + @Test + public void testIntZeroConversion() throws Throwable { + MethodHandle h1 = MethodHandles.constant(Integer.class, 42); + MethodHandle h2 = h1.asType(MethodType.methodType(void.class)); // drop 42 + MethodHandle h3 = h2.asType(MethodType.methodType(int.class)); // add 0 + MethodHandle h4 = h3.asType(MethodType.methodType(Object.class)); // box + + Object x = h4.invokeExact(); + assertEquals(x, 0); + assertTrue(x == Integer.valueOf(0)); + assertTrue(x == Wrapper.INT.zero()); + } + + @Test + public void testLongZeroConversion() throws Throwable { + MethodHandle h1 = MethodHandles.constant(Long.class, 42L); + MethodHandle h2 = h1.asType(MethodType.methodType(void.class)); // drop 42 + MethodHandle h3 = h2.asType(MethodType.methodType(long.class)); // add 0 + MethodHandle h4 = h3.asType(MethodType.methodType(Object.class)); // box + + Object x = h4.invokeExact(); + assertEquals(x, 0L); + assertTrue(x == Long.valueOf(0)); + assertTrue(x == Wrapper.LONG.zero()); + } + + @Test + public void testByteZeroConversion() throws Throwable { + MethodHandle h1 = MethodHandles.constant(Byte.class, (byte)42); + MethodHandle h2 = h1.asType(MethodType.methodType(void.class)); // drop 42 + MethodHandle h3 = h2.asType(MethodType.methodType(byte.class)); // add 0 + MethodHandle h4 = h3.asType(MethodType.methodType(Object.class)); // box + + Object x = h4.invokeExact(); + assertEquals(x, (byte)0); + assertTrue(x == Byte.valueOf((byte)0)); + assertTrue(x == Wrapper.BYTE.zero()); + } + + @Test + public void testCharacterZeroConversion() throws Throwable { + MethodHandle h1 = MethodHandles.constant(Character.class, (char)42); + MethodHandle h2 = h1.asType(MethodType.methodType(void.class)); // drop 42 + MethodHandle h3 = h2.asType(MethodType.methodType(char.class)); // add 0 + MethodHandle h4 = h3.asType(MethodType.methodType(Object.class)); // box + + Object x = h4.invokeExact(); + assertEquals(x, (char)0); + assertTrue(x == Character.valueOf((char)0)); + assertTrue(x == Wrapper.CHAR.zero()); + } +} diff --git a/jdk/test/sun/jvmstat/monitor/MonitoredVm/TestPollingInterval.java b/jdk/test/sun/jvmstat/monitor/MonitoredVm/TestPollingInterval.java index abed0385ebe..826edfb4e58 100644 --- a/jdk/test/sun/jvmstat/monitor/MonitoredVm/TestPollingInterval.java +++ b/jdk/test/sun/jvmstat/monitor/MonitoredVm/TestPollingInterval.java @@ -42,7 +42,7 @@ import sun.jvmstat.monitor.VmIdentifier; * @summary setInterval() for local MonitoredHost and local MonitoredVm * @modules jdk.jvmstat/sun.jvmstat.monitor * @library /lib/testlibrary - * @library /../../test/lib/share/classes + * @library /test/lib/share/classes * @build jdk.testlibrary.* * @build jdk.test.lib.apps.* * @run main TestPollingInterval diff --git a/jdk/test/sun/nio/cs/TestStringCoding.java b/jdk/test/sun/nio/cs/TestStringCoding.java index f9f7021dca3..4dd85f490a3 100644 --- a/jdk/test/sun/nio/cs/TestStringCoding.java +++ b/jdk/test/sun/nio/cs/TestStringCoding.java @@ -22,7 +22,7 @@ */ /* @test - * @bug 6636323 6636319 7040220 7096080 7183053 8080248 + * @bug 6636323 6636319 7040220 7096080 7183053 8080248 8054307 * @summary Test if StringCoding and NIO result have the same de/encoding result * @modules java.base/sun.nio.cs * @run main/othervm/timeout=2000 TestStringCoding @@ -36,41 +36,61 @@ import java.nio.charset.*; public class TestStringCoding { public static void main(String[] args) throws Throwable { + // full bmp first + char[] bmp = new char[0x10000]; + for (int i = 0; i < 0x10000; i++) { + bmp[i] = (char)i; + } + char[] latin = Arrays.copyOf(bmp, 0x100); + char[] ascii = Arrays.copyOf(bmp, 0x80); + + byte[] latinBA = new byte[0x100]; + for (int i = 0; i < 0x100; i++) { + latinBA[i] = (byte)i; + } + byte[] asciiBA = Arrays.copyOf(latinBA, 0x80); + for (Boolean hasSM: new boolean[] { false, true }) { - if (hasSM) + if (hasSM) { System.setSecurityManager(new PermissiveSecurityManger()); + } for (Charset cs: Charset.availableCharsets().values()) { if ("ISO-2022-CN".equals(cs.name()) || "x-COMPOUND_TEXT".equals(cs.name()) || "x-JISAutoDetect".equals(cs.name())) continue; System.out.printf("Testing(sm=%b) " + cs.name() + "....", hasSM); - // full bmp first - char[] bmpCA = new char[0x10000]; - for (int i = 0; i < 0x10000; i++) { - bmpCA[i] = (char)i; - } - byte[] sbBA = new byte[0x100]; - for (int i = 0; i < 0x100; i++) { - sbBA[i] = (byte)i; - } - test(cs, bmpCA, sbBA); + + testNewString(cs, testGetBytes(cs, new String(bmp))); + testNewString(cs, testGetBytes(cs, new String(latin))); + testNewString(cs, testGetBytes(cs, new String(ascii))); + testGetBytes(cs, testNewString(cs, latinBA)); + testGetBytes(cs, testNewString(cs, asciiBA)); + // "randomed" sizes Random rnd = new Random(); for (int i = 0; i < 10; i++) { - int clen = rnd.nextInt(0x10000); - int blen = rnd.nextInt(0x100); //System.out.printf(" blen=%d, clen=%d%n", blen, clen); - test(cs, Arrays.copyOf(bmpCA, clen), Arrays.copyOf(sbBA, blen)); + char[] bmp0 = Arrays.copyOf(bmp, rnd.nextInt(0x10000)); + testNewString(cs, testGetBytes(cs, new String(bmp0))); //add a pair of surrogates - int pos = clen / 2; - if ((pos + 1) < blen) { - bmpCA[pos] = '\uD800'; - bmpCA[pos+1] = '\uDC00'; + int pos = bmp0.length / 2; + if ((pos + 1) < bmp0.length) { + bmp0[pos] = '\uD800'; + bmp0[pos+1] = '\uDC00'; } - test(cs, Arrays.copyOf(bmpCA, clen), Arrays.copyOf(sbBA, blen)); - } + testNewString(cs, testGetBytes(cs, new String(bmp0))); + char[] latin0 = Arrays.copyOf(latin, rnd.nextInt(0x100)); + char[] ascii0 = Arrays.copyOf(ascii, rnd.nextInt(0x80)); + byte[] latinBA0 = Arrays.copyOf(latinBA, rnd.nextInt(0x100)); + byte[] asciiBA0 = Arrays.copyOf(asciiBA, rnd.nextInt(0x80)); + testNewString(cs, testGetBytes(cs, new String(latin0))); + testNewString(cs, testGetBytes(cs, new String(ascii0))); + testGetBytes(cs, testNewString(cs, latinBA0)); + testGetBytes(cs, testNewString(cs, asciiBA0)); + } + testSurrogates(cs); testMixed(cs); System.out.println("done!"); } @@ -109,8 +129,9 @@ public class TestStringCoding { //getBytes(cs); bmpBA = bmpStr.getBytes(cs); - if (!Arrays.equals(bmpBA, baNIO)) + if (!Arrays.equals(bmpBA, baNIO)) { throw new RuntimeException("getBytes(cs) failed -> " + cs.name()); + } //new String(csn); String strSC = new String(bmpBA, cs.name()); @@ -118,49 +139,61 @@ public class TestStringCoding { if(!strNIO.equals(strSC)) { throw new RuntimeException("new String(csn) failed -> " + cs.name()); } - //new String(cs); strSC = new String(bmpBA, cs); - if (!strNIO.equals(strSC)) + if (!strNIO.equals(strSC)) { throw new RuntimeException("new String(cs) failed -> " + cs.name()); - + } } - static void test(Charset cs, char[] bmpCA, byte[] sbBA) throws Throwable { - String bmpStr = new String(bmpCA); - CharsetDecoder dec = cs.newDecoder() - .onMalformedInput(CodingErrorAction.REPLACE) - .onUnmappableCharacter(CodingErrorAction.REPLACE); + static byte[] getBytes(CharsetEncoder enc, String str) throws Throwable { + ByteBuffer bf = enc.reset().encode(CharBuffer.wrap(str.toCharArray())); + byte[] ba = new byte[bf.limit()]; + bf.get(ba, 0, ba.length); + return ba; + } + + static byte[] testGetBytes(Charset cs, String str) throws Throwable { CharsetEncoder enc = cs.newEncoder() .onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE); - //getBytes(csn); - byte[] baSC = bmpStr.getBytes(cs.name()); - ByteBuffer bf = enc.reset().encode(CharBuffer.wrap(bmpCA)); - byte[] baNIO = new byte[bf.limit()]; - bf.get(baNIO, 0, baNIO.length); - if (!Arrays.equals(baSC, baNIO)) + byte[] baSC = str.getBytes(cs.name()); + byte[] baNIO = getBytes(enc, str); + if (!Arrays.equals(baSC, baNIO)) { throw new RuntimeException("getBytes(csn) failed -> " + cs.name()); - + } //getBytes(cs); - baSC = bmpStr.getBytes(cs); - if (!Arrays.equals(baSC, baNIO)) + baSC = str.getBytes(cs); + if (!Arrays.equals(baSC, baNIO)) { throw new RuntimeException("getBytes(cs) failed -> " + cs.name()); + } + return baSC; + } + static String testNewString(Charset cs, byte[] ba) throws Throwable { + CharsetDecoder dec = cs.newDecoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); //new String(csn); - String strSC = new String(sbBA, cs.name()); - String strNIO = dec.reset().decode(ByteBuffer.wrap(sbBA)).toString(); - - if(!strNIO.equals(strSC)) + String strSC = new String(ba, cs.name()); + String strNIO = dec.reset().decode(ByteBuffer.wrap(ba)).toString(); + if(!strNIO.equals(strSC)) { throw new RuntimeException("new String(csn) failed -> " + cs.name()); - + } //new String(cs); - strSC = new String(sbBA, cs); - if (!strNIO.equals(strSC)) - throw new RuntimeException("new String(cs) failed -> " + cs.name()); + strSC = new String(ba, cs); + if (!strNIO.equals(strSC)) { + throw new RuntimeException("new String(cs)/bmp failed -> " + cs.name()); + } + return strSC; + } + static void testSurrogates(Charset cs) throws Throwable { //encode unmappable surrogates + CharsetEncoder enc = cs.newEncoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); if (enc instanceof sun.nio.cs.ArrayEncoder && cs.contains(Charset.forName("ASCII"))) { if (cs.name().equals("UTF-8") || // utf8 handles surrogates diff --git a/jdk/test/sun/nio/cs/TestStringCodingUTF8.java b/jdk/test/sun/nio/cs/TestStringCodingUTF8.java index b936838ea13..ad07f19a6a3 100644 --- a/jdk/test/sun/nio/cs/TestStringCodingUTF8.java +++ b/jdk/test/sun/nio/cs/TestStringCodingUTF8.java @@ -22,7 +22,7 @@ */ /* @test - @bug 7040220 + @bug 7040220 8054307 @summary Test if StringCoding and NIO result have the same de/encoding result for UTF-8 * @run main/othervm/timeout=2000 TestStringCodingUTF8 * @key randomness @@ -50,6 +50,18 @@ public class TestStringCodingUTF8 { } test(cs, bmp, 0, bmp.length); + char[] ascii = new char[0x80]; + for (int i = 0; i < 0x80; i++) { + ascii[i] = (char)i; + } + test(cs, ascii, 0, ascii.length); + + char[] latin1 = new char[0x100]; + for (int i = 0; i < 0x100; i++) { + latin1[i] = (char)i; + } + test(cs, latin1, 0, latin1.length); + ArrayList list = new ArrayList<>(0x20000); for (int i = 0; i < 0x20000; i++) { list.add(i, i); diff --git a/jdk/test/sun/security/jca/PreferredProviderNegativeTest.java b/jdk/test/sun/security/jca/PreferredProviderNegativeTest.java new file mode 100644 index 00000000000..5d69d998eb9 --- /dev/null +++ b/jdk/test/sun/security/jca/PreferredProviderNegativeTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2015, 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. + */ + +/** + * @test + * @bug 8076359 8133151 + * @summary Test for jdk.security.provider.preferred security property + * @requires os.name == "SunOS" + * @run main/othervm PreferredProviderNegativeTest preJCESet AES:OracleUcrypto false + * @run main/othervm PreferredProviderNegativeTest preJCESet AES:SunNegative true + * @run main/othervm PreferredProviderNegativeTest afterJCESet AES:SunJGSS + * @run main/othervm PreferredProviderNegativeTest afterJCESet AES:SunECNegative + * @run main/othervm PreferredProviderNegativeTest invalidAlg AESNegative:SunJCE + */ + +import java.security.Security; +import java.security.NoSuchAlgorithmException; +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; + +public class PreferredProviderNegativeTest { + + /* + * Test security property could be set by valid and invalid provider + * before JCE was loaded + */ + public static void preJCESet(String value, boolean negativeProvider) + throws NoSuchAlgorithmException, NoSuchPaddingException { + Security.setProperty("jdk.security.provider.preferred", value); + + if (!Security.getProperty("jdk.security.provider.preferred") + .equals(value)) { + throw new RuntimeException( + "Test Failed:The property wasn't set"); + } + + String[] arrays = value.split(":"); + Cipher cipher = Cipher.getInstance(arrays[0]); + + if (negativeProvider) { + if (cipher.getProvider().getName().equals(arrays[1])) { + throw new RuntimeException( + "Test Failed:The provider shouldn't be set"); + } + } else { + if (!cipher.getProvider().getName().equals(arrays[1])) { + throw new RuntimeException( + "Test Faild:The provider could be set " + + "by valid provider "); + } + } + System.out.println("Test Pass"); + } + + /* + * Test that the setting of the security property after Cipher.getInstance() + * does not influence previously loaded instances + */ + public static void afterJCESet(String value) + throws NoSuchAlgorithmException, NoSuchPaddingException { + String[] arrays = value.split(":"); + Cipher cipher = Cipher.getInstance(arrays[0]); + + Security.setProperty("jdk.security.provider.preferred", value); + if (!cipher.getProvider().getName().equals("SunJCE")) { + throw new RuntimeException( + "Test Failed:The security property can't be updated after JCE load."); + } + System.out.println("Test Pass"); + } + + /* Test the security property with negative algorithm */ + public static void invalidAlg(String value) throws NoSuchPaddingException { + String[] arrays = value.split(":"); + + try { + Security.setProperty("jdk.security.provider.preferred", value); + Cipher.getInstance(arrays[0]); + } catch (NoSuchAlgorithmException e) { + System.out.println("Test Pass:Got NoSuchAlgorithmException as expired"); + return; + } + throw new RuntimeException( + "Test Failed:Expected NoSuchAlgorithmException was not thrown"); + } + + public static void main(String[] args) + throws NoSuchAlgorithmException, NoSuchPaddingException { + boolean negativeProvider; + + if (args.length >= 2) { + switch (args[0]) { + case "preJCESet": + negativeProvider = Boolean.valueOf(args[2]); + PreferredProviderNegativeTest.preJCESet(args[1], negativeProvider); + break; + case "afterJCESet": + PreferredProviderNegativeTest.afterJCESet(args[1]); + break; + case "invalidAlg": + PreferredProviderNegativeTest.invalidAlg(args[1]); + break; + } + } else { + throw new RuntimeException( + "Test Failed:Please pass the correct args"); + } + } +} + diff --git a/jdk/test/sun/security/jca/PreferredProviderTest.java b/jdk/test/sun/security/jca/PreferredProviderTest.java new file mode 100644 index 00000000000..7120817a1b5 --- /dev/null +++ b/jdk/test/sun/security/jca/PreferredProviderTest.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2015, 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. + */ + +/** + * @test + * @bug 8076359 8133151 + * @summary Test the value for new jdk.security.provider.preferred security property + * @requires os.name == "SunOS" + */ + +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.util.Arrays; +import java.util.List; +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; + +public class PreferredProviderTest { + + private static final List SPARC_DATA = Arrays.asList( + new DataTuple("SHA-256", "SUN"), new DataTuple("SHA-384", "SUN"), + new DataTuple("SHA-512", "SUN")); + private static final List X86_DATA = Arrays + .asList(new DataTuple("RSA", "SunRsaSign")); + + public void RunTest(String type) + throws NoSuchAlgorithmException, NoSuchPaddingException { + String preferredProvider = Security + .getProperty("jdk.security.provider.preferred"); + String actualProvider = null; + if (type.equals("sparcv9")) { + if (!preferredProvider.equals( + "AES:SunJCE, SHA-256:SUN, SHA-384:SUN, SHA-512:SUN")) { + throw new RuntimeException( + "Test Failed: wrong jdk.security.provider.preferred " + + "value on solaris-sparcv9"); + } + for (DataTuple dataTuple : SPARC_DATA) { + MessageDigest md = MessageDigest + .getInstance(dataTuple.algorithm); + actualProvider = md.getProvider().getName(); + if (!actualProvider.equals(dataTuple.provider)) { + throw new RuntimeException(String.format( + "Test Failed:Got wrong " + + "provider from Solaris-sparcv9 platform," + + "Expected Provider: %s, Returned Provider: %s", + dataTuple.provider, actualProvider)); + } + } + } else if (type.equals("amd64")) { + if (!preferredProvider.equals("AES:SunJCE, RSA:SunRsaSign")) { + throw new RuntimeException( + "Test Failed: wrong jdk.security.provider.preferred " + + "value on solaris-x86"); + } + for (DataTuple dataTuple : X86_DATA) { + KeyFactory keyFactory = KeyFactory + .getInstance(dataTuple.algorithm); + actualProvider = keyFactory.getProvider().getName(); + if (!actualProvider.equals(dataTuple.provider)) { + throw new RuntimeException(String.format( + "Test Failed:Got wrong " + + "provider from Solaris-x86 platform," + + "Expected Provider: %s, Returned Provider: %s", + dataTuple.provider, actualProvider)); + } + } + } else { + throw new RuntimeException("Test Failed: wrong platform value"); + } + + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + actualProvider = cipher.getProvider().getName(); + if (!actualProvider.equals("SunJCE")) { + throw new RuntimeException(String.format( + "Test Failed:Got wrong provider from Solaris-%s platform, " + + "Expected Provider: SunJCE, Returned Provider: %s", + type, actualProvider)); + } + + MessageDigest md = MessageDigest.getInstance("MD5"); + actualProvider = md.getProvider().getName(); + if (!actualProvider.equals("OracleUcrypto")) { + throw new RuntimeException(String.format( + "Test Failed:Got wrong provider from Solaris-%s platform," + + "Expected Provider: OracleUcrypto, Returned Provider: %s", + type, actualProvider)); + } + } + + private static class DataTuple { + private final String provider; + private final String algorithm; + + private DataTuple(String algorithm, String provider) { + this.algorithm = algorithm; + this.provider = provider; + } + } + + public static void main(String[] args) + throws NoSuchAlgorithmException, NoSuchPaddingException { + + String arch = System.getProperty("os.arch"); + PreferredProviderTest pp = new PreferredProviderTest(); + pp.RunTest(arch); + } +} + diff --git a/jdk/test/sun/security/mscapi/AccessKeyStore.java b/jdk/test/sun/security/mscapi/AccessKeyStore.java index 357984b8aef..81f0dbcb2ca 100644 --- a/jdk/test/sun/security/mscapi/AccessKeyStore.java +++ b/jdk/test/sun/security/mscapi/AccessKeyStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2015, 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 @@ -36,17 +36,6 @@ public class AccessKeyStore { public static void main(String[] args) throws Exception { - // Check if the provider is available - try { - Class.forName("sun.security.mscapi.SunMSCAPI"); - - } catch (Exception e) { - System.out.println( - "The SunMSCAPI provider is not available on this platform: " + - e); - return; - } - // Check that a security manager has been installed if (System.getSecurityManager() == null) { throw new Exception("A security manager has not been installed"); @@ -86,8 +75,8 @@ public class AccessKeyStore { } int i = 0; - for (Enumeration e = keyStore.aliases(); e.hasMoreElements(); ) { - String alias = (String) e.nextElement(); + for (Enumeration e = keyStore.aliases(); e.hasMoreElements(); ) { + String alias = e.nextElement(); displayEntry(keyStore, alias, i++); } } diff --git a/jdk/test/sun/security/mscapi/AccessKeyStore.sh b/jdk/test/sun/security/mscapi/AccessKeyStore.sh index 0998de4821f..e8250027d43 100644 --- a/jdk/test/sun/security/mscapi/AccessKeyStore.sh +++ b/jdk/test/sun/security/mscapi/AccessKeyStore.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2015, 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 @@ -26,6 +26,7 @@ # @test # @bug 6324295 6931562 +# @requires os.family == "windows" # @run shell AccessKeyStore.sh # @summary Confirm that permission must be granted to access keystores. diff --git a/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.java b/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.java index d48f7855c36..ac3c2ffcf37 100644 --- a/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.java +++ b/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2015, 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 @@ -33,16 +33,6 @@ public class IsSunMSCAPIAvailable { public static void main(String[] args) throws Exception { - // Check if the provider is available - try { - Class.forName("sun.security.mscapi.SunMSCAPI"); - - } catch (Exception e) { - System.out.println( - "The SunMSCAPI provider is not available on this platform"); - return; - } - // Dynamically register the SunMSCAPI provider Security.addProvider(new sun.security.mscapi.SunMSCAPI()); @@ -58,7 +48,6 @@ public class IsSunMSCAPIAvailable { /* * Secure Random */ - SecureRandom random = SecureRandom.getInstance("Windows-PRNG", p); System.out.println(" Windows-PRNG is implemented by: " + random.getClass().getName()); diff --git a/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.sh b/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.sh index 6e7ffa302b0..accf1bf4e43 100644 --- a/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.sh +++ b/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2015, 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 @@ -26,6 +26,7 @@ # @test # @bug 6318171 6931562 +# @requires os.family == "windows" # @run shell IsSunMSCAPIAvailable.sh # @summary Basic test of the Microsoft CryptoAPI provider. diff --git a/jdk/test/sun/security/mscapi/IterateWindowsRootStore.java b/jdk/test/sun/security/mscapi/IterateWindowsRootStore.java new file mode 100644 index 00000000000..aea35817371 --- /dev/null +++ b/jdk/test/sun/security/mscapi/IterateWindowsRootStore.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.io.InputStream; +import java.security.KeyStore; +import java.security.Provider; +import java.security.Security; +import java.security.cert.CRL; +import java.security.cert.CRLException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactorySpi; +import java.util.Collection; +import java.util.Enumeration; + +/* + * @test + * @bug 8139436 + * @summary This test validates an iteration over the Windows-ROOT certificate store + * and retrieving all certificates. + * Bug 8139436 reports an issue when 3rd party JCE providers would throw exceptions + * upon creating Certificate objects. + * This would for instance happen when using IAIK 3.15 and Elliptic Curve certificates + * are contained in the Windows-ROOT certificate store. + * The test uses a simple dummy provider which just throws Exceptions in its CertificateFactory. + * To test an external provider, you can use property sun.security.mscapi.testprovider and + * set it to the provider class name which has to be constructible by a constructor without + * arguments. The provider jar has to be added to the classpath. + * E.g. run jtreg with -javaoption:-Dsun.security.mscapi.testprovider=iaik.security.provider.IAIK and + * -cpa: + * + * @requires os.family == "windows" + * @author Christoph Langer + * @run main IterateWindowsRootStore + */ +public class IterateWindowsRootStore { + public static class TestFactory extends CertificateFactorySpi { + @Override + public Certificate engineGenerateCertificate(InputStream inStream) throws CertificateException { + throw new CertificateException("unimplemented"); + } + + @Override + public Collection engineGenerateCertificates(InputStream inStream) throws CertificateException { + throw new CertificateException("unimplemented"); + } + + @Override + public CRL engineGenerateCRL(InputStream inStream) throws CRLException { + throw new CRLException("unimplemented"); + } + + @Override + public Collection engineGenerateCRLs(InputStream inStream) throws CRLException { + throw new CRLException("unimplemented"); + } + } + + public static class TestProvider extends Provider { + private static final long serialVersionUID = 1L; + + public TestProvider() { + super("TestProvider", 0.1, "Test provider for IterateWindowsRootStore"); + + /* + * Certificates + */ + this.put("CertificateFactory.X.509", "IterateWindowsRootStore$TestFactory"); + this.put("Alg.Alias.CertificateFactory.X509", "X.509"); + } + } + + public static void main(String[] args) throws Exception { + // Try to register a JCE provider from property sun.security.mscapi.testprovider in the first slot + // otherwise register a dummy provider which would provoke the issue of bug 8139436 + boolean providerPrepended = false; + String testprovider = System.getProperty("sun.security.mscapi.testprovider"); + if (testprovider != null && !testprovider.isEmpty()) { + try { + System.out.println("Trying to prepend external JCE provider " + testprovider); + Class providerclass = Class.forName(testprovider); + Object provider = providerclass.newInstance(); + Security.insertProviderAt((Provider)provider, 1); + } catch (Exception e) { + System.out.println("Could not load JCE provider " + testprovider +". Exception is:"); + e.printStackTrace(System.out); + } + providerPrepended = true; + System.out.println("Sucessfully prepended JCE provider " + testprovider); + } + if (!providerPrepended) { + System.out.println("Trying to prepend dummy JCE provider"); + Security.insertProviderAt(new TestProvider(), 1); + System.out.println("Sucessfully prepended dummy JCE provider"); + } + + // load Windows-ROOT KeyStore + KeyStore keyStore = KeyStore.getInstance("Windows-ROOT", "SunMSCAPI"); + keyStore.load(null, null); + + // iterate KeyStore + Enumeration aliases = keyStore.aliases(); + while (aliases.hasMoreElements()) { + String alias = aliases.nextElement(); + System.out.print("Reading certificate for alias: " + alias + "..."); + keyStore.getCertificate(alias); + System.out.println(" done."); + } + } +} diff --git a/jdk/test/sun/security/mscapi/KeyStoreCompatibilityMode.java b/jdk/test/sun/security/mscapi/KeyStoreCompatibilityMode.java index 04ce94c782d..b18abca674f 100644 --- a/jdk/test/sun/security/mscapi/KeyStoreCompatibilityMode.java +++ b/jdk/test/sun/security/mscapi/KeyStoreCompatibilityMode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2015, 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 @@ -38,17 +38,6 @@ public class KeyStoreCompatibilityMode { public static void main(String[] args) throws Exception { - // Check if the provider is available - try { - Class.forName("sun.security.mscapi.SunMSCAPI"); - - } catch (Exception e) { - System.out.println( - "The SunMSCAPI provider is not available on this platform: " + - e); - return; - } - if (args.length > 0 && "-disable".equals(args[0])) { mode = false; } else { diff --git a/jdk/test/sun/security/mscapi/KeyStoreCompatibilityMode.sh b/jdk/test/sun/security/mscapi/KeyStoreCompatibilityMode.sh index b6ca1395d76..80a9d565420 100644 --- a/jdk/test/sun/security/mscapi/KeyStoreCompatibilityMode.sh +++ b/jdk/test/sun/security/mscapi/KeyStoreCompatibilityMode.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2015, 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 @@ -23,9 +23,9 @@ # questions. # - # @test # @bug 6324294 6931562 +# @requires os.family == "windows" # @run shell KeyStoreCompatibilityMode.sh # @summary Confirm that a null stream or password is not permitted when # compatibility mode is enabled (and vice versa). diff --git a/jdk/test/sun/security/mscapi/KeytoolChangeAlias.sh b/jdk/test/sun/security/mscapi/KeytoolChangeAlias.sh index 03e296d3652..1996490109c 100644 --- a/jdk/test/sun/security/mscapi/KeytoolChangeAlias.sh +++ b/jdk/test/sun/security/mscapi/KeytoolChangeAlias.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2006, 2015, 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 @@ -26,6 +26,7 @@ # @test # @bug 6415696 6931562 +# @requires os.family == "windows" # @run shell KeytoolChangeAlias.sh # @summary Test "keytool -changealias" using the Microsoft CryptoAPI provider. diff --git a/jdk/test/sun/security/mscapi/PrngSlow.java b/jdk/test/sun/security/mscapi/PrngSlow.java index 3420cc6b2a1..b8abd93a834 100644 --- a/jdk/test/sun/security/mscapi/PrngSlow.java +++ b/jdk/test/sun/security/mscapi/PrngSlow.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2015 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 @@ -24,6 +24,7 @@ /** * @test * @bug 6449335 + * @requires os.family == "windows" * @summary MSCAPI's PRNG is too slow * @key randomness */ @@ -34,23 +35,15 @@ public class PrngSlow { public static void main(String[] args) throws Exception { double t = 0.0; - try { - SecureRandom sr = null; - sr = SecureRandom.getInstance("PRNG", "SunMSCAPI"); - long start = System.nanoTime(); - int x = 0; - for(int i = 0; i < 10000; i++) { - if (i % 100 == 0) System.err.print("."); - if (sr.nextBoolean()) x++; - }; - t = (System.nanoTime() - start) / 1000000000.0; - System.err.println("\nSpend " + t + " seconds"); - } catch (Exception e) { - // Not supported here, maybe not a Win32 - System.err.println("Cannot find PRNG for SunMSCAPI or other mysterious bugs"); - e.printStackTrace(); - return; - } + SecureRandom sr = null; + sr = SecureRandom.getInstance("Windows-PRNG", "SunMSCAPI"); + long start = System.nanoTime(); + for (int i = 0; i < 10000; i++) { + if (i % 100 == 0) System.err.print("."); + sr.nextBoolean(); + }; + t = (System.nanoTime() - start) / 1000000000.0; + System.err.println("\nSpend " + t + " seconds"); if (t > 5) throw new RuntimeException("Still too slow"); } diff --git a/jdk/test/sun/security/mscapi/PublicKeyInterop.java b/jdk/test/sun/security/mscapi/PublicKeyInterop.java index 53d5b094681..a7570cdd818 100644 --- a/jdk/test/sun/security/mscapi/PublicKeyInterop.java +++ b/jdk/test/sun/security/mscapi/PublicKeyInterop.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2015, 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 @@ -38,8 +38,6 @@ import sun.misc.HexDumpEncoder; public class PublicKeyInterop { public static void main(String[] arg) throws Exception { - PrivateKey privKey = null; - Certificate cert = null; KeyStore ks = KeyStore.getInstance("Windows-MY"); ks.load(null, null); System.out.println("Loaded keystore: Windows-MY"); diff --git a/jdk/test/sun/security/mscapi/PublicKeyInterop.sh b/jdk/test/sun/security/mscapi/PublicKeyInterop.sh index 73f0e6e45fe..f5b6c913142 100644 --- a/jdk/test/sun/security/mscapi/PublicKeyInterop.sh +++ b/jdk/test/sun/security/mscapi/PublicKeyInterop.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2015 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 @@ -25,6 +25,7 @@ # @test # @bug 6888925 +# @requires os.family == "windows" # @run shell PublicKeyInterop.sh # @summary SunMSCAPI's Cipher can't use RSA public keys obtained from other # sources. diff --git a/jdk/test/sun/security/mscapi/RSAEncryptDecrypt.sh b/jdk/test/sun/security/mscapi/RSAEncryptDecrypt.sh index ed17bd1159e..9c5efb656b8 100644 --- a/jdk/test/sun/security/mscapi/RSAEncryptDecrypt.sh +++ b/jdk/test/sun/security/mscapi/RSAEncryptDecrypt.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2006, 2015, 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 @@ -25,6 +25,7 @@ # @test # @bug 6457422 6931562 +# @requires os.family == "windows" # @run shell RSAEncryptDecrypt.sh # @summary Confirm that plaintext can be encrypted and then decrypted using the # RSA cipher in the SunMSCAPI crypto provider. NOTE: The RSA cipher is diff --git a/jdk/test/sun/security/mscapi/ShortRSAKey1024.sh b/jdk/test/sun/security/mscapi/ShortRSAKey1024.sh index f7094f210af..41ae34f9e8d 100644 --- a/jdk/test/sun/security/mscapi/ShortRSAKey1024.sh +++ b/jdk/test/sun/security/mscapi/ShortRSAKey1024.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2015, 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 @@ -27,6 +27,7 @@ # @test # @bug 7106773 # @summary 512 bits RSA key cannot work with SHA384 and SHA512 +# @requires os.family == "windows" # @run shell ShortRSAKey1024.sh 1024 # @run shell ShortRSAKey1024.sh 768 # @run shell ShortRSAKey1024.sh 512 diff --git a/jdk/test/sun/security/mscapi/ShortRSAKeyWithinTLS.java b/jdk/test/sun/security/mscapi/ShortRSAKeyWithinTLS.java index 8958718ae7a..f2a752599eb 100644 --- a/jdk/test/sun/security/mscapi/ShortRSAKeyWithinTLS.java +++ b/jdk/test/sun/security/mscapi/ShortRSAKeyWithinTLS.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, 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 @@ -22,12 +22,9 @@ */ import java.io.*; -import java.net.*; -import java.util.*; import java.security.*; import javax.net.*; import javax.net.ssl.*; -import java.lang.reflect.*; import sun.security.util.KeyUtil; diff --git a/jdk/test/sun/security/mscapi/SignUsingNONEwithRSA.java b/jdk/test/sun/security/mscapi/SignUsingNONEwithRSA.java index 02cf4f67c80..ec8c0c5f9e1 100644 --- a/jdk/test/sun/security/mscapi/SignUsingNONEwithRSA.java +++ b/jdk/test/sun/security/mscapi/SignUsingNONEwithRSA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2015, 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 @@ -118,12 +118,12 @@ public class SignUsingNONEwithRSA { ks.load(null, null); System.out.println("Loaded keystore: Windows-MY"); - Enumeration e = ks.aliases(); + Enumeration e = ks.aliases(); PrivateKey privateKey = null; PublicKey publicKey = null; while (e.hasMoreElements()) { - String alias = (String) e.nextElement(); + String alias = e.nextElement(); if (alias.equals("6578658")) { System.out.println("Loaded entry: " + alias); privateKey = (PrivateKey) ks.getKey(alias, null); diff --git a/jdk/test/sun/security/mscapi/SignUsingNONEwithRSA.sh b/jdk/test/sun/security/mscapi/SignUsingNONEwithRSA.sh index e8c93a75781..cbd7629f73d 100644 --- a/jdk/test/sun/security/mscapi/SignUsingNONEwithRSA.sh +++ b/jdk/test/sun/security/mscapi/SignUsingNONEwithRSA.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2015, 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 @@ -26,6 +26,7 @@ # @test # @bug 6578658 +# @requires os.family == "windows" # @run shell SignUsingNONEwithRSA.sh # @summary Sign using the NONEwithRSA signature algorithm from SunMSCAPI # @key intermittent diff --git a/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.java b/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.java index 6835bd3f959..90973ecce4d 100644 --- a/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.java +++ b/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2015, 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 @@ -58,12 +58,12 @@ public class SignUsingSHA2withRSA { ks.load(null, null); System.out.println("Loaded keystore: Windows-MY"); - Enumeration e = ks.aliases(); + Enumeration e = ks.aliases(); PrivateKey privateKey = null; PublicKey publicKey = null; while (e.hasMoreElements()) { - String alias = (String) e.nextElement(); + String alias = e.nextElement(); if (alias.equals("6753664")) { System.out.println("Loaded entry: " + alias); privateKey = (PrivateKey) ks.getKey(alias, null); diff --git a/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.sh b/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.sh index d70a4e1c973..6c1685ff153 100644 --- a/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.sh +++ b/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2015, 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 @@ -26,6 +26,7 @@ # @test # @bug 6753664 +# @requires os.family == "windows" # @run shell SignUsingSHA2withRSA.sh # @summary Support SHA256 (and higher) in SunMSCAPI # @key intermittent diff --git a/jdk/test/sun/security/mscapi/SignatureOffsets.java b/jdk/test/sun/security/mscapi/SignatureOffsets.java index f9f8e2c6133..4c710396840 100644 --- a/jdk/test/sun/security/mscapi/SignatureOffsets.java +++ b/jdk/test/sun/security/mscapi/SignatureOffsets.java @@ -36,6 +36,7 @@ import java.security.SignatureException; * and passing in different signature offset (0, 33, 66, 99). * @library /lib/testlibrary * @compile ../../../java/security/Signature/Offsets.java + * @requires os.family == "windows" * @run main SignatureOffsets SunMSCAPI NONEwithRSA * @run main SignatureOffsets SunMSCAPI MD2withRSA * @run main SignatureOffsets SunMSCAPI MD5withRSA diff --git a/jdk/test/sun/security/mscapi/SignedObjectChain.java b/jdk/test/sun/security/mscapi/SignedObjectChain.java index 9790daa919b..d436612798f 100644 --- a/jdk/test/sun/security/mscapi/SignedObjectChain.java +++ b/jdk/test/sun/security/mscapi/SignedObjectChain.java @@ -25,6 +25,7 @@ * @test * @bug 8050374 * @compile ../../../java/security/SignedObject/Chain.java + * @requires os.family == "windows" * @summary Verify a chain of signed objects */ public class SignedObjectChain { diff --git a/jdk/test/sun/security/mscapi/SmallPrimeExponentP.java b/jdk/test/sun/security/mscapi/SmallPrimeExponentP.java index 00c3bec673a..1ee0f5bb069 100644 --- a/jdk/test/sun/security/mscapi/SmallPrimeExponentP.java +++ b/jdk/test/sun/security/mscapi/SmallPrimeExponentP.java @@ -28,8 +28,6 @@ import java.security.KeyStore; import java.security.SecureRandom; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateCrtKey; -import java.util.HashSet; -import java.util.Set; /* * @test @@ -37,6 +35,7 @@ import java.util.Set; * @modules java.base/sun.security.x509 * java.base/sun.security.tools.keytool * @summary sun/security/mscapi/ShortRSAKey1024.sh fails intermittently + * @requires os.family == "windows" */ public class SmallPrimeExponentP { diff --git a/jdk/test/sun/security/pkcs/pkcs10/PKCS10AttrEncoding.java b/jdk/test/sun/security/pkcs/pkcs10/PKCS10AttrEncoding.java new file mode 100644 index 00000000000..d9ecc2bf435 --- /dev/null +++ b/jdk/test/sun/security/pkcs/pkcs10/PKCS10AttrEncoding.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8048357 + * @summary test DER encoding of PKCS10 attributes + * @modules java.base/sun.security.pkcs + * java.base/sun.security.pkcs10 + * java.base/sun.security.util + * java.base/sun.security.x509 + * @compile -XDignore.symbol.file PKCS10AttrEncoding.java + * @run main PKCS10AttrEncoding + */ +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.Signature; +import java.util.Enumeration; +import java.util.GregorianCalendar; +import java.util.HashMap; +import sun.security.pkcs.PKCS9Attribute; +import sun.security.pkcs10.PKCS10; +import sun.security.pkcs10.PKCS10Attribute; +import sun.security.pkcs10.PKCS10Attributes; +import sun.security.util.ObjectIdentifier; +import sun.security.x509.X500Name; +import sun.security.x509.X509Key; + +public class PKCS10AttrEncoding { + + static final ObjectIdentifier[] ids = { + PKCS9Attribute.CONTENT_TYPE_OID, // ContentType + PKCS9Attribute.SIGNING_TIME_OID, // SigningTime + PKCS9Attribute.CHALLENGE_PASSWORD_OID // ChallengePassword + }; + static int failedCount = 0; + static HashMap constructedMap = new HashMap<>(); + + public static void main(String[] args) throws Exception { + + // initializations + int len = ids.length; + Object[] values = { + new ObjectIdentifier("1.2.3.4"), + new GregorianCalendar(1970, 1, 25, 8, 56, 7).getTime(), + "challenging" + }; + for (int j = 0; j < len; j++) { + constructedMap.put(ids[j], values[j]); + } + + X500Name subject = new X500Name("cn=Test"); + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA"); + String sigAlg = "DSA"; + + keyGen.initialize(512); + + KeyPair pair = keyGen.generateKeyPair(); + X509Key publicKey = (X509Key) pair.getPublic(); + PrivateKey privateKey = pair.getPrivate(); + + Signature signature = Signature.getInstance(sigAlg); + signature.initSign(privateKey); + + // Create the PKCS10 request + PKCS10Attribute[] attrs = new PKCS10Attribute[len]; + for (int j = 0; j < len; j++) { + attrs[j] = new PKCS10Attribute(ids[j], values[j]); + } + PKCS10 req = new PKCS10(publicKey, new PKCS10Attributes(attrs)); + System.out.println("List of attributes in constructed PKCS10 " + + "request: "); + checkAttributes(req.getAttributes().getElements()); + + // Encode the PKCS10 request and generate another PKCS10 request from + // the encoded byte array + req.encodeAndSign(subject, signature); + PKCS10 resp = new PKCS10(req.getEncoded()); + System.out.println("List of attributes in DER encoded PKCS10 Request:"); + checkAttributes(resp.getAttributes().getElements()); + + if (failedCount > 0) { + throw new RuntimeException("Attributes Compared : Failed"); + } + System.out.println("Attributes Compared : Pass"); + } + + static void checkAttributes(Enumeration attrs) { + int numOfAttrs = 0; + while (attrs.hasMoreElements()) { + numOfAttrs ++; + PKCS10Attribute attr = (PKCS10Attribute) attrs.nextElement(); + + if (constructedMap.containsKey(attr.getAttributeId())) { + if (constructedMap.get(attr.getAttributeId()). + equals(attr.getAttributeValue())) { + System.out.print("AttributeId: " + attr.getAttributeId()); + System.out.println(" AttributeValue: " + + attr.getAttributeValue()); + } else { + failedCount++; + System.out.print("< AttributeId: " + attr.getAttributeId()); + System.out.println(" AttributeValue: " + constructedMap. + get(attr.getAttributeId())); + System.out.print("< AttributeId: " + attr.getAttributeId()); + System.out.println(" AttributeValue: " + + attr.getAttributeValue()); + } + } else { + failedCount++; + System.out.println("No " + attr.getAttributeId() + + " in DER encoded PKCS10 Request"); + } + } + if(numOfAttrs != constructedMap.size()){ + failedCount++; + System.out.println("Incorrect number of attributes."); + + } + System.out.println(); + } + +} diff --git a/jdk/test/sun/security/pkcs/pkcs10/PKCS10AttributeReader.java b/jdk/test/sun/security/pkcs/pkcs10/PKCS10AttributeReader.java new file mode 100644 index 00000000000..aef650c68a5 --- /dev/null +++ b/jdk/test/sun/security/pkcs/pkcs10/PKCS10AttributeReader.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8048357 + * @summary Read in a file containing a DER encoded PKCS10 certificate request, + * flanked with "begin" and "end" lines. + * @modules java.base/sun.security.pkcs + * java.base/sun.security.pkcs10 + * java.base/sun.security.util + * @compile -XDignore.symbol.file PKCS10AttributeReader.java + * @run main PKCS10AttributeReader + */ +import java.util.Base64; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Date; +import sun.security.pkcs.PKCS9Attribute; +import sun.security.pkcs10.PKCS10Attribute; +import sun.security.pkcs10.PKCS10Attributes; +import sun.security.util.DerInputStream; +import sun.security.util.ObjectIdentifier; + +/* + Tests only reads DER encoding files, contents of corresponding asn.1 files + are copied below for reference. + + # An attribute set for testing with PKCS10. + + {A0 # implicit tag + {SEQ # Content Type + {OID 1.2.840.113549.1.9.3} + {SET + {OID "1234"} + } + } + {SEQ # Challenge Password + {OID 1.2.840.113549.1.9.7} + {SET + {T61String "GuessWhoAmI"} + } + } + {SEQ # Signing Time + {OID 1.2.840.113549.1.9.5} + {SET + {UTCTime "970422145010Z"} + } + } + } + */ +public class PKCS10AttributeReader { + // DER encoded files are binary files, to avoid attaching binary files, + // DER files were encoded in base64 + static final String ATTRIBS = "oE8wEwYJKoZIhvcNAQkDMQYGBDEyMzQwGgYJKoZIhv" + + "cNAQkHMQ0UC0d1ZXNzV2hv\nQW1JMBwGCSqGSIb3DQEJBTEPFw05NzA0MjIxND" + + "UwMTBa"; + + public static void main(String[] args) throws Exception { + + // Decode base64 encoded DER file + byte[] pkcs10Bytes = Base64.getMimeDecoder().decode(ATTRIBS.getBytes()); + + HashMap RequestStander = new HashMap() { + { + put(PKCS9Attribute.CHALLENGE_PASSWORD_OID, "GuessWhoAmI"); + put(PKCS9Attribute.SIGNING_TIME_OID, new Date(861720610000L)); + put(PKCS9Attribute.CONTENT_TYPE_OID, + new ObjectIdentifier("1.9.50.51.52")); + } + }; + + int invalidNum = 0; + PKCS10Attributes resp = new PKCS10Attributes( + new DerInputStream(pkcs10Bytes)); + Enumeration eReq = resp.getElements(); + int numOfAttrs = 0; + while (eReq.hasMoreElements()) { + numOfAttrs++; + PKCS10Attribute attr = (PKCS10Attribute) eReq.nextElement(); + if (RequestStander.containsKey(attr.getAttributeId())) { + if (RequestStander.get(attr.getAttributeId()) + .equals(attr.getAttributeValue())) { + System.out.println(attr.getAttributeId() + " " + + attr.getAttributeValue()); + } else { + invalidNum++; + System.out.println("< " + attr.getAttributeId() + " " + + attr.getAttributeValue()); + System.out.println("< " + attr.getAttributeId() + " " + + RequestStander.get(attr.getAttributeId())); + } + } else { + invalidNum++; + System.out.println("No" + attr.getAttributeId() + + "in Certificate Request list"); + } + } + if (numOfAttrs != RequestStander.size()) { + invalidNum++; + System.out.println("Incorrect number of attributes."); + } + System.out.println(); + if (invalidNum > 0) { + throw new RuntimeException( + "Attributes Compared with Stander :" + " Failed"); + } + System.out.println("Attributes Compared with Stander: Pass"); + } + +} diff --git a/jdk/test/sun/security/pkcs/pkcs7/PKCS7VerifyTest.java b/jdk/test/sun/security/pkcs/pkcs7/PKCS7VerifyTest.java new file mode 100644 index 00000000000..868bdc7bc51 --- /dev/null +++ b/jdk/test/sun/security/pkcs/pkcs7/PKCS7VerifyTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8048357 + * @summary Read signed data in one or more PKCS7 objects from individual files, + * verify SignerInfos and certificate chain. + * @modules java.base/sun.security.pkcs + * @run main PKCS7VerifyTest PKCS7TEST.DSA.base64 + * @run main PKCS7VerifyTest PKCS7TEST.DSA.base64 PKCS7TEST.SF + */ +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import sun.security.pkcs.PKCS7; +import sun.security.pkcs.SignerInfo; + +public class PKCS7VerifyTest { + + static final String TESTSRC = System.getProperty("test.src", "."); + static final String FS = File.separator; + static final String FILEPATH = TESTSRC + FS + "jarsigner" + FS + "META-INF" + + FS; + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + throw new RuntimeException("usage: java JarVerify "); + } + + // The command " java PKCS7VerifyTest file1 [file2] " + // treats file1 as containing the DER encoding of a PKCS7 signed data + // object. If file2 is absent, the program verifies that some signature + // (SignerInfo) file1 correctly signs the data contained in the + // ContentInfo component of the PKCS7 object encoded by file1. If file2 + // is present, the program verifies file1 contains a correct signature + // for the contents of file2. + + PKCS7 pkcs7; + byte[] data; + + // to avoid attaching binary DSA file, the DSA file was encoded + // in Base64, decode encoded Base64 DSA file below + byte[] base64Bytes = Files.readAllBytes(Paths.get(FILEPATH + args[0])); + pkcs7 = new PKCS7(new ByteArrayInputStream( + Base64.getMimeDecoder().decode(base64Bytes))); + if (args.length < 2) { + data = null; + } else { + data = Files.readAllBytes(Paths.get(FILEPATH + args[1])); + + } + + SignerInfo[] signerInfos = pkcs7.verify(data); + + if (signerInfos == null) { + throw new RuntimeException("no signers verify"); + } + System.out.println("Verifying SignerInfos:"); + for (SignerInfo signerInfo : signerInfos) { + System.out.println(signerInfo.toString()); + } + + X509Certificate certs[] = pkcs7.getCertificates(); + + HashMap certTable = new HashMap(certs.length); + for (X509Certificate cert : certs) { + certTable.put(cert.getSubjectDN().toString(), cert); + } + + // try to verify all the certs + for (Map.Entry entry : certTable.entrySet()) { + + X509Certificate cert = entry.getValue(); + X509Certificate issuerCert = certTable + .get(cert.getIssuerDN().toString()); + + System.out.println("Subject: " + cert.getSubjectDN()); + if (issuerCert == null) { + System.out.println("Issuer certificate not found"); + } else { + System.out.println("Issuer: " + cert.getIssuerDN()); + cert.verify(issuerCert.getPublicKey()); + System.out.println("Cert verifies."); + } + System.out.println(); + } + } + +} diff --git a/jdk/test/sun/security/pkcs/pkcs7/SignerOrder.java b/jdk/test/sun/security/pkcs/pkcs7/SignerOrder.java new file mode 100644 index 00000000000..e80a76d4f9b --- /dev/null +++ b/jdk/test/sun/security/pkcs/pkcs7/SignerOrder.java @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8048357 + * @summary test PKCS7 data signing, encoding and verification + * @modules java.base/sun.security.pkcs + * java.base/sun.security.util + * java.base/sun.security.x509 + * @run main SignerOrder + */ +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.X509Certificate; +import java.util.Date; +import sun.misc.HexDumpEncoder; +import sun.security.pkcs.ContentInfo; +import sun.security.pkcs.PKCS7; +import sun.security.pkcs.SignerInfo; +import sun.security.util.DerOutputStream; +import sun.security.x509.AlgorithmId; +import sun.security.x509.CertificateAlgorithmId; +import sun.security.x509.CertificateSerialNumber; +import sun.security.x509.CertificateValidity; +import sun.security.x509.CertificateVersion; +import sun.security.x509.CertificateX509Key; +import sun.security.x509.X500Name; +import sun.security.x509.X509CertImpl; +import sun.security.x509.X509CertInfo; +import sun.security.x509.X509Key; + +public class SignerOrder { + + static final HexDumpEncoder hexDump = new HexDumpEncoder(); + + //signer infos + static final byte[] data1 = "12345".getBytes(); + static final byte[] data2 = "abcde".getBytes(); + + public static void main(String[] argv) throws Exception { + + SignerInfo[] signerInfos = new SignerInfo[9]; + SimpleSigner signer1 = new SimpleSigner(null, null, null, null); + signerInfos[8] = signer1.genSignerInfo(data1); + signerInfos[7] = signer1.genSignerInfo(new byte[]{}); + signerInfos[6] = signer1.genSignerInfo(data2); + + SimpleSigner signer2 = new SimpleSigner(null, null, null, null); + signerInfos[5] = signer2.genSignerInfo(data1); + signerInfos[4] = signer2.genSignerInfo(new byte[]{}); + signerInfos[3] = signer2.genSignerInfo(data2); + + SimpleSigner signer3 = new SimpleSigner(null, null, null, null); + signerInfos[2] = signer3.genSignerInfo(data1); + signerInfos[1] = signer3.genSignerInfo(new byte[]{}); + signerInfos[0] = signer3.genSignerInfo(data2); + + ContentInfo contentInfo = new ContentInfo(data1); + + AlgorithmId[] algIds = {new AlgorithmId(AlgorithmId.SHA256_oid)}; + + X509Certificate[] certs = {signer3.getCert(), signer2.getCert(), + signer1.getCert()}; + + PKCS7 pkcs71 = new PKCS7(algIds, contentInfo, + certs, + signerInfos); + + System.out.println("SignerInfos in original."); + printSignerInfos(pkcs71.getSignerInfos()); + + DerOutputStream out = new DerOutputStream(); + pkcs71.encodeSignedData(out); + + PKCS7 pkcs72 = new PKCS7(out.toByteArray()); + System.out.println("\nSignerInfos read back in:"); + printSignerInfos(pkcs72.getSignerInfos()); + + System.out.println("Verified signers of original:"); + SignerInfo[] verifs1 = pkcs71.verify(); + + System.out.println("Verified signers of after read-in:"); + SignerInfo[] verifs2 = pkcs72.verify(); + + if (verifs1.length != verifs2.length) { + throw new RuntimeException("Length or Original vs read-in " + + "should be same"); + } + } + + static void printSignerInfos(SignerInfo signerInfo) throws IOException { + ByteArrayOutputStream strm = new ByteArrayOutputStream(); + signerInfo.derEncode(strm); + System.out.println("SignerInfo, length: " + + strm.toByteArray().length); + System.out.println(hexDump.encode(strm.toByteArray())); + System.out.println("\n"); + strm.reset(); + } + + static void printSignerInfos(SignerInfo[] signerInfos) throws IOException { + ByteArrayOutputStream strm = new ByteArrayOutputStream(); + for (int i = 0; i < signerInfos.length; i++) { + signerInfos[i].derEncode(strm); + System.out.println("SignerInfo[" + i + "], length: " + + strm.toByteArray().length); + System.out.println(hexDump.encode(strm.toByteArray())); + System.out.println("\n"); + strm.reset(); + } + } + +} + +/** + * A simple extension of sun.security.x509.X500Signer that adds a no-fuss + * signing algorithm. + */ +class SimpleSigner { + + private final Signature sig; + private final X500Name agent; + private final AlgorithmId digestAlgId; + private final AlgorithmId encryptionAlgId; + private final AlgorithmId algId; // signature algid; + //combines digest + encryption + private final X509Key publicKey; + private final PrivateKey privateKey; + private final X509Certificate cert; + + public SimpleSigner(String digestAlg, + String encryptionAlg, + KeyPair keyPair, + X500Name agent) throws Exception { + + if (agent == null) { + agent = new X500Name("cn=test"); + } + if (digestAlg == null) { + digestAlg = "SHA"; + } + if (encryptionAlg == null) { + encryptionAlg = "DSA"; + } + if (keyPair == null) { + KeyPairGenerator keyGen = + KeyPairGenerator.getInstance(encryptionAlg); + keyGen.initialize(1024); + keyPair = keyGen.generateKeyPair(); + } + publicKey = (X509Key) keyPair.getPublic(); + privateKey = keyPair.getPrivate(); + + if ("DSA".equals(encryptionAlg)) { + this.sig = Signature.getInstance(encryptionAlg); + } else { // RSA + this.sig = Signature.getInstance(digestAlg + "/" + encryptionAlg); + } + this.sig.initSign(privateKey); + + this.agent = agent; + this.digestAlgId = AlgorithmId.get(digestAlg); + this.encryptionAlgId = AlgorithmId.get(encryptionAlg); + this.algId = AlgorithmId.get(this.sig.getAlgorithm()); + + this.cert = getSelfCert(); + } + + /** + * Take the data and sign it. + * + * @param buf buffer holding the next chunk of the data to be signed + * @param offset starting point of to-be-signed data + * @param len how many bytes of data are to be signed + * @return the signature for the input data. + * @exception SignatureException on errors. + */ + public byte[] simpleSign(byte[] buf, int offset, int len) + throws SignatureException { + sig.update(buf, offset, len); + return sig.sign(); + } + + /** + * Returns the digest algorithm used to sign. + */ + public AlgorithmId getDigestAlgId() { + return digestAlgId; + } + + /** + * Returns the encryption algorithm used to sign. + */ + public AlgorithmId getEncryptionAlgId() { + return encryptionAlgId; + } + + /** + * Returns the name of the signing agent. + */ + public X500Name getSigner() { + return agent; + } + + public X509Certificate getCert() { + return cert; + } + + private X509Certificate getSelfCert() throws Exception { + long validity = 1000; + X509CertImpl certLocal; + Date firstDate, lastDate; + + firstDate = new Date(); + lastDate = new Date(); + lastDate.setTime(lastDate.getTime() + validity + 1000); + + CertificateValidity interval = new CertificateValidity(firstDate, + lastDate); + + X509CertInfo info = new X509CertInfo(); + // Add all mandatory attributes + info.set(X509CertInfo.VERSION, + new CertificateVersion(CertificateVersion.V1)); + info.set(X509CertInfo.SERIAL_NUMBER, + new CertificateSerialNumber( + (int) (firstDate.getTime() / 1000))); + info.set(X509CertInfo.ALGORITHM_ID, + new CertificateAlgorithmId(algId)); + info.set(X509CertInfo.SUBJECT, agent); + info.set(X509CertInfo.KEY, new CertificateX509Key(publicKey)); + info.set(X509CertInfo.VALIDITY, interval); + info.set(X509CertInfo.ISSUER, agent); + + certLocal = new X509CertImpl(info); + certLocal.sign(privateKey, algId.getName()); + + return certLocal; + } + + public SignerInfo genSignerInfo(byte[] data) throws SignatureException { + return new SignerInfo((X500Name) cert.getIssuerDN(), + new BigInteger("" + cert.getSerialNumber()), + getDigestAlgId(), algId, + simpleSign(data, 0, data.length)); + } +} diff --git a/jdk/test/sun/security/pkcs/pkcs7/jarsigner/META-INF/MANIFEST.MF b/jdk/test/sun/security/pkcs/pkcs7/jarsigner/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..6be546d4daf --- /dev/null +++ b/jdk/test/sun/security/pkcs/pkcs7/jarsigner/META-INF/MANIFEST.MF @@ -0,0 +1,82 @@ +Manifest-Version: 1.0 + +Name: CheckCerts.class +Digest-Algorithms: SHA +SHA-Digest: xLygljhRro6990piIVEilVI8szQ= + +Name: ContentInfoTest.class +Digest-Algorithms: SHA +SHA-Digest: TSVdEMQW2gdFi6qeba+UixdHSdo= + +Name: JarVerify.class +Digest-Algorithms: SHA +SHA-Digest: Wg+PiDzunNGH4KrWAp00/okp39s= + +Name: JarVerify2.class +Digest-Algorithms: SHA +SHA-Digest: 5uYBQxwGWgYmNBwhnWRbymeXmWM= + +Name: PKCS7Read.class +Digest-Algorithms: SHA +SHA-Digest: JPIxttHBfRpQaFyiQJ2Wfkvj/ls= + +Name: PKCS7Test.class +Digest-Algorithms: SHA +SHA-Digest: R64SXXgZrOvGiO/eMsfG/T1Vn30= + +Name: PKCS7Test10.class +Digest-Algorithms: SHA +SHA-Digest: 2R0yxuxRHTPqdAzJJcrvqkpbQgo= + +Name: PKCS7Test11.class +Digest-Algorithms: SHA +SHA-Digest: /0HcwnpQi0hwJsJtvt5peWFGvtc= + +Name: PKCS7Test12.class +Digest-Algorithms: SHA +SHA-Digest: s5CcqimfRqR9CW25tFBY0JK3RVU= + +Name: PKCS7Test2.class +Digest-Algorithms: SHA +SHA-Digest: 71VkFEMUle5sjXNFbSW31F1ZJ58= + +Name: PKCS7Test3.class +Digest-Algorithms: SHA +SHA-Digest: mU/D5C6SgPRmwoLQzwF5VnN3aqM= + +Name: PKCS7Test4.class +Digest-Algorithms: SHA +SHA-Digest: ss9NFvxF8emaEjdKdvtzWXfs0/E= + +Name: PKCS7Test5.class +Digest-Algorithms: SHA +SHA-Digest: DHvQ20UAXoYgfCPAOeCOrglsJwU= + +Name: PKCS7Test6.class +Digest-Algorithms: SHA +SHA-Digest: aiCb8chroH7XDaNfAz6wr57lXsA= + +Name: PKCS7Test7.class +Digest-Algorithms: SHA +SHA-Digest: UoieXLC68alFgfD/Q1NW9/r2kaY= + +Name: PKCS7Test8.class +Digest-Algorithms: SHA +SHA-Digest: eMW7mq5b/KVB1M5L76wcV1+uFQs= + +Name: PKCS7Test9.class +Digest-Algorithms: SHA +SHA-Digest: EEWCZG1creWjqVZVIEgr0on3y6A= + +Name: SignerInfoTest.class +Digest-Algorithms: SHA +SHA-Digest: l6SNfpnFipGg8gy4XqY3HhA0RrY= + +Name: SignerInfoTest2.class +Digest-Algorithms: SHA +SHA-Digest: 5jbzlkZqXKNmmmE+pcjQka8D6WE= + +Name: SimpleSigner.class +Digest-Algorithms: SHA +SHA-Digest: l9ODQHY4wxhIvLw4/B0qe9NjwxQ= + diff --git a/jdk/test/sun/security/pkcs/pkcs7/jarsigner/META-INF/PKCS7TEST.DSA.base64 b/jdk/test/sun/security/pkcs/pkcs7/jarsigner/META-INF/PKCS7TEST.DSA.base64 new file mode 100644 index 00000000000..f084beb89b6 --- /dev/null +++ b/jdk/test/sun/security/pkcs/pkcs7/jarsigner/META-INF/PKCS7TEST.DSA.base64 @@ -0,0 +1,60 @@ +MIILKAYJKoZIhvcNAQcCoIILGTCCCxUCAQExCzAJBgUrDgMCGgUAMIIHbQYJKoZI +hvcNAQcBoIIHXgSCB1pTaWduYXR1cmUtVmVyc2lvbjogMS4wDQoNCk5hbWU6IENo +ZWNrQ2VydHMuY2xhc3MNCkRpZ2VzdC1BbGdvcml0aG1zOiBTSEENClNIQS1EaWdl +c3Q6IHlhMXh3dnNRTytEUnBRYnczRmgyblJCMkpRYz0NCg0KTmFtZTogQ29udGVu +dEluZm9UZXN0LmNsYXNzDQpEaWdlc3QtQWxnb3JpdGhtczogU0hBDQpTSEEtRGln +ZXN0OiBDYStFSmFrVTZ6dzRLQWhvcWNuQ3BOcWsyTEk9DQoNCk5hbWU6IEphclZl +cmlmeS5jbGFzcw0KRGlnZXN0LUFsZ29yaXRobXM6IFNIQQ0KU0hBLURpZ2VzdDog +K0RHYVdXa25md2U0Wk9wc29NVEZ6ZldSdmhRPQ0KDQpOYW1lOiBKYXJWZXJpZnky +LmNsYXNzDQpEaWdlc3QtQWxnb3JpdGhtczogU0hBDQpTSEEtRGlnZXN0OiBHcUR6 +WXlZNFAvV0g1SEt2aVdxWHR0UGc1ckU9DQoNCk5hbWU6IFBLQ1M3UmVhZC5jbGFz +cw0KRGlnZXN0LUFsZ29yaXRobXM6IFNIQQ0KU0hBLURpZ2VzdDogUW1mOEs5aFhW +bHdJZFBZNm52MmpGUGZHcWtBPQ0KDQpOYW1lOiBQS0NTN1Rlc3QuY2xhc3MNCkRp +Z2VzdC1BbGdvcml0aG1zOiBTSEENClNIQS1EaWdlc3Q6IEdiZS9nenl2MkY1OGY2 +RUVoU1oxQnFHWHRsbz0NCg0KTmFtZTogUEtDUzdUZXN0MTAuY2xhc3MNCkRpZ2Vz +dC1BbGdvcml0aG1zOiBTSEENClNIQS1EaWdlc3Q6IDh3QnFXLy9lVzJzTlJJOTFi +TFlFT29kY2dhRT0NCg0KTmFtZTogUEtDUzdUZXN0MTEuY2xhc3MNCkRpZ2VzdC1B +bGdvcml0aG1zOiBTSEENClNIQS1EaWdlc3Q6IGJYaExLRXNsY3VFWGk0dS9haGdU +MnE2dGNFVT0NCg0KTmFtZTogUEtDUzdUZXN0MTIuY2xhc3MNCkRpZ2VzdC1BbGdv +cml0aG1zOiBTSEENClNIQS1EaWdlc3Q6IDlLRVkxYjUyUUxtTjBxei81ejB3QkZy +T216MD0NCg0KTmFtZTogUEtDUzdUZXN0Mi5jbGFzcw0KRGlnZXN0LUFsZ29yaXRo +bXM6IFNIQQ0KU0hBLURpZ2VzdDogK1VhMzIvMlE4RjJiclFRbVNYWCtYUytNL2g0 +PQ0KDQpOYW1lOiBQS0NTN1Rlc3QzLmNsYXNzDQpEaWdlc3QtQWxnb3JpdGhtczog +U0hBDQpTSEEtRGlnZXN0OiAwSFhVWnlhU2ZkZUtlZThuWnpFalJTeXJldTQ9DQoN +Ck5hbWU6IFBLQ1M3VGVzdDQuY2xhc3MNCkRpZ2VzdC1BbGdvcml0aG1zOiBTSEEN +ClNIQS1EaWdlc3Q6IEo3eXJTMjRvS3VTZ2F1dHZkemhxQmo3ZGJjUT0NCg0KTmFt +ZTogUEtDUzdUZXN0NS5jbGFzcw0KRGlnZXN0LUFsZ29yaXRobXM6IFNIQQ0KU0hB +LURpZ2VzdDogSlR2OVdTb3gxTEVTUjJMcTdzMFVxU2x0RFNRPQ0KDQpOYW1lOiBQ +S0NTN1Rlc3Q2LmNsYXNzDQpEaWdlc3QtQWxnb3JpdGhtczogU0hBDQpTSEEtRGln +ZXN0OiBnR3Yra05oK3UzSFExdHp4bGNBVzdTcEZUS2s9DQoNCk5hbWU6IFBLQ1M3 +VGVzdDcuY2xhc3MNCkRpZ2VzdC1BbGdvcml0aG1zOiBTSEENClNIQS1EaWdlc3Q6 +IGZpSEYxYUExYWN6czFPd0V5OEc3VkMrcjdMST0NCg0KTmFtZTogUEtDUzdUZXN0 +OC5jbGFzcw0KRGlnZXN0LUFsZ29yaXRobXM6IFNIQQ0KU0hBLURpZ2VzdDogNzRU +VzdJOVZPdzVWZ0x2aFJtRGZxRVd2ZkFRPQ0KDQpOYW1lOiBQS0NTN1Rlc3Q5LmNs +YXNzDQpEaWdlc3QtQWxnb3JpdGhtczogU0hBDQpTSEEtRGlnZXN0OiAxY0JJbkdU +Y08xQVFaKy8wdmhGa2laV3dsQTA9DQoNCk5hbWU6IFNpZ25lckluZm9UZXN0LmNs +YXNzDQpEaWdlc3QtQWxnb3JpdGhtczogU0hBDQpTSEEtRGlnZXN0OiBjRlk0Q3RT +anphMUErV2pBS05TVnF1cGpSWUU9DQoNCk5hbWU6IFNpZ25lckluZm9UZXN0Mi5j +bGFzcw0KRGlnZXN0LUFsZ29yaXRobXM6IFNIQQ0KU0hBLURpZ2VzdDogYU5NMEZQ +MHpFelF6eGxYeDZxQ0J4dWtta0hRPQ0KDQpOYW1lOiBTaW1wbGVTaWduZXIuY2xh +c3MNCkRpZ2VzdC1BbGdvcml0aG1zOiBTSEENClNIQS1EaWdlc3Q6IC9MV0NzbkM3 +TVpNUjZHb3czeTJjdnA3STBTTT0NCg0KoIICvzCCArswggJ3AgUA59UzNDALBgcq +hkjOOAQDBQAwdTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlD +dXBlcnRpbm8xGTAXBgNVBAoTEFN1biBNaWNyb3N5c3RlbXMxETAPBgNVBAsTCEph +dmFTb2Z0MRcwFQYDVQQDEw5Eb3VnbGFzIEhvb3ZlcjAeFw05NzEwMDIxODEyMDda +Fw05NzEyMzExNzEyMDdaMHUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAG +A1UEBxMJQ3VwZXJ0aW5vMRkwFwYDVQQKExBTdW4gTWljcm9zeXN0ZW1zMREwDwYD +VQQLEwhKYXZhU29mdDEXMBUGA1UEAxMORG91Z2xhcyBIb292ZXIwggFRMIHoBgcq +hkjOOAQBMIHcAmEA6eZCWZ01XzfJf/01ZxILjiXJzUPpJ7OpZw++xdiQFBki0sOz +rSSACTeZhp0ehGqrSfqwrSbSzmoiIZ1HC859d31KIfvpwnC1f2BwAvPO+Dk2lM9F +7jaIwRqMVqsSej2vAhUAnNvYTJ8awvOND4D0KrlS5zOL9RECYDBHCtWgBfsUzi2d +zYfji8fRscX6y67L6V8ZCqejHSPE27y+BhdFREAaWywCCWXYwr0hcdNmhEV3H3S6 +CE0gKdg8HBWFR/Op8aJxW+I9Ua5NPlofanBk8xaTOjRtP1KSUgNkAAJhAMN5uB+B +ZJ0W2UjXMyKoFUFXRYiLpnaSw63kl9tKnR9R5rEreiyHQ5IelPxjwCHGgTbYK0y+ +xKTGHVWiQN/YJmHLbSrcSSM/d89aR/sVbGoAwQOyYraFGUNIOTQjjXcXCjALBgcq +hkjOOAQDBQADMQAwLgIVAJxmL029GLXDJVbk72d4cSPQ4/rvAhUAll9UPl8aOMEg +V4egANhwbynMGSgxgc4wgcsCAQEwfjB1MQswCQYDVQQGEwJVUzELMAkGA1UECBMC +Q0ExEjAQBgNVBAcTCUN1cGVydGlubzEZMBcGA1UEChMQU3VuIE1pY3Jvc3lzdGVt +czERMA8GA1UECxMISmF2YVNvZnQxFzAVBgNVBAMTDkRvdWdsYXMgSG9vdmVyAgUA +59UzNDAJBgUrDgMCGgUAMAsGByqGSM44BAMFAAQuMCwCFDmry17kzDD6Y5X1BqIS +lq6swckPAhRtiXvBHa5CRGjbwk8yqf9hGgZfFA== diff --git a/jdk/test/sun/security/pkcs/pkcs7/jarsigner/META-INF/PKCS7TEST.SF b/jdk/test/sun/security/pkcs/pkcs7/jarsigner/META-INF/PKCS7TEST.SF new file mode 100644 index 00000000000..05a79382189 --- /dev/null +++ b/jdk/test/sun/security/pkcs/pkcs7/jarsigner/META-INF/PKCS7TEST.SF @@ -0,0 +1,82 @@ +Signature-Version: 1.0 + +Name: CheckCerts.class +Digest-Algorithms: SHA +SHA-Digest: ya1xwvsQO+DRpQbw3Fh2nRB2JQc= + +Name: ContentInfoTest.class +Digest-Algorithms: SHA +SHA-Digest: Ca+EJakU6zw4KAhoqcnCpNqk2LI= + +Name: JarVerify.class +Digest-Algorithms: SHA +SHA-Digest: +DGaWWknfwe4ZOpsoMTFzfWRvhQ= + +Name: JarVerify2.class +Digest-Algorithms: SHA +SHA-Digest: GqDzYyY4P/WH5HKviWqXttPg5rE= + +Name: PKCS7Read.class +Digest-Algorithms: SHA +SHA-Digest: Qmf8K9hXVlwIdPY6nv2jFPfGqkA= + +Name: PKCS7Test.class +Digest-Algorithms: SHA +SHA-Digest: Gbe/gzyv2F58f6EEhSZ1BqGXtlo= + +Name: PKCS7Test10.class +Digest-Algorithms: SHA +SHA-Digest: 8wBqW//eW2sNRI91bLYEOodcgaE= + +Name: PKCS7Test11.class +Digest-Algorithms: SHA +SHA-Digest: bXhLKEslcuEXi4u/ahgT2q6tcEU= + +Name: PKCS7Test12.class +Digest-Algorithms: SHA +SHA-Digest: 9KEY1b52QLmN0qz/5z0wBFrOmz0= + +Name: PKCS7Test2.class +Digest-Algorithms: SHA +SHA-Digest: +Ua32/2Q8F2brQQmSXX+XS+M/h4= + +Name: PKCS7Test3.class +Digest-Algorithms: SHA +SHA-Digest: 0HXUZyaSfdeKee8nZzEjRSyreu4= + +Name: PKCS7Test4.class +Digest-Algorithms: SHA +SHA-Digest: J7yrS24oKuSgautvdzhqBj7dbcQ= + +Name: PKCS7Test5.class +Digest-Algorithms: SHA +SHA-Digest: JTv9WSox1LESR2Lq7s0UqSltDSQ= + +Name: PKCS7Test6.class +Digest-Algorithms: SHA +SHA-Digest: gGv+kNh+u3HQ1tzxlcAW7SpFTKk= + +Name: PKCS7Test7.class +Digest-Algorithms: SHA +SHA-Digest: fiHF1aA1aczs1OwEy8G7VC+r7LI= + +Name: PKCS7Test8.class +Digest-Algorithms: SHA +SHA-Digest: 74TW7I9VOw5VgLvhRmDfqEWvfAQ= + +Name: PKCS7Test9.class +Digest-Algorithms: SHA +SHA-Digest: 1cBInGTcO1AQZ+/0vhFkiZWwlA0= + +Name: SignerInfoTest.class +Digest-Algorithms: SHA +SHA-Digest: cFY4CtSjza1A+WjAKNSVqupjRYE= + +Name: SignerInfoTest2.class +Digest-Algorithms: SHA +SHA-Digest: aNM0FP0zEzQzxlXx6qCBxukmkHQ= + +Name: SimpleSigner.class +Digest-Algorithms: SHA +SHA-Digest: /LWCsnC7MZMR6Gow3y2cvp7I0SM= + diff --git a/jdk/test/sun/security/pkcs/pkcs8/PKCS8Test.java b/jdk/test/sun/security/pkcs/pkcs8/PKCS8Test.java new file mode 100644 index 00000000000..25396734e5d --- /dev/null +++ b/jdk/test/sun/security/pkcs/pkcs8/PKCS8Test.java @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8048357 + * @summary PKCS8 Standards Conformance Tests + * @modules java.base/sun.security.pkcs + * java.base/sun.security.util + * java.base/sun.security.provider + * java.base/sun.security.x509 + * java.base/sun.misc + * @compile -XDignore.symbol.file PKCS8Test.java + * @run main PKCS8Test + */ +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.util.Arrays; +import sun.misc.HexDumpEncoder; +import sun.security.pkcs.PKCS8Key; +import sun.security.provider.DSAPrivateKey; +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; +import sun.security.x509.AlgorithmId; + +import static java.lang.System.out; + +public class PKCS8Test { + + static final HexDumpEncoder hexDump = new HexDumpEncoder(); + + static final DerOutputStream derOutput = new DerOutputStream(); + + static final String FORMAT = "PKCS#8"; + static final String EXPECTED_ALG_ID_CHRS = "DSA\n\tp: 02\n\tq: 03\n" + + "\tg: 04\n"; + static final String ALGORITHM = "DSA"; + static final String EXCEPTION_MESSAGE = "version mismatch: (supported: " + + "00, parsed: 01"; + + // test second branch in byte[] encode() + // DER encoding,include (empty) set of attributes + static final int[] NEW_ENCODED_KEY_INTS = { 0x30, + // length 30 = 0x1e + 0x1e, + // first element + // version Version (= INTEGER) + 0x02, + // length 1 + 0x01, + // value 0 + 0x00, + // second element + // privateKeyAlgorithmIdentifier PrivateKeyAlgorithmIdentifier + // (sequence) + // (an object identifier?) + 0x30, + // length 18 + 0x12, + // contents + // object identifier, 5 bytes + 0x06, 0x05, + // { 1 3 14 3 2 12 } + 0x2b, 0x0e, 0x03, 0x02, 0x0c, + // sequence, 9 bytes + 0x30, 0x09, + // integer 2 + 0x02, 0x01, 0x02, + // integer 3 + 0x02, 0x01, 0x03, + // integer 4 + 0x02, 0x01, 0x04, + // third element + // privateKey PrivateKey (= OCTET STRING) + 0x04, + // length + 0x03, + // privateKey contents + 0x02, 0x01, 0x01, + // 4th (optional) element -- attributes [0] IMPLICIT Attributes + // OPTIONAL + // (Attributes = SET OF Attribute) Here, it will be empty. + 0xA0, + // length + 0x00 }; + + // encoding originally created, but with the version changed + static final int[] NEW_ENCODED_KEY_INTS_2 = { + // sequence + 0x30, + // length 28 = 0x1c + 0x1c, + // first element + // version Version (= INTEGER) + 0x02, + // length 1 + 0x01, + // value 1 (illegal) + 0x01, + // second element + // privateKeyAlgorithmIdentifier PrivateKeyAlgorithmIdentifier + // (sequence) + // (an object identifier?) + 0x30, + // length 18 + 0x12, + // contents + // object identifier, 5 bytes + 0x06, 0x05, + // { 1 3 14 3 2 12 } + 0x2b, 0x0e, 0x03, 0x02, 0x0c, + // sequence, 9 bytes + 0x30, 0x09, + // integer 2 + 0x02, 0x01, 0x02, + // integer 3 + 0x02, 0x01, 0x03, + // integer 4 + 0x02, 0x01, 0x04, + // third element + // privateKey PrivateKey (= OCTET STRING) + 0x04, + // length + 0x03, + // privateKey contents + 0x02, 0x01, 0x01 }; + + // 0000: 30 1E 02 01 00 30 14 06 07 2A 86 48 CE 38 04 01 0....0...*.H.8.. + // 0010: 30 09 02 01 02 02 01 03 02 01 04 04 03 02 01 01 0............... + static final int[] EXPECTED = { 0x30, + // length 30 = 0x1e + 0x1e, + // first element + // version Version (= INTEGER) + 0x02, + // length 1 + 0x01, + // value 0 + 0x00, + // second element + // privateKeyAlgorithmIdentifier PrivateKeyAlgorithmIdentifier + // (sequence) + // (an object identifier?) + 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x01, + // integer 2 + 0x30, 0x09, 0x02, + // integer 3 + 0x01, 0x02, 0x02, + // integer 4 + 0x01, 0x03, 0x02, + // third element + // privateKey PrivateKey (= OCTET STRING) + 0x01, + // length + 0x04, + // privateKey contents + 0x04, 0x03, 0x02, + // 4th (optional) element -- attributes [0] IMPLICIT Attributes + // OPTIONAL + // (Attributes = SET OF Attribute) Here, it will be empty. + 0x01, + // length + 0x01 }; + + static void raiseException(String expected, String received) { + throw new RuntimeException( + "Expected " + expected + "; Received " + received); + } + + public static void main(String[] args) + throws IOException, InvalidKeyException { + + byte[] encodedKey = getEncodedKey(); + byte[] expectedBytes = new byte[EXPECTED.length]; + for (int i = 0; i < EXPECTED.length; i++) { + expectedBytes[i] = (byte) EXPECTED[i]; + } + + dumpByteArray("encodedKey :", encodedKey); + if (!Arrays.equals(encodedKey, expectedBytes)) { + raiseException(new String(expectedBytes), new String(encodedKey)); + } + + PKCS8Key decodedKey = PKCS8Key.parse(new DerValue(encodedKey)); + String alg = decodedKey.getAlgorithm(); + AlgorithmId algId = decodedKey.getAlgorithmId(); + out.println("Algorithm :" + alg); + out.println("AlgorithmId: " + algId); + + if (!ALGORITHM.equals(alg)) { + raiseException(ALGORITHM, alg); + } + if (!EXPECTED_ALG_ID_CHRS.equalsIgnoreCase(algId.toString())) { + raiseException(EXPECTED_ALG_ID_CHRS, algId.toString()); + } + + decodedKey.encode(derOutput); + dumpByteArray("Stream encode: ", derOutput.toByteArray()); + if (!Arrays.equals(derOutput.toByteArray(), expectedBytes)) { + raiseException(new String(expectedBytes), derOutput.toString()); + } + + dumpByteArray("byte[] encoding: ", decodedKey.getEncoded()); + if (!Arrays.equals(decodedKey.getEncoded(), expectedBytes)) { + raiseException(new String(expectedBytes), + new String(decodedKey.getEncoded())); + } + + if (!FORMAT.equals(decodedKey.getFormat())) { + raiseException(FORMAT, decodedKey.getFormat()); + } + + try { + byte[] newEncodedKey = new byte[NEW_ENCODED_KEY_INTS.length]; + for (int i = 0; i < newEncodedKey.length; i++) { + newEncodedKey[i] = (byte) NEW_ENCODED_KEY_INTS[i]; + } + PKCS8Key newDecodedKey = PKCS8Key + .parse(new DerValue(newEncodedKey)); + + throw new RuntimeException( + "key1: Expected an IOException during " + "parsing"); + } catch (IOException e) { + System.out.println("newEncodedKey: should have excess data due to " + + "attributes, which are not supported"); + } + + try { + byte[] newEncodedKey2 = new byte[NEW_ENCODED_KEY_INTS_2.length]; + for (int i = 0; i < newEncodedKey2.length; i++) { + newEncodedKey2[i] = (byte) NEW_ENCODED_KEY_INTS_2[i]; + } + + PKCS8Key newDecodedKey2 = PKCS8Key + .parse(new DerValue(newEncodedKey2)); + + throw new RuntimeException( + "key2: Expected an IOException during " + "parsing"); + } catch (IOException e) { + out.println("Key 2: should be illegal version"); + out.println(e.getMessage()); + if (!EXCEPTION_MESSAGE.equals(e.getMessage())) { + throw new RuntimeException("Key2: expected: " + + EXCEPTION_MESSAGE + " get: " + e.getMessage()); + } + } + + } + + // get a byte array from somewhere + static byte[] getEncodedKey() throws InvalidKeyException { + BigInteger p = BigInteger.valueOf(1); + BigInteger q = BigInteger.valueOf(2); + BigInteger g = BigInteger.valueOf(3); + BigInteger x = BigInteger.valueOf(4); + + DSAPrivateKey priv = new DSAPrivateKey(p, q, g, x); + return priv.getEncoded(); + } + + static void dumpByteArray(String nm, byte[] bytes) throws IOException { + out.println(nm + " length: " + bytes.length); + hexDump.encodeBuffer(bytes, out); + } + + static String toString(PKCS8Key key) { + StringBuilder builder = new StringBuilder(key.getAlgorithm()); + builder.append('\n').append("parameters:") + .append(key.getAlgorithmId().toString()); + return builder.toString(); + } + +} diff --git a/jdk/test/sun/security/tools/jarsigner/Options.java b/jdk/test/sun/security/tools/jarsigner/Options.java new file mode 100644 index 00000000000..e70903d06e3 --- /dev/null +++ b/jdk/test/sun/security/tools/jarsigner/Options.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2015, 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. + */ + +/** + * @test + * @bug 8056174 + * @summary Make sure the jarsigner tool still works after it's modified to + * be based on JarSigner API + * @library /lib/testlibrary + * @modules java.base/sun.security.tools.keytool + * jdk.jartool/sun.security.tools.jarsigner + * java.base/sun.security.pkcs + * java.base/sun.security.x509 + */ + +import com.sun.jarsigner.ContentSigner; +import com.sun.jarsigner.ContentSignerParameters; +import jdk.testlibrary.JarUtils; +import sun.security.pkcs.PKCS7; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.*; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +public class Options { + + public static void main(String[] args) throws Exception { + + // Prepares raw file + Files.write(Paths.get("a"), "a".getBytes()); + + // Pack + JarUtils.createJar("a.jar", "a"); + + // Prepare a keystore + sun.security.tools.keytool.Main.main( + ("-keystore jks -storepass changeit -keypass changeit -dname" + + " CN=A -alias a -genkeypair -keyalg rsa").split(" ")); + + // -altsign + sun.security.tools.jarsigner.Main.main( + ("-debug -signedjar altsign.jar -keystore jks -storepass changeit" + + " -altsigner Options$X a.jar a").split(" ")); + + try (JarFile jf = new JarFile("altsign.jar")) { + JarEntry je = jf.getJarEntry("META-INF/A.RSA"); + try (InputStream is = jf.getInputStream(je)) { + if (!Arrays.equals(is.readAllBytes(), "1234".getBytes())) { + throw new Exception("altsign go wrong"); + } + } + } + + // -sigfile, -digestalg, -sigalg, -internalsf, -sectionsonly + sun.security.tools.jarsigner.Main.main( + ("-debug -signedjar new.jar -keystore jks -storepass changeit" + + " -sigfile olala -digestalg SHA1 -sigalg SHA224withRSA" + + " -internalsf -sectionsonly a.jar a").split(" ")); + + try (JarFile jf = new JarFile("new.jar")) { + JarEntry je = jf.getJarEntry("META-INF/OLALA.SF"); + Objects.requireNonNull(je); // check -sigfile + byte[] sf = null; // content of .SF + try (InputStream is = jf.getInputStream(je)) { + sf = is.readAllBytes(); // save for later comparison + Attributes attrs = new Manifest(new ByteArrayInputStream(sf)) + .getMainAttributes(); + // check -digestalg + if (!attrs.containsKey(new Attributes.Name( + "SHA1-Digest-Manifest-Main-Attributes"))) { + throw new Exception("digestalg incorrect"); + } + // check -sectionsonly + if (attrs.containsKey(new Attributes.Name( + "SHA1-Digest-Manifest"))) { + throw new Exception("SF should not have file digest"); + } + } + + je = jf.getJarEntry("META-INF/OLALA.RSA"); + try (InputStream is = jf.getInputStream(je)) { + PKCS7 p7 = new PKCS7(is.readAllBytes()); + String alg = p7.getSignerInfos()[0] + .getDigestAlgorithmId().getName(); + if (!alg.equals("SHA-224")) { // check -sigalg + throw new Exception("PKCS7 signing is using " + alg); + } + // check -internalsf + if (!Arrays.equals(sf, p7.getContentInfo().getData())) { + throw new Exception("SF not in RSA"); + } + } + + } + + // TSA-related ones are checked in ts.sh + } + + public static class X extends ContentSigner { + @Override + public byte[] generateSignedData(ContentSignerParameters parameters, + boolean omitContent, boolean applyTimestamp) + throws NoSuchAlgorithmException, CertificateException, + IOException { + return "1234".getBytes(); + } + } +} diff --git a/jdk/test/sun/tools/jhsdb/BasicLauncherTest.java b/jdk/test/sun/tools/jhsdb/BasicLauncherTest.java index 7111598d316..37dd7ec0131 100644 --- a/jdk/test/sun/tools/jhsdb/BasicLauncherTest.java +++ b/jdk/test/sun/tools/jhsdb/BasicLauncherTest.java @@ -24,7 +24,7 @@ /* * @test * @summary Basic test for jhsdb launcher - * @library /../../test/lib/share/classes + * @library /test/lib/share/classes * @library /lib/testlibrary * @build jdk.testlibrary.* * @build jdk.test.lib.apps.* diff --git a/jdk/test/sun/tools/jmap/BasicJMapTest.java b/jdk/test/sun/tools/jmap/BasicJMapTest.java index f9cb1eafa02..03efac77cf0 100644 --- a/jdk/test/sun/tools/jmap/BasicJMapTest.java +++ b/jdk/test/sun/tools/jmap/BasicJMapTest.java @@ -38,7 +38,7 @@ import jdk.testlibrary.ProcessTools; * @summary Unit test for jmap utility * @key intermittent * @library /lib/testlibrary - * @library /../../test/lib/share/classes + * @library /test/lib/share/classes * @modules java.management * @build jdk.testlibrary.* * @build jdk.test.lib.hprof.* diff --git a/jdk/test/sun/tools/jmap/heapconfig/JMapHeapConfigTest.java b/jdk/test/sun/tools/jmap/heapconfig/JMapHeapConfigTest.java index 69fbf206f62..5a84b6261d6 100644 --- a/jdk/test/sun/tools/jmap/heapconfig/JMapHeapConfigTest.java +++ b/jdk/test/sun/tools/jmap/heapconfig/JMapHeapConfigTest.java @@ -36,7 +36,7 @@ import jdk.testlibrary.Platform; * @test * @bug 8042397 * @summary Unit test for jmap utility test heap configuration reader - * @library /../../test/lib/share/classes + * @library /test/lib/share/classes * @library /lib/testlibrary * @modules java.management * @build jdk.testlibrary.* diff --git a/jdk/test/sun/tools/jstack/DeadlockDetectionTest.java b/jdk/test/sun/tools/jstack/DeadlockDetectionTest.java index 31cdf311830..4ff2508673b 100644 --- a/jdk/test/sun/tools/jstack/DeadlockDetectionTest.java +++ b/jdk/test/sun/tools/jstack/DeadlockDetectionTest.java @@ -37,7 +37,7 @@ import jdk.testlibrary.ProcessTools; /* * @test * @summary Test deadlock detection - * @library /../../test/lib/share/classes + * @library /test/lib/share/classes * @library /lib/testlibrary * @modules java.management * @build jdk.testlibrary.* diff --git a/jdk/test/sun/util/logging/PlatformLoggerTest.java b/jdk/test/sun/util/logging/PlatformLoggerTest.java index 968f35c6d64..c3719c96eb5 100644 --- a/jdk/test/sun/util/logging/PlatformLoggerTest.java +++ b/jdk/test/sun/util/logging/PlatformLoggerTest.java @@ -38,7 +38,6 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.logging.*; import sun.util.logging.PlatformLogger; -import sun.util.logging.LoggingSupport; import static sun.util.logging.PlatformLogger.Level.*; public class PlatformLoggerTest { @@ -195,7 +194,9 @@ public class PlatformLoggerTest { System.out.println("Testing Java Level with: " + level.getName()); // create a brand new java logger - Logger javaLogger = (Logger) LoggingSupport.getLogger(logger.getName()+"."+level.getName()); + Logger javaLogger = sun.util.logging.internal.LoggingProviderImpl.getLogManagerAccess() + .demandLoggerFor(LogManager.getLogManager(), + logger.getName()+"."+level.getName(), Thread.class); // Set a non standard java.util.logging.Level on the java logger // (except for OFF & ALL - which will remain unchanged) diff --git a/langtools/.hgtags b/langtools/.hgtags index 1a826fd5602..49fc59e7644 100644 --- a/langtools/.hgtags +++ b/langtools/.hgtags @@ -334,3 +334,5 @@ ac57d80b205db48d726084ade228e0199735831b jdk9-b88 16873e56156e9917ad97ba5da0d0abe44fc94003 jdk9-b89 00a25f93cee8a82096a0736716da392cafdb0cb0 jdk9-b90 79501a97ca5720af846509f4bf3c6c04d7bdf82a jdk9-b91 +a3415b57507c928af8f2ad1c771eebafcd00c6c7 jdk9-b92 +7f880f98506c9046f8fb69597a41762ea1b7d042 jdk9-b93 diff --git a/langtools/make/CompileInterim.gmk b/langtools/make/CompileInterim.gmk index a0e1e144ef1..018343bb4ae 100644 --- a/langtools/make/CompileInterim.gmk +++ b/langtools/make/CompileInterim.gmk @@ -47,7 +47,7 @@ $(eval $(call SetupJavaCompilation,BUILD_INTERIM_LANGTOOLS, \ $(SUPPORT_OUTPUTDIR)/gensrc/jdk.javadoc \ $(SUPPORT_OUTPUTDIR)/gensrc/jdk.jdeps, \ EXCLUDES := sun jdk, \ - COPY := .gif .xml .css .js javax.tools.JavaCompilerTool, \ + COPY := .gif .png .xml .css .js javax.tools.JavaCompilerTool, \ BIN := $(BUILDTOOLS_OUTPUTDIR)/langtools_interim_classes, \ JAR := $(INTERIM_LANGTOOLS_JAR))) diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java index 3bbd7175cf9..02cf1242ace 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java @@ -126,10 +126,9 @@ public class Flags { */ public static final int BLOCK = 1<<20; - /** Flag is set for compiler-generated abstract methods that implement - * an interface method (Miranda methods). + /** Flag bit 21 is available. (used earlier to tag compiler-generated abstract methods that implement + * an interface method (Miranda methods)). */ - public static final int IPROXY = 1<<21; /** Flag is set for nested classes that do not access instance members * or `this' of an outer class and therefore don't need to be passed @@ -362,7 +361,6 @@ public class Flags { BLOCK(Flags.BLOCK), ENUM(Flags.ENUM), MANDATED(Flags.MANDATED), - IPROXY(Flags.IPROXY), NOOUTERTHIS(Flags.NOOUTERTHIS), EXISTS(Flags.EXISTS), COMPOUND(Flags.COMPOUND), diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Printer.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Printer.java index 0ab4e71058d..8971e6cbba0 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Printer.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Printer.java @@ -175,8 +175,8 @@ public abstract class Printer implements Type.Visitor, Symbol.Vi @Override public String visitUndetVar(UndetVar t, Locale locale) { - if (t.inst != null) { - return printAnnotations(t) + visit(t.inst, locale); + if (t.getInst() != null) { + return printAnnotations(t) + visit(t.getInst(), locale); } else { return printAnnotations(t) + visit(t.qtype, locale) + "?"; } diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java index f272aa3bf3a..0fbe7da988a 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java @@ -180,6 +180,14 @@ public abstract class Symbol extends AnnoConstruct implements Element { : metadata.getInitTypeAttributes(); } + public void setInitTypeAttributes(List l) { + initedMetadata().setInitTypeAttributes(l); + } + + public void setClassInitTypeAttributes(List l) { + initedMetadata().setClassInitTypeAttributes(l); + } + public List getDeclarationAttributes() { return (metadata == null) ? List.nil() diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java index b97b5579b31..e594e25e5d7 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java @@ -26,19 +26,20 @@ package com.sun.tools.javac.code; import java.lang.annotation.Annotation; +import java.util.ArrayDeque; import java.util.Collections; import java.util.EnumMap; -import java.util.EnumSet; import java.util.Map; -import java.util.Set; import java.util.function.Function; import javax.lang.model.type.*; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.TypeMetadata.Entry; +import com.sun.tools.javac.comp.Infer.IncorporationAction; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.DefinedBy.Api; + import static com.sun.tools.javac.code.BoundKind.*; import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Kinds.Kind.*; @@ -1826,17 +1827,15 @@ public abstract class Type extends AnnoConstruct implements TypeMirror { */ public interface UndetVarListener { /** called when some inference variable bounds (of given kinds ibs) change */ - void varChanged(UndetVar uv, Set ibs); + void varBoundChanged(UndetVar uv, InferenceBound ib, Type bound, boolean update); + /** called when the inferred type is set on some inference variable */ + default void varInstantiated(UndetVar uv) { Assert.error(); } } /** * Inference variable bound kinds */ public enum InferenceBound { - /** upper bounds */ - UPPER { - public InferenceBound complement() { return LOWER; } - }, /** lower bounds */ LOWER { public InferenceBound complement() { return UPPER; } @@ -1844,16 +1843,38 @@ public abstract class Type extends AnnoConstruct implements TypeMirror { /** equality constraints */ EQ { public InferenceBound complement() { return EQ; } + }, + /** upper bounds */ + UPPER { + public InferenceBound complement() { return LOWER; } }; public abstract InferenceBound complement(); + + public boolean lessThan(InferenceBound that) { + if (that == this) { + return false; + } else { + switch (that) { + case UPPER: return true; + case LOWER: return false; + case EQ: return (this != UPPER); + default: + Assert.error("Cannot get here!"); + return false; + } + } + } } + /** list of incorporation actions (used by the incorporation engine). */ + public ArrayDeque incorporationActions = new ArrayDeque<>(); + /** inference variable bounds */ protected Map> bounds; /** inference variable's inferred type (set from Infer.java) */ - public Type inst = null; + private Type inst = null; /** number of declared (upper) bounds */ public int declaredCount; @@ -1866,15 +1887,20 @@ public abstract class Type extends AnnoConstruct implements TypeMirror { return v.visitUndetVar(this, s); } - public UndetVar(TypeVar origin, Types types) { + public UndetVar(TypeVar origin, UndetVarListener listener, Types types) { // This is a synthesized internal type, so we cannot annotate it. super(UNDETVAR, origin); + this.listener = listener; bounds = new EnumMap<>(InferenceBound.class); List declaredBounds = types.getBounds(origin); declaredCount = declaredBounds.length(); - bounds.put(InferenceBound.UPPER, declaredBounds); - bounds.put(InferenceBound.LOWER, List.nil()); - bounds.put(InferenceBound.EQ, List.nil()); + bounds.put(InferenceBound.UPPER, List.nil()); + bounds.put(InferenceBound.LOWER, List.nil()); + bounds.put(InferenceBound.EQ, List.nil()); + for (Type t : declaredBounds.reverse()) { + //add bound works in reverse order + addBound(InferenceBound.UPPER, t, types, true); + } } @DefinedBy(Api.LANGUAGE_MODEL) @@ -1904,6 +1930,32 @@ public abstract class Type extends AnnoConstruct implements TypeMirror { return result; } + /** + * Returns a new copy of this undet var. + */ + public UndetVar dup(Types types) { + UndetVar uv2 = new UndetVar((TypeVar)qtype, listener, types); + dupTo(uv2, types); + return uv2; + } + + /** + * Dumps the contents of this undet var on another undet var. + */ + public void dupTo(UndetVar uv2, Types types) { + uv2.listener = null; + uv2.bounds.clear(); + for (InferenceBound ib : InferenceBound.values()) { + uv2.bounds.put(ib, List.nil()); + for (Type t : getBounds(ib)) { + uv2.addBound(ib, t, types, true); + } + } + uv2.inst = inst; + uv2.listener = listener; + uv2.incorporationActions = new ArrayDeque<>(incorporationActions); + } + @Override public UndetVar cloneWithMetadata(TypeMetadata md) { throw new AssertionError("Cannot add metadata to an UndetVar type"); @@ -1919,6 +1971,17 @@ public abstract class Type extends AnnoConstruct implements TypeMirror { return (inst == null) ? this : inst.baseType(); } + public Type getInst() { + return inst; + } + + public void setInst(Type inst) { + this.inst = inst; + if (listener != null) { + listener.varInstantiated(this); + } + } + /** get all bounds of a given kind */ public List getBounds(InferenceBound... ibs) { ListBuffer buf = new ListBuffer<>(); @@ -1952,13 +2015,14 @@ public abstract class Type extends AnnoConstruct implements TypeMirror { protected void addBound(InferenceBound ib, Type bound, Types types, boolean update) { Type bound2 = bound.map(toTypeVarMap).baseType(); List prevBounds = bounds.get(ib); + if (bound == qtype) return; for (Type b : prevBounds) { //check for redundancy - use strict version of isSameType on tvars //(as the standard version will lead to false positives w.r.t. clones ivars) - if (types.isSameType(b, bound2, true) || bound == qtype) return; + if (types.isSameType(b, bound2, true)) return; } bounds.put(ib, prevBounds.prepend(bound2)); - notifyChange(EnumSet.of(ib)); + notifyBoundChange(ib, bound2, false); } //where TypeMapping toTypeVarMap = new TypeMapping() { @@ -1970,16 +2034,14 @@ public abstract class Type extends AnnoConstruct implements TypeMirror { /** replace types in all bounds - this might trigger listener notification */ public void substBounds(List from, List to, Types types) { - List instVars = from.diff(to); - //if set of instantiated ivars is empty, there's nothing to do! - if (instVars.isEmpty()) return; - final EnumSet boundsChanged = EnumSet.noneOf(InferenceBound.class); + final ListBuffer> boundsChanged = new ListBuffer<>(); UndetVarListener prevListener = listener; try { //setup new listener for keeping track of changed bounds listener = new UndetVarListener() { - public void varChanged(UndetVar uv, Set ibs) { - boundsChanged.addAll(ibs); + public void varBoundChanged(UndetVar uv, InferenceBound ib, Type t, boolean _ignored) { + Assert.check(uv == UndetVar.this); + boundsChanged.add(new Pair<>(ib, t)); } }; for (Map.Entry> _entry : bounds.entrySet()) { @@ -1989,7 +2051,7 @@ public abstract class Type extends AnnoConstruct implements TypeMirror { ListBuffer deps = new ListBuffer<>(); //step 1 - re-add bounds that are not dependent on ivars for (Type t : prevBounds) { - if (!t.containsAny(instVars)) { + if (!t.containsAny(from)) { newBounds.append(t); } else { deps.append(t); @@ -2004,15 +2066,15 @@ public abstract class Type extends AnnoConstruct implements TypeMirror { } } finally { listener = prevListener; - if (!boundsChanged.isEmpty()) { - notifyChange(boundsChanged); + for (Pair boundUpdate : boundsChanged) { + notifyBoundChange(boundUpdate.fst, boundUpdate.snd, true); } } } - private void notifyChange(EnumSet ibs) { + private void notifyBoundChange(InferenceBound ib, Type bound, boolean update) { if (listener != null) { - listener.varChanged(this, ibs); + listener.varBoundChanged(this, ib, bound, update); } } @@ -2029,10 +2091,10 @@ public abstract class Type extends AnnoConstruct implements TypeMirror { */ public static class CapturedUndetVar extends UndetVar { - public CapturedUndetVar(CapturedType origin, Types types) { - super(origin, types); + public CapturedUndetVar(CapturedType origin, UndetVarListener listener, Types types) { + super(origin, listener, types); if (!origin.lower.hasTag(BOT)) { - bounds.put(InferenceBound.LOWER, List.of(origin.lower)); + addBound(InferenceBound.LOWER, origin.lower, types, true); } } @@ -2051,6 +2113,12 @@ public abstract class Type extends AnnoConstruct implements TypeMirror { public boolean isCaptured() { return true; } + + public UndetVar dup(Types types) { + UndetVar uv2 = new CapturedUndetVar((CapturedType)qtype, listener, types); + dupTo(uv2, types); + return uv2; + } } /** Represents NONE. diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java index 4bc4fc6ddfd..14318bba74e 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java @@ -32,6 +32,7 @@ import javax.tools.JavaFileObject; import com.sun.tools.javac.code.Attribute.Array; import com.sun.tools.javac.code.Attribute.TypeCompound; +import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.TypeSymbol; import com.sun.tools.javac.code.Type.ArrayType; import com.sun.tools.javac.code.Type.CapturedType; @@ -388,8 +389,21 @@ public class TypeAnnotations { sym.getKind() == ElementKind.RESOURCE_VARIABLE || sym.getKind() == ElementKind.EXCEPTION_PARAMETER) { // Make sure all type annotations from the symbol are also - // on the owner. - sym.owner.appendUniqueTypeAttributes(sym.getRawTypeAttributes()); + // on the owner. If the owner is an initializer block, propagate + // to the type. + final long ownerFlags = sym.owner.flags(); + if ((ownerFlags & Flags.BLOCK) != 0) { + // Store init and clinit type annotations with the ClassSymbol + // to allow output in Gen.normalizeDefs. + ClassSymbol cs = (ClassSymbol) sym.owner.owner; + if ((ownerFlags & Flags.STATIC) != 0) { + cs.appendClassInitTypeAttributes(typeAnnotations); + } else { + cs.appendInitTypeAttributes(typeAnnotations); + } + } else { + sym.owner.appendUniqueTypeAttributes(sym.getRawTypeAttributes()); + } } } diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index 13136d79e3f..97b56f1070d 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -2181,6 +2181,12 @@ public class Types { } } + @Override + public Type visitWildcardType(WildcardType t, Boolean recurse) { + Type erased = erasure(wildUpperBound(t), recurse); + return combineMetadata(erased, t); + } + @Override public Type visitClassType(ClassType t, Boolean recurse) { Type erased = t.tsym.erasure(Types.this); @@ -2763,7 +2769,7 @@ public class Types { Scope s = c.members(); for (Symbol sym : s.getSymbols(NON_RECURSIVE)) { if (sym.kind == MTH && - (sym.flags() & (ABSTRACT|IPROXY|DEFAULT|PRIVATE)) == ABSTRACT) { + (sym.flags() & (ABSTRACT|DEFAULT|PRIVATE)) == ABSTRACT) { MethodSymbol absmeth = (MethodSymbol)sym; MethodSymbol implmeth = absmeth.implementation(impl, this, true); if (implmeth == null || implmeth == absmeth) { @@ -3837,7 +3843,7 @@ public class Types { @Override public Integer visitTypeVar(TypeVar t, Void ignored) { - return System.identityHashCode(t.tsym); + return System.identityHashCode(t); } @Override diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 76666ce5313..3a663f981ae 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -840,8 +840,10 @@ public class Attr extends JCTree.Visitor { boolean classExpected, boolean interfaceExpected, boolean checkExtensible) { + final DiagnosticPosition pos = tree.hasTag(TYPEAPPLY) ? + (((JCTypeApply) tree).clazz).pos() : tree.pos(); if (t.tsym.isAnonymous()) { - log.error(tree.pos(), "cant.inherit.from.anon"); + log.error(pos, "cant.inherit.from.anon"); return types.createErrorType(t); } if (t.isErroneous()) @@ -849,29 +851,29 @@ public class Attr extends JCTree.Visitor { if (t.hasTag(TYPEVAR) && !classExpected && !interfaceExpected) { // check that type variable is already visible if (t.getUpperBound() == null) { - log.error(tree.pos(), "illegal.forward.ref"); + log.error(pos, "illegal.forward.ref"); return types.createErrorType(t); } } else { - t = chk.checkClassType(tree.pos(), t, checkExtensible); + t = chk.checkClassType(pos, t, checkExtensible); } if (interfaceExpected && (t.tsym.flags() & INTERFACE) == 0) { - log.error(tree.pos(), "intf.expected.here"); + log.error(pos, "intf.expected.here"); // return errType is necessary since otherwise there might // be undetected cycles which cause attribution to loop return types.createErrorType(t); } else if (checkExtensible && classExpected && (t.tsym.flags() & INTERFACE) != 0) { - log.error(tree.pos(), "no.intf.expected.here"); + log.error(pos, "no.intf.expected.here"); return types.createErrorType(t); } if (checkExtensible && ((t.tsym.flags() & FINAL) != 0)) { - log.error(tree.pos(), + log.error(pos, "cant.inherit.from.final", t.tsym); } - chk.checkNonCyclic(tree.pos(), t); + chk.checkNonCyclic(pos, t); return t; } @@ -1890,7 +1892,8 @@ public class Attr extends JCTree.Visitor { // Check that value of resulting type is admissible in the // current context. Also, capture the return type - result = check(tree, capture(restype), KindSelector.VAL, resultInfo); + Type capturedRes = resultInfo.checkContext.inferenceContext().cachedCapture(tree, restype, true); + result = check(tree, capturedRes, KindSelector.VAL, resultInfo); } chk.validate(tree.typeargs, localEnv); } diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 001443e0579..fc62e8a7865 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -1730,11 +1730,18 @@ public class Check { boolean resultTypesOK = types.returnTypeSubstitutable(mt, ot, otres, overrideWarner); if (!resultTypesOK) { - log.error(TreeInfo.diagnosticPositionFor(m, tree), - "override.incompatible.ret", - cannotOverride(m, other), - mtres, otres); - m.flags_field |= BAD_OVERRIDE; + if ((m.flags() & STATIC) != 0 && (other.flags() & STATIC) != 0) { + log.error(TreeInfo.diagnosticPositionFor(m, tree), + Errors.OverrideIncompatibleRet(Fragments.CantHide(m, m.location(), other, + other.location()), mtres, otres)); + m.flags_field |= BAD_OVERRIDE; + } else { + log.error(TreeInfo.diagnosticPositionFor(m, tree), + "override.incompatible.ret", + cannotOverride(m, other), + mtres, otres); + m.flags_field |= BAD_OVERRIDE; + } return; } else if (overrideWarner.hasNonSilentLint(LintCategory.UNCHECKED)) { warnUnchecked(TreeInfo.diagnosticPositionFor(m, tree), @@ -2371,7 +2378,6 @@ public class Check { types.isSameType(types.erasure(sym.type), types.erasure(sym2.type)) && sym != sym2 && (sym.flags() & Flags.SYNTHETIC) != (sym2.flags() & Flags.SYNTHETIC) && - (sym.flags() & IPROXY) == 0 && (sym2.flags() & IPROXY) == 0 && (sym.flags() & BRIDGE) == 0 && (sym2.flags() & BRIDGE) == 0) { syntheticError(pos, (sym2.flags() & SYNTHETIC) == 0 ? sym2 : sym); return; diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java index fa61200d21e..9f30e750db3 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java @@ -25,6 +25,7 @@ package com.sun.tools.javac.comp; +import com.sun.tools.javac.code.Type.UndetVar.UndetVarListener; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCTypeCast; import com.sun.tools.javac.tree.TreeInfo; @@ -57,6 +58,8 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; import static com.sun.tools.javac.code.TypeTag.*; @@ -182,20 +185,29 @@ public class Infer { argtypes, mt.getParameterTypes(), warn); if (allowGraphInference && resultInfo != null && resultInfo.pt == anyPoly) { + doIncorporation(inferenceContext, warn); //we are inside method attribution - just return a partially inferred type return new PartiallyInferredMethodType(mt, inferenceContext, env, warn); } else if (allowGraphInference && resultInfo != null && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) { //inject return constraints earlier - checkWithinBounds(inferenceContext, warn); //propagation + doIncorporation(inferenceContext, warn); //propagation + + boolean shouldPropagate = resultInfo.checkContext.inferenceContext().free(resultInfo.pt); + + InferenceContext minContext = shouldPropagate ? + inferenceContext.min(roots(mt, deferredAttrContext), true, warn) : + inferenceContext; + Type newRestype = generateReturnConstraints(env.tree, resultInfo, //B3 - mt, inferenceContext); + mt, minContext); mt = (MethodType)types.createMethodTypeWithReturn(mt, newRestype); + //propagate outwards if needed - if (resultInfo.checkContext.inferenceContext().free(resultInfo.pt)) { + if (shouldPropagate) { //propagate inference context outwards and exit - inferenceContext.dupTo(resultInfo.checkContext.inferenceContext()); + minContext.dupTo(resultInfo.checkContext.inferenceContext()); deferredAttrContext.complete(); return mt; } @@ -242,6 +254,19 @@ public class Infer { dumpGraphsIfNeeded(env.tree, msym, resolveContext); } } + //where + private List roots(MethodType mt, DeferredAttrContext deferredAttrContext) { + ListBuffer roots = new ListBuffer<>(); + roots.add(mt.getReturnType()); + if (deferredAttrContext != null && deferredAttrContext.mode == AttrMode.CHECK) { + roots.addAll(mt.getThrownTypes()); + for (DeferredAttr.DeferredAttrNode n : deferredAttrContext.deferredAttrNodes) { + roots.addAll(n.deferredStuckPolicy.stuckVars()); + roots.addAll(n.deferredStuckPolicy.depVars()); + } + } + return roots.toList(); + } /** * A partially infered method/constructor type; such a type can be checked multiple times @@ -284,16 +309,21 @@ public class Infer { */ saved_undet = inferenceContext.save(); if (allowGraphInference && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) { - //inject return constraints earlier - checkWithinBounds(inferenceContext, noWarnings); //propagation - Type res = generateReturnConstraints(env.tree, resultInfo, //B3 - this, inferenceContext); + boolean shouldPropagate = resultInfo.checkContext.inferenceContext().free(resultInfo.pt); - if (resultInfo.checkContext.inferenceContext().free(resultInfo.pt)) { + InferenceContext minContext = shouldPropagate ? + inferenceContext.min(roots(asMethodType(), null), false, warn) : + inferenceContext; + + MethodType other = (MethodType)minContext.update(asMethodType()); + Type newRestype = generateReturnConstraints(env.tree, resultInfo, //B3 + other, minContext); + + if (shouldPropagate) { //propagate inference context outwards and exit - inferenceContext.dupTo(resultInfo.checkContext.inferenceContext(), + minContext.dupTo(resultInfo.checkContext.inferenceContext(), resultInfo.checkContext.deferredAttrContext().insideOverloadPhase()); - return res; + return newRestype; } } inferenceContext.solve(noWarnings); @@ -372,6 +402,9 @@ public class Infer { resultInfo, inferenceContext); } } + } else if (rsInfoInfContext.free(resultInfo.pt)) { + //propagation - cache captured vars + qtype = inferenceContext.asUndetVar(rsInfoInfContext.cachedCapture(tree, from, false)); } Assert.check(allowGraphInference || !rsInfoInfContext.free(to), "legacy inference engine cannot handle constraints on both sides of a subtyping assertion"); @@ -471,7 +504,7 @@ public class Infer { inferenceContext.solve(List.of(from.qtype), new Warner()); inferenceContext.notifyChange(); Type capturedType = resultInfo.checkContext.inferenceContext() - .cachedCapture(tree, from.inst, false); + .cachedCapture(tree, from.getInst(), false); if (types.isConvertible(capturedType, resultInfo.checkContext.inferenceContext().asUndetVar(to))) { //effectively skip additional return-type constraint generation (compatibility) @@ -493,22 +526,22 @@ public class Infer { TypeSymbol fresh_tvar = new TypeVariableSymbol(Flags.SYNTHETIC, uv.qtype.tsym.name, null, uv.qtype.tsym.owner); fresh_tvar.type = new TypeVar(fresh_tvar, types.makeIntersectionType(uv.getBounds(InferenceBound.UPPER)), null); todo.append(uv); - uv.inst = fresh_tvar.type; + uv.setInst(fresh_tvar.type); } else if (upperBounds.nonEmpty()) { - uv.inst = types.glb(upperBounds); + uv.setInst(types.glb(upperBounds)); } else { - uv.inst = syms.objectType; + uv.setInst(syms.objectType); } } //step 2 - replace fresh tvars in their bounds List formals = vars; for (Type t : todo) { UndetVar uv = (UndetVar)t; - TypeVar ct = (TypeVar)uv.inst; + TypeVar ct = (TypeVar)uv.getInst(); ct.bound = types.glb(inferenceContext.asInstTypes(types.getBounds(ct))); if (ct.bound.isErroneous()) { //report inference error if glb fails - reportBoundError(uv, BoundErrorKind.BAD_UPPER); + reportBoundError(uv, InferenceBound.UPPER); } formals = formals.tail; } @@ -586,6 +619,18 @@ public class Infer { } } + TypeMapping fromTypeVarFun = new TypeMapping() { + @Override + public Type visitTypeVar(TypeVar tv, Void aVoid) { + return new UndetVar(tv, incorporationEngine(), types); + } + + @Override + public Type visitCapturedType(CapturedType t, Void aVoid) { + return new CapturedUndetVar(t, incorporationEngine(), types); + } + }; + /** * This method is used to infer a suitable target SAM in case the original * SAM type contains one or more wildcards. An inference process is applied @@ -629,8 +674,8 @@ public class Infer { List actualTypeargs = funcInterface.getTypeArguments(); for (Type t : funcInterfaceContext.undetvars) { UndetVar uv = (UndetVar)t; - if (uv.inst == null) { - uv.inst = actualTypeargs.head; + if (uv.getInst() == null) { + uv.setInst(actualTypeargs.head); } actualTypeargs = actualTypeargs.tail; } @@ -648,88 +693,399 @@ public class Infer { } // - // + // + /** - * Check bounds and perform incorporation + * This class is the root of all incorporation actions. */ - void checkWithinBounds(InferenceContext inferenceContext, - Warner warn) throws InferenceException { - MultiUndetVarListener mlistener = new MultiUndetVarListener(inferenceContext.undetvars); - List saved_undet = inferenceContext.save(); - try { - while (true) { - mlistener.reset(); - if (!allowGraphInference) { - //in legacy mode we lack of transitivity, so bound check - //cannot be run in parallel with other incoprporation rounds - for (Type t : inferenceContext.undetvars) { - UndetVar uv = (UndetVar)t; - IncorporationStep.CHECK_BOUNDS.apply(uv, inferenceContext, warn); + public abstract class IncorporationAction { + UndetVar uv; + Type t; + + IncorporationAction(UndetVar uv, Type t) { + this.uv = uv; + this.t = t; + } + + /** + * Incorporation action entry-point. Subclasses should define the logic associated with + * this incorporation action. + */ + abstract void apply(InferenceContext ic, Warner warn); + + /** + * Helper function: perform subtyping through incorporation cache. + */ + boolean isSubtype(Type s, Type t, Warner warn) { + return doIncorporationOp(IncorporationBinaryOpKind.IS_SUBTYPE, s, t, warn); + } + + /** + * Helper function: perform type-equivalence through incorporation cache. + */ + boolean isSameType(Type s, Type t) { + return doIncorporationOp(IncorporationBinaryOpKind.IS_SAME_TYPE, s, t, null); + } + + @Override + public String toString() { + return String.format("%s[undet=%s,t=%s]", getClass().getSimpleName(), uv.qtype, t); + } + } + + /** + * Bound-check incorporation action. A newly added bound is checked against existing bounds, + * to verify its compatibility; each bound is checked using either subtyping or type equivalence. + */ + class CheckBounds extends IncorporationAction { + + InferenceBound from; + BiFunction typeFunc; + BiPredicate optFilter; + + CheckBounds(UndetVar uv, Type t, InferenceBound from) { + this(uv, t, InferenceContext::asUndetVar, null, from); + } + + CheckBounds(UndetVar uv, Type t, BiFunction typeFunc, + BiPredicate typeFilter, InferenceBound from) { + super(uv, t); + this.from = from; + this.typeFunc = typeFunc; + this.optFilter = typeFilter; + } + + @Override + void apply(InferenceContext inferenceContext, Warner warn) { + t = typeFunc.apply(inferenceContext, t); + if (optFilter != null && optFilter.test(inferenceContext, t)) return; + for (InferenceBound to : boundsToCheck()) { + for (Type b : uv.getBounds(to)) { + b = typeFunc.apply(inferenceContext, b); + if (optFilter != null && optFilter.test(inferenceContext, b)) continue; + boolean success = checkBound(t, b, from, to, warn); + if (!success) { + report(from, to); } } - for (Type t : inferenceContext.undetvars) { - UndetVar uv = (UndetVar)t; - //bound incorporation - EnumSet incorporationSteps = allowGraphInference ? - incorporationStepsGraph : incorporationStepsLegacy; - for (IncorporationStep is : incorporationSteps) { - if (is.accepts(uv, inferenceContext)) { - is.apply(uv, inferenceContext, warn); - } - } - } - if (!mlistener.changed || !allowGraphInference) break; } } - finally { - mlistener.detach(); - if (incorporationCache.size() == MAX_INCORPORATION_STEPS) { - inferenceContext.rollback(saved_undet); + + /** + * The list of bound kinds to be checked. + */ + EnumSet boundsToCheck() { + return (from == InferenceBound.EQ) ? + EnumSet.allOf(InferenceBound.class) : + EnumSet.complementOf(EnumSet.of(from)); + } + + /** + * Is source type 's' compatible with target type 't' given source and target bound kinds? + */ + boolean checkBound(Type s, Type t, InferenceBound ib_s, InferenceBound ib_t, Warner warn) { + if (ib_s.lessThan(ib_t)) { + return isSubtype(s, t, warn); + } else if (ib_t.lessThan(ib_s)) { + return isSubtype(t, s, warn); + } else { + return isSameType(s, t); } + } + + /** + * Report a bound check error. + */ + void report(InferenceBound from, InferenceBound to) { + //this is a workaround to preserve compatibility with existing messages + if (from == to) { + reportBoundError(uv, from); + } else if (from == InferenceBound.LOWER || to == InferenceBound.EQ) { + reportBoundError(uv, to, from); + } else { + reportBoundError(uv, from, to); + } + } + + @Override + public String toString() { + return String.format("%s[undet=%s,t=%s,bound=%s]", getClass().getSimpleName(), uv.qtype, t, from); + } + } + + /** + * Custom check executed by the legacy incorporation engine. Newly added bounds are checked + * against existing eq bounds. + */ + class EqCheckLegacy extends CheckBounds { + EqCheckLegacy(UndetVar uv, Type t, InferenceBound from) { + super(uv, t, InferenceContext::asInstType, InferenceContext::free, from); + } + + @Override + EnumSet boundsToCheck() { + return (from == InferenceBound.EQ) ? + EnumSet.allOf(InferenceBound.class) : + EnumSet.of(InferenceBound.EQ); + } + } + + /** + * Check that the inferred type conforms to all bounds. + */ + class CheckInst extends CheckBounds { + + EnumSet to; + + CheckInst(UndetVar uv, InferenceBound ib, InferenceBound... rest) { + super(uv, uv.getInst(), InferenceBound.EQ); + this.to = EnumSet.of(ib, rest); + } + + @Override + EnumSet boundsToCheck() { + return to; + } + + @Override + void report(InferenceBound from, InferenceBound to) { + reportInstError(uv, to); + } + } + + /** + * Replace undetvars in bounds and check that the inferred type conforms to all bounds. + */ + class SubstBounds extends CheckInst { + SubstBounds(UndetVar uv) { + super(uv, InferenceBound.LOWER, InferenceBound.EQ, InferenceBound.UPPER); + } + + @Override + void apply(InferenceContext inferenceContext, Warner warn) { + for (Type undet : inferenceContext.undetvars) { + //we could filter out variables not mentioning uv2... + UndetVar uv2 = (UndetVar)undet; + uv2.substBounds(List.of(uv.qtype), List.of(uv.getInst()), types); + checkCompatibleUpperBounds(uv2, inferenceContext); + } + super.apply(inferenceContext, warn); + } + + /** + * Make sure that the upper bounds we got so far lead to a solvable inference + * variable by making sure that a glb exists. + */ + void checkCompatibleUpperBounds(UndetVar uv, InferenceContext inferenceContext) { + List hibounds = + Type.filter(uv.getBounds(InferenceBound.UPPER), new BoundFilter(inferenceContext)); + final Type hb; + if (hibounds.isEmpty()) + hb = syms.objectType; + else if (hibounds.tail.isEmpty()) + hb = hibounds.head; + else + hb = types.glb(hibounds); + if (hb == null || hb.isErroneous()) + reportBoundError(uv, InferenceBound.UPPER); + } + } + + /** + * Perform pairwise comparison between common generic supertypes of two upper bounds. + */ + class CheckUpperBounds extends IncorporationAction { + + public CheckUpperBounds(UndetVar uv, Type t) { + super(uv, t); + } + + @Override + void apply(InferenceContext inferenceContext, Warner warn) { + List boundList = uv.getBounds(InferenceBound.UPPER).stream() + .collect(types.closureCollector(true, types::isSameType)); + for (Type b2 : boundList) { + if (t == b2) continue; + /* This wildcard check is temporary workaround. This code may need to be + * revisited once spec bug JDK-7034922 is fixed. + */ + if (t != b2 && !t.hasTag(WILDCARD) && !b2.hasTag(WILDCARD)) { + for (Pair commonSupers : getParameterizedSupers(t, b2)) { + List allParamsSuperBound1 = commonSupers.fst.allparams(); + List allParamsSuperBound2 = commonSupers.snd.allparams(); + while (allParamsSuperBound1.nonEmpty() && allParamsSuperBound2.nonEmpty()) { + //traverse the list of all params comparing them + if (!allParamsSuperBound1.head.hasTag(WILDCARD) && + !allParamsSuperBound2.head.hasTag(WILDCARD)) { + if (!isSameType(inferenceContext.asUndetVar(allParamsSuperBound1.head), + inferenceContext.asUndetVar(allParamsSuperBound2.head))) { + reportBoundError(uv, InferenceBound.UPPER); + } + } + allParamsSuperBound1 = allParamsSuperBound1.tail; + allParamsSuperBound2 = allParamsSuperBound2.tail; + } + Assert.check(allParamsSuperBound1.isEmpty() && allParamsSuperBound2.isEmpty()); + } + } + } + } + } + + /** + * Perform propagation of bounds. Given a constraint of the kind {@code alpha <: T}, three + * kind of propagation occur: + * + *

  • T is copied into all matching bounds (i.e. lower/eq bounds) B of alpha such that B=beta (forward propagation)
  • + *
  • if T=beta, matching bounds (i.e. upper bounds) of beta are copied into alpha (backwards propagation)
  • + *
  • if T=beta, sets a symmetric bound on beta (i.e. beta :> alpha) (symmetric propagation)
  • + */ + class PropagateBounds extends IncorporationAction { + + InferenceBound ib; + + public PropagateBounds(UndetVar uv, Type t, InferenceBound ib) { + super(uv, t); + this.ib = ib; + } + + void apply(InferenceContext inferenceContext, Warner warner) { + Type undetT = inferenceContext.asUndetVar(t); + if (undetT.hasTag(UNDETVAR) && !((UndetVar)undetT).isCaptured()) { + UndetVar uv2 = (UndetVar)undetT; + //symmetric propagation + uv2.addBound(ib.complement(), uv, types); + //backwards propagation + for (InferenceBound ib2 : backwards()) { + for (Type b : uv2.getBounds(ib2)) { + uv.addBound(ib2, b, types); + } + } + } + //forward propagation + for (InferenceBound ib2 : forward()) { + for (Type l : uv.getBounds(ib2)) { + Type undet = inferenceContext.asUndetVar(l); + if (undet.hasTag(TypeTag.UNDETVAR) && !((UndetVar)undet).isCaptured()) { + UndetVar uv2 = (UndetVar)undet; + uv2.addBound(ib, inferenceContext.asInstType(t), types); + } + } + } + } + + EnumSet forward() { + return (ib == InferenceBound.EQ) ? + EnumSet.of(InferenceBound.EQ) : EnumSet.complementOf(EnumSet.of(ib)); + } + + EnumSet backwards() { + return (ib == InferenceBound.EQ) ? + EnumSet.allOf(InferenceBound.class) : EnumSet.of(ib); + } + + @Override + public String toString() { + return String.format("%s[undet=%s,t=%s,bound=%s]", getClass().getSimpleName(), uv.qtype, t, ib); + } + } + + /** + * This class models an incorporation engine. The engine is responsible for listening to + * changes in inference variables and register incorporation actions accordingly. + */ + abstract class AbstractIncorporationEngine implements UndetVarListener { + + @Override + public void varInstantiated(UndetVar uv) { + uv.incorporationActions.addFirst(new SubstBounds(uv)); + } + + @Override + public void varBoundChanged(UndetVar uv, InferenceBound ib, Type bound, boolean update) { + if (uv.isCaptured()) return; + uv.incorporationActions.addAll(getIncorporationActions(uv, ib, bound, update)); + } + + abstract List getIncorporationActions(UndetVar uv, InferenceBound ib, Type t, boolean update); + } + + /** + * A legacy incorporation engine. Used for source <= 7. + */ + AbstractIncorporationEngine legacyEngine = new AbstractIncorporationEngine() { + + List getIncorporationActions(UndetVar uv, InferenceBound ib, Type t, boolean update) { + ListBuffer actions = new ListBuffer<>(); + Type inst = uv.getInst(); + if (inst != null) { + actions.add(new CheckInst(uv, ib)); + } + actions.add(new EqCheckLegacy(uv, t, ib)); + return actions.toList(); + } + }; + + /** + * The standard incorporation engine. Used for source >= 8. + */ + AbstractIncorporationEngine graphEngine = new AbstractIncorporationEngine() { + + @Override + List getIncorporationActions(UndetVar uv, InferenceBound ib, Type t, boolean update) { + ListBuffer actions = new ListBuffer<>(); + Type inst = uv.getInst(); + if (inst != null) { + actions.add(new CheckInst(uv, ib)); + } + actions.add(new CheckBounds(uv, t, ib)); + + if (update) { + return actions.toList(); + } + + if (ib == InferenceBound.UPPER) { + actions.add(new CheckUpperBounds(uv, t)); + } + + actions.add(new PropagateBounds(uv, t, ib)); + + return actions.toList(); + } + }; + + /** + * Get the incorporation engine to be used in this compilation. + */ + AbstractIncorporationEngine incorporationEngine() { + return allowGraphInference ? graphEngine : legacyEngine; + } + + /** max number of incorporation rounds. */ + static final int MAX_INCORPORATION_STEPS = 10000; + + /** + * Check bounds and perform incorporation. + */ + void doIncorporation(InferenceContext inferenceContext, Warner warn) throws InferenceException { + try { + boolean progress = true; + int round = 0; + while (progress && round < MAX_INCORPORATION_STEPS) { + progress = false; + for (Type t : inferenceContext.undetvars) { + UndetVar uv = (UndetVar)t; + if (!uv.incorporationActions.isEmpty()) { + progress = true; + uv.incorporationActions.removeFirst().apply(inferenceContext, warn); + } + } + round++; + } + } finally { incorporationCache.clear(); } } - //where - /** - * This listener keeps track of changes on a group of inference variable - * bounds. Note: the listener must be detached (calling corresponding - * method) to make sure that the underlying inference variable is - * left in a clean state. - */ - class MultiUndetVarListener implements UndetVar.UndetVarListener { - - boolean changed; - List undetvars; - - public MultiUndetVarListener(List undetvars) { - this.undetvars = undetvars; - for (Type t : undetvars) { - UndetVar uv = (UndetVar)t; - uv.listener = this; - } - } - - public void varChanged(UndetVar uv, Set ibs) { - //avoid non-termination - if (incorporationCache.size() < MAX_INCORPORATION_STEPS) { - changed = true; - } - } - - void reset() { - changed = false; - } - - void detach() { - for (Type t : undetvars) { - UndetVar uv = (UndetVar)t; - uv.listener = null; - } - } - } - - /** max number of incorporation rounds */ - static final int MAX_INCORPORATION_STEPS = 100; /* If for two types t and s there is a least upper bound that contains * parameterized types G1, G2 ... Gn, then there exists supertypes of 't' of the form @@ -771,407 +1127,14 @@ public class Infer { types.asSuper(t, sup.tsym); } - /** - * This enumeration defines an entry point for doing inference variable - * bound incorporation - it can be used to inject custom incorporation - * logic into the basic bound checking routine - */ - enum IncorporationStep { - /** - * Performs basic bound checking - i.e. is the instantiated type for a given - * inference variable compatible with its bounds? - */ - CHECK_BOUNDS() { - public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { - Infer infer = inferenceContext.infer; - uv.substBounds(inferenceContext.inferenceVars(), inferenceContext.instTypes(), infer.types); - infer.checkCompatibleUpperBounds(uv, inferenceContext); - if (uv.inst != null) { - Type inst = uv.inst; - for (Type u : uv.getBounds(InferenceBound.UPPER)) { - if (!isSubtype(inst, inferenceContext.asUndetVar(u), warn, infer)) { - infer.reportBoundError(uv, BoundErrorKind.UPPER); - } - } - for (Type l : uv.getBounds(InferenceBound.LOWER)) { - if (!isSubtype(inferenceContext.asUndetVar(l), inst, warn, infer)) { - infer.reportBoundError(uv, BoundErrorKind.LOWER); - } - } - for (Type e : uv.getBounds(InferenceBound.EQ)) { - if (!isSameType(inst, inferenceContext.asUndetVar(e), infer)) { - infer.reportBoundError(uv, BoundErrorKind.EQ); - } - } - } - } - - @Override - boolean accepts(UndetVar uv, InferenceContext inferenceContext) { - //applies to all undetvars - return true; - } - }, - /** - * Check consistency of equality constraints. This is a slightly more aggressive - * inference routine that is designed as to maximize compatibility with JDK 7. - * Note: this is not used in graph mode. - */ - EQ_CHECK_LEGACY() { - public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { - Infer infer = inferenceContext.infer; - Type eq = null; - for (Type e : uv.getBounds(InferenceBound.EQ)) { - Assert.check(!inferenceContext.free(e)); - if (eq != null && !isSameType(e, eq, infer)) { - infer.reportBoundError(uv, BoundErrorKind.EQ); - } - eq = e; - for (Type l : uv.getBounds(InferenceBound.LOWER)) { - Assert.check(!inferenceContext.free(l)); - if (!isSubtype(l, e, warn, infer)) { - infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER); - } - } - for (Type u : uv.getBounds(InferenceBound.UPPER)) { - if (inferenceContext.free(u)) continue; - if (!isSubtype(e, u, warn, infer)) { - infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER); - } - } - } - } - - @Override - boolean accepts(UndetVar uv, InferenceContext inferenceContext) { - return !uv.isCaptured() && uv.getBounds(InferenceBound.EQ).nonEmpty(); - } - }, - /** - * Check consistency of equality constraints. - */ - EQ_CHECK() { - @Override - public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { - Infer infer = inferenceContext.infer; - for (Type e : uv.getBounds(InferenceBound.EQ)) { - if (e.containsAny(inferenceContext.inferenceVars())) continue; - for (Type u : uv.getBounds(InferenceBound.UPPER)) { - if (!isSubtype(e, inferenceContext.asUndetVar(u), warn, infer)) { - infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER); - } - } - for (Type l : uv.getBounds(InferenceBound.LOWER)) { - if (!isSubtype(inferenceContext.asUndetVar(l), e, warn, infer)) { - infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER); - } - } - } - } - - @Override - boolean accepts(UndetVar uv, InferenceContext inferenceContext) { - return !uv.isCaptured() && uv.getBounds(InferenceBound.EQ).nonEmpty(); - } - }, - /** - * Given a bound set containing {@code alpha <: T} and {@code alpha :> S} - * perform {@code S <: T} (which could lead to new bounds). - */ - CROSS_UPPER_LOWER() { - public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { - Infer infer = inferenceContext.infer; - for (Type b1 : uv.getBounds(InferenceBound.UPPER)) { - for (Type b2 : uv.getBounds(InferenceBound.LOWER)) { - if (!isSubtype(inferenceContext.asUndetVar(b2), inferenceContext.asUndetVar(b1), warn , infer)) { - infer.reportBoundError(uv, BoundErrorKind.BAD_UPPER_LOWER); - } - } - } - } - - @Override - boolean accepts(UndetVar uv, InferenceContext inferenceContext) { - return !uv.isCaptured() && - uv.getBounds(InferenceBound.UPPER).nonEmpty() && - uv.getBounds(InferenceBound.LOWER).nonEmpty(); - } - }, - /** - * Given a bound set containing {@code alpha <: T} and {@code alpha == S} - * perform {@code S <: T} (which could lead to new bounds). - */ - CROSS_UPPER_EQ() { - public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { - Infer infer = inferenceContext.infer; - for (Type b1 : uv.getBounds(InferenceBound.UPPER)) { - for (Type b2 : uv.getBounds(InferenceBound.EQ)) { - if (!isSubtype(inferenceContext.asUndetVar(b2), inferenceContext.asUndetVar(b1), warn, infer)) { - infer.reportBoundError(uv, BoundErrorKind.BAD_UPPER_EQUAL); - } - } - } - } - - @Override - boolean accepts(UndetVar uv, InferenceContext inferenceContext) { - return !uv.isCaptured() && - uv.getBounds(InferenceBound.EQ).nonEmpty() && - uv.getBounds(InferenceBound.UPPER).nonEmpty(); - } - }, - /** - * Given a bound set containing {@code alpha :> S} and {@code alpha == T} - * perform {@code S <: T} (which could lead to new bounds). - */ - CROSS_EQ_LOWER() { - public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { - Infer infer = inferenceContext.infer; - for (Type b1 : uv.getBounds(InferenceBound.EQ)) { - for (Type b2 : uv.getBounds(InferenceBound.LOWER)) { - if (!isSubtype(inferenceContext.asUndetVar(b2), inferenceContext.asUndetVar(b1), warn, infer)) { - infer.reportBoundError(uv, BoundErrorKind.BAD_EQUAL_LOWER); - } - } - } - } - - @Override - boolean accepts(UndetVar uv, InferenceContext inferenceContext) { - return !uv.isCaptured() && - uv.getBounds(InferenceBound.EQ).nonEmpty() && - uv.getBounds(InferenceBound.LOWER).nonEmpty(); - } - }, - /** - * Given a bound set containing {@code alpha <: P} and - * {@code alpha <: P} where P is a parameterized type, - * perform {@code T = S} (which could lead to new bounds). - */ - CROSS_UPPER_UPPER() { - @Override - public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { - Infer infer = inferenceContext.infer; - List boundList = uv.getBounds(InferenceBound.UPPER).stream() - .collect(infer.types.closureCollector(true, infer.types::isSameType)); - List boundListTail = boundList.tail; - while (boundList.nonEmpty()) { - List tmpTail = boundListTail; - while (tmpTail.nonEmpty()) { - Type b1 = boundList.head; - Type b2 = tmpTail.head; - /* This wildcard check is temporary workaround. This code may need to be - * revisited once spec bug JDK-7034922 is fixed. - */ - if (b1 != b2 && !b1.hasTag(WILDCARD) && !b2.hasTag(WILDCARD)) { - for (Pair commonSupers : infer.getParameterizedSupers(b1, b2)) { - List allParamsSuperBound1 = commonSupers.fst.allparams(); - List allParamsSuperBound2 = commonSupers.snd.allparams(); - while (allParamsSuperBound1.nonEmpty() && allParamsSuperBound2.nonEmpty()) { - //traverse the list of all params comparing them - if (!allParamsSuperBound1.head.hasTag(WILDCARD) && - !allParamsSuperBound2.head.hasTag(WILDCARD)) { - if (!isSameType(inferenceContext.asUndetVar(allParamsSuperBound1.head), - inferenceContext.asUndetVar(allParamsSuperBound2.head), infer)) { - infer.reportBoundError(uv, BoundErrorKind.BAD_UPPER); - } - } - allParamsSuperBound1 = allParamsSuperBound1.tail; - allParamsSuperBound2 = allParamsSuperBound2.tail; - } - Assert.check(allParamsSuperBound1.isEmpty() && allParamsSuperBound2.isEmpty()); - } - } - tmpTail = tmpTail.tail; - } - boundList = boundList.tail; - boundListTail = boundList.tail; - } - } - - @Override - boolean accepts(UndetVar uv, InferenceContext inferenceContext) { - return !uv.isCaptured() && - uv.getBounds(InferenceBound.UPPER).nonEmpty(); - } - }, - /** - * Given a bound set containing {@code alpha == S} and {@code alpha == T} - * perform {@code S == T} (which could lead to new bounds). - */ - CROSS_EQ_EQ() { - public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { - Infer infer = inferenceContext.infer; - for (Type b1 : uv.getBounds(InferenceBound.EQ)) { - for (Type b2 : uv.getBounds(InferenceBound.EQ)) { - if (b1 != b2) { - if (!isSameType(inferenceContext.asUndetVar(b2), inferenceContext.asUndetVar(b1), infer)) { - infer.reportBoundError(uv, BoundErrorKind.BAD_EQ); - } - } - } - } - } - - @Override - boolean accepts(UndetVar uv, InferenceContext inferenceContext) { - return !uv.isCaptured() && - uv.getBounds(InferenceBound.EQ).nonEmpty(); - } - }, - /** - * Given a bound set containing {@code alpha <: beta} propagate lower bounds - * from alpha to beta; also propagate upper bounds from beta to alpha. - */ - PROP_UPPER() { - public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { - Infer infer = inferenceContext.infer; - for (Type b : uv.getBounds(InferenceBound.UPPER)) { - if (inferenceContext.inferenceVars().contains(b)) { - UndetVar uv2 = (UndetVar)inferenceContext.asUndetVar(b); - if (uv2.isCaptured()) continue; - //alpha <: beta - //0. set beta :> alpha - addBound(InferenceBound.LOWER, uv2, inferenceContext.asInstType(uv.qtype), infer); - //1. copy alpha's lower to beta's - for (Type l : uv.getBounds(InferenceBound.LOWER)) { - addBound(InferenceBound.LOWER, uv2, inferenceContext.asInstType(l), infer); - } - //2. copy beta's upper to alpha's - for (Type u : uv2.getBounds(InferenceBound.UPPER)) { - addBound(InferenceBound.UPPER, uv, inferenceContext.asInstType(u), infer); - } - } - } - } - - @Override - boolean accepts(UndetVar uv, InferenceContext inferenceContext) { - return !uv.isCaptured() && - uv.getBounds(InferenceBound.UPPER).nonEmpty(); - } - }, - /** - * Given a bound set containing {@code alpha :> beta} propagate lower bounds - * from beta to alpha; also propagate upper bounds from alpha to beta. - */ - PROP_LOWER() { - public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { - Infer infer = inferenceContext.infer; - for (Type b : uv.getBounds(InferenceBound.LOWER)) { - if (inferenceContext.inferenceVars().contains(b)) { - UndetVar uv2 = (UndetVar)inferenceContext.asUndetVar(b); - if (uv2.isCaptured()) continue; - //alpha :> beta - //0. set beta <: alpha - addBound(InferenceBound.UPPER, uv2, inferenceContext.asInstType(uv.qtype), infer); - //1. copy alpha's upper to beta's - for (Type u : uv.getBounds(InferenceBound.UPPER)) { - addBound(InferenceBound.UPPER, uv2, inferenceContext.asInstType(u), infer); - } - //2. copy beta's lower to alpha's - for (Type l : uv2.getBounds(InferenceBound.LOWER)) { - addBound(InferenceBound.LOWER, uv, inferenceContext.asInstType(l), infer); - } - } - } - } - - @Override - boolean accepts(UndetVar uv, InferenceContext inferenceContext) { - return !uv.isCaptured() && - uv.getBounds(InferenceBound.LOWER).nonEmpty(); - } - }, - /** - * Given a bound set containing {@code alpha == beta} propagate lower/upper - * bounds from alpha to beta and back. - */ - PROP_EQ() { - public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { - Infer infer = inferenceContext.infer; - for (Type b : uv.getBounds(InferenceBound.EQ)) { - if (inferenceContext.inferenceVars().contains(b)) { - UndetVar uv2 = (UndetVar)inferenceContext.asUndetVar(b); - if (uv2.isCaptured()) continue; - //alpha == beta - //0. set beta == alpha - addBound(InferenceBound.EQ, uv2, inferenceContext.asInstType(uv.qtype), infer); - //1. copy all alpha's bounds to beta's - for (InferenceBound ib : InferenceBound.values()) { - for (Type b2 : uv.getBounds(ib)) { - if (b2 != uv2) { - addBound(ib, uv2, inferenceContext.asInstType(b2), infer); - } - } - } - //2. copy all beta's bounds to alpha's - for (InferenceBound ib : InferenceBound.values()) { - for (Type b2 : uv2.getBounds(ib)) { - if (b2 != uv) { - addBound(ib, uv, inferenceContext.asInstType(b2), infer); - } - } - } - } - } - } - - @Override - boolean accepts(UndetVar uv, InferenceContext inferenceContext) { - return !uv.isCaptured() && - uv.getBounds(InferenceBound.EQ).nonEmpty(); - } - }; - - abstract void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn); - - boolean accepts(UndetVar uv, InferenceContext inferenceContext) { - return !uv.isCaptured(); - } - - boolean isSubtype(Type s, Type t, Warner warn, Infer infer) { - return doIncorporationOp(IncorporationBinaryOpKind.IS_SUBTYPE, s, t, warn, infer); - } - - boolean isSameType(Type s, Type t, Infer infer) { - return doIncorporationOp(IncorporationBinaryOpKind.IS_SAME_TYPE, s, t, null, infer); - } - - void addBound(InferenceBound ib, UndetVar uv, Type b, Infer infer) { - doIncorporationOp(opFor(ib), uv, b, null, infer); - } - - IncorporationBinaryOpKind opFor(InferenceBound boundKind) { - switch (boundKind) { - case EQ: - return IncorporationBinaryOpKind.ADD_EQ_BOUND; - case LOWER: - return IncorporationBinaryOpKind.ADD_LOWER_BOUND; - case UPPER: - return IncorporationBinaryOpKind.ADD_UPPER_BOUND; - default: - Assert.error("Can't get here!"); - return null; - } - } - - boolean doIncorporationOp(IncorporationBinaryOpKind opKind, Type op1, Type op2, Warner warn, Infer infer) { - IncorporationBinaryOp newOp = infer.new IncorporationBinaryOp(opKind, op1, op2); - Boolean res = infer.incorporationCache.get(newOp); + boolean doIncorporationOp(IncorporationBinaryOpKind opKind, Type op1, Type op2, Warner warn) { + IncorporationBinaryOp newOp = new IncorporationBinaryOp(opKind, op1, op2); + Boolean res = incorporationCache.get(newOp); if (res == null) { - infer.incorporationCache.put(newOp, res = newOp.apply(warn)); + incorporationCache.put(newOp, res = newOp.apply(warn)); } return res; } - } - - /** incorporation steps to be executed when running in legacy mode */ - EnumSet incorporationStepsLegacy = EnumSet.of(IncorporationStep.EQ_CHECK_LEGACY); - - /** incorporation steps to be executed when running in graph mode */ - EnumSet incorporationStepsGraph = - EnumSet.complementOf(EnumSet.of(IncorporationStep.EQ_CHECK_LEGACY)); /** * Three kinds of basic operation are supported as part of an incorporation step: @@ -1190,30 +1153,6 @@ public class Infer { boolean apply(Type op1, Type op2, Warner warn, Types types) { return types.isSameType(op1, op2); } - }, - ADD_UPPER_BOUND() { - @Override - boolean apply(Type op1, Type op2, Warner warn, Types types) { - UndetVar uv = (UndetVar)op1; - uv.addBound(InferenceBound.UPPER, op2, types); - return true; - } - }, - ADD_LOWER_BOUND() { - @Override - boolean apply(Type op1, Type op2, Warner warn, Types types) { - UndetVar uv = (UndetVar)op1; - uv.addBound(InferenceBound.LOWER, op2, types); - return true; - } - }, - ADD_EQ_BOUND() { - @Override - boolean apply(Type op1, Type op2, Warner warn, Types types) { - UndetVar uv = (UndetVar)op1; - uv.addBound(InferenceBound.EQ, op2, types); - return true; - } }; abstract boolean apply(Type op1, Type op2, Warner warn, Types types); @@ -1254,9 +1193,9 @@ public class Infer { public int hashCode() { int result = opKind.hashCode(); result *= 127; - result += types.hashCode(op1, true); + result += types.hashCode(op1); result *= 127; - result += types.hashCode(op2, true); + result += types.hashCode(op2); return result; } @@ -1268,152 +1207,59 @@ public class Infer { /** an incorporation cache keeps track of all executed incorporation-related operations */ Map incorporationCache = new HashMap<>(); - /** - * Make sure that the upper bounds we got so far lead to a solvable inference - * variable by making sure that a glb exists. - */ - void checkCompatibleUpperBounds(UndetVar uv, InferenceContext inferenceContext) { - List hibounds = - Type.filter(uv.getBounds(InferenceBound.UPPER), new BoundFilter(inferenceContext)); - Type hb = null; - if (hibounds.isEmpty()) - hb = syms.objectType; - else if (hibounds.tail.isEmpty()) - hb = hibounds.head; - else - hb = types.glb(hibounds); - if (hb == null || hb.isErroneous()) - reportBoundError(uv, BoundErrorKind.BAD_UPPER); - } - //where - protected static class BoundFilter implements Filter { + protected static class BoundFilter implements Filter { - InferenceContext inferenceContext; + InferenceContext inferenceContext; - public BoundFilter(InferenceContext inferenceContext) { - this.inferenceContext = inferenceContext; - } - - @Override - public boolean accepts(Type t) { - return !t.isErroneous() && !inferenceContext.free(t) && - !t.hasTag(BOT); - } + public BoundFilter(InferenceContext inferenceContext) { + this.inferenceContext = inferenceContext; } - /** - * This enumeration defines all possible bound-checking related errors. - */ - enum BoundErrorKind { - /** - * The (uninstantiated) inference variable has incompatible upper bounds. - */ - BAD_UPPER() { - @Override - InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { - return ex.setMessage("incompatible.upper.bounds", uv.qtype, - uv.getBounds(InferenceBound.UPPER)); - } - }, - /** - * The (uninstantiated) inference variable has incompatible equality constraints. - */ - BAD_EQ() { - @Override - InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { - return ex.setMessage("incompatible.eq.bounds", uv.qtype, - uv.getBounds(InferenceBound.EQ)); - } - }, - /** - * The (uninstantiated) inference variable has incompatible upper lower bounds. - */ - BAD_UPPER_LOWER() { - @Override - InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { - return ex.setMessage("incompatible.upper.lower.bounds", uv.qtype, - uv.getBounds(InferenceBound.UPPER), uv.getBounds(InferenceBound.LOWER)); - } - }, - /** - * The (uninstantiated) inference variable has incompatible upper equal bounds. - */ - BAD_UPPER_EQUAL() { - @Override - InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { - return ex.setMessage("incompatible.upper.eq.bounds", uv.qtype, - uv.getBounds(InferenceBound.UPPER), uv.getBounds(InferenceBound.EQ)); - } - }, - /** - * The (uninstantiated) inference variable has incompatible upper equal bounds. - */ - BAD_EQUAL_LOWER() { - @Override - InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { - return ex.setMessage("incompatible.eq.lower.bounds", uv.qtype, - uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.LOWER)); - } - }, - /** - * An equality constraint is not compatible with an upper bound. - */ - BAD_EQ_UPPER() { - @Override - InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { - return ex.setMessage("incompatible.eq.upper.bounds", uv.qtype, - uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.UPPER)); - } - }, - /** - * An equality constraint is not compatible with a lower bound. - */ - BAD_EQ_LOWER() { - @Override - InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { - return ex.setMessage("incompatible.eq.lower.bounds", uv.qtype, - uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.LOWER)); - } - }, - /** - * Instantiated inference variable is not compatible with an upper bound. - */ - UPPER() { - @Override - InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { - return ex.setMessage("inferred.do.not.conform.to.upper.bounds", uv.inst, - uv.getBounds(InferenceBound.UPPER)); - } - }, - /** - * Instantiated inference variable is not compatible with a lower bound. - */ - LOWER() { - @Override - InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { - return ex.setMessage("inferred.do.not.conform.to.lower.bounds", uv.inst, - uv.getBounds(InferenceBound.LOWER)); - } - }, - /** - * Instantiated inference variable is not compatible with an equality constraint. - */ - EQ() { - @Override - InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { - return ex.setMessage("inferred.do.not.conform.to.eq.bounds", uv.inst, - uv.getBounds(InferenceBound.EQ)); - } - }; - - abstract InapplicableMethodException setMessage(InferenceException ex, UndetVar uv); + @Override + public boolean accepts(Type t) { + return !t.isErroneous() && !inferenceContext.free(t) && + !t.hasTag(BOT); + } } /** - * Report a bound-checking error of given kind + * Incorporation error: mismatch between inferred type and given bound. */ - void reportBoundError(UndetVar uv, BoundErrorKind bk) { - throw bk.setMessage(inferenceException, uv); + void reportInstError(UndetVar uv, InferenceBound ib) { + reportInferenceError( + String.format("inferred.do.not.conform.to.%s.bounds", StringUtils.toLowerCase(ib.name())), + uv.getInst(), + uv.getBounds(ib)); + } + + /** + * Incorporation error: mismatch between two (or more) bounds of same kind. + */ + void reportBoundError(UndetVar uv, InferenceBound ib) { + reportInferenceError( + String.format("incompatible.%s.bounds", StringUtils.toLowerCase(ib.name())), + uv.qtype, + uv.getBounds(ib)); + } + + /** + * Incorporation error: mismatch between two (or more) bounds of different kinds. + */ + void reportBoundError(UndetVar uv, InferenceBound ib1, InferenceBound ib2) { + reportInferenceError( + String.format("incompatible.%s.%s.bounds", + StringUtils.toLowerCase(ib1.name()), + StringUtils.toLowerCase(ib2.name())), + uv.qtype, + uv.getBounds(ib1), + uv.getBounds(ib2)); + } + + /** + * Helper method: reports an inference error. + */ + void reportInferenceError(String key, Object... args) { + throw inferenceException.setMessage(key, args); } // @@ -1460,41 +1306,6 @@ public class Infer { } return g.nodes.get(0); } - - boolean isSubtype(Type s, Type t, Warner warn, Infer infer) { - return doIncorporationOp(IncorporationBinaryOpKind.IS_SUBTYPE, s, t, warn, infer); - } - - boolean isSameType(Type s, Type t, Infer infer) { - return doIncorporationOp(IncorporationBinaryOpKind.IS_SAME_TYPE, s, t, null, infer); - } - - void addBound(InferenceBound ib, UndetVar uv, Type b, Infer infer) { - doIncorporationOp(opFor(ib), uv, b, null, infer); - } - - IncorporationBinaryOpKind opFor(InferenceBound boundKind) { - switch (boundKind) { - case EQ: - return IncorporationBinaryOpKind.ADD_EQ_BOUND; - case LOWER: - return IncorporationBinaryOpKind.ADD_LOWER_BOUND; - case UPPER: - return IncorporationBinaryOpKind.ADD_UPPER_BOUND; - default: - Assert.error("Can't get here!"); - return null; - } - } - - boolean doIncorporationOp(IncorporationBinaryOpKind opKind, Type op1, Type op2, Warner warn, Infer infer) { - IncorporationBinaryOp newOp = infer.new IncorporationBinaryOp(opKind, op1, op2); - Boolean res = infer.incorporationCache.get(newOp); - if (res == null) { - infer.incorporationCache.put(newOp, res = newOp.apply(warn)); - } - return res; - } } /** @@ -1814,7 +1625,7 @@ public class Infer { * depends on the selected solver strategy. */ void solve(GraphStrategy sstrategy) { - checkWithinBounds(inferenceContext, warn); //initial propagation of bounds + doIncorporation(inferenceContext, warn); //initial propagation of bounds InferenceGraph inferenceGraph = new InferenceGraph(stuckDeps); while (!sstrategy.done()) { if (dependenciesFolder != null) { @@ -1829,8 +1640,8 @@ public class Infer { outer: while (Type.containsAny(inferenceContext.restvars(), varsToSolve)) { //for each inference phase for (GraphInferenceSteps step : GraphInferenceSteps.values()) { - if (inferenceContext.solveBasic(varsToSolve, step.steps)) { - checkWithinBounds(inferenceContext, warn); + if (inferenceContext.solveBasic(varsToSolve, step.steps).nonEmpty()) { + doIncorporation(inferenceContext, warn); continue outer; } } @@ -1842,7 +1653,7 @@ public class Infer { //did we fail because of interdependent ivars? inferenceContext.rollback(saved_undet); instantiateAsUninferredVars(varsToSolve, inferenceContext); - checkWithinBounds(inferenceContext, warn); + doIncorporation(inferenceContext, warn); } inferenceGraph.deleteNode(nodeToSolve); } diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java index c45655a8571..f59705f637f 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/InferenceContext.java @@ -25,37 +25,32 @@ package com.sun.tools.javac.comp; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; -import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.code.Type.CapturedType; -import com.sun.tools.javac.code.Type.CapturedUndetVar; -import com.sun.tools.javac.code.Type.TypeMapping; +import com.sun.tools.javac.code.Type.ArrayType; +import com.sun.tools.javac.code.Type.ClassType; import com.sun.tools.javac.code.Type.TypeVar; import com.sun.tools.javac.code.Type.UndetVar; import com.sun.tools.javac.code.Type.UndetVar.InferenceBound; +import com.sun.tools.javac.code.Type.WildcardType; +import com.sun.tools.javac.code.TypeTag; import com.sun.tools.javac.code.Types; -import com.sun.tools.javac.comp.Infer.BestLeafSolver; import com.sun.tools.javac.comp.Infer.FreeTypeListener; import com.sun.tools.javac.comp.Infer.GraphSolver; import com.sun.tools.javac.comp.Infer.GraphStrategy; import com.sun.tools.javac.comp.Infer.InferenceException; import com.sun.tools.javac.comp.Infer.InferenceStep; -import com.sun.tools.javac.comp.Infer.LeafSolver; import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.util.Assert; -import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Filter; -import com.sun.tools.javac.util.JCDiagnostic; -import com.sun.tools.javac.util.JCDiagnostic.Factory; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; -import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Warner; /** @@ -76,43 +71,34 @@ class InferenceContext { /** list of inference vars as undet vars */ List undetvars; + Type update(Type t) { + return t; + } + /** list of inference vars in this context */ List inferencevars; Map> freeTypeListeners = new HashMap<>(); - List freetypeListeners = List.nil(); - Types types; Infer infer; public InferenceContext(Infer infer, List inferencevars) { - this.inferencevars = inferencevars; - - this.infer = infer; - this.types = infer.types; - - fromTypeVarFun = new TypeMapping() { - @Override - public Type visitTypeVar(TypeVar tv, Void aVoid) { - return new UndetVar(tv, types); - } - - @Override - public Type visitCapturedType(CapturedType t, Void aVoid) { - return new CapturedUndetVar(t, types); - } - }; - this.undetvars = inferencevars.map(fromTypeVarFun); + this(infer, inferencevars, inferencevars.map(infer.fromTypeVarFun)); } - TypeMapping fromTypeVarFun; + public InferenceContext(Infer infer, List inferencevars, List undetvars) { + this.inferencevars = inferencevars; + this.undetvars = undetvars; + this.infer = infer; + this.types = infer.types; + } /** * add a new inference var to this inference context */ void addVar(TypeVar t) { - this.undetvars = this.undetvars.prepend(fromTypeVarFun.apply(t)); + this.undetvars = this.undetvars.prepend(infer.fromTypeVarFun.apply(t)); this.inferencevars = this.inferencevars.prepend(t); } @@ -131,7 +117,7 @@ class InferenceContext { List restvars() { return filterVars(new Filter() { public boolean accepts(UndetVar uv) { - return uv.inst == null; + return uv.getInst() == null; } }); } @@ -143,7 +129,7 @@ class InferenceContext { List instvars() { return filterVars(new Filter() { public boolean accepts(UndetVar uv) { - return uv.inst != null; + return uv.getInst() != null; } }); } @@ -237,7 +223,7 @@ class InferenceContext { ListBuffer buf = new ListBuffer<>(); for (Type t : undetvars) { UndetVar uv = (UndetVar)t; - buf.append(uv.inst != null ? uv.inst : uv.qtype); + buf.append(uv.getInst() != null ? uv.getInst() : uv.qtype); } return buf.toList(); } @@ -302,15 +288,7 @@ class InferenceContext { List save() { ListBuffer buf = new ListBuffer<>(); for (Type t : undetvars) { - UndetVar uv = (UndetVar)t; - UndetVar uv2 = new UndetVar((TypeVar)uv.qtype, types); - for (InferenceBound ib : InferenceBound.values()) { - for (Type b : uv.getBounds(ib)) { - uv2.addBound(ib, b, types); - } - } - uv2.inst = uv.inst; - buf.add(uv2); + buf.add(((UndetVar)t).dup(infer.types)); } return buf.toList(); } @@ -328,10 +306,7 @@ class InferenceContext { UndetVar uv = (UndetVar)undetvars.head; UndetVar uv_saved = (UndetVar)saved_undet.head; if (uv.qtype == uv_saved.qtype) { - for (InferenceBound ib : InferenceBound.values()) { - uv.setBounds(ib, uv_saved.getBounds(ib)); - } - uv.inst = uv_saved.inst; + uv_saved.dupTo(uv, types); undetvars = undetvars.tail; saved_undet = saved_undet.tail; newUndetVars.add(uv); @@ -366,6 +341,124 @@ class InferenceContext { } } + InferenceContext min(List roots, boolean shouldSolve, Warner warn) { + ReachabilityVisitor rv = new ReachabilityVisitor(); + rv.scan(roots); + if (rv.min.size() == inferencevars.length()) { + return this; + } + + List minVars = List.from(rv.min); + List redundantVars = inferencevars.diff(minVars); + + //compute new undet variables (bounds associated to redundant variables are dropped) + ListBuffer minUndetVars = new ListBuffer<>(); + for (Type minVar : minVars) { + UndetVar uv = (UndetVar)asUndetVar(minVar); + UndetVar uv2 = new UndetVar((TypeVar)minVar, infer.incorporationEngine(), types); + for (InferenceBound ib : InferenceBound.values()) { + List newBounds = uv.getBounds(ib).stream() + .filter(b -> !redundantVars.contains(b)) + .collect(List.collector()); + uv2.setBounds(ib, newBounds); + } + minUndetVars.add(uv2); + } + + //compute new minimal inference context + InferenceContext minContext = new InferenceContext(infer, minVars, minUndetVars.toList()); + for (Type t : minContext.inferencevars) { + //add listener that forwards notifications to original context + minContext.addFreeTypeListener(List.of(t), (inferenceContext) -> { + List depVars = List.from(rv.minMap.get(t)); + solve(depVars, warn); + notifyChange(); + }); + } + if (shouldSolve) { + //solve definitively unreachable variables + List unreachableVars = redundantVars.diff(List.from(rv.equiv)); + solve(unreachableVars, warn); + } + return minContext; + } + + class ReachabilityVisitor extends Types.UnaryVisitor { + + Set equiv = new HashSet<>(); + Set min = new HashSet<>(); + Map> minMap = new HashMap<>(); + + void scan(List roots) { + roots.stream().forEach(this::visit); + } + + @Override + public Void visitType(Type t, Void _unused) { + return null; + } + + @Override + public Void visitUndetVar(UndetVar t, Void _unused) { + if (min.add(t.qtype)) { + Set deps = minMap.getOrDefault(t.qtype, new HashSet<>(Collections.singleton(t.qtype))); + for (Type b : t.getBounds(InferenceBound.values())) { + Type undet = asUndetVar(b); + if (!undet.hasTag(TypeTag.UNDETVAR)) { + visit(undet); + } else if (isEquiv((UndetVar)undet, b)){ + deps.add(b); + equiv.add(b); + } else { + visit(undet); + } + } + minMap.put(t.qtype, deps); + } + return null; + } + + @Override + public Void visitWildcardType(WildcardType t, Void _unused) { + return visit(t.type); + } + + @Override + public Void visitTypeVar(TypeVar t, Void aVoid) { + Type undet = asUndetVar(t); + if (undet.hasTag(TypeTag.UNDETVAR)) { + visitUndetVar((UndetVar)undet, null); + } + return null; + } + + @Override + public Void visitArrayType(ArrayType t, Void _unused) { + return visit(t.elemtype); + } + + @Override + public Void visitClassType(ClassType t, Void _unused) { + visit(t.getEnclosingType()); + for (Type targ : t.getTypeArguments()) { + visit(targ); + } + return null; + } + + boolean isEquiv(UndetVar from, Type t) { + UndetVar uv = (UndetVar)asUndetVar(t); + for (InferenceBound ib : InferenceBound.values()) { + List b1 = uv.getBounds(ib); + List b2 = from.getBounds(ib); + if (!b1.containsAll(b2) || !b2.containsAll(b1)) { + return false; + } + } + return true; + } + } + private void solve(GraphStrategy ss, Warner warn) { solve(ss, new HashMap>(), warn); } @@ -414,23 +507,23 @@ class InferenceContext { /** * Apply a set of inference steps */ - private boolean solveBasic(EnumSet steps) { + private List solveBasic(EnumSet steps) { return solveBasic(inferencevars, steps); } - boolean solveBasic(List varsToSolve, EnumSet steps) { - boolean changed = false; + List solveBasic(List varsToSolve, EnumSet steps) { + ListBuffer solvedVars = new ListBuffer<>(); for (Type t : varsToSolve.intersect(restvars())) { UndetVar uv = (UndetVar)asUndetVar(t); for (InferenceStep step : steps) { if (step.accepts(uv, this)) { - uv.inst = step.solve(uv, this); - changed = true; + uv.setInst(step.solve(uv, this)); + solvedVars.add(uv.qtype); break; } } } - return changed; + return solvedVars.toList(); } /** @@ -442,11 +535,11 @@ class InferenceContext { */ public void solveLegacy(boolean partial, Warner warn, EnumSet steps) { while (true) { - boolean stuck = !solveBasic(steps); + List solvedVars = solveBasic(steps); if (restvars().isEmpty() || partial) { //all variables have been instantiated - exit break; - } else if (stuck) { + } else if (solvedVars.isEmpty()) { //some variables could not be instantiated because of cycles in //upper bounds - provide a (possibly recursive) default instantiation infer.instantiateAsUninferredVars(restvars(), this); @@ -456,11 +549,11 @@ class InferenceContext { //variables in remaining upper bounds and continue for (Type t : undetvars) { UndetVar uv = (UndetVar)t; - uv.substBounds(inferenceVars(), instTypes(), types); + uv.substBounds(solvedVars, asInstTypes(solvedVars), types); } } } - infer.checkWithinBounds(this, warn); + infer.doIncorporation(this, warn); } @Override diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java index d8072920f60..6cfa8e539e0 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java @@ -55,12 +55,16 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Supplier; import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*; import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Kinds.Kind.*; import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.tree.JCTree.Tag.*; + +import javax.lang.model.element.ElementKind; import javax.lang.model.type.TypeKind; /** @@ -268,21 +272,34 @@ public class LambdaToMethod extends TreeTranslator { MethodSymbol sym = localContext.translatedSym; MethodType lambdaType = (MethodType) sym.type; - { - Symbol owner = localContext.owner; - ListBuffer ownerTypeAnnos = new ListBuffer<>(); - ListBuffer lambdaTypeAnnos = new ListBuffer<>(); + { /* Type annotation management: Based on where the lambda features, type annotations that + are interior to it, may at this point be attached to the enclosing method, or the first + constructor in the class, or in the enclosing class symbol or in the field whose + initializer is the lambda. In any event, gather up the annotations that belong to the + lambda and attach it to the implementation method. + */ - for (Attribute.TypeCompound tc : owner.getRawTypeAttributes()) { - if (tc.position.onLambda == tree) { - lambdaTypeAnnos.append(tc); - } else { - ownerTypeAnnos.append(tc); - } + Symbol owner = localContext.owner; + apportionTypeAnnotations(tree, + owner::getRawTypeAttributes, + owner::setTypeAttributes, + sym::setTypeAttributes); + + + boolean init; + if ((init = (owner.name == names.init)) || owner.name == names.clinit) { + owner = owner.owner; + apportionTypeAnnotations(tree, + init ? owner::getInitTypeAttributes : owner::getClassInitTypeAttributes, + init ? owner::setInitTypeAttributes : owner::setClassInitTypeAttributes, + sym::appendUniqueTypeAttributes); } - if (lambdaTypeAnnos.nonEmpty()) { - owner.setTypeAttributes(ownerTypeAnnos.toList()); - sym.setTypeAttributes(lambdaTypeAnnos.toList()); + if (localContext.self != null && localContext.self.getKind() == ElementKind.FIELD) { + owner = localContext.self; + apportionTypeAnnotations(tree, + owner::getRawTypeAttributes, + owner::setTypeAttributes, + sym::appendUniqueTypeAttributes); } } @@ -338,6 +355,11 @@ public class LambdaToMethod extends TreeTranslator { syntheticInits.append((JCExpression) captured_local); } } + // add captured outer this instances (used only when `this' capture itself is illegal) + for (Symbol fv : localContext.getSymbolMap(CAPTURED_OUTER_THIS).keySet()) { + JCTree captured_local = make.QualThis(fv.type); + syntheticInits.append((JCExpression) captured_local); + } //then, determine the arguments to the indy call List indy_args = translate(syntheticInits.toList(), localContext.prev); @@ -349,6 +371,29 @@ public class LambdaToMethod extends TreeTranslator { result = makeMetafactoryIndyCall(context, refKind, sym, indy_args); } + // where + // Reassign type annotations from the source that should really belong to the lambda + private void apportionTypeAnnotations(JCLambda tree, + Supplier> source, + Consumer> owner, + Consumer> lambda) { + + ListBuffer ownerTypeAnnos = new ListBuffer<>(); + ListBuffer lambdaTypeAnnos = new ListBuffer<>(); + + for (Attribute.TypeCompound tc : source.get()) { + if (tc.position.onLambda == tree) { + lambdaTypeAnnos.append(tc); + } else { + ownerTypeAnnos.append(tc); + } + } + if (lambdaTypeAnnos.nonEmpty()) { + owner.accept(ownerTypeAnnos.toList()); + lambda.accept(lambdaTypeAnnos.toList()); + } + } + private JCIdent makeThis(Type type, Symbol owner) { VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC, names._this, @@ -434,6 +479,32 @@ public class LambdaToMethod extends TreeTranslator { } } + /** + * Translate qualified `this' references within a lambda to the mapped identifier + * @param tree + */ + @Override + public void visitSelect(JCFieldAccess tree) { + if (context == null || !analyzer.lambdaFieldAccessFilter(tree)) { + super.visitSelect(tree); + } else { + int prevPos = make.pos; + try { + make.at(tree); + + LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context; + JCTree ltree = lambdaContext.translate(tree); + if (ltree != null) { + result = ltree; + } else { + super.visitSelect(tree); + } + } finally { + make.at(prevPos); + } + } + } + @Override public void visitVarDef(JCVariableDecl tree) { LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context; @@ -1127,6 +1198,11 @@ public class LambdaToMethod extends TreeTranslator { */ private int lambdaCount = 0; + /** + * List of types undergoing construction via explicit constructor chaining. + */ + private List typesUnderConstruction; + /** * keep the count of lambda expression defined in given context (used to * generate unambiguous names for serializable lambdas) @@ -1157,10 +1233,35 @@ public class LambdaToMethod extends TreeTranslator { private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) { frameStack = List.nil(); + typesUnderConstruction = List.nil(); localClassDefs = new HashMap<>(); return translate(tree); } + @Override + public void visitApply(JCMethodInvocation tree) { + List previousNascentTypes = typesUnderConstruction; + try { + Name methName = TreeInfo.name(tree.meth); + if (methName == names._this || methName == names._super) { + typesUnderConstruction = typesUnderConstruction.prepend(currentClass()); + } + super.visitApply(tree); + } finally { + typesUnderConstruction = previousNascentTypes; + } + } + // where + private ClassSymbol currentClass() { + for (Frame frame : frameStack) { + if (frame.tree.hasTag(JCTree.Tag.CLASSDEF)) { + JCClassDecl cdef = (JCClassDecl) frame.tree; + return cdef.sym; + } + } + return null; + } + @Override public void visitBlock(JCBlock tree) { List prevStack = frameStack; @@ -1278,9 +1379,6 @@ public class LambdaToMethod extends TreeTranslator { List prevStack = frameStack; try { LambdaTranslationContext context = new LambdaTranslationContext(tree); - if (dumpLambdaToMethodStats) { - log.note(tree, statKey, context.needsAltMetafactory(), context.translatedSym); - } frameStack = frameStack.prepend(new Frame(tree)); for (JCVariableDecl param : tree.params) { context.addSymbol(param.sym, PARAM); @@ -1289,6 +1387,9 @@ public class LambdaToMethod extends TreeTranslator { contextMap.put(tree, context); super.visitLambda(tree); context.complete(); + if (dumpLambdaToMethodStats) { + log.note(tree, statKey, context.needsAltMetafactory(), context.translatedSym); + } return context; } finally { @@ -1632,6 +1733,22 @@ public class LambdaToMethod extends TreeTranslator { && sym.name != names.init; } + /** + * This is used to filter out those select nodes that need to be adjusted + * when translating away lambda expressions - at the moment, this is the + * set of nodes that select `this' (qualified this) + */ + private boolean lambdaFieldAccessFilter(JCFieldAccess fAccess) { + LambdaTranslationContext lambdaContext = + context instanceof LambdaTranslationContext ? + (LambdaTranslationContext) context : null; + return lambdaContext != null + && !fAccess.sym.isStatic() + && fAccess.name == names._this + && (fAccess.sym.owner.kind == TYP) + && !lambdaContext.translatedSymbols.get(CAPTURED_OUTER_THIS).isEmpty(); + } + /** * This is used to filter out those new class expressions that need to * be qualified with an enclosing tree @@ -1806,6 +1923,7 @@ public class LambdaToMethod extends TreeTranslator { translatedSymbols.put(LOCAL_VAR, new LinkedHashMap()); translatedSymbols.put(CAPTURED_VAR, new LinkedHashMap()); translatedSymbols.put(CAPTURED_THIS, new LinkedHashMap()); + translatedSymbols.put(CAPTURED_OUTER_THIS, new LinkedHashMap()); translatedSymbols.put(TYPE_VAR, new LinkedHashMap()); freeVarProcessedLocalClasses = new HashSet<>(); @@ -1918,6 +2036,16 @@ public class LambdaToMethod extends TreeTranslator { } }; break; + case CAPTURED_OUTER_THIS: + Name name = names.fromString(new String(sym.flatName().toString() + names.dollarThis)); + ret = new VarSymbol(SYNTHETIC | FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym) { + @Override + public Symbol baseSymbol() { + //keep mapping with original captured symbol + return sym; + } + }; + break; case LOCAL_VAR: ret = new VarSymbol(sym.flags() & FINAL, sym.name, sym.type, translatedSym); ((VarSymbol) ret).pos = ((VarSymbol) sym).pos; @@ -1938,6 +2066,14 @@ public class LambdaToMethod extends TreeTranslator { } void addSymbol(Symbol sym, LambdaSymbolKind skind) { + if (skind == CAPTURED_THIS && sym != null && sym.kind == TYP && !typesUnderConstruction.isEmpty()) { + ClassSymbol currentClass = currentClass(); + if (currentClass != null && typesUnderConstruction.contains(currentClass)) { + // reference must be to enclosing outer instance, mutate capture kind. + Assert.check(sym != currentClass); // should have been caught right in Attr + skind = CAPTURED_OUTER_THIS; + } + } Map transMap = getSymbolMap(skind); if (!transMap.containsKey(sym)) { transMap.put(sym, translate(sym, skind)); @@ -1951,17 +2087,49 @@ public class LambdaToMethod extends TreeTranslator { } JCTree translate(JCIdent lambdaIdent) { - for (Map m : translatedSymbols.values()) { - if (m.containsKey(lambdaIdent.sym)) { - Symbol tSym = m.get(lambdaIdent.sym); - JCTree t = make.Ident(tSym).setType(lambdaIdent.type); - tSym.setTypeAttributes(lambdaIdent.sym.getRawTypeAttributes()); - return t; + for (LambdaSymbolKind kind : LambdaSymbolKind.values()) { + Map m = getSymbolMap(kind); + switch(kind) { + default: + if (m.containsKey(lambdaIdent.sym)) { + Symbol tSym = m.get(lambdaIdent.sym); + JCTree t = make.Ident(tSym).setType(lambdaIdent.type); + tSym.setTypeAttributes(lambdaIdent.sym.getRawTypeAttributes()); + return t; + } + break; + case CAPTURED_OUTER_THIS: + if (lambdaIdent.sym.owner.kind == TYP && m.containsKey(lambdaIdent.sym.owner)) { + // Transform outer instance variable references anchoring them to the captured synthetic. + Symbol tSym = m.get(lambdaIdent.sym.owner); + JCExpression t = make.Ident(tSym).setType(lambdaIdent.sym.owner.type); + tSym.setTypeAttributes(lambdaIdent.sym.owner.getRawTypeAttributes()); + t = make.Select(t, lambdaIdent.name); + t.setType(lambdaIdent.type); + TreeInfo.setSymbol(t, lambdaIdent.sym); + return t; + } + break; } } return null; } + /* Translate away qualified this expressions, anchoring them to synthetic parameters that + capture the qualified this handle. `fieldAccess' is guaranteed to one such. + */ + public JCTree translate(JCFieldAccess fieldAccess) { + Assert.check(fieldAccess.name == names._this); + Map m = translatedSymbols.get(LambdaSymbolKind.CAPTURED_OUTER_THIS); + if (m.containsKey(fieldAccess.sym.owner)) { + Symbol tSym = m.get(fieldAccess.sym.owner); + JCExpression t = make.Ident(tSym).setType(fieldAccess.sym.owner.type); + tSym.setTypeAttributes(fieldAccess.sym.owner.getRawTypeAttributes()); + return t; + } + return null; + } + /** * The translatedSym is not complete/accurate until the analysis is * finished. Once the analysis is finished, the translatedSym is @@ -1999,6 +2167,10 @@ public class LambdaToMethod extends TreeTranslator { params.append(make.VarDef((VarSymbol) thisSym, null)); parameterSymbols.append((VarSymbol) thisSym); } + for (Symbol thisSym : getSymbolMap(CAPTURED_OUTER_THIS).values()) { + params.append(make.VarDef((VarSymbol) thisSym, null)); + parameterSymbols.append((VarSymbol) thisSym); + } for (Symbol thisSym : getSymbolMap(PARAM).values()) { params.append(make.VarDef((VarSymbol) thisSym, null)); parameterSymbols.append((VarSymbol) thisSym); @@ -2096,9 +2268,6 @@ public class LambdaToMethod extends TreeTranslator { */ boolean interfaceParameterIsIntersectionType() { List tl = tree.getDescriptorType(types).getParameterTypes(); - if (tree.kind == ReferenceKind.UNBOUND) { - tl = tl.tail; - } for (; tl.nonEmpty(); tl = tl.tail) { Type pt = tl.head; if (pt.getKind() == TypeKind.TYPEVAR) { @@ -2147,6 +2316,7 @@ public class LambdaToMethod extends TreeTranslator { LOCAL_VAR, // original to translated lambda locals CAPTURED_VAR, // variables in enclosing scope to translated synthetic parameters CAPTURED_THIS, // class symbols to translated synthetic parameters (for captured member access) + CAPTURED_OUTER_THIS, // used when `this' capture is illegal, but outer this capture is legit (JDK-8129740) TYPE_VAR // original to translated lambda type variables } diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index b5e3a8e5db9..423adf2e121 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -1740,7 +1740,7 @@ public class Lower extends TreeTranslator { private JCStatement makeResourceCloseInvocation(JCExpression resource) { // convert to AutoCloseable if needed if (types.asSuper(resource.type, syms.autoCloseableType.tsym) == null) { - resource = (JCExpression) convert(resource, syms.autoCloseableType); + resource = convert(resource, syms.autoCloseableType); } // create resource.close() method invocation @@ -2179,7 +2179,7 @@ public class Lower extends TreeTranslator { *************************************************************************/ interface TreeBuilder { - JCTree build(JCTree arg); + JCExpression build(JCExpression arg); } /** Construct an expression using the builder, with the given rval @@ -2197,7 +2197,7 @@ public class Lower extends TreeTranslator { * where TEMP is a newly declared variable * in the let expression. */ - JCTree abstractRval(JCTree rval, Type type, TreeBuilder builder) { + JCExpression abstractRval(JCExpression rval, Type type, TreeBuilder builder) { rval = TreeInfo.skipParens(rval); switch (rval.getTag()) { case LITERAL: @@ -2215,15 +2215,15 @@ public class Lower extends TreeTranslator { type, currentMethodSym); rval = convert(rval,type); - JCVariableDecl def = make.VarDef(var, (JCExpression)rval); // XXX cast - JCTree built = builder.build(make.Ident(var)); - JCTree res = make.LetExpr(def, built); + JCVariableDecl def = make.VarDef(var, rval); // XXX cast + JCExpression built = builder.build(make.Ident(var)); + JCExpression res = make.LetExpr(def, built); res.type = built.type; return res; } // same as above, with the type of the temporary variable computed - JCTree abstractRval(JCTree rval, TreeBuilder builder) { + JCExpression abstractRval(JCExpression rval, TreeBuilder builder) { return abstractRval(rval, rval.type, builder); } @@ -2232,30 +2232,28 @@ public class Lower extends TreeTranslator { // Select expressions, where we place the left-hand-side of the // select in a temporary, and for Indexed expressions, where we // place both the indexed expression and the index value in temps. - JCTree abstractLval(JCTree lval, final TreeBuilder builder) { + JCExpression abstractLval(JCExpression lval, final TreeBuilder builder) { lval = TreeInfo.skipParens(lval); switch (lval.getTag()) { case IDENT: return builder.build(lval); case SELECT: { final JCFieldAccess s = (JCFieldAccess)lval; - JCTree selected = TreeInfo.skipParens(s.selected); Symbol lid = TreeInfo.symbol(s.selected); if (lid != null && lid.kind == TYP) return builder.build(lval); return abstractRval(s.selected, new TreeBuilder() { - public JCTree build(final JCTree selected) { - return builder.build(make.Select((JCExpression)selected, s.sym)); + public JCExpression build(final JCExpression selected) { + return builder.build(make.Select(selected, s.sym)); } }); } case INDEXED: { final JCArrayAccess i = (JCArrayAccess)lval; return abstractRval(i.indexed, new TreeBuilder() { - public JCTree build(final JCTree indexed) { + public JCExpression build(final JCExpression indexed) { return abstractRval(i.index, syms.intType, new TreeBuilder() { - public JCTree build(final JCTree index) { - JCTree newLval = make.Indexed((JCExpression)indexed, - (JCExpression)index); + public JCExpression build(final JCExpression index) { + JCExpression newLval = make.Indexed(indexed, index); newLval.setType(i.type); return builder.build(newLval); } @@ -2271,9 +2269,9 @@ public class Lower extends TreeTranslator { } // evaluate and discard the first expression, then evaluate the second. - JCTree makeComma(final JCTree expr1, final JCTree expr2) { + JCExpression makeComma(final JCExpression expr1, final JCExpression expr2) { return abstractRval(expr1, new TreeBuilder() { - public JCTree build(final JCTree discarded) { + public JCExpression build(final JCExpression discarded) { return expr2; } }); @@ -2306,7 +2304,7 @@ public class Lower extends TreeTranslator { /** Visitor method: Translate a single node, boxing or unboxing if needed. */ - public T translate(T tree, Type type) { + public T translate(T tree, Type type) { return (tree == null) ? null : boxIfNeeded(translate(tree), type); } @@ -2332,7 +2330,7 @@ public class Lower extends TreeTranslator { /** Visitor method: Translate list of trees. */ - public List translate(List trees, Type type) { + public List translate(List trees, Type type) { if (trees == null) return null; for (List l = trees; l.nonEmpty(); l = l.tail) l.head = translate(l.head, type); @@ -2907,10 +2905,10 @@ public class Lower extends TreeTranslator { } } //where - private JCTree convert(JCTree tree, Type pt) { + private JCExpression convert(JCExpression tree, Type pt) { if (tree.type == pt || tree.type.hasTag(BOT)) return tree; - JCTree result = make_at(tree.pos()).TypeCast(make.Type(pt), (JCExpression)tree); + JCExpression result = make_at(tree.pos()).TypeCast(make.Type(pt), tree); result.type = (tree.type.constValue() != null) ? cfolder.coerce(tree.type, pt) : pt; return result; @@ -3075,7 +3073,7 @@ public class Lower extends TreeTranslator { /** Expand a boxing or unboxing conversion if needed. */ @SuppressWarnings("unchecked") // XXX unchecked - T boxIfNeeded(T tree, Type type) { + T boxIfNeeded(T tree, Type type) { boolean havePrimitive = tree.type.isPrimitive(); if (havePrimitive == type.isPrimitive()) return tree; @@ -3084,12 +3082,12 @@ public class Lower extends TreeTranslator { if (!unboxedTarget.hasTag(NONE)) { if (!types.isSubtype(tree.type, unboxedTarget)) //e.g. Character c = 89; tree.type = unboxedTarget.constType(tree.type.constValue()); - return (T)boxPrimitive((JCExpression)tree, types.erasure(type)); + return (T)boxPrimitive(tree, types.erasure(type)); } else { - tree = (T)boxPrimitive((JCExpression)tree); + tree = (T)boxPrimitive(tree); } } else { - tree = (T)unbox((JCExpression)tree, type); + tree = (T)unbox(tree, type); } return tree; } @@ -3172,7 +3170,7 @@ public class Lower extends TreeTranslator { // or if x == (typeof x)z then z = (unbox typeof x)((typeof x)z op y) // (but without recomputing x) JCTree newTree = abstractLval(tree.lhs, new TreeBuilder() { - public JCTree build(final JCTree lhs) { + public JCExpression build(final JCExpression lhs) { JCTree.Tag newTag = tree.getTag().noAssignOp(); // Erasure (TransTypes) can change the type of // tree.lhs. However, we can still get the @@ -3182,7 +3180,7 @@ public class Lower extends TreeTranslator { newTag, tree.type, tree.rhs.type); - JCExpression expr = (JCExpression)lhs; + JCExpression expr = lhs; if (expr.type != tree.type) expr = make.TypeCast(tree.type, expr); JCBinary opResult = make.Binary(newTag, expr, tree.rhs); @@ -3191,7 +3189,7 @@ public class Lower extends TreeTranslator { JCExpression newRhs = boxingReq ? make.TypeCast(types.unboxedType(tree.type), opResult) : opResult; - return make.Assign((JCExpression)lhs, newRhs).setType(tree.type); + return make.Assign(lhs, newRhs).setType(tree.type); } }); result = translate(newTree); @@ -3218,22 +3216,22 @@ public class Lower extends TreeTranslator { } /** Lower a tree of the form e++ or e-- where e is an object type */ - JCTree lowerBoxedPostop(final JCUnary tree) { + JCExpression lowerBoxedPostop(final JCUnary tree) { // translate to tmp1=lval(e); tmp2=tmp1; tmp1 OP 1; tmp2 // or // translate to tmp1=lval(e); tmp2=tmp1; (typeof tree)tmp1 OP 1; tmp2 // where OP is += or -= final boolean cast = TreeInfo.skipParens(tree.arg).hasTag(TYPECAST); return abstractLval(tree.arg, new TreeBuilder() { - public JCTree build(final JCTree tmp1) { + public JCExpression build(final JCExpression tmp1) { return abstractRval(tmp1, tree.arg.type, new TreeBuilder() { - public JCTree build(final JCTree tmp2) { + public JCExpression build(final JCExpression tmp2) { JCTree.Tag opcode = (tree.hasTag(POSTINC)) ? PLUS_ASG : MINUS_ASG; JCTree lhs = cast - ? make.TypeCast(tree.arg.type, (JCExpression)tmp1) + ? make.TypeCast(tree.arg.type, tmp1) : tmp1; - JCTree update = makeAssignop(opcode, + JCExpression update = makeAssignop(opcode, lhs, make.Literal(1)); return makeComma(update, tmp2); diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java index 7e174217cf4..9f2ff731f2e 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java @@ -32,6 +32,7 @@ import com.sun.tools.javac.code.Attribute.TypeCompound; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.tree.*; import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.List; @@ -834,10 +835,6 @@ public class TransTypes extends TreeTranslator { public void visitSelect(JCFieldAccess tree) { Type t = types.skipTypeVars(tree.selected.type, false); if (t.isCompound()) { - if ((tree.sym.flags() & IPROXY) != 0) { - tree.sym = ((MethodSymbol)tree.sym). - implemented((TypeSymbol)tree.sym.owner, types); - } tree.selected = coerce( translate(tree.selected, erasure(tree.selected.type)), erasure(tree.sym.owner.type)); @@ -859,7 +856,14 @@ public class TransTypes extends TreeTranslator { } public void visitReference(JCMemberReference tree) { - tree.expr = translate(tree.expr, erasure(tree.expr.type)); + Type t = types.skipTypeVars(tree.expr.type, false); + Type receiverTarget = t.isCompound() ? erasure(tree.sym.owner.type) : erasure(t); + if (tree.kind == ReferenceKind.UNBOUND) { + tree.expr = make.Type(receiverTarget); + } else { + tree.expr = translate(tree.expr, receiverTarget); + } + tree.type = erasure(tree.type); if (tree.varargsElement != null) tree.varargsElement = erasure(tree.varargsElement); diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index 10db4ef33ac..81dd6225b07 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -1112,7 +1112,8 @@ public class ClassWriter extends ClassFile { acount += writeMethodParametersAttr(m); } acount += writeMemberAttrs(m); - acount += writeParameterAttrs(m); + if (!m.isLambdaMethod()) + acount += writeParameterAttrs(m); endAttrs(acountIdx, acount); } diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index 5151729a668..53eb42f39d6 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -1981,18 +1981,6 @@ compiler.misc.incompatible.upper.lower.bounds=\ upper bounds: {1}\n\ lower bounds: {2} -# 0: type, 1: list of type, 2: list of type -compiler.misc.incompatible.upper.eq.bounds=\ - inference variable {0} has incompatible bounds\n\ - upper bounds: {1}\n\ - equality constraints: {2} - -# 0: type, 1: list of type, 2: list of type -compiler.misc.incompatible.eq.lower.bounds=\ - inference variable {0} has incompatible bounds\n\ - equality constraints: {1}\n\ - lower bounds: {2} - # 0: type, 1: list of type, 2: list of type compiler.misc.incompatible.eq.lower.bounds=\ inference variable {0} has incompatible bounds\n\ @@ -2283,6 +2271,10 @@ compiler.warn.override.equals.but.not.hashcode=\ compiler.misc.cant.override=\ {0} in {1} cannot override {2} in {3} +# 0: symbol, 1: symbol, 2: symbol, 3: symbol +compiler.misc.cant.hide=\ + {0} in {1} cannot hide {2} in {3} + # 0: symbol, 1: symbol, 2: symbol, 3: symbol compiler.misc.cant.implement=\ {0} in {1} cannot implement {2} in {3} diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index b93f51a47c7..3016af5189e 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -2623,8 +2623,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { /** (let int x = 3; in x+2) */ public static class LetExpr extends JCExpression { public List defs; - public JCTree expr; - protected LetExpr(List defs, JCTree expr) { + public JCExpression expr; + protected LetExpr(List defs, JCExpression expr) { this.defs = defs; this.expr = expr; } @@ -2731,7 +2731,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { JCAnnotation Annotation(JCTree annotationType, List args); JCModifiers Modifiers(long flags, List annotations); JCErroneous Erroneous(List errs); - LetExpr LetExpr(List defs, JCTree expr); + LetExpr LetExpr(List defs, JCExpression expr); } /** A generic visitor class for trees. diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java index 9fc7aa381e7..e36cbaae9c2 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java @@ -512,7 +512,7 @@ public class TreeCopier

    implements TreeVisitor { case LETEXPR: { LetExpr t = (LetExpr) node; List defs = copy(t.defs, p); - JCTree expr = copy(t.expr, p); + JCExpression expr = copy(t.expr, p); return M.at(t.pos).LetExpr(defs, expr); } default: diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java index 1bd0ac6d723..959d81258ec 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java @@ -552,7 +552,7 @@ public class TreeMaker implements JCTree.Factory { return tree; } - public LetExpr LetExpr(List defs, JCTree expr) { + public LetExpr LetExpr(List defs, JCExpression expr) { LetExpr tree = new LetExpr(defs, expr); tree.pos = pos; return tree; @@ -573,7 +573,7 @@ public class TreeMaker implements JCTree.Factory { defs); } - public LetExpr LetExpr(JCVariableDecl def, JCTree expr) { + public LetExpr LetExpr(JCVariableDecl def, JCExpression expr) { LetExpr tree = new LetExpr(List.of(def), expr); tree.pos = pos; return tree; @@ -628,6 +628,12 @@ public class TreeMaker implements JCTree.Factory { return Ident(new VarSymbol(FINAL, names._this, t, t.tsym)); } + /** Create a tree representing qualified `this' given its type + */ + public JCExpression QualThis(Type t) { + return Select(Type(t), new VarSymbol(FINAL, names._this, t, t.tsym)); + } + /** Create a tree representing a class literal. */ public JCExpression ClassLiteral(ClassSymbol clazz) { diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java index aec703c91b2..4f5b11f07a5 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java @@ -177,6 +177,7 @@ public class Names { public final Name lambda; public final Name metafactory; public final Name altMetafactory; + public final Name dollarThis; public final Name.Table table; @@ -235,6 +236,7 @@ public class Names { value = fromString("value"); valueOf = fromString("valueOf"); values = fromString("values"); + dollarThis = fromString("$this"); // class names java_io_Serializable = fromString("java.io.Serializable"); diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/AbstractIndexWriter.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/AbstractIndexWriter.java index a2993f50603..30a4e8d1acb 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/AbstractIndexWriter.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/AbstractIndexWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2015, 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 @@ -27,6 +27,8 @@ package com.sun.tools.doclets.formats.html; import java.io.*; import java.util.*; +import java.nio.file.*; +import java.util.zip.*; import com.sun.javadoc.*; import com.sun.tools.doclets.formats.html.markup.*; @@ -85,43 +87,106 @@ public class AbstractIndexWriter extends HtmlDocletWriter { * Add the member information for the unicode character along with the * list of the members. * - * @param unicode Unicode for which member list information to be generated + * @param uc Unicode for which member list information to be generated * @param memberlist List of members for the unicode character * @param contentTree the content tree to which the information will be added */ protected void addContents(Character uc, List memberlist, Content contentTree) { + addHeading(uc, contentTree); + int memberListSize = memberlist.size(); + // Display the list only if there are elements to be displayed. + if (memberListSize > 0) { + Content dl = new HtmlTree(HtmlTag.DL); + for (Doc element : memberlist) { + addDescription(dl, element); + } + contentTree.addContent(dl); + } + } + + protected void addSearchContents(Character uc, List searchList, + Content contentTree) { + addHeading(uc, contentTree); + // Display the list only if there are elements to be displayed. + if (!searchList.isEmpty()) { + Content dl = new HtmlTree(HtmlTag.DL); + for (SearchIndexItem sii : searchList) { + addDescription(sii, dl); + } + contentTree.addContent(dl); + } + } + + protected void addContents(Character uc, List memberlist, List searchList, + Content contentTree) { + addHeading(uc, contentTree); + int memberListSize = memberlist.size(); + int searchListSize = searchList.size(); + int i = 0; + int j = 0; + Content dl = new HtmlTree(HtmlTag.DL); + while (i < memberListSize && j < searchListSize) { + if (memberlist.get(i).name().compareTo(searchList.get(j).getLabel()) < 0) { + addDescription(dl, memberlist.get(i)); + i++; + } else if (memberlist.get(i).name().compareTo(searchList.get(j).getLabel()) > 0) { + addDescription(searchList.get(j), dl); + j++; + } else { + addDescription(dl, memberlist.get(i)); + addDescription(searchList.get(j), dl); + j++; + i++; + } + } + if (i >= memberListSize) { + while (j < searchListSize) { + addDescription(searchList.get(j), dl); + j++; + } + } + if (j >= searchListSize) { + while (i < memberListSize) { + addDescription(dl, memberlist.get(i)); + i++; + } + } + contentTree.addContent(dl); + } + + protected void addHeading(Character uc, Content contentTree) { String unicode = uc.toString(); contentTree.addContent(getMarkerAnchorForIndex(unicode)); Content headContent = new StringContent(unicode); Content heading = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING, false, HtmlStyle.title, headContent); contentTree.addContent(heading); - int memberListSize = memberlist.size(); - // Display the list only if there are elements to be displayed. - if (memberListSize > 0) { - Content dl = new HtmlTree(HtmlTag.DL); - for (Doc element : memberlist) { - if (element instanceof MemberDoc) { - addDescription((MemberDoc) element, dl); - } else if (element instanceof ClassDoc) { - addDescription((ClassDoc) element, dl); - } else if (element instanceof PackageDoc) { - addDescription((PackageDoc) element, dl); - } - } - contentTree.addContent(dl); - } } + protected void addDescription(Content dl, Doc element) { + SearchIndexItem si = new SearchIndexItem(); + if (element instanceof MemberDoc) { + addDescription((MemberDoc) element, dl, si); + configuration.memberSearchIndex.add(si); + } else if (element instanceof ClassDoc) { + addDescription((ClassDoc) element, dl, si); + configuration.typeSearchIndex.add(si); + } else if (element instanceof PackageDoc) { + addDescription((PackageDoc) element, dl, si); + configuration.packageSearchIndex.add(si); + } + } /** * Add one line summary comment for the package. * * @param pkg the package to be documented * @param dlTree the content tree to which the description will be added */ - protected void addDescription(PackageDoc pkg, Content dlTree) { + protected void addDescription(PackageDoc pkg, Content dlTree, SearchIndexItem si) { Content link = getPackageLink(pkg, new StringContent(utils.getPackageName(pkg))); + si.setLabel(utils.getPackageName(pkg)); + si.setCategory(getResource("doclet.Packages").toString()); Content dt = HtmlTree.DT(link); dt.addContent(" - "); dt.addContent(getResource("doclet.package")); @@ -138,9 +203,12 @@ public class AbstractIndexWriter extends HtmlDocletWriter { * @param cd the class being documented * @param dlTree the content tree to which the description will be added */ - protected void addDescription(ClassDoc cd, Content dlTree) { + protected void addDescription(ClassDoc cd, Content dlTree, SearchIndexItem si) { Content link = getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.INDEX, cd).strong(true)); + si.setContainingPackage(utils.getPackageName(cd.containingPackage())); + si.setLabel(cd.typeName()); + si.setCategory(getResource("doclet.Types").toString()); Content dt = HtmlTree.DT(link); dt.addContent(" - "); addClassInfo(cd, dt); @@ -171,10 +239,22 @@ public class AbstractIndexWriter extends HtmlDocletWriter { * @param member MemberDoc for the member of the Class Kind * @param dlTree the content tree to which the description will be added */ - protected void addDescription(MemberDoc member, Content dlTree) { + protected void addDescription(MemberDoc member, Content dlTree, SearchIndexItem si) { String name = (member instanceof ExecutableMemberDoc)? member.name() + ((ExecutableMemberDoc)member).flatSignature() : member.name(); + si.setContainingPackage(utils.getPackageName((member.containingClass()).containingPackage())); + si.setContainingClass((member.containingClass()).typeName()); + if (member instanceof ExecutableMemberDoc) { + ExecutableMemberDoc emd = (ExecutableMemberDoc)member; + si.setLabel(member.name() + emd.flatSignature()); + if (!((emd.signature()).equals(emd.flatSignature()))) { + si.setUrl(getName(getAnchor((ExecutableMemberDoc) member))); + } + } else { + si.setLabel(member.name()); + } + si.setCategory(getResource("doclet.Members").toString()); Content span = HtmlTree.SPAN(HtmlStyle.memberNameLink, getDocLink(LinkInfoImpl.Kind.INDEX, member, name)); Content dt = HtmlTree.DT(span); @@ -186,6 +266,23 @@ public class AbstractIndexWriter extends HtmlDocletWriter { dlTree.addContent(dd); } + protected void addDescription(SearchIndexItem sii, Content dlTree) { + String path = pathToRoot.isEmpty() ? "" : pathToRoot.getPath() + "/"; + path += sii.getUrl(); + HtmlTree labelLink = HtmlTree.A(path, new StringContent(sii.getLabel())); + Content dt = HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.searchTagLink, labelLink)); + dt.addContent(" - "); + dt.addContent(getResource("doclet.Search_tag_in", sii.getHolder())); + dlTree.addContent(dt); + Content dd = new HtmlTree(HtmlTag.DD); + if (sii.getDescription().isEmpty()) { + dd.addContent(getSpace()); + } else { + dd.addContent(sii.getDescription()); + } + dlTree.addContent(dd); + } + /** * Add comment for each element in the index. If the element is deprecated * and it has a @deprecated tag, use that comment. Else if the containing @@ -273,4 +370,66 @@ public class AbstractIndexWriter extends HtmlDocletWriter { public String getNameForIndex(String unicode) { return "I:" + getName(unicode); } + + protected void createSearchIndexFiles() { + createSearchIndexFile(DocPaths.PACKAGE_SEARCH_INDEX_JSON, DocPaths.PACKAGE_SEARCH_INDEX_ZIP, + configuration.packageSearchIndex); + createSearchIndexFile(DocPaths.TYPE_SEARCH_INDEX_JSON, DocPaths.TYPE_SEARCH_INDEX_ZIP, + configuration.typeSearchIndex); + createSearchIndexFile(DocPaths.MEMBER_SEARCH_INDEX_JSON, DocPaths.MEMBER_SEARCH_INDEX_ZIP, + configuration.memberSearchIndex); + createSearchIndexFile(DocPaths.TAG_SEARCH_INDEX_JSON, DocPaths.TAG_SEARCH_INDEX_ZIP, + configuration.tagSearchIndex); + } + + protected void createSearchIndexFile(DocPath searchIndexFile, DocPath searchIndexZip, + List searchIndex) { + if (!searchIndex.isEmpty()) { + try { + StringBuilder searchVar = new StringBuilder("["); + boolean first = true; + DocFile searchFile = DocFile.createFileForOutput(configuration, searchIndexFile); + Path p = Paths.get(searchFile.getPath()); + for (SearchIndexItem item : searchIndex) { + if (first) { + searchVar.append(item.toString()); + first = false; + } else { + searchVar.append(",").append(item.toString()); + } + } + searchVar.append("]"); + Files.write(p, searchVar.toString().getBytes()); + DocFile zipFile = DocFile.createFileForOutput(configuration, searchIndexZip); + try (FileOutputStream fos = new FileOutputStream(zipFile.getPath()); + ZipOutputStream zos = new ZipOutputStream(fos)) { + zipFile(searchFile.getPath(), searchIndexFile, zos); + } + Files.delete(p); + } catch (IOException ie) { + throw new DocletAbortException(ie); + } + } + } + + protected void zipFile(String inputFile, DocPath file, ZipOutputStream zos) { + try { + try { + ZipEntry ze = new ZipEntry(file.getPath()); + zos.putNextEntry(ze); + try (FileInputStream fis = new FileInputStream(new File(inputFile))) { + byte[] buf = new byte[2048]; + int len = fis.read(buf); + while (len > 0) { + zos.write(buf, 0, len); + len = fis.read(buf); + } + } + } finally { + zos.closeEntry(); + } + } catch (IOException e) { + throw new DocletAbortException(e); + } + } } diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/ConfigurationImpl.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/ConfigurationImpl.java index 00e3e7f8edd..b4e3440b5fd 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/ConfigurationImpl.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/ConfigurationImpl.java @@ -201,6 +201,18 @@ public class ConfigurationImpl extends Configuration { */ public ClassDoc currentcd = null; // Set this classdoc in the ClassWriter. + protected List memberSearchIndex = new ArrayList<>(); + + protected List packageSearchIndex = new ArrayList<>(); + + protected List tagSearchIndex = new ArrayList<>(); + + protected List typeSearchIndex = new ArrayList<>(); + + protected Map> tagSearchIndexMap = new HashMap<>(); + + protected Set tagSearchIndexKeys; + /** * Constructor. Initializes resource for the * {@link com.sun.tools.doclets.internal.toolkit.util.MessageRetriever MessageRetriever}. @@ -628,4 +640,21 @@ public class ConfigurationImpl extends Configuration { public Content newContent() { return new ContentBuilder(); } + + protected void buildSearchTagIndex() { + for (SearchIndexItem sii : tagSearchIndex) { + String tagLabel = sii.getLabel(); + char ch = (tagLabel.length() == 0) + ? '*' + : Character.toUpperCase(tagLabel.charAt(0)); + Character unicode = ch; + List list = tagSearchIndexMap.get(unicode); + if (list == null) { + list = new ArrayList<>(); + tagSearchIndexMap.put(unicode, list); + } + list.add(sii); + } + tagSearchIndexKeys = tagSearchIndexMap.keySet(); + } } diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/HtmlDoclet.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/HtmlDoclet.java index d82eb3cf1ab..0cdba5f611c 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/HtmlDoclet.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/HtmlDoclet.java @@ -124,6 +124,7 @@ public class HtmlDoclet extends AbstractDoclet { TreeWriter.generate(configuration, classtree); } if (configuration.createindex) { + configuration.buildSearchTagIndex(); if (configuration.splitindex) { SplitIndexWriter.generate(configuration, indexbuilder); } else { @@ -156,6 +157,52 @@ public class HtmlDoclet extends AbstractDoclet { } f = DocFile.createFileForOutput(configuration, DocPaths.JAVASCRIPT); f.copyResource(DocPaths.RESOURCES.resolve(DocPaths.JAVASCRIPT), true, true); + if (configuration.createindex) { + f = DocFile.createFileForOutput(configuration, DocPaths.SEARCH_JS); + f.copyResource(DocPaths.RESOURCES.resolve(DocPaths.SEARCH_JS), true, true); + f = DocFile.createFileForOutput(configuration, DocPaths.RESOURCES.resolve(DocPaths.GLASS_IMG)); + f.copyResource(DocPaths.RESOURCES.resolve(DocPaths.GLASS_IMG), true, false); + f = DocFile.createFileForOutput(configuration, DocPaths.RESOURCES.resolve(DocPaths.X_IMG)); + f.copyResource(DocPaths.RESOURCES.resolve(DocPaths.X_IMG), true, false); + copyJqueryFiles(); + } + } + + protected void copyJqueryFiles() { + List files = Arrays.asList( + "jquery-1.10.2.js", + "jquery-ui.js", + "jquery-ui.css", + "jquery-ui.min.js", + "jquery-ui.min.css", + "jquery-ui.structure.min.css", + "jquery-ui.structure.css", + "external/jquery/jquery.js", + "jszip/dist/jszip.js", + "jszip/dist/jszip.min.js", + "jszip-utils/dist/jszip-utils.js", + "jszip-utils/dist/jszip-utils.min.js", + "jszip-utils/dist/jszip-utils-ie.js", + "jszip-utils/dist/jszip-utils-ie.min.js", + "images/ui-bg_flat_0_aaaaaa_40x100.png", + "images/ui-icons_454545_256x240.png", + "images/ui-bg_glass_95_fef1ec_1x400.png", + "images/ui-bg_glass_75_dadada_1x400.png", + "images/ui-bg_highlight-soft_75_cccccc_1x100.png", + "images/ui-icons_888888_256x240.png", + "images/ui-icons_2e83ff_256x240.png", + "images/ui-bg_glass_65_ffffff_1x400.png", + "images/ui-icons_cd0a0a_256x240.png", + "images/ui-bg_glass_55_fbf9ee_1x400.png", + "images/ui-icons_222222_256x240.png", + "images/ui-bg_glass_75_e6e6e6_1x400.png", + "images/ui-bg_flat_75_ffffff_40x100.png"); + DocFile f; + for (String file : files) { + DocPath filePath = DocPaths.JQUERY_FILES.resolve(file); + f = DocFile.createFileForOutput(configuration, filePath); + f.copyResource(DocPaths.RESOURCES.resolve(filePath), true, false); + } } /** diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/HtmlDocletWriter.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/HtmlDocletWriter.java index a7f295a7cf7..63a64a0ae6e 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/HtmlDocletWriter.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/HtmlDocletWriter.java @@ -104,6 +104,8 @@ public class HtmlDocletWriter extends HtmlDocWriter { */ private boolean isContainerDocumented = false; + HtmlTree fixedNavDiv = new HtmlTree(HtmlTag.DIV); + /** * Constructor to construct the HtmlStandardWriter object. * @@ -420,8 +422,8 @@ public class HtmlDocletWriter extends HtmlDocWriter { head.addContent(meta); } } - head.addContent(getStyleSheetProperties()); - head.addContent(getScriptProperties()); + addStyleSheetProperties(head); + addScriptProperties(head); Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(), head, body); Content htmlDocument = new HtmlDocument(htmlDocType, @@ -470,7 +472,7 @@ public class HtmlDocletWriter extends HtmlDocWriter { */ public void addTop(Content htmlTree) { Content top = new RawHtml(replaceDocRootDir(configuration.top)); - htmlTree.addContent(top); + fixedNavDiv.addContent(top); } /** @@ -498,9 +500,10 @@ public class HtmlDocletWriter extends HtmlDocWriter { : htmlTree; String allClassesId = "allclasses_"; HtmlTree navDiv = new HtmlTree(HtmlTag.DIV); + fixedNavDiv.addStyle(HtmlStyle.fixedNav); Content skipNavLinks = configuration.getResource("doclet.Skip_navigation_links"); if (header) { - tree.addContent(HtmlConstants.START_OF_TOP_NAVBAR); + fixedNavDiv.addContent(HtmlConstants.START_OF_TOP_NAVBAR); navDiv.addStyle(HtmlStyle.topNav); allClassesId += "navbar_top"; Content a = getMarkerAnchor(SectionName.NAVBAR_TOP); @@ -558,7 +561,11 @@ public class HtmlDocletWriter extends HtmlDocWriter { navDiv.addContent(navList); Content aboutDiv = HtmlTree.DIV(HtmlStyle.aboutLanguage, getUserHeaderFooter(header)); navDiv.addContent(aboutDiv); - tree.addContent(navDiv); + if (header) { + fixedNavDiv.addContent(navDiv); + } else { + tree.addContent(navDiv); + } Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious()); ulNav.addContent(getNavLinkNext()); Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav); @@ -568,12 +575,24 @@ public class HtmlDocletWriter extends HtmlDocWriter { HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex()); ulAllClasses.addAttr(HtmlAttr.ID, allClassesId.toString()); subDiv.addContent(ulAllClasses); + if (header && configuration.createindex) { + HtmlTree inputText = HtmlTree.INPUT("text", "search"); + HtmlTree inputReset = HtmlTree.INPUT("reset", "reset"); + Content searchTxt = configuration.getResource("doclet.search"); + searchTxt.addContent(getSpace()); + HtmlTree liInput = HtmlTree.LI(HtmlTree.SPAN(searchTxt)); + liInput.addContent(inputText); + liInput.addContent(inputReset); + HtmlTree ulSearch = HtmlTree.UL(HtmlStyle.navListSearch, liInput); + subDiv.addContent(ulSearch); + } subDiv.addContent(getAllClassesLinkScript(allClassesId.toString())); addSummaryDetailLinks(subDiv); if (header) { subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_TOP)); - tree.addContent(subDiv); - tree.addContent(HtmlConstants.END_OF_TOP_NAVBAR); + fixedNavDiv.addContent(subDiv); + fixedNavDiv.addContent(HtmlConstants.END_OF_TOP_NAVBAR); + tree.addContent(fixedNavDiv); } else { subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_BOTTOM)); tree.addContent(subDiv); @@ -1819,11 +1838,11 @@ public class HtmlDocletWriter extends HtmlDocWriter { } /** - * Returns a link to the stylesheet file. + * Add a link to the stylesheet file. * - * @return an HtmlTree for the lINK tag which provides the stylesheet location + * @param head the content tree to which the files will be added */ - public HtmlTree getStyleSheetProperties() { + public void addStyleSheetProperties(Content head) { String stylesheetfile = configuration.stylesheetfile; DocPath stylesheet; if (stylesheetfile.isEmpty()) { @@ -1835,17 +1854,48 @@ public class HtmlDocletWriter extends HtmlDocWriter { HtmlTree link = HtmlTree.LINK("stylesheet", "text/css", pathToRoot.resolve(stylesheet).getPath(), "Style"); - return link; + head.addContent(link); + if (configuration.createindex) { + HtmlTree jq_link = HtmlTree.LINK("stylesheet", "text/css", + pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(DocPaths.JQUERY_STYLESHEET_FILE)).getPath(), + "Style"); + head.addContent(jq_link); + } } /** - * Returns a link to the JavaScript file. + * Add a link to the JavaScript file. * - * @return an HtmlTree for the Script tag which provides the JavaScript location + * @param head the content tree to which the files will be added */ - public HtmlTree getScriptProperties() { - HtmlTree script = HtmlTree.SCRIPT(pathToRoot.resolve(DocPaths.JAVASCRIPT).getPath()); - return script; + public void addScriptProperties(Content head) { + HtmlTree javascript = HtmlTree.SCRIPT(pathToRoot.resolve(DocPaths.JAVASCRIPT).getPath()); + head.addContent(javascript); + if (configuration.createindex) { + if (pathToRoot != null && script != null) { + String path = pathToRoot.isEmpty() ? "." : pathToRoot.getPath(); + script.addContent(new RawHtml("var pathtoroot = \"" + path + "/\";loadScripts(document, \'script\');")); + } + addJQueryFile(head, DocPaths.JSZIP_MIN); + addJQueryFile(head, DocPaths.JSZIPUTILS_MIN); + head.addContent(new RawHtml("")); + addJQueryFile(head, DocPaths.JQUERY_JS_1_10); + addJQueryFile(head, DocPaths.JQUERY_JS); + } + } + + /** + * Add a link to the JQuery javascript file. + * + * @param head the content tree to which the files will be added + * @param filePath the DocPath of the file that needs to be added + */ + private void addJQueryFile(Content head, DocPath filePath) { + HtmlTree jqyeryScriptFile = HtmlTree.SCRIPT( + pathToRoot.resolve(DocPaths.JQUERY_FILES.resolve(filePath)).getPath()); + head.addContent(jqyeryScriptFile); } /** diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/SearchIndexItem.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/SearchIndexItem.java new file mode 100644 index 00000000000..2c68967f091 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/SearchIndexItem.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2015, 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 com.sun.tools.doclets.formats.html; + +/** + * Index item for search. + * + *

    This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public class SearchIndexItem { + + private String label = ""; + private String url = ""; + private String category = ""; + private String containingPackage = ""; + private String containingClass = ""; + private String holder = ""; + private String description = ""; + + public void setLabel(String l) { + label = l; + } + + public String getLabel() { + return label; + } + + public void setUrl(String u) { + url = u; + } + + public String getUrl() { + return url; + } + + public void setContainingPackage(String p) { + containingPackage = p; + } + + public void setContainingClass(String c) { + containingClass = c; + } + + public void setCategory(String c) { + category = c; + } + + public void setHolder(String h) { + holder = h; + } + + public String getHolder() { + return holder; + } + + public void setDescription(String d) { + description = d; + } + + public String getDescription() { + return description; + } + + public String toString() { + StringBuilder item = new StringBuilder(""); + if (category.equals("Packages")) { + item.append("{") + .append("\"l\":\"").append(label).append("\"") + .append("}"); + } else if (category.equals("Types")) { + item.append("{") + .append("\"p\":\"").append(containingPackage).append("\",") + .append("\"l\":\"").append(label).append("\"") + .append("}"); + } else if (category.equals("Members")) { + item.append("{") + .append("\"p\":\"").append(containingPackage).append("\",") + .append("\"c\":\"").append(containingClass).append("\",") + .append("\"l\":\"").append(label).append("\""); + if (!url.equals("")) { + item.append(",\"url\":\"").append(url).append("\""); + } + item.append("}"); + } else { + item.append("{") + .append("\"l\":\"").append(label).append("\",") + .append("\"h\":\"").append(holder).append("\","); + if (!description.equals("")) { + item.append("\"d\":\"").append(description).append("\","); + } + item.append("\"u\":\"").append(url).append("\"") + .append("}"); + } + return item.toString(); + } +} diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/SingleIndexWriter.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/SingleIndexWriter.java index b233c597123..990f062b397 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/SingleIndexWriter.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/SingleIndexWriter.java @@ -26,6 +26,7 @@ package com.sun.tools.doclets.formats.html; import java.io.*; +import java.util.*; import com.sun.tools.doclets.formats.html.markup.*; import com.sun.tools.doclets.internal.toolkit.*; @@ -47,6 +48,8 @@ import com.sun.tools.doclets.internal.toolkit.util.*; */ public class SingleIndexWriter extends AbstractIndexWriter { + private List elements; + /** * Construct the SingleIndexWriter with filename "index-all.html" and the * {@link IndexBuilder} @@ -100,10 +103,20 @@ public class SingleIndexWriter extends AbstractIndexWriter { } HtmlTree divTree = new HtmlTree(HtmlTag.DIV); divTree.addStyle(HtmlStyle.contentContainer); + Set keys = new TreeSet<>(Arrays.asList(indexbuilder.elements())); + keys.addAll(configuration.tagSearchIndexKeys); + elements = new ArrayList<>(keys); addLinksForIndexes(divTree); - for (int i = 0; i < indexbuilder.elements().length; i++) { - Character unicode = (Character)((indexbuilder.elements())[i]); - addContents(unicode, indexbuilder.getMemberList(unicode), divTree); + for (Object ch : elements) { + Character unicode = (Character) ch; + if (configuration.tagSearchIndexMap.get(unicode) == null) { + addContents(unicode, indexbuilder.getMemberList(unicode), divTree); + } else if (indexbuilder.getMemberList(unicode) == null) { + addSearchContents(unicode, configuration.tagSearchIndexMap.get(unicode), divTree); + } else { + addContents(unicode, indexbuilder.getMemberList(unicode), + configuration.tagSearchIndexMap.get(unicode), divTree); + } } addLinksForIndexes(divTree); body.addContent((configuration.allowTag(HtmlTag.MAIN)) @@ -117,6 +130,7 @@ public class SingleIndexWriter extends AbstractIndexWriter { if (configuration.allowTag(HtmlTag.FOOTER)) { body.addContent(htmlTree); } + createSearchIndexFiles(); printHtmlDocument(null, true, body); } @@ -126,11 +140,11 @@ public class SingleIndexWriter extends AbstractIndexWriter { * @param contentTree the content tree to which the links for indexes will be added */ protected void addLinksForIndexes(Content contentTree) { - for (int i = 0; i < indexbuilder.elements().length; i++) { - String unicode = (indexbuilder.elements())[i].toString(); + for (Object ch : elements) { + String unicode = ch.toString(); contentTree.addContent( getHyperLink(getNameForIndex(unicode), - new StringContent(unicode))); + new StringContent(unicode))); contentTree.addContent(getSpace()); } } diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/SplitIndexWriter.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/SplitIndexWriter.java index b2687bfe996..0415381041e 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/SplitIndexWriter.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/SplitIndexWriter.java @@ -26,6 +26,7 @@ package com.sun.tools.doclets.formats.html; import java.io.*; +import java.util.*; import com.sun.tools.doclets.formats.html.markup.*; import com.sun.tools.doclets.internal.toolkit.*; @@ -57,6 +58,8 @@ public class SplitIndexWriter extends AbstractIndexWriter { */ protected int next; + private List indexElements; + /** * Construct the SplitIndexWriter. Uses path to this file and relative path * from this file. @@ -66,9 +69,10 @@ public class SplitIndexWriter extends AbstractIndexWriter { */ public SplitIndexWriter(ConfigurationImpl configuration, DocPath path, - IndexBuilder indexbuilder, + IndexBuilder indexbuilder, List elements, int prev, int next) throws IOException { super(configuration, path, indexbuilder); + this.indexElements = elements; this.prev = prev; this.next = next; } @@ -86,16 +90,20 @@ public class SplitIndexWriter extends AbstractIndexWriter { DocPath filename = DocPath.empty; DocPath path = DocPaths.INDEX_FILES; try { - for (int i = 0; i < indexbuilder.elements().length; i++) { - int j = i + 1; - int prev = (j == 1)? -1: i; - int next = (j == indexbuilder.elements().length)? -1: j + 1; - filename = DocPaths.indexN(j); + Set keys = new TreeSet<>(Arrays.asList(indexbuilder.elements())); + keys.addAll(configuration.tagSearchIndexKeys); + List elements = new ArrayList<>(keys); + ListIterator li = elements.listIterator(); + while (li.hasNext()) { + Object ch = li.next(); + filename = DocPaths.indexN(li.nextIndex()); indexgen = new SplitIndexWriter(configuration, - path.resolve(filename), - indexbuilder, prev, next); - indexgen.generateIndexFile((Character)indexbuilder. - elements()[i]); + path.resolve(filename), + indexbuilder, elements, li.previousIndex(), li.nextIndex()); + indexgen.generateIndexFile((Character) ch); + if (!li.hasNext()) { + indexgen.createSearchIndexFiles(); + } indexgen.close(); } } catch (IOException exc) { @@ -128,7 +136,14 @@ public class SplitIndexWriter extends AbstractIndexWriter { HtmlTree divTree = new HtmlTree(HtmlTag.DIV); divTree.addStyle(HtmlStyle.contentContainer); addLinksForIndexes(divTree); - addContents(unicode, indexbuilder.getMemberList(unicode), divTree); + if (configuration.tagSearchIndexMap.get(unicode) == null) { + addContents(unicode, indexbuilder.getMemberList(unicode), divTree); + } else if (indexbuilder.getMemberList(unicode) == null) { + addSearchContents(unicode, configuration.tagSearchIndexMap.get(unicode), divTree); + } else { + addContents(unicode, indexbuilder.getMemberList(unicode), + configuration.tagSearchIndexMap.get(unicode), divTree); + } addLinksForIndexes(divTree); body.addContent((configuration.allowTag(HtmlTag.MAIN)) ? HtmlTree.MAIN(divTree) : divTree); if (configuration.allowTag(HtmlTag.FOOTER)) { @@ -148,11 +163,10 @@ public class SplitIndexWriter extends AbstractIndexWriter { * @param contentTree the content tree to which the links for indexes will be added */ protected void addLinksForIndexes(Content contentTree) { - Object[] unicodeChars = indexbuilder.elements(); - for (int i = 0; i < unicodeChars.length; i++) { + for (int i = 0; i < indexElements.size(); i++) { int j = i + 1; contentTree.addContent(getHyperLink(DocPaths.indexN(j), - new StringContent(unicodeChars[i].toString()))); + new StringContent(indexElements.get(i).toString()))); contentTree.addContent(getSpace()); } } diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/TagletWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/TagletWriterImpl.java index 8c0c3765156..9b671d1d3ae 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/TagletWriterImpl.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/TagletWriterImpl.java @@ -77,6 +77,87 @@ public class TagletWriterImpl extends TagletWriter { return result; } + protected Content indexTagOutput(Tag tag) { + String text = tag.text(); + String tagText = ""; + String desc = ""; + if (text.isEmpty() || text.trim().isEmpty()) { + configuration.message.warning(tag.position(), "doclet.invalid_usage_of_tag", tag.name()); + } else { + int len = text.length(); + int tagTextEnd = 0; + int descstart = 0; + int start = 0; + Character term = ' '; + int cp = text.codePointAt(0); + if (cp == '"') { + term = '"'; + start++; + } + for (int i = start; i < len; i += Character.charCount(cp)) { + cp = text.codePointAt(i); + if (cp == term) { + tagTextEnd = i; + break; + } + } + if (tagTextEnd < len - 1 && tagTextEnd != 0) { + descstart = tagTextEnd + 1; + } + String desctext = ""; + if (descstart > 0) { + tagText = text.substring(start, tagTextEnd).trim(); + desctext = text.substring(descstart, len).trim(); + // strip off the white space which can be between tag description and the + // actual label. + for (int i = 0; i < desctext.length(); i++) { + char ch2 = desctext.charAt(i); + if (!(ch2 == ' ' || ch2 == '\t' || ch2 == '\n')) { + desc = desctext.substring(i); + break; + } + } + } else { + if (term == '"') { + if (tagTextEnd == 0) { + // If unclosed quote, print out a warning and ignore the invalid tag text. + configuration.message.warning(tag.position(), "doclet.invalid_usage_of_tag", tag.name()); + tagText = ""; + } else { + tagText = text.substring(start, tagTextEnd).trim(); + } + } else { + tagText = text.trim(); + } + desc = ""; + } + } + String anchorName = htmlWriter.getName(tagText); + Content result = HtmlTree.A_ID(anchorName, new StringContent(tagText)); + if (configuration.createindex && !tagText.isEmpty()) { + SearchIndexItem si = new SearchIndexItem(); + si.setLabel(tagText); + si.setDescription(desc); + if (tag.holder() instanceof ProgramElementDoc) { + if (tag.holder() instanceof MemberDoc) { + si.setUrl(DocPath.forClass(((MemberDoc) tag.holder()).containingClass()).getPath() + + "#" + anchorName); + si.setHolder(((MemberDoc) tag.holder()).qualifiedName()); + } else { + si.setUrl(DocPath.forClass((ClassDoc) tag.holder()).getPath() + "#" + anchorName); + si.setHolder(((ClassDoc) tag.holder()).qualifiedName()); + } + } else if (tag.holder() instanceof PackageDoc) { + si.setUrl(DocPath.forPackage((PackageDoc) tag.holder()).getPath() + + "/" + DocPaths.PACKAGE_SUMMARY.getPath() + "#" + anchorName); + si.setHolder(((PackageDoc) tag.holder()).name()); + } + si.setCategory(configuration.getResource("doclet.SearchTags").toString()); + configuration.tagSearchIndex.add(si); + } + return result; + } + /** * {@inheritDoc} */ diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlAttr.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlAttr.java index ff561c98652..5f389abc845 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlAttr.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlAttr.java @@ -43,6 +43,7 @@ public enum HtmlAttr { CLEAR, COLS, CONTENT, + DISABLED, HREF, HTTP_EQUIV("http-equiv"), ID, @@ -59,6 +60,7 @@ public enum HtmlAttr { TARGET, TITLE, TYPE, + VALUE, WIDTH; private final String value; diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlStyle.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlStyle.java index 261f4a435d3..75f291b3342 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlStyle.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlStyle.java @@ -61,6 +61,7 @@ public enum HtmlStyle { details, docSummary, emphasizedPhrase, + fixedNav, header, horizontal, footer, @@ -79,6 +80,7 @@ public enum HtmlStyle { nameValue, navBarCell1Rev, navList, + navListSearch, overrideSpecifyLabel, overviewSummary, packageHierarchyLabel, @@ -87,6 +89,7 @@ public enum HtmlStyle { rightContainer, rightIframe, rowColor, + searchTagLink, seeLabel, serializedFormContainer, simpleTagLabel, diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlTag.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlTag.java index f141b1880f4..1f82922809f 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlTag.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlTag.java @@ -66,6 +66,7 @@ public enum HtmlTag { I(BlockType.INLINE, EndTag.END), IFRAME(BlockType.OTHER, EndTag.END), IMG(BlockType.INLINE, EndTag.NOEND), + INPUT(BlockType.BLOCK, EndTag.NOEND), LI, LISTING, LINK(BlockType.OTHER, EndTag.NOEND), diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlTree.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlTree.java index fd02a46160d..23e9c6d7a93 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlTree.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlTree.java @@ -243,6 +243,20 @@ public class HtmlTree extends Content { return htmltree; } + /** + * Generates an HTML anchor tag with id attribute and a body. + * + * @param id id for the anchor tag + * @param body body for the anchor tag + * @return an HtmlTree object + */ + public static HtmlTree A_ID(String id, Content body) { + HtmlTree htmltree = new HtmlTree(HtmlTag.A); + htmltree.addAttr(HtmlAttr.ID, nullCheck(id)); + htmltree.addContent(nullCheck(body)); + return htmltree; + } + /** * Generates a CAPTION tag with some content. * @@ -433,6 +447,22 @@ public class HtmlTree extends Content { return htmltree; } + /** + * Generates a INPUT tag with some id. + * + * @param type the type of input + * @param id id for the tag + * @return an HtmlTree object for the INPUT tag + */ + public static HtmlTree INPUT(String type, String id) { + HtmlTree htmltree = new HtmlTree(HtmlTag.INPUT); + htmltree.addAttr(HtmlAttr.TYPE, nullCheck(type)); + htmltree.addAttr(HtmlAttr.ID, nullCheck(id)); + htmltree.addAttr(HtmlAttr.VALUE, " "); + htmltree.addAttr(HtmlAttr.DISABLED, "disabled"); + return htmltree; + } + /** * Generates a LI tag with some content. * @@ -850,13 +880,13 @@ public class HtmlTree extends Content { public boolean isValid() { switch (htmlTag) { case A : - return (hasAttr(HtmlAttr.NAME) || hasAttr(HtmlAttr.ID) || (hasAttr(HtmlAttr.HREF) - && hasContent())); + return (hasAttr(HtmlAttr.NAME) || hasAttr(HtmlAttr.ID) || (hasAttr(HtmlAttr.HREF) && hasContent())); case BR : return (!hasContent() && (!hasAttrs() || hasAttr(HtmlAttr.CLEAR))); case IFRAME : return (hasAttr(HtmlAttr.SRC) && !hasContent()); case HR : + case INPUT: return (!hasContent()); case IMG : return (hasAttr(HtmlAttr.SRC) && hasAttr(HtmlAttr.ALT) && !hasContent()); diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlWriter.java b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlWriter.java index 34a4cef0bae..5c5d65b3585 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlWriter.java +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlWriter.java @@ -166,7 +166,7 @@ public class HtmlWriter { private final Writer writer; - private Content script; + protected Content script; /** * Constructor. diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/resources/standard.properties b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/resources/standard.properties index 6bde77cc4e7..e5cadd9852f 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/resources/standard.properties +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/formats/html/resources/standard.properties @@ -55,6 +55,7 @@ doclet.Static_variable_in=Static variable in {0} doclet.Variable_in=Variable in {0} doclet.Constructor_for=Constructor for {0} doclet.Static_method_in=Static method in {0} +doclet.Search_tag_in=Search tag in {0} doclet.Method_in=Method in {0} doclet.package=package doclet.MalformedURL=Malformed URL: {0} diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/resources/doclets.properties b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/resources/doclets.properties index c4ba4663674..5d4e330a265 100644 --- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/resources/doclets.properties +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/resources/doclets.properties @@ -170,6 +170,11 @@ doclet.subclasses=subclasses doclet.subinterfaces=subinterfaces doclet.Modifier=Modifier doclet.Type=Type +doclet.Types=Types +doclet.Members=Members +doclet.SearchTags=SearchTags +doclet.search=SEARCH: +doclet.invalid_usage_of_tag=invalid usage of {0} tag. doclet.Field=Field doclet.Property=Property doclet.Constructor=Constructor diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/resources/glass.png b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/resources/glass.png new file mode 100644 index 00000000000..a7f591f467a Binary files /dev/null and b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/resources/glass.png differ diff --git a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/resources/jquery/external/jquery/jquery.js b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/resources/jquery/external/jquery/jquery.js new file mode 100644 index 00000000000..c5c648255c1 --- /dev/null +++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/doclets/internal/toolkit/resources/jquery/external/jquery/jquery.js @@ -0,0 +1,9789 @@ +/*! + * jQuery JavaScript Library v1.10.2 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2013-07-03T13:48Z + */ +(function( window, undefined ) { + +// Can't do this because several apps including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// Support: Firefox 18+ +//"use strict"; +var + // The deferred used on DOM ready + readyList, + + // A central reference to the root jQuery(document) + rootjQuery, + + // Support: IE<10 + // For `typeof xmlNode.method` instead of `xmlNode.method !== undefined` + core_strundefined = typeof undefined, + + // Use the correct document accordingly with window argument (sandbox) + location = window.location, + document = window.document, + docElem = document.documentElement, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // [[Class]] -> type pairs + class2type = {}, + + // List of deleted data cache ids, so we can reuse them + core_deletedIds = [], + + core_version = "1.10.2", + + // Save a reference to some core methods + core_concat = core_deletedIds.concat, + core_push = core_deletedIds.push, + core_slice = core_deletedIds.slice, + core_indexOf = core_deletedIds.indexOf, + core_toString = class2type.toString, + core_hasOwn = class2type.hasOwnProperty, + core_trim = core_version.trim, + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Used for matching numbers + core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, + + // Used for splitting on whitespace + core_rnotwhite = /\S+/g, + + // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }, + + // The ready event handler + completed = function( event ) { + + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { + detach(); + jQuery.ready(); + } + }, + // Clean-up method for dom ready events + detach = function() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: core_version, + + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // scripts is true for back-compat + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return core_slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; + }, + + slice: function() { + return this.pushStack( core_slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: core_push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var src, copyIsArray, copy, name, options, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), + + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger("ready").off("ready"); + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + /* jshint eqeqeq: false */ + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + if ( obj == null ) { + return String( obj ); + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ core_toString.call(obj) ] || "object" : + typeof obj; + }, + + isPlainObject: function( obj ) { + var key; + + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !core_hasOwn.call(obj, "constructor") && + !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Support: IE<9 + // Handle iteration over inherited properties before own properties. + if ( jQuery.support.ownLast ) { + for ( key in obj ) { + return core_hasOwn.call( obj, key ); + } + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + for ( key in obj ) {} + + return key === undefined || core_hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + // data: string of html + // context (optional): If specified, the fragment will be created in this context, defaults to document + // keepScripts (optional): If true, will include scripts passed in the html string + parseHTML: function( data, context, keepScripts ) { + if ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + context = context || document; + + var parsed = rsingleTag.exec( data ), + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[1] ) ]; + } + + parsed = jQuery.buildFragment( [ data ], context, scripts ); + if ( scripts ) { + jQuery( scripts ).remove(); + } + return jQuery.merge( [], parsed.childNodes ); + }, + + parseJSON: function( data ) { + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + if ( data === null ) { + return data; + } + + if ( typeof data === "string" ) { + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + if ( data ) { + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + } + } + } + + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Use native String.trim function wherever possible + trim: core_trim && !core_trim.call("\uFEFF\xA0") ? + function( text ) { + return text == null ? + "" : + core_trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object(arr) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + core_push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( core_indexOf ) { + return core_indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var l = second.length, + i = first.length, + j = 0; + + if ( typeof l === "number" ) { + for ( ; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var retVal, + ret = [], + i = 0, + length = elems.length; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return core_concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var args, proxy, tmp; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = core_slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + // Multifunctional method to get and set values of a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < length; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations. + // Note: this method belongs to the css module but it's needed here for the support module. + // If support gets modularized, this method should be moved back to the css module. + swap: function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; + } +}); + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", completed ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", completed ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch(e) {} + + if ( top && top.doScroll ) { + (function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll("left"); + } catch(e) { + return setTimeout( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + jQuery.ready(); + } + })(); + } + } + } + return readyList.promise( obj ); +}; + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); + + if ( jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || type !== "function" && + ( length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj ); +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); +/*! + * Sizzle CSS Selector Engine v1.10.2 + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2013-07-03 + */ +(function( window, undefined ) { + +var i, + support, + cachedruns, + Expr, + getText, + isXML, + compile, + outermostContext, + sortInput, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + -(new Date()), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + hasDuplicate = false, + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + return 0; + }, + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments quoted, + // then not containing pseudos/brackets, + // then attribute selectors/non-parenthetical expressions, + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rsibling = new RegExp( whitespace + "*[+~]" ), + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + // BMP codepoint + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( documentIsHTML && !seed ) { + + // Shortcuts + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key += " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = attrs.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Detect xml + * @param {Element|Object} elem An element or a document + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var doc = node ? node.ownerDocument || node : preferredDoc, + parent = doc.defaultView; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsHTML = !isXML( doc ); + + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if ( parent && parent.attachEvent && parent !== parent.top ) { + parent.attachEvent( "onbeforeunload", function() { + setDocument(); + }); + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Check if getElementsByClassName can be trusted + support.getElementsByClassName = assert(function( div ) { + div.innerHTML = "
    "; + + // Support: Safari<4 + // Catch class over-caching + div.firstChild.className = "i"; + // Support: Opera<10 + // Catch gEBCN failure to find non-leading classes + return div.getElementsByClassName("i").length === 2; + }); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && documentIsHTML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + + // Support: Opera 10-12/IE8 + // ^= $= *= and empty values + // Should not select anything + // Support: Windows 8 Native Apps + // The type attribute is restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "t", "" ); + + if ( div.querySelectorAll("[t^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = rnative.test( docElem.contains ) || docElem.compareDocumentPosition ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = docElem.compareDocumentPosition ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b ); + + if ( compare ) { + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === doc || contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } + + // Not directly comparable, sort on existence of method + return a.compareDocumentPosition ? -1 : 1; + } : + function( a, b ) { + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Parentless nodes are either documents or disconnected + } else if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return doc; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, document, null, [elem] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val === undefined ? + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null : + val; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + for ( ; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[5] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] && match[4] !== undefined ) { + match[2] = match[4]; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), + // not comment, processing instructions, or others + // Thanks to Diego Perini for the nodeName shortcut + // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( tokens = [] ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var data, cache, outerCache, + dirkey = dirruns + " " + doneName; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { + if ( (data = cache[1]) === true || data === cachedruns ) { + return data === true; + } + } else { + cache = outerCache[ dir ] = [ dirkey ]; + cache[1] = matcher( elem, context, xml ) || cachedruns; + if ( cache[1] === true ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + // A counter to specify which element is currently being matched + var matcherCachedRuns = 0, + bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + // We must always have either seed elements or context + elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = matcherCachedRuns; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + for ( ; (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++matcherCachedRuns; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function select( selector, context, results, seed ) { + var i, tokens, token, type, find, + match = tokenize( selector ); + + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + } + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && context.parentNode || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + !documentIsHTML, + results, + rsibling.test( selector ) + ); + return results; +} + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome<14 +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + elem[ name ] === true ? name.toLowerCase() : null; + } + }); +} + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})( window ); +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // First callback to fire (used internally by add and fireWith) + firingStart, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( list && ( !fired || stack ) ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var action = tuple[ 0 ], + fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = core_slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; + if( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); +jQuery.support = (function( support ) { + + var all, a, input, select, fragment, opt, eventName, isSupported, i, + div = document.createElement("div"); + + // Setup + div.setAttribute( "className", "t" ); + div.innerHTML = "
    a"; + + // Finish early in limited (non-browser) environments + all = div.getElementsByTagName("*") || []; + a = div.getElementsByTagName("a")[ 0 ]; + if ( !a || !a.style || !all.length ) { + return support; + } + + // First batch of tests + select = document.createElement("select"); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName("input")[ 0 ]; + + a.style.cssText = "top:1px;float:left;opacity:.5"; + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + support.getSetAttribute = div.className !== "t"; + + // IE strips leading whitespace when .innerHTML is used + support.leadingWhitespace = div.firstChild.nodeType === 3; + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + support.tbody = !div.getElementsByTagName("tbody").length; + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + support.htmlSerialize = !!div.getElementsByTagName("link").length; + + // Get the style information from getAttribute + // (IE uses .cssText instead) + support.style = /top/.test( a.getAttribute("style") ); + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + support.hrefNormalized = a.getAttribute("href") === "/a"; + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + support.opacity = /^0.5/.test( a.style.opacity ); + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + support.cssFloat = !!a.style.cssFloat; + + // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere) + support.checkOn = !!input.value; + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + support.optSelected = opt.selected; + + // Tests for enctype support on a form (#6743) + support.enctype = !!document.createElement("form").enctype; + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + support.html5Clone = document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>"; + + // Will be defined later + support.inlineBlockNeedsLayout = false; + support.shrinkWrapBlocks = false; + support.pixelPosition = false; + support.deleteExpando = true; + support.noCloneEvent = true; + support.reliableMarginRight = true; + support.boxSizingReliable = true; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Support: IE<9 + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + // Check if we can trust getAttribute("value") + input = document.createElement("input"); + input.setAttribute( "value", "" ); + support.input = input.getAttribute( "value" ) === ""; + + // Check if an input maintains its value after becoming a radio + input.value = "t"; + input.setAttribute( "type", "radio" ); + support.radioValue = input.value === "t"; + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "checked", "t" ); + input.setAttribute( "name", "t" ); + + fragment = document.createDocumentFragment(); + fragment.appendChild( input ); + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<9 + // Opera does not clone events (and typeof div.attachEvent === undefined). + // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() + if ( div.attachEvent ) { + div.attachEvent( "onclick", function() { + support.noCloneEvent = false; + }); + + div.cloneNode( true ).click(); + } + + // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event) + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) + for ( i in { submit: true, change: true, focusin: true }) { + div.setAttribute( eventName = "on" + i, "t" ); + + support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false; + } + + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + // Support: IE<9 + // Iteration over object's inherited properties before its own. + for ( i in jQuery( support ) ) { + break; + } + support.ownLast = i !== "0"; + + // Run tests that need a body at doc ready + jQuery(function() { + var container, marginDiv, tds, + divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;", + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + container = document.createElement("div"); + container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; + + body.appendChild( container ).appendChild( div ); + + // Support: IE8 + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + div.innerHTML = "
    t
    "; + tds = div.getElementsByTagName("td"); + tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Support: IE8 + // Check if empty table cells still have offsetWidth/Height + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check box-sizing and margin behavior. + div.innerHTML = ""; + div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; + + // Workaround failing boxSizing test due to offsetWidth returning wrong value + // with some non-1 values of body zoom, ticket #13543 + jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() { + support.boxSizing = div.offsetWidth === 4; + }); + + // Use window.getComputedStyle because jsdom on node.js will break without it. + if ( window.getComputedStyle ) { + support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; + support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. (#3333) + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + marginDiv = div.appendChild( document.createElement("div") ); + marginDiv.style.cssText = div.style.cssText = divReset; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + + support.reliableMarginRight = + !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); + } + + if ( typeof div.style.zoom !== core_strundefined ) { + // Support: IE<8 + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + div.innerHTML = ""; + div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Support: IE6 + // Check if elements with layout shrink-wrap their children + div.style.display = "block"; + div.innerHTML = "
    "; + div.firstChild.style.width = "5px"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + + if ( support.inlineBlockNeedsLayout ) { + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + // Support: IE<8 + body.style.zoom = 1; + } + } + + body.removeChild( container ); + + // Null elements to avoid leaks in IE + container = div = tds = marginDiv = null; + }); + + // Null elements to avoid leaks in IE + all = select = fragment = opt = a = input = null; + + return support; +})({}); + +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rmultiDash = /([A-Z])/g; + +function internalData( elem, name, data, pvt /* Internal Use Only */ ){ + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var ret, thisCache, + internalKey = jQuery.expando, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + id = elem[ internalKey ] = core_deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + // Avoid exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( typeof name === "string" ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; +} + +function internalRemoveData( elem, name, pvt ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split(" "); + } + } + } else { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = name.concat( jQuery.map( name, jQuery.camelCase ) ); + } + + i = name.length; + while ( i-- ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + /* jshint eqeqeq: false */ + } else if ( jQuery.support.deleteExpando || cache != cache.window ) { + /* jshint eqeqeq: true */ + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } +} + +jQuery.extend({ + cache: {}, + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "applet": true, + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data ) { + return internalData( elem, name, data ); + }, + + removeData: function( elem, name ) { + return internalRemoveData( elem, name ); + }, + + // For internal use only. + _data: function( elem, name, data ) { + return internalData( elem, name, data, true ); + }, + + _removeData: function( elem, name ) { + return internalRemoveData( elem, name, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + // Do not set data on non-element because it will not be cleared (#8335). + if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) { + return false; + } + + var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; + + // nodes accept data unless otherwise specified; rejection can be conditional + return !noData || noData !== true && elem.getAttribute("classid") === noData; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var attrs, name, + data = null, + i = 0, + elem = this[0]; + + // Special expections of .data basically thwart jQuery.access, + // so implement the relevant behavior ourselves + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attrs = elem.attributes; + for ( ; i < attrs.length; i++ ) { + name = attrs[i].name; + + if ( name.indexOf("data-") === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + return arguments.length > 1 ? + + // Sets one value + this.each(function() { + jQuery.data( this, key, value ); + }) : + + // Gets one value + // Try to fetch any internally stored data first + elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null; + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery._removeData( elem, type + "queue" ); + jQuery._removeData( elem, key ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var nodeHook, boolHook, + rclass = /[\t\r\n\f]/g, + rreturn = /\r/g, + rfocusable = /^(?:input|select|textarea|button|object)$/i, + rclickable = /^(?:a|area)$/i, + ruseDefault = /^(?:checked|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + getSetInput = jQuery.support.input; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call( this, j, this.className ) ); + }); + } + + if ( proceed ) { + // The disjunction here is for better compressibility (see removeClass) + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + " " + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + elem.className = jQuery.trim( cur ); + + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = arguments.length === 0 || typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call( this, j, this.className ) ); + }); + } + if ( proceed ) { + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + "" + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + elem.className = value ? jQuery.trim( cur ) : ""; + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value; + + if ( typeof stateVal === "boolean" && type === "string" ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + classNames = value.match( core_rnotwhite ) || []; + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( type === core_strundefined || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // If the element has a class name or if we're passed "false", + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var ret, hooks, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // Use proper attribute retrieval(#6932, #12072) + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + elem.text; + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) { + optionSet = true; + } + } + + // force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attr: function( elem, name, value ) { + var hooks, ret, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === core_strundefined ) { + return jQuery.prop( elem, name, value ); + } + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + + } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var name, propName, + i = 0, + attrNames = value && value.match( core_rnotwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( (name = attrNames[i++]) ) { + propName = jQuery.propFix[ name ] || name; + + // Boolean attributes get special treatment (#10870) + if ( jQuery.expr.match.bool.test( name ) ) { + // Set corresponding property to false + if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + elem[ propName ] = false; + // Support: IE<9 + // Also clear defaultChecked/defaultSelected (if appropriate) + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = + elem[ propName ] = false; + } + + // See #9699 for explanation of this approach (setting first, then removal) + } else { + jQuery.attr( elem, name, "" ); + } + + elem.removeAttribute( getSetAttribute ? name : propName ); + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to default in case type is set after value during creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? + ret : + ( elem[ name ] = value ); + + } else { + return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? + ret : + elem[ name ]; + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + return tabindex ? + parseInt( tabindex, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + -1; + } + } + } +}); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + // IE<8 needs the *property* name + elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name ); + + // Use defaultChecked and defaultSelected for oldIE + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true; + } + + return name; + } +}; +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr; + + jQuery.expr.attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ? + function( elem, name, isXML ) { + var fn = jQuery.expr.attrHandle[ name ], + ret = isXML ? + undefined : + /* jshint eqeqeq: false */ + (jQuery.expr.attrHandle[ name ] = undefined) != + getter( elem, name, isXML ) ? + + name.toLowerCase() : + null; + jQuery.expr.attrHandle[ name ] = fn; + return ret; + } : + function( elem, name, isXML ) { + return isXML ? + undefined : + elem[ jQuery.camelCase( "default-" + name ) ] ? + name.toLowerCase() : + null; + }; +}); + +// fix oldIE attroperties +if ( !getSetInput || !getSetAttribute ) { + jQuery.attrHooks.value = { + set: function( elem, value, name ) { + if ( jQuery.nodeName( elem, "input" ) ) { + // Does not return so that setAttribute is also used + elem.defaultValue = value; + } else { + // Use nodeHook if defined (#1954); otherwise setAttribute is fine + return nodeHook && nodeHook.set( elem, value, name ); + } + } + }; +} + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = { + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + elem.setAttributeNode( + (ret = elem.ownerDocument.createAttribute( name )) + ); + } + + ret.value = value += ""; + + // Break association with cloned elements by also using setAttribute (#9646) + return name === "value" || value === elem.getAttribute( name ) ? + value : + undefined; + } + }; + jQuery.expr.attrHandle.id = jQuery.expr.attrHandle.name = jQuery.expr.attrHandle.coords = + // Some attributes are constructed with empty-string values when not defined + function( elem, name, isXML ) { + var ret; + return isXML ? + undefined : + (ret = elem.getAttributeNode( name )) && ret.value !== "" ? + ret.value : + null; + }; + jQuery.valHooks.button = { + get: function( elem, name ) { + var ret = elem.getAttributeNode( name ); + return ret && ret.specified ? + ret.value : + undefined; + }, + set: nodeHook.set + }; + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + set: function( elem, value, name ) { + nodeHook.set( elem, value === "" ? false : value, name ); + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }; + }); +} + + +// Some attributes require a special call on IE +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !jQuery.support.hrefNormalized ) { + // href/src property should get the full normalized URL (#10299/#12915) + jQuery.each([ "href", "src" ], function( i, name ) { + jQuery.propHooks[ name ] = { + get: function( elem ) { + return elem.getAttribute( name, 4 ); + } + }; + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Note: IE uppercases css property names, but if we were to .toLowerCase() + // .cssText, that would destroy case senstitivity in URL's, like in "background" + return elem.style.cssText || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = value + "" ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }; +} + +jQuery.each([ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +}); + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }; + if ( !jQuery.support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + // Support: Webkit + // "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + }; + } +}); +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + var tmp, events, t, handleObjIn, + special, eventHandle, handleObj, + handlers, type, namespaces, origType, + elemData = jQuery._data( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !(events = elemData.events) ) { + events = elemData.events = {}; + } + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !(handlers = events[ type ]) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + var j, handleObj, tmp, + origCount, t, events, + special, handlers, type, + namespaces, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery._removeData( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + var handle, ontype, cur, + bubbleType, special, tmp, i, + eventPath = [ elem || document ], + type = core_hasOwn.call( event, "type" ) ? event.type : event, + namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join("."); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + try { + elem[ type ](); + } catch ( e ) { + // IE<9 dies on focus/blur to hidden element (#1486,#12518) + // only reproducible on winXP IE8 native, not IE9 in IE8 mode + } + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, ret, handleObj, matched, j, + handlerQueue = [], + args = core_slice.call( arguments ), + handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var sel, handleObj, matches, i, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + /* jshint eqeqeq: false */ + for ( ; cur != this; cur = cur.parentNode || this ) { + /* jshint eqeqeq: true */ + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Support: Chrome 23+, Safari? + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var body, eventDoc, doc, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + try { + this.focus(); + return false; + } catch ( e ) { + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers + } + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Even when returnValue equals to undefined Firefox will still show alert + if ( event.result !== undefined ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === core_strundefined ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + if ( !e ) { + return; + } + + // If preventDefault exists, run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // Support: IE + // Otherwise set the returnValue property of the original event to false + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + if ( !e ) { + return; + } + // If stopPropagation exists, run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + + // Support: IE + // Set the cancelBubble property of the original event to true + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "submitBubbles" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + jQuery._data( form, "submitBubbles", true ); + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + jQuery._data( elem, "changeBubbles", true ); + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var type, origFn; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +}); +var isSimple = /^.[^:#\[\.,]*$/, + rparentsprev = /^(?:parents|prev(?:Until|All))/, + rneedsContext = jQuery.expr.match.needsContext, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var i, + ret = [], + self = this, + len = self.length; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector || [], true) ); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector || [], false) ); + }, + + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + ret = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.index(cur) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector(cur, selectors)) ) { + + cur = ret.push( cur ); + break; + } + } + } + + return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( jQuery.unique(all) ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + if ( this.length > 1 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + ret = jQuery.unique( ret ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + } + + return this.pushStack( ret ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); + + } + + if ( typeof qualifier === "string" ) { + if ( isSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; + }); +} +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
    ", "
    " ], + area: [ 1, "", "" ], + param: [ 1, "", "" ], + thead: [ 1, "", "
    " ], + tr: [ 2, "", "
    " ], + col: [ 2, "", "
    " ], + td: [ 3, "", "
    " ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
    ", "
    " ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +jQuery.fn.extend({ + text: function( value ) { + return jQuery.access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + append: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + // keepData is for internal use only--do not document + remove: function( selector, keepData ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return jQuery.access( this, function( value ) { + var elem = this[0] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var + // Snapshot the DOM in case .domManip sweeps something relevant into its fragment + args = jQuery.map( this, function( elem ) { + return [ elem.nextSibling, elem.parentNode ]; + }), + i = 0; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + var next = args[ i++ ], + parent = args[ i++ ]; + + if ( parent ) { + // Don't use the snapshot next if it has moved (#13810) + if ( next && next.parentNode !== parent ) { + next = this.nextSibling; + } + jQuery( this ).remove(); + parent.insertBefore( elem, next ); + } + // Allow new content to include elements from the context set + }, true ); + + // Force removal if there was no new content (e.g., from empty arguments) + return i ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback, allowIntersection ) { + + // Flatten any nested arrays + args = core_concat.apply( [], args ); + + var first, node, hasScripts, + scripts, doc, fragment, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[0], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[0] = value.call( this, index, self.html() ); + } + self.domManip( args, callback, allowIntersection ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, !allowIntersection && this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[i], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Hope ajax is available... + jQuery._evalUrl( node.src ); + } else { + jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return this; + } +}); + +// Support: IE<8 +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType === 1 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { + elem.type = match[1]; + } else { + elem.removeAttribute("type"); + } + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; (elem = elems[i]) != null; i++ ) { + jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); + } +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function fixCloneNodeIssues( src, dest ) { + var nodeName, e, data; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); + + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } + + // IE blanks contents when cloning scripts, and tries to evaluate newly-set text + if ( nodeName === "script" && dest.text !== src.text ) { + disableScript( dest ).text = src.text; + restoreScript( dest ); + + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + } else if ( nodeName === "object" ) { + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.defaultSelected = dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone(true); + jQuery( insert[i] )[ original ]( elems ); + + // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + core_push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } + } + } + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} + +// Used in buildFragment, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( manipulation_rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, node, clone, i, srcElements, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + // Fix all IE cloning issues + for ( i = 0; (node = srcElements[i]) != null; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + fixCloneNodeIssues( node, destElements[i] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0; (node = srcElements[i]) != null; i++ ) { + cloneCopyEvent( node, destElements[i] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + destElements = srcElements = node = null; + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var j, elem, contains, + tmp, tag, tbody, wrap, + l = elems.length, + + // Ensure a safe fragment + safe = createSafeFragment( context ), + + nodes = [], + i = 0; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || safe.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + + tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; + + // Descend through wrappers to the right content + j = wrap[0]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Manually add leading whitespace removed by IE + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); + } + + // Remove IE's autoinserted from table fragments + if ( !jQuery.support.tbody ) { + + // String was a , *may* have spurious + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare or + wrap[1] === "
    " && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !jQuery.support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + tmp = null; + + return safe; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var elem, type, id, data, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = jQuery.support.deleteExpando, + special = jQuery.event.special; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( typeof elem.removeAttribute !== core_strundefined ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + core_deletedIds.push( id ); + } + } + } + } + }, + + _evalUrl: function( url ) { + return jQuery.ajax({ + url: url, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + }); + } +}); +jQuery.fn.extend({ + wrapAll: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each(function(i) { + jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); + }); + }, + + unwrap: function() { + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + } +}); +var iframe, getStyles, curCSS, + ralpha = /alpha\([^)]*\)/i, + ropacity = /opacity\s*=\s*([^)]*)/, + rposition = /^(top|right|bottom|left)$/, + // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rmargin = /^margin/, + rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), + rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), + rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), + elemdisplay = { BODY: "block" }, + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: 0, + fontWeight: 400 + }, + + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; + +// return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // check for vendor prefixed names + var capName = name.charAt(0).toUpperCase() + name.slice(1), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +function isHidden( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); +} + +function showHide( elements, show ) { + var display, elem, hidden, + values = [], + index = 0, + length = elements.length; + + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + values[ index ] = jQuery._data( elem, "olddisplay" ); + display = elem.style.display; + if ( show ) { + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !values[ index ] && display === "none" ) { + elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( elem.style.display === "" && isHidden( elem ) ) { + values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); + } + } else { + + if ( !values[ index ] ) { + hidden = isHidden( elem ); + + if ( display && display !== "none" || !hidden ) { + jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) ); + } + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( index = 0; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + if ( !show || elem.style.display === "none" || elem.style.display === "" ) { + elem.style.display = show ? values[ index ] || "" : "none"; + } + } + + return elements; +} + +jQuery.fn.extend({ + css: function( name, value ) { + return jQuery.access( this, function( elem, name, value ) { + var len, styles, + map = {}, + i = 0; + + if ( jQuery.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + }, + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each(function() { + if ( isHidden( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + }); + } +}); + +jQuery.extend({ + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "columnCount": true, + "fillOpacity": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + // normalize float css property + "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && (ret = rrelNum.exec( value )) ) { + value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // Make sure that NaN and null values aren't set. See: #7116 + if ( value == null || type === "number" && isNaN( value ) ) { + return; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // Fixes #8908, it can be done more correctly by specifing setters in cssHooks, + // but it would mean to define eight (for every problematic property) identical functions + if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { + + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var num, val, hooks, + origName = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + //convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Return, converting to number if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; + } + return val; + } +}); + +// NOTE: we've included the "window" in window.getComputedStyle +// because jsdom on node.js will break without it. +if ( window.getComputedStyle ) { + getStyles = function( elem ) { + return window.getComputedStyle( elem, null ); + }; + + curCSS = function( elem, name, _computed ) { + var width, minWidth, maxWidth, + computed = _computed || getStyles( elem ), + + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined, + style = elem.style; + + if ( computed ) { + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right + // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret; + }; +} else if ( document.documentElement.currentStyle ) { + getStyles = function( elem ) { + return elem.currentStyle; + }; + + curCSS = function( elem, name, _computed ) { + var left, rs, rsLeft, + computed = _computed || getStyles( elem ), + ret = computed ? computed[ name ] : undefined, + style = elem.style; + + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret == null && style && style[ name ] ) { + ret = style[ name ]; + } + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + // but not position css attributes, as those are proportional to the parent element instead + // and we can't measure the parent instead because it might trigger a "stacking dolls" problem + if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { + + // Remember the original values + left = style.left; + rs = elem.runtimeStyle; + rsLeft = rs && rs.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + rs.left = elem.currentStyle.left; + } + style.left = name === "fontSize" ? "1em" : ret; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + rs.left = rsLeft; + } + } + + return ret === "" ? "auto" : ret; + }; +} + +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + // If we already have the right measurement, avoid augmentation + 4 : + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + // both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); + } + + if ( isBorderBox ) { + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // at this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } else { + // at this point, extra isn't content, so add padding + val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // at this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var valueIsBorderBox = true, + val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + styles = getStyles( elem ), + isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name, styles ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // we need the check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles + ) + ) + "px"; +} + +// Try to determine the default display value of an element +function css_defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + // Use the already-created iframe if possible + iframe = ( iframe || + jQuery("