# HG changeset patch # User lana # Date 1368082485 25200 # Node ID 4ea383c26563a4cb8f8da3c30027cc791343add9 # Parent 241d13ccb08baaab36a95366ab9004f3c67b4279# Parent 4dec41b3c5e3bb616f0c6f15830d940905aa5d16 Merge diff -r 241d13ccb08b -r 4ea383c26563 .hgtags --- a/.hgtags Wed May 08 14:10:53 2013 +0200 +++ b/.hgtags Wed May 08 23:54:45 2013 -0700 @@ -209,3 +209,4 @@ 1872c12529090e1c1dbf567f02ad7ae6231b8f0c jdk8-b85 da9a4c9312816451884aa6db6f18be51a07bff13 jdk8-b86 5ebf6c63714de2c9dcf831074086d31daec819df jdk8-b87 +e517701a4d0e25ae9c7945bca6e1762a8c5d8aa6 jdk8-b88 diff -r 241d13ccb08b -r 4ea383c26563 .hgtags-top-repo --- a/.hgtags-top-repo Wed May 08 14:10:53 2013 +0200 +++ b/.hgtags-top-repo Wed May 08 23:54:45 2013 -0700 @@ -209,3 +209,4 @@ 7fc358f5943676b82f1dccd3152b1ac07d92e38b jdk8-b85 df9b5240f0a76c91cfe1a5b39da4d08df56e05be jdk8-b86 b9415faa7066a4d3b16d466556d5428446918d95 jdk8-b87 +e1a929afcfc492470d50be0b6b0e8dc77d3760b9 jdk8-b88 diff -r 241d13ccb08b -r 4ea383c26563 common/makefiles/NativeCompilation.gmk --- a/common/makefiles/NativeCompilation.gmk Wed May 08 14:10:53 2013 +0200 +++ b/common/makefiles/NativeCompilation.gmk Wed May 08 23:54:45 2013 -0700 @@ -411,6 +411,8 @@ $1_EXTRA_LDFLAGS+="-implib:$$($1_OBJECT_DIR)/$$($1_LIBRARY).lib" endif + $1_EXTRA_LDFLAGS_SUFFIX += $(GLOBAL_LDFLAGS_SUFFIX) + ifneq (,$$($1_DEBUG_SYMBOLS)) ifeq ($(ENABLE_DEBUG_SYMBOLS), true) ifeq ($(OPENJDK_TARGET_OS), windows) @@ -549,6 +551,8 @@ endif endif + $1_EXTRA_LDFLAGS_SUFFIX += $(GLOBAL_LDFLAGS_SUFFIX) + $$($1_TARGET) : $$($1_EXPECTED_OBJS) $$($1_RES) $$($1_GEN_MANIFEST) $$(call LINKING_EXE_MSG,$$($1_BASENAME)) $$($1_LDEXE) $$($1_LDFLAGS) $$($1_EXTRA_LDFLAGS) $(EXE_OUT_OPTION)$$($1_TARGET) \ diff -r 241d13ccb08b -r 4ea383c26563 common/makefiles/javadoc/CORE_PKGS.gmk --- a/common/makefiles/javadoc/CORE_PKGS.gmk Wed May 08 14:10:53 2013 +0200 +++ b/common/makefiles/javadoc/CORE_PKGS.gmk Wed May 08 23:54:45 2013 -0700 @@ -142,6 +142,7 @@ java.util.prefs \ java.util.regex \ java.util.spi \ + java.util.stream \ java.util.zip \ javax.accessibility \ javax.activation \ diff -r 241d13ccb08b -r 4ea383c26563 common/makefiles/javadoc/Javadoc.gmk --- a/common/makefiles/javadoc/Javadoc.gmk Wed May 08 14:10:53 2013 +0200 +++ b/common/makefiles/javadoc/Javadoc.gmk Wed May 08 23:54:45 2013 -0700 @@ -390,6 +390,17 @@ $(call OptionPair,-tag,specdefault:X) ; \ $(call OptionPair,-tag,Note:X) ; \ $(call OptionPair,-tag,ToDo:X) ; \ + $(call OptionPair,-tag,apiNote:a:API Note:) ; \ + $(call OptionPair,-tag,implSpec:a:Implementation Requirements:) ; \ + $(call OptionPair,-tag,implNote:a:Implementation Note:) ; \ + $(call OptionPair,-tag,param) ; \ + $(call OptionPair,-tag,return) ; \ + $(call OptionPair,-tag,throws) ; \ + $(call OptionPair,-tag,since) ; \ + $(call OptionPair,-tag,version) ; \ + $(call OptionPair,-tag,serialData) ; \ + $(call OptionPair,-tag,factory) ; \ + $(call OptionPair,-tag,see) ; \ $(call OptionPair,-tag,$(TAG_JLS)) ; \ $(call OptionOnly,-splitIndex) ; \ $(call OptionPair,-overview,$(COREAPI_OVERVIEW)) ; \ diff -r 241d13ccb08b -r 4ea383c26563 corba/.hgtags --- a/corba/.hgtags Wed May 08 14:10:53 2013 +0200 +++ b/corba/.hgtags Wed May 08 23:54:45 2013 -0700 @@ -209,3 +209,4 @@ 9583a6431596bac1959d2d8828f5ea217843dd12 jdk8-b85 44a8ce4a759f2668ff434661a93ff462ea472478 jdk8-b86 f1709874d55a06bc3d5dfa02dbcdfbc59f4cba34 jdk8-b87 +4e3a881ebb1ee96ce0872508b0066d74f310dbfa jdk8-b88 diff -r 241d13ccb08b -r 4ea383c26563 corba/src/share/classes/com/sun/tools/corba/se/idl/toJavaPortable/UnionGen.java --- a/corba/src/share/classes/com/sun/tools/corba/se/idl/toJavaPortable/UnionGen.java Wed May 08 14:10:53 2013 +0200 +++ b/corba/src/share/classes/com/sun/tools/corba/se/idl/toJavaPortable/UnionGen.java Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -258,6 +258,19 @@ { Vector labels = vectorizeLabels (u.branches (), true); + if (Util.javaName(utype).equals ("boolean")) { + stream.println( "" ) ; + stream.println( " private void verifyDefault (boolean discriminator)" ) ; + stream.println( " {" ) ; + if (labels.contains ("true")) + stream.println (" if ( discriminator )"); + else + stream.println (" if ( !discriminator )"); + stream.println( " throw new org.omg.CORBA.BAD_OPERATION();" ) ; + stream.println( " }" ) ; + return; + } + stream.println( "" ) ; stream.println( " private void verifyDefault( " + Util.javaName(utype) + " value )" ) ; @@ -763,7 +776,7 @@ stream.println (indent + "if (" + disName + ')'); if (firstBranch == null) - stream.println (indent + " throw new org.omg.CORBA.BAD_OPERATION ();"); + stream.println (indent + " value._default(" + disName + ");"); else { stream.println (indent + '{'); index = readBranch (index, indent + " ", firstBranch.typedef.name (), @@ -774,7 +787,7 @@ stream.println (indent + "else"); if (secondBranch == null) - stream.println (indent + " throw new org.omg.CORBA.BAD_OPERATION ();"); + stream.println (indent + " value._default(" + disName + ");"); else { stream.println (indent + '{'); index = readBranch (index, indent + " ", secondBranch.typedef.name (), @@ -924,23 +937,25 @@ firstBranch = secondBranch; secondBranch = tmp; } - stream.println (indent + "if (" + disName + ')'); - if (firstBranch == null) - stream.println (indent + " throw new org.omg.CORBA.BAD_OPERATION ();"); - else - { - stream.println (indent + '{'); - index = writeBranch (index, indent + " ", name, firstBranch.typedef, stream); - stream.println (indent + '}'); - } - stream.println (indent + "else"); - if (secondBranch == null) - stream.println (indent + " throw new org.omg.CORBA.BAD_OPERATION ();"); - else - { - stream.println (indent + '{'); - index = writeBranch (index, indent + " ", name, secondBranch.typedef, stream); - stream.println (indent + '}'); + if (firstBranch != null && secondBranch != null) { + stream.println (indent + "if (" + disName + ')'); + stream.println (indent + '{'); + index = writeBranch (index, indent + " ", name, firstBranch.typedef, stream); + stream.println (indent + '}'); + stream.println (indent + "else"); + stream.println (indent + '{'); + index = writeBranch (index, indent + " ", name, secondBranch.typedef, stream); + stream.println (indent + '}'); + } else if (firstBranch != null) { + stream.println (indent + "if (" + disName + ')'); + stream.println (indent + '{'); + index = writeBranch (index, indent + " ", name, firstBranch.typedef, stream); + stream.println (indent + '}'); + } else { + stream.println (indent + "if (!" + disName + ')'); + stream.println (indent + '{'); + index = writeBranch (index, indent + " ", name, secondBranch.typedef, stream); + stream.println (indent + '}'); } } return index; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/.hgtags --- a/hotspot/.hgtags Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/.hgtags Wed May 08 23:54:45 2013 -0700 @@ -337,3 +337,5 @@ d4c2667846607042370760e23f64c3ab9350e60d jdk8-b87 01d5f04e64dc2d64625b2db2056f5ed4de918a45 hs25-b29 c4af77d2045476c56fbf3f914b336bb1b7cd18af hs25-b30 +8482058e74bc8c1a890e6f3be3eff192dba6ce67 jdk8-b88 +4ec91349972255650f97bedfd07e6423e02428cf hs25-b31 diff -r 241d13ccb08b -r 4ea383c26563 hotspot/agent/doc/c2replay.html --- a/hotspot/agent/doc/c2replay.html Wed May 08 14:10:53 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ - - - -C2 Replay - - - - -

C2 compiler replay

-

-The C2 compiler replay is a function to repeat the compiling process from a crashed java process in compiled method
-This function only exists in debug version of VM -

-

Usage

-
 
-First, use SA to attach to the core file, if suceeded, do
-       clhsdb>dumpreplaydata 
| -a | [> replay.txt] - create file replay.txt, address is address of Method, or nmethod(CodeBlob) - clhsdb>buildreplayjars [all | boot | app] - create files: - all: - app.jar, boot.jar - boot: - boot.jar - app: - app.jar - exit SA now. -Second, use the obtained replay text file, replay.txt and jar files, app.jar and boot.jar, using debug version of java - java -Xbootclasspath/p:boot.jar -cp app.jar -XX:ReplayDataFile= -XX:+ReplayCompiles .... - This will replay the compiling process. - - With ReplayCompiles, the replay will recompile all the methods in app.jar, and in boot.jar to emulate the process in java app. - -notes: - 1) Most time, we don't need the boot.jar which is the classes loaded from JDK. It will be only modified when an agent(JVMDI) is running and modifies the classes. - 2) If encounter error as "" not found, that means the SA is using a VMStructs which is different from the one with corefile. In this case, SA has a utility tool vmstructsdump which is located at agent/src/os//proc/ - - Use this tool to dump VM type library: - vmstructsdump libjvm.so > .db - - set env SA_TYPEDB=.db (refer different shell for set envs) diff -r 241d13ccb08b -r 4ea383c26563 hotspot/agent/doc/cireplay.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/doc/cireplay.html Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,41 @@ + + + +Replay + + + + +

Compiler replay

+

+The compiler replay is a function to repeat the compiling process from a crashed java process in compiled method
+This function only exists in debug version of VM +

+

Usage

+
+First, use SA to attach to the core file, if succeeded, do
+       hsdb> dumpreplaydata <address> | -a | <thread_id> [> replay.txt]
+       create file replay.txt, address is address of Method, or nmethod(CodeBlob)
+       hsdb> buildreplayjars [all | boot | app]
+       create files:
+         all:
+           app.jar, boot.jar
+         boot:
+           boot.jar
+         app:
+           app.jar
+       exit SA now.
+Second, use the obtained replay text file, replay.txt and jar files, app.jar and boot.jar, using debug version of java
+       java -Xbootclasspath/p:boot.jar -cp app.jar -XX:ReplayDataFile=<datafile> -XX:+ReplayCompiles ....
+       This will replay the compiling process.
+
+       With ReplayCompiles, the replay will recompile all the methods in app.jar, and in boot.jar to emulate the process in java app.
+
+notes:
+       1) Most time, we don't need the boot.jar which is the classes loaded from JDK. It will be only modified when an agent(JVMDI) is running and modifies the classes.
+       2) If encounter error as "<flag>" not found, that means the SA is using a VMStructs which is different from the one with corefile. In this case, SA has a utility tool vmstructsdump which is located at agent/src/os/<os>/proc/<os_platform>
+
+       Use this tool to dump VM type library:
+       vmstructsdump libjvm.so > <type_name>.db
+
+       set env SA_TYPEDB=<type_name>.db (refer different shell for set envs)
diff -r 241d13ccb08b -r 4ea383c26563 hotspot/agent/doc/clhsdb.html
--- a/hotspot/agent/doc/clhsdb.html	Wed May 08 14:10:53 2013 +0200
+++ b/hotspot/agent/doc/clhsdb.html	Wed May 08 23:54:45 2013 -0700
@@ -15,7 +15,7 @@
 

There is also JavaScript based SA command line interface called jsdb. But, CLHSDB supports Unix shell-like (or dbx/gdb-like) command line interface with -support for output redirection/appending (familiar >, >>), command history and so on. +support for output redirection/appending (familiar >, >>), command history and so on. Each CLHSDB command can have zero or more arguments and optionally end with output redirection (or append) to a file. Commands may be stored in a file and run using source command. help command prints usage message for all supported commands (or a specific command) @@ -49,7 +49,7 @@ dumpheap [ file ] dump heap in hprof binary format dumpideal -a | id dump ideal graph like debug flag -XX:+PrintIdeal dumpilt -a | id dump inline tree for C2 compilation - dumpreplaydata

| -a | [>replay.txt] dump replay data into a file + dumpreplaydata <address> | -a | <thread_id> [>replay.txt] dump replay data into a file echo [ true | false ] turn on/off command echo mode examine [ address/count ] | [ address,address] show contents of memory from given address field [ type [ name fieldtype isStatic offset address ] ] print info about a field of HotSpot type @@ -96,11 +96,11 @@

JavaScript integration

-

Few CLHSDB commands are already implemented in JavaScript. It is possible to extend CLHSDB command set +

Few CLHSDB commands are already implemented in JavaScript. It is possible to extend CLHSDB command set by implementing more commands in a JavaScript file and by loading it by jsload command. jseval command may be used to evaluate arbitrary JavaScript expression from a string. Any JavaScript function may be exposed as a CLHSDB command by registering it using JavaScript registerCommand -function. This function accepts command name, usage and name of the JavaScript implementation function +function. This function accepts command name, usage and name of the JavaScript implementation function as arguments.

@@ -127,11 +127,11 @@
-

C2 Compilation Replay

+

Compilation Replay

When a java process crashes in compiled method, usually a core file is saved. -The C2 replay function can reproduce the compiling process in the core. -c2replay.html +The replay function can reproduce the compiling process in the core. +cireplay.html diff -r 241d13ccb08b -r 4ea383c26563 hotspot/agent/src/os/bsd/MacosxDebuggerLocal.m --- a/hotspot/agent/src/os/bsd/MacosxDebuggerLocal.m Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/agent/src/os/bsd/MacosxDebuggerLocal.m Wed May 08 23:54:45 2013 -0700 @@ -204,7 +204,7 @@ jstring objectName, jstring symbolName) { struct ps_prochandle* ph = get_proc_handle(env, this_obj); - if (ph->core != NULL) { + if (ph != NULL && ph->core != NULL) { return lookupByNameIncore(env, ph, this_obj, objectName, symbolName); } @@ -238,10 +238,13 @@ const char* sym = NULL; struct ps_prochandle* ph = get_proc_handle(env, this_obj); - sym = symbol_for_pc(ph, (uintptr_t) addr, &offset); - if (sym == NULL) return 0; - return (*env)->CallObjectMethod(env, this_obj, createClosestSymbol_ID, + if (ph != NULL && ph->core != NULL) { + sym = symbol_for_pc(ph, (uintptr_t) addr, &offset); + if (sym == NULL) return 0; + return (*env)->CallObjectMethod(env, this_obj, createClosestSymbol_ID, (*env)->NewStringUTF(env, sym), (jlong)offset); + } + return 0; } /** called from Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_readBytesFromProcess0 */ @@ -279,7 +282,7 @@ jbyteArray array; struct ps_prochandle* ph = get_proc_handle(env, this_obj); - if (ph->core != NULL) { + if (ph != NULL && ph->core != NULL) { return readBytesFromCore(env, ph, this_obj, addr, numBytes); } @@ -394,9 +397,9 @@ /* For core file only, called from * Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getThreadIntegerRegisterSet0 */ -jlongArray getThreadIntegerRegisterSetFromCore(JNIEnv *env, jobject this_obj, long lwp_id) { +jlongArray getThreadIntegerRegisterSetFromCore(JNIEnv *env, jobject this_obj, long lwp_id, struct ps_prochandle* ph) { if (!_threads_filled) { - if (!fill_java_threads(env, this_obj, get_proc_handle(env, this_obj))) { + if (!fill_java_threads(env, this_obj, ph)) { throw_new_debugger_exception(env, "Failed to fill in threads"); return 0; } else { @@ -409,7 +412,6 @@ jlongArray array; jlong *regs; - struct ps_prochandle* ph = get_proc_handle(env, this_obj); if (get_lwp_regs(ph, lwp_id, &gregs) != true) { THROW_NEW_DEBUGGER_EXCEPTION_("get_thread_regs failed for a lwp", 0); } @@ -521,8 +523,8 @@ print_debug("getThreadRegisterSet0 called\n"); struct ps_prochandle* ph = get_proc_handle(env, this_obj); - if (ph->core != NULL) { - return getThreadIntegerRegisterSetFromCore(env, this_obj, thread_id); + if (ph != NULL && ph->core != NULL) { + return getThreadIntegerRegisterSetFromCore(env, this_obj, thread_id, ph); } kern_return_t result; @@ -705,8 +707,8 @@ task_t gTask = 0; result = task_for_pid(mach_task_self(), jpid, &gTask); if (result != KERN_SUCCESS) { - print_error("attach: task_for_pid(%d) failed (%d)\n", (int)jpid, result); - THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the process"); + print_error("attach: task_for_pid(%d) failed: '%s' (%d)\n", (int)jpid, mach_error_string(result), result); + THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the process. Could be caused by an incorrect pid or lack of privileges."); } putTask(env, this_obj, gTask); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/agent/src/share/classes/sun/jvm/hotspot/ci/ciEnv.java --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ci/ciEnv.java Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ci/ciEnv.java Wed May 08 23:54:45 2013 -0700 @@ -93,10 +93,11 @@ CompileTask task = task(); Method method = task.method(); int entryBci = task.osrBci(); + int compLevel = task.compLevel(); Klass holder = method.getMethodHolder(); out.println("compile " + holder.getName().asString() + " " + OopUtilities.escapeString(method.getName().asString()) + " " + method.getSignature().asString() + " " + - entryBci); + entryBci + " " + compLevel); } } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java Wed May 08 23:54:45 2013 -0700 @@ -78,6 +78,8 @@ current sweep traversal index. */ private static CIntegerField stackTraversalMarkField; + private static CIntegerField compLevelField; + static { VM.registerVMInitializedObserver(new Observer() { public void update(Observable o, Object data) { @@ -113,7 +115,7 @@ osrEntryPointField = type.getAddressField("_osr_entry_point"); lockCountField = type.getJIntField("_lock_count"); stackTraversalMarkField = type.getCIntegerField("_stack_traversal_mark"); - + compLevelField = type.getCIntegerField("_comp_level"); pcDescSize = db.lookupType("PcDesc").getSize(); } @@ -530,7 +532,7 @@ out.println("compile " + holder.getName().asString() + " " + OopUtilities.escapeString(method.getName().asString()) + " " + method.getSignature().asString() + " " + - getEntryBCI()); + getEntryBCI() + " " + getCompLevel()); } @@ -551,4 +553,5 @@ private int getHandlerTableOffset() { return (int) handlerTableOffsetField.getValue(addr); } private int getNulChkTableOffset() { return (int) nulChkTableOffsetField .getValue(addr); } private int getNMethodEndOffset() { return (int) nmethodEndOffsetField .getValue(addr); } + private int getCompLevel() { return (int) compLevelField .getValue(addr); } } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/CompileTask.java --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/CompileTask.java Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/CompileTask.java Wed May 08 23:54:45 2013 -0700 @@ -46,10 +46,12 @@ Type type = db.lookupType("CompileTask"); methodField = type.getAddressField("_method"); osrBciField = new CIntField(type.getCIntegerField("_osr_bci"), 0); + compLevelField = new CIntField(type.getCIntegerField("_comp_level"), 0); } private static AddressField methodField; private static CIntField osrBciField; + private static CIntField compLevelField; public CompileTask(Address addr) { super(addr); @@ -63,4 +65,8 @@ public int osrBci() { return (int)osrBciField.getValue(getAddress()); } + + public int compLevel() { + return (int)compLevelField.getValue(getAddress()); + } } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JMap.java --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JMap.java Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JMap.java Wed May 08 23:54:45 2013 -0700 @@ -117,8 +117,6 @@ mode = MODE_HEAP_SUMMARY; } else if (modeFlag.equals("-histo")) { mode = MODE_HISTOGRAM; - } else if (modeFlag.equals("-permstat")) { - mode = MODE_CLSTATS; } else if (modeFlag.equals("-clstats")) { mode = MODE_CLSTATS; } else if (modeFlag.equals("-finalizerinfo")) { diff -r 241d13ccb08b -r 4ea383c26563 hotspot/make/hotspot_version --- a/hotspot/make/hotspot_version Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/make/hotspot_version Wed May 08 23:54:45 2013 -0700 @@ -35,7 +35,7 @@ HS_MAJOR_VER=25 HS_MINOR_VER=0 -HS_BUILD_NUMBER=30 +HS_BUILD_NUMBER=31 JDK_MAJOR_VER=1 JDK_MINOR_VER=8 diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/cpu/sparc/vm/compiledIC_sparc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/cpu/sparc/vm/compiledIC_sparc.cpp Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,193 @@ +/* + * Copyright (c) 1997, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 "asm/macroAssembler.inline.hpp" +#include "code/compiledIC.hpp" +#include "code/icBuffer.hpp" +#include "code/nmethod.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/safepoint.hpp" +#ifdef COMPILER2 +#include "opto/matcher.hpp" +#endif + +// Release the CompiledICHolder* associated with this call site is there is one. +void CompiledIC::cleanup_call_site(virtual_call_Relocation* call_site) { + // This call site might have become stale so inspect it carefully. + NativeCall* call = nativeCall_at(call_site->addr()); + if (is_icholder_entry(call->destination())) { + NativeMovConstReg* value = nativeMovConstReg_at(call_site->cached_value()); + InlineCacheBuffer::queue_for_release((CompiledICHolder*)value->data()); + } +} + +bool CompiledIC::is_icholder_call_site(virtual_call_Relocation* call_site) { + // This call site might have become stale so inspect it carefully. + NativeCall* call = nativeCall_at(call_site->addr()); + return is_icholder_entry(call->destination()); +} + +//----------------------------------------------------------------------------- +// High-level access to an inline cache. Guaranteed to be MT-safe. + +CompiledIC::CompiledIC(nmethod* nm, NativeCall* call) + : _ic_call(call) +{ + address ic_call = call->instruction_address(); + + assert(ic_call != NULL, "ic_call address must be set"); + assert(nm != NULL, "must pass nmethod"); + assert(nm->contains(ic_call), "must be in nmethod"); + + // Search for the ic_call at the given address. + RelocIterator iter(nm, ic_call, ic_call+1); + bool ret = iter.next(); + assert(ret == true, "relocInfo must exist at this address"); + assert(iter.addr() == ic_call, "must find ic_call"); + if (iter.type() == relocInfo::virtual_call_type) { + virtual_call_Relocation* r = iter.virtual_call_reloc(); + _is_optimized = false; + _value = nativeMovConstReg_at(r->cached_value()); + } else { + assert(iter.type() == relocInfo::opt_virtual_call_type, "must be a virtual call"); + _is_optimized = true; + _value = NULL; + } +} + +// ---------------------------------------------------------------------------- + +#define __ _masm. +void CompiledStaticCall::emit_to_interp_stub(CodeBuffer &cbuf) { +#ifdef COMPILER2 + // Stub is fixed up when the corresponding call is converted from calling + // compiled code to calling interpreted code. + // set (empty), G5 + // jmp -1 + + address mark = cbuf.insts_mark(); // Get mark within main instrs section. + + MacroAssembler _masm(&cbuf); + + address base = + __ start_a_stub(to_interp_stub_size()*2); + if (base == NULL) return; // CodeBuffer::expand failed. + + // Static stub relocation stores the instruction address of the call. + __ relocate(static_stub_Relocation::spec(mark)); + + __ set_metadata(NULL, as_Register(Matcher::inline_cache_reg_encode())); + + __ set_inst_mark(); + AddressLiteral addrlit(-1); + __ JUMP(addrlit, G3, 0); + + __ delayed()->nop(); + + // Update current stubs pointer and restore code_end. + __ end_a_stub(); +#else + ShouldNotReachHere(); +#endif +} +#undef __ + +int CompiledStaticCall::to_interp_stub_size() { + // This doesn't need to be accurate but it must be larger or equal to + // the real size of the stub. + return (NativeMovConstReg::instruction_size + // sethi/setlo; + NativeJump::instruction_size + // sethi; jmp; nop + (TraceJumps ? 20 * BytesPerInstWord : 0) ); +} + +// Relocation entries for call stub, compiled java to interpreter. +int CompiledStaticCall::reloc_to_interp_stub() { + return 10; // 4 in emit_java_to_interp + 1 in Java_Static_Call +} + +void CompiledStaticCall::set_to_interpreted(methodHandle callee, address entry) { + address stub = find_stub(); + guarantee(stub != NULL, "stub not found"); + + if (TraceICs) { + ResourceMark rm; + tty->print_cr("CompiledStaticCall@" INTPTR_FORMAT ": set_to_interpreted %s", + instruction_address(), + callee->name_and_sig_as_C_string()); + } + + // Creation also verifies the object. + NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); + NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); + + assert(method_holder->data() == 0 || method_holder->data() == (intptr_t)callee(), + "a) MT-unsafe modification of inline cache"); + assert(jump->jump_destination() == (address)-1 || jump->jump_destination() == entry, + "b) MT-unsafe modification of inline cache"); + + // Update stub. + method_holder->set_data((intptr_t)callee()); + jump->set_jump_destination(entry); + + // Update jump to call. + set_destination_mt_safe(stub); +} + +void CompiledStaticCall::set_stub_to_clean(static_stub_Relocation* static_stub) { + assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), "mt unsafe call"); + // Reset stub. + address stub = static_stub->addr(); + assert(stub != NULL, "stub not found"); + // Creation also verifies the object. + NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); + NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); + method_holder->set_data(0); + jump->set_jump_destination((address)-1); +} + +//----------------------------------------------------------------------------- +// Non-product mode code +#ifndef PRODUCT + +void CompiledStaticCall::verify() { + // Verify call. + NativeCall::verify(); + if (os::is_MP()) { + verify_alignment(); + } + + // Verify stub. + address stub = find_stub(); + assert(stub != NULL, "no stub found for static call"); + // Creation also verifies the object. + NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); + NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); + + // Verify state. + assert(is_clean() || is_call_to_compiled() || is_call_to_interpreted(), "sanity check"); +} + +#endif // !PRODUCT diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/cpu/sparc/vm/sparc.ad --- a/hotspot/src/cpu/sparc/vm/sparc.ad Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/cpu/sparc/vm/sparc.ad Wed May 08 23:54:45 2013 -0700 @@ -1656,53 +1656,6 @@ } //============================================================================= - -// emit call stub, compiled java to interpretor -void emit_java_to_interp(CodeBuffer &cbuf ) { - - // Stub is fixed up when the corresponding call is converted from calling - // compiled code to calling interpreted code. - // set (empty), G5 - // jmp -1 - - address mark = cbuf.insts_mark(); // get mark within main instrs section - - MacroAssembler _masm(&cbuf); - - address base = - __ start_a_stub(Compile::MAX_stubs_size); - if (base == NULL) return; // CodeBuffer::expand failed - - // static stub relocation stores the instruction address of the call - __ relocate(static_stub_Relocation::spec(mark)); - - __ set_metadata(NULL, reg_to_register_object(Matcher::inline_cache_reg_encode())); - - __ set_inst_mark(); - AddressLiteral addrlit(-1); - __ JUMP(addrlit, G3, 0); - - __ delayed()->nop(); - - // Update current stubs pointer and restore code_end. - __ end_a_stub(); -} - -// size of call stub, compiled java to interpretor -uint size_java_to_interp() { - // This doesn't need to be accurate but it must be larger or equal to - // the real size of the stub. - return (NativeMovConstReg::instruction_size + // sethi/setlo; - NativeJump::instruction_size + // sethi; jmp; nop - (TraceJumps ? 20 * BytesPerInstWord : 0) ); -} -// relocation entries for call stub, compiled java to interpretor -uint reloc_java_to_interp() { - return 10; // 4 in emit_java_to_interp + 1 in Java_Static_Call -} - - -//============================================================================= #ifndef PRODUCT void MachUEPNode::format( PhaseRegAlloc *ra_, outputStream *st ) const { st->print_cr("\nUEP:"); @@ -2576,15 +2529,15 @@ enc_class Java_Static_Call (method meth) %{ // JAVA STATIC CALL // CALL to fixup routine. Fixup routine uses ScopeDesc info to determine // who we intended to call. - if ( !_method ) { + if (!_method) { emit_call_reloc(cbuf, $meth$$method, relocInfo::runtime_call_type); } else if (_optimized_virtual) { emit_call_reloc(cbuf, $meth$$method, relocInfo::opt_virtual_call_type); } else { emit_call_reloc(cbuf, $meth$$method, relocInfo::static_call_type); } - if( _method ) { // Emit stub for static call - emit_java_to_interp(cbuf); + if (_method) { // Emit stub for static call. + CompiledStaticCall::emit_to_interp_stub(cbuf); } %} diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/cpu/x86/vm/compiledIC_x86.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/cpu/x86/vm/compiledIC_x86.cpp Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,180 @@ +/* + * Copyright (c) 1997, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 "asm/macroAssembler.inline.hpp" +#include "code/compiledIC.hpp" +#include "code/icBuffer.hpp" +#include "code/nmethod.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/safepoint.hpp" + +// Release the CompiledICHolder* associated with this call site is there is one. +void CompiledIC::cleanup_call_site(virtual_call_Relocation* call_site) { + // This call site might have become stale so inspect it carefully. + NativeCall* call = nativeCall_at(call_site->addr()); + if (is_icholder_entry(call->destination())) { + NativeMovConstReg* value = nativeMovConstReg_at(call_site->cached_value()); + InlineCacheBuffer::queue_for_release((CompiledICHolder*)value->data()); + } +} + +bool CompiledIC::is_icholder_call_site(virtual_call_Relocation* call_site) { + // This call site might have become stale so inspect it carefully. + NativeCall* call = nativeCall_at(call_site->addr()); + return is_icholder_entry(call->destination()); +} + +//----------------------------------------------------------------------------- +// High-level access to an inline cache. Guaranteed to be MT-safe. + +CompiledIC::CompiledIC(nmethod* nm, NativeCall* call) + : _ic_call(call) +{ + address ic_call = call->instruction_address(); + + assert(ic_call != NULL, "ic_call address must be set"); + assert(nm != NULL, "must pass nmethod"); + assert(nm->contains(ic_call), "must be in nmethod"); + + // Search for the ic_call at the given address. + RelocIterator iter(nm, ic_call, ic_call+1); + bool ret = iter.next(); + assert(ret == true, "relocInfo must exist at this address"); + assert(iter.addr() == ic_call, "must find ic_call"); + if (iter.type() == relocInfo::virtual_call_type) { + virtual_call_Relocation* r = iter.virtual_call_reloc(); + _is_optimized = false; + _value = nativeMovConstReg_at(r->cached_value()); + } else { + assert(iter.type() == relocInfo::opt_virtual_call_type, "must be a virtual call"); + _is_optimized = true; + _value = NULL; + } +} + +// ---------------------------------------------------------------------------- + +#define __ _masm. +void CompiledStaticCall::emit_to_interp_stub(CodeBuffer &cbuf) { + // Stub is fixed up when the corresponding call is converted from + // calling compiled code to calling interpreted code. + // movq rbx, 0 + // jmp -5 # to self + + address mark = cbuf.insts_mark(); // Get mark within main instrs section. + + // Note that the code buffer's insts_mark is always relative to insts. + // That's why we must use the macroassembler to generate a stub. + MacroAssembler _masm(&cbuf); + + address base = + __ start_a_stub(to_interp_stub_size()*2); + if (base == NULL) return; // CodeBuffer::expand failed. + // Static stub relocation stores the instruction address of the call. + __ relocate(static_stub_Relocation::spec(mark), Assembler::imm_operand); + // Static stub relocation also tags the Method* in the code-stream. + __ mov_metadata(rbx, (Metadata*) NULL); // Method is zapped till fixup time. + // This is recognized as unresolved by relocs/nativeinst/ic code. + __ jump(RuntimeAddress(__ pc())); + + // Update current stubs pointer and restore insts_end. + __ end_a_stub(); +} +#undef __ + +int CompiledStaticCall::to_interp_stub_size() { + return NOT_LP64(10) // movl; jmp + LP64_ONLY(15); // movq (1+1+8); jmp (1+4) +} + +// Relocation entries for call stub, compiled java to interpreter. +int CompiledStaticCall::reloc_to_interp_stub() { + return 4; // 3 in emit_to_interp_stub + 1 in emit_call +} + +void CompiledStaticCall::set_to_interpreted(methodHandle callee, address entry) { + address stub = find_stub(); + guarantee(stub != NULL, "stub not found"); + + if (TraceICs) { + ResourceMark rm; + tty->print_cr("CompiledStaticCall@" INTPTR_FORMAT ": set_to_interpreted %s", + instruction_address(), + callee->name_and_sig_as_C_string()); + } + + // Creation also verifies the object. + NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); + NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); + + assert(method_holder->data() == 0 || method_holder->data() == (intptr_t)callee(), + "a) MT-unsafe modification of inline cache"); + assert(jump->jump_destination() == (address)-1 || jump->jump_destination() == entry, + "b) MT-unsafe modification of inline cache"); + + // Update stub. + method_holder->set_data((intptr_t)callee()); + jump->set_jump_destination(entry); + + // Update jump to call. + set_destination_mt_safe(stub); +} + +void CompiledStaticCall::set_stub_to_clean(static_stub_Relocation* static_stub) { + assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), "mt unsafe call"); + // Reset stub. + address stub = static_stub->addr(); + assert(stub != NULL, "stub not found"); + // Creation also verifies the object. + NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); + NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); + method_holder->set_data(0); + jump->set_jump_destination((address)-1); +} + +//----------------------------------------------------------------------------- +// Non-product mode code +#ifndef PRODUCT + +void CompiledStaticCall::verify() { + // Verify call. + NativeCall::verify(); + if (os::is_MP()) { + verify_alignment(); + } + + // Verify stub. + address stub = find_stub(); + assert(stub != NULL, "no stub found for static call"); + // Creation also verifies the object. + NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); + NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); + + // Verify state. + assert(is_clean() || is_call_to_compiled() || is_call_to_interpreted(), "sanity check"); +} + +#endif // !PRODUCT diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/cpu/x86/vm/x86_32.ad --- a/hotspot/src/cpu/x86/vm/x86_32.ad Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/cpu/x86/vm/x86_32.ad Wed May 08 23:54:45 2013 -0700 @@ -1257,43 +1257,6 @@ } //============================================================================= - -// emit call stub, compiled java to interpreter -void emit_java_to_interp(CodeBuffer &cbuf ) { - // Stub is fixed up when the corresponding call is converted from calling - // compiled code to calling interpreted code. - // mov rbx,0 - // jmp -1 - - address mark = cbuf.insts_mark(); // get mark within main instrs section - - // Note that the code buffer's insts_mark is always relative to insts. - // That's why we must use the macroassembler to generate a stub. - MacroAssembler _masm(&cbuf); - - address base = - __ start_a_stub(Compile::MAX_stubs_size); - if (base == NULL) return; // CodeBuffer::expand failed - // static stub relocation stores the instruction address of the call - __ relocate(static_stub_Relocation::spec(mark), RELOC_IMM32); - // static stub relocation also tags the Method* in the code-stream. - __ mov_metadata(rbx, (Metadata*)NULL); // method is zapped till fixup time - // This is recognized as unresolved by relocs/nativeInst/ic code - __ jump(RuntimeAddress(__ pc())); - - __ end_a_stub(); - // Update current stubs pointer and restore insts_end. -} -// size of call stub, compiled java to interpretor -uint size_java_to_interp() { - return 10; // movl; jmp -} -// relocation entries for call stub, compiled java to interpretor -uint reloc_java_to_interp() { - return 4; // 3 in emit_java_to_interp + 1 in Java_Static_Call -} - -//============================================================================= #ifndef PRODUCT void MachUEPNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { st->print_cr( "CMP EAX,[ECX+4]\t# Inline cache check"); @@ -1909,8 +1872,8 @@ emit_d32_reloc(cbuf, ($meth$$method - (int)(cbuf.insts_end()) - 4), static_call_Relocation::spec(), RELOC_IMM32 ); } - if (_method) { // Emit stub for static call - emit_java_to_interp(cbuf); + if (_method) { // Emit stub for static call. + CompiledStaticCall::emit_to_interp_stub(cbuf); } %} diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/cpu/x86/vm/x86_64.ad --- a/hotspot/src/cpu/x86/vm/x86_64.ad Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/cpu/x86/vm/x86_64.ad Wed May 08 23:54:45 2013 -0700 @@ -1388,48 +1388,6 @@ } //============================================================================= - -// emit call stub, compiled java to interpreter -void emit_java_to_interp(CodeBuffer& cbuf) -{ - // Stub is fixed up when the corresponding call is converted from - // calling compiled code to calling interpreted code. - // movq rbx, 0 - // jmp -5 # to self - - address mark = cbuf.insts_mark(); // get mark within main instrs section - - // Note that the code buffer's insts_mark is always relative to insts. - // That's why we must use the macroassembler to generate a stub. - MacroAssembler _masm(&cbuf); - - address base = - __ start_a_stub(Compile::MAX_stubs_size); - if (base == NULL) return; // CodeBuffer::expand failed - // static stub relocation stores the instruction address of the call - __ relocate(static_stub_Relocation::spec(mark), RELOC_IMM64); - // static stub relocation also tags the Method* in the code-stream. - __ mov_metadata(rbx, (Metadata*) NULL); // method is zapped till fixup time - // This is recognized as unresolved by relocs/nativeinst/ic code - __ jump(RuntimeAddress(__ pc())); - - // Update current stubs pointer and restore insts_end. - __ end_a_stub(); -} - -// size of call stub, compiled java to interpretor -uint size_java_to_interp() -{ - return 15; // movq (1+1+8); jmp (1+4) -} - -// relocation entries for call stub, compiled java to interpretor -uint reloc_java_to_interp() -{ - return 4; // 3 in emit_java_to_interp + 1 in Java_Static_Call -} - -//============================================================================= #ifndef PRODUCT void MachUEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const { @@ -2078,8 +2036,8 @@ RELOC_DISP32); } if (_method) { - // Emit stub for static call - emit_java_to_interp(cbuf); + // Emit stub for static call. + CompiledStaticCall::emit_to_interp_stub(cbuf); } %} diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/cpu/zero/vm/compiledIC_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/cpu/zero/vm/compiledIC_zero.cpp Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,122 @@ +/* + * Copyright (c) 1997, 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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/systemDictionary.hpp" +#include "code/codeCache.hpp" +#include "code/compiledIC.hpp" +#include "code/icBuffer.hpp" +#include "code/nmethod.hpp" +#include "code/vtableStubs.hpp" +#include "interpreter/interpreter.hpp" +#include "interpreter/linkResolver.hpp" +#include "memory/metadataFactory.hpp" +#include "memory/oopFactory.hpp" +#include "oops/method.hpp" +#include "oops/oop.inline.hpp" +#include "oops/symbol.hpp" +#include "runtime/icache.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" +#include "utilities/events.hpp" + + +// Release the CompiledICHolder* associated with this call site is there is one. +void CompiledIC::cleanup_call_site(virtual_call_Relocation* call_site) { + // This call site might have become stale so inspect it carefully. + NativeCall* call = nativeCall_at(call_site->addr()); + if (is_icholder_entry(call->destination())) { + NativeMovConstReg* value = nativeMovConstReg_at(call_site->cached_value()); + InlineCacheBuffer::queue_for_release((CompiledICHolder*)value->data()); + } +} + +bool CompiledIC::is_icholder_call_site(virtual_call_Relocation* call_site) { + // This call site might have become stale so inspect it carefully. + NativeCall* call = nativeCall_at(call_site->addr()); + return is_icholder_entry(call->destination()); +} + +//----------------------------------------------------------------------------- +// High-level access to an inline cache. Guaranteed to be MT-safe. + +CompiledIC::CompiledIC(nmethod* nm, NativeCall* call) + : _ic_call(call) +{ + address ic_call = call->instruction_address(); + + assert(ic_call != NULL, "ic_call address must be set"); + assert(nm != NULL, "must pass nmethod"); + assert(nm->contains(ic_call), "must be in nmethod"); + + // Search for the ic_call at the given address. + RelocIterator iter(nm, ic_call, ic_call+1); + bool ret = iter.next(); + assert(ret == true, "relocInfo must exist at this address"); + assert(iter.addr() == ic_call, "must find ic_call"); + if (iter.type() == relocInfo::virtual_call_type) { + virtual_call_Relocation* r = iter.virtual_call_reloc(); + _is_optimized = false; + _value = nativeMovConstReg_at(r->cached_value()); + } else { + assert(iter.type() == relocInfo::opt_virtual_call_type, "must be a virtual call"); + _is_optimized = true; + _value = NULL; + } +} + +// ---------------------------------------------------------------------------- + +void CompiledStaticCall::emit_to_interp_stub(CodeBuffer &cbuf) { + ShouldNotReachHere(); // Only needed for COMPILER2. +} + +int CompiledStaticCall::to_interp_stub_size() { + ShouldNotReachHere(); // Only needed for COMPILER2. + return 0; +} + +// Relocation entries for call stub, compiled java to interpreter. +int CompiledStaticCall::reloc_to_interp_stub() { + ShouldNotReachHere(); // Only needed for COMPILER2. + return 0; +} + +void CompiledStaticCall::set_to_interpreted(methodHandle callee, address entry) { + ShouldNotReachHere(); // Only needed for COMPILER2. +} + +void CompiledStaticCall::set_stub_to_clean(static_stub_Relocation* static_stub) { + ShouldNotReachHere(); // Only needed for COMPILER2. +} + +//----------------------------------------------------------------------------- +// Non-product mode code. +#ifndef PRODUCT + +void CompiledStaticCall::verify() { + ShouldNotReachHere(); // Only needed for COMPILER2. +} + +#endif // !PRODUCT diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/os/bsd/vm/os_bsd.cpp --- a/hotspot/src/os/bsd/vm/os_bsd.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/os/bsd/vm/os_bsd.cpp Wed May 08 23:54:45 2013 -0700 @@ -1230,10 +1230,6 @@ return retval; } -const char* os::get_current_directory(char *buf, int buflen) { - return getcwd(buf, buflen); -} - // check if addr is inside libjvm.so bool os::address_is_in_vm(address addr) { static address libjvm_base_addr; @@ -2080,9 +2076,10 @@ flags |= MAP_FIXED; } - // Map uncommitted pages PROT_READ and PROT_WRITE, change access - // to PROT_EXEC if executable when we commit the page. - addr = (char*)::mmap(requested_addr, bytes, PROT_READ|PROT_WRITE, + // Map reserved/uncommitted pages PROT_NONE so we fail early if we + // touch an uncommitted page. Otherwise, the read/write might + // succeed if we have enough swap space to back the physical page. + addr = (char*)::mmap(requested_addr, bytes, PROT_NONE, flags, -1, 0); if (addr != MAP_FAILED) { diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/os/linux/vm/os_linux.cpp --- a/hotspot/src/os/linux/vm/os_linux.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/os/linux/vm/os_linux.cpp Wed May 08 23:54:45 2013 -0700 @@ -119,6 +119,7 @@ Mutex* os::Linux::_createThread_lock = NULL; pthread_t os::Linux::_main_thread; int os::Linux::_page_size = -1; +const int os::Linux::_vm_default_page_size = (8 * K); bool os::Linux::_is_floating_stack = false; bool os::Linux::_is_NPTL = false; bool os::Linux::_supports_fast_thread_cpu_time = false; @@ -1662,10 +1663,6 @@ return retval; } -const char* os::get_current_directory(char *buf, int buflen) { - return getcwd(buf, buflen); -} - // check if addr is inside libjvm.so bool os::address_is_in_vm(address addr) { static address libjvm_base_addr; @@ -2906,9 +2903,10 @@ flags |= MAP_FIXED; } - // Map uncommitted pages PROT_READ and PROT_WRITE, change access - // to PROT_EXEC if executable when we commit the page. - addr = (char*)::mmap(requested_addr, bytes, PROT_READ|PROT_WRITE, + // Map reserved/uncommitted pages PROT_NONE so we fail early if we + // touch an uncommitted page. Otherwise, the read/write might + // succeed if we have enough swap space to back the physical page. + addr = (char*)::mmap(requested_addr, bytes, PROT_NONE, flags, -1, 0); if (addr != MAP_FAILED) { @@ -4249,6 +4247,15 @@ Linux::clock_init(); initial_time_count = os::elapsed_counter(); pthread_mutex_init(&dl_mutex, NULL); + + // If the pagesize of the VM is greater than 8K determine the appropriate + // number of initial guard pages. The user can change this with the + // command line arguments, if needed. + if (vm_page_size() > (int)Linux::vm_default_page_size()) { + StackYellowPages = 1; + StackRedPages = 1; + StackShadowPages = round_to((StackShadowPages*Linux::vm_default_page_size()), vm_page_size()) / vm_page_size(); + } } // To install functions for atexit system call @@ -4302,8 +4309,8 @@ // Add in 2*BytesPerWord times page size to account for VM stack during // class initialization depending on 32 or 64 bit VM. os::Linux::min_stack_allowed = MAX2(os::Linux::min_stack_allowed, - (size_t)(StackYellowPages+StackRedPages+StackShadowPages+ - 2*BytesPerWord COMPILER2_PRESENT(+1)) * Linux::page_size()); + (size_t)(StackYellowPages+StackRedPages+StackShadowPages) * Linux::page_size() + + (2*BytesPerWord COMPILER2_PRESENT(+1)) * Linux::vm_default_page_size()); size_t threadStackSizeInBytes = ThreadStackSize * K; if (threadStackSizeInBytes != 0 && diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/os/linux/vm/os_linux.hpp --- a/hotspot/src/os/linux/vm/os_linux.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/os/linux/vm/os_linux.hpp Wed May 08 23:54:45 2013 -0700 @@ -70,6 +70,7 @@ static pthread_t _main_thread; static Mutex* _createThread_lock; static int _page_size; + static const int _vm_default_page_size; static julong available_memory(); static julong physical_memory() { return _physical_memory; } @@ -116,6 +117,8 @@ static int page_size(void) { return _page_size; } static void set_page_size(int val) { _page_size = val; } + static int vm_default_page_size(void) { return _vm_default_page_size; } + static address ucontext_get_pc(ucontext_t* uc); static intptr_t* ucontext_get_sp(ucontext_t* uc); static intptr_t* ucontext_get_fp(ucontext_t* uc); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/os/posix/vm/os_posix.cpp --- a/hotspot/src/os/posix/vm/os_posix.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/os/posix/vm/os_posix.cpp Wed May 08 23:54:45 2013 -0700 @@ -251,3 +251,11 @@ return true; #endif } + +const char* os::get_current_directory(char *buf, size_t buflen) { + return getcwd(buf, buflen); +} + +FILE* os::open(int fd, const char* mode) { + return ::fdopen(fd, mode); +} diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/os/solaris/vm/os_solaris.cpp --- a/hotspot/src/os/solaris/vm/os_solaris.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/os/solaris/vm/os_solaris.cpp Wed May 08 23:54:45 2013 -0700 @@ -824,7 +824,7 @@ // allocate new buffer and initialize info = (Dl_serinfo*)malloc(_info.dls_size); if (info == NULL) { - vm_exit_out_of_memory(_info.dls_size, + vm_exit_out_of_memory(_info.dls_size, OOM_MALLOC_ERROR, "init_system_properties_values info"); } info->dls_size = _info.dls_size; @@ -866,7 +866,7 @@ common_path = malloc(bufsize); if (common_path == NULL) { free(info); - vm_exit_out_of_memory(bufsize, + vm_exit_out_of_memory(bufsize, OOM_MALLOC_ERROR, "init_system_properties_values common_path"); } sprintf(common_path, COMMON_DIR "/lib/%s", cpu_arch); @@ -879,7 +879,7 @@ if (library_path == NULL) { free(info); free(common_path); - vm_exit_out_of_memory(bufsize, + vm_exit_out_of_memory(bufsize, OOM_MALLOC_ERROR, "init_system_properties_values library_path"); } library_path[0] = '\0'; @@ -1623,7 +1623,8 @@ // %%% this is used only in threadLocalStorage.cpp if (thr_setspecific((thread_key_t)index, value)) { if (errno == ENOMEM) { - vm_exit_out_of_memory(SMALLINT, "thr_setspecific: out of swap space"); + vm_exit_out_of_memory(SMALLINT, OOM_MALLOC_ERROR, + "thr_setspecific: out of swap space"); } else { fatal(err_msg("os::thread_local_storage_at_put: thr_setspecific failed " "(%s)", strerror(errno))); @@ -1915,10 +1916,6 @@ return retval; } -const char* os::get_current_directory(char *buf, int buflen) { - return getcwd(buf, buflen); -} - // check if addr is inside libjvm.so bool os::address_is_in_vm(address addr) { static address libjvm_base_addr; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/os/windows/vm/os_windows.cpp --- a/hotspot/src/os/windows/vm/os_windows.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/os/windows/vm/os_windows.cpp Wed May 08 23:54:45 2013 -0700 @@ -1221,8 +1221,10 @@ // Needs to be in os specific directory because windows requires another // header file -const char* os::get_current_directory(char *buf, int buflen) { - return _getcwd(buf, buflen); +const char* os::get_current_directory(char *buf, size_t buflen) { + int n = static_cast(buflen); + if (buflen > INT_MAX) n = INT_MAX; + return _getcwd(buf, n); } //----------------------------------------------------------- @@ -4098,6 +4100,10 @@ return ::open(pathbuf, oflag | O_BINARY | O_NOINHERIT, mode); } +FILE* os::open(int fd, const char* mode) { + return ::_fdopen(fd, mode); +} + // Is a (classpath) directory empty? bool os::dir_is_empty(const char* path) { WIN32_FIND_DATA fd; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp --- a/hotspot/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp Wed May 08 23:54:45 2013 -0700 @@ -178,7 +178,7 @@ // JVM needs to know exact stack location, abort if it fails if (rslt != 0) { if (rslt == ENOMEM) { - vm_exit_out_of_memory(0, "pthread_getattr_np"); + vm_exit_out_of_memory(0, OOM_MMAP_ERROR, "pthread_getattr_np"); } else { fatal(err_msg("pthread_getattr_np failed with errno = %d", rslt)); } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp --- a/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp Wed May 08 23:54:45 2013 -0700 @@ -710,7 +710,7 @@ // JVM needs to know exact stack location, abort if it fails if (rslt != 0) { if (rslt == ENOMEM) { - vm_exit_out_of_memory(0, "pthread_getattr_np"); + vm_exit_out_of_memory(0, OOM_MMAP_ERROR, "pthread_getattr_np"); } else { fatal(err_msg("pthread_getattr_np failed with errno = %d", rslt)); } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/os_cpu/linux_zero/vm/os_linux_zero.cpp --- a/hotspot/src/os_cpu/linux_zero/vm/os_linux_zero.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/os_cpu/linux_zero/vm/os_linux_zero.cpp Wed May 08 23:54:45 2013 -0700 @@ -313,7 +313,7 @@ int res = pthread_getattr_np(pthread_self(), &attr); if (res != 0) { if (res == ENOMEM) { - vm_exit_out_of_memory(0, "pthread_getattr_np"); + vm_exit_out_of_memory(0, OOM_MMAP_ERROR, "pthread_getattr_np"); } else { fatal(err_msg("pthread_getattr_np failed with errno = %d", res)); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp --- a/hotspot/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp Wed May 08 23:54:45 2013 -0700 @@ -591,7 +591,7 @@ // on the thread stack, which could get a mapping error when touched. address addr = (address) info->si_addr; if (sig == SIGBUS && info->si_code == BUS_OBJERR && info->si_errno == ENOMEM) { - vm_exit_out_of_memory(0, "Out of swap space to map in thread stack."); + vm_exit_out_of_memory(0, OOM_MMAP_ERROR, "Out of swap space to map in thread stack."); } VMError err(t, sig, pc, info, ucVoid); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp --- a/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp Wed May 08 23:54:45 2013 -0700 @@ -745,7 +745,7 @@ // on the thread stack, which could get a mapping error when touched. address addr = (address) info->si_addr; if (sig == SIGBUS && info->si_code == BUS_OBJERR && info->si_errno == ENOMEM) { - vm_exit_out_of_memory(0, "Out of swap space to map in thread stack."); + vm_exit_out_of_memory(0, OOM_MMAP_ERROR, "Out of swap space to map in thread stack."); } VMError err(t, sig, pc, info, ucVoid); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/adlc/main.cpp --- a/hotspot/src/share/vm/adlc/main.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/adlc/main.cpp Wed May 08 23:54:45 2013 -0700 @@ -213,6 +213,7 @@ AD.addInclude(AD._CPP_file, "adfiles", get_basename(AD._HPP_file._name)); AD.addInclude(AD._CPP_file, "memory/allocation.inline.hpp"); AD.addInclude(AD._CPP_file, "asm/macroAssembler.inline.hpp"); + AD.addInclude(AD._CPP_file, "code/compiledIC.hpp"); AD.addInclude(AD._CPP_file, "code/vmreg.hpp"); AD.addInclude(AD._CPP_file, "gc_interface/collectedHeap.inline.hpp"); AD.addInclude(AD._CPP_file, "oops/compiledICHolder.hpp"); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/asm/assembler.cpp --- a/hotspot/src/share/vm/asm/assembler.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/asm/assembler.cpp Wed May 08 23:54:45 2013 -0700 @@ -44,7 +44,7 @@ CodeSection* cs = code->insts(); cs->clear_mark(); // new assembler kills old mark if (cs->start() == NULL) { - vm_exit_out_of_memory(0, err_msg("CodeCache: no room for %s", + vm_exit_out_of_memory(0, OOM_MMAP_ERROR, err_msg("CodeCache: no room for %s", code->name())); } _code_section = cs; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/ci/ciEnv.cpp --- a/hotspot/src/share/vm/ci/ciEnv.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/ci/ciEnv.cpp Wed May 08 23:54:45 2013 -0700 @@ -483,7 +483,8 @@ { // We have to lock the cpool to keep the oop from being resolved // while we are accessing it. - MonitorLockerEx ml(cpool->lock()); + oop cplock = cpool->lock(); + ObjectLocker ol(cplock, THREAD, cplock != NULL); constantTag tag = cpool->tag_at(index); if (tag.is_klass()) { // The klass has been inserted into the constant pool @@ -1149,23 +1150,9 @@ record_method_not_compilable("out of memory"); } -fileStream* ciEnv::_replay_data_stream = NULL; - -void ciEnv::dump_replay_data() { +void ciEnv::dump_replay_data(outputStream* out) { VM_ENTRY_MARK; MutexLocker ml(Compile_lock); - if (_replay_data_stream == NULL) { - _replay_data_stream = new (ResourceObj::C_HEAP, mtCompiler) fileStream(ReplayDataFile); - if (_replay_data_stream == NULL) { - fatal(err_msg("Can't open %s for replay data", ReplayDataFile)); - } - } - dump_replay_data(_replay_data_stream); -} - - -void ciEnv::dump_replay_data(outputStream* out) { - ASSERT_IN_VM; ResourceMark rm; #if INCLUDE_JVMTI out->print_cr("JvmtiExport can_access_local_variables %d", _jvmti_can_access_local_variables); @@ -1178,13 +1165,15 @@ for (int i = 0; i < objects->length(); i++) { objects->at(i)->dump_replay_data(out); } - Method* method = task()->method(); - int entry_bci = task()->osr_bci(); + CompileTask* task = this->task(); + Method* method = task->method(); + int entry_bci = task->osr_bci(); + int comp_level = task->comp_level(); // Klass holder = method->method_holder(); - out->print_cr("compile %s %s %s %d", + out->print_cr("compile %s %s %s %d %d", method->klass_name()->as_quoted_ascii(), method->name()->as_quoted_ascii(), method->signature()->as_quoted_ascii(), - entry_bci); + entry_bci, comp_level); out->flush(); } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/ci/ciEnv.hpp --- a/hotspot/src/share/vm/ci/ciEnv.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/ci/ciEnv.hpp Wed May 08 23:54:45 2013 -0700 @@ -46,8 +46,6 @@ friend class CompileBroker; friend class Dependencies; // for get_object, during logging - static fileStream* _replay_data_stream; - private: Arena* _arena; // Alias for _ciEnv_arena except in init_shared_objects() Arena _ciEnv_arena; @@ -451,10 +449,6 @@ // RedefineClasses support void metadata_do(void f(Metadata*)) { _factory->metadata_do(f); } - // Dump the compilation replay data for this ciEnv to - // ReplayDataFile, creating the file if needed. - void dump_replay_data(); - // Dump the compilation replay data for the ciEnv to the stream. void dump_replay_data(outputStream* out); }; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/ci/ciMethod.hpp --- a/hotspot/src/share/vm/ci/ciMethod.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/ci/ciMethod.hpp Wed May 08 23:54:45 2013 -0700 @@ -196,7 +196,6 @@ // Analysis and profiling. // // Usage note: liveness_at_bci and init_vars should be wrapped in ResourceMarks. - bool uses_monitors() const { return _uses_monitors; } // this one should go away, it has a misleading name bool has_monitor_bytecodes() const { return _uses_monitors; } bool has_balanced_monitors(); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/ci/ciReplay.cpp --- a/hotspot/src/share/vm/ci/ciReplay.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/ci/ciReplay.cpp Wed May 08 23:54:45 2013 -0700 @@ -89,7 +89,7 @@ loader = Handle(thread, SystemDictionary::java_system_loader()); stream = fopen(filename, "rt"); if (stream == NULL) { - fprintf(stderr, "Can't open replay file %s\n", filename); + fprintf(stderr, "ERROR: Can't open replay file %s\n", filename); } buffer_length = 32; buffer = NEW_RESOURCE_ARRAY(char, buffer_length); @@ -327,7 +327,6 @@ if (had_error()) { tty->print_cr("Error while parsing line %d: %s\n", line_no, _error_message); tty->print_cr("%s", buffer); - assert(false, "error"); return; } pos = 0; @@ -370,11 +369,47 @@ } } - // compile + // validation of comp_level + bool is_valid_comp_level(int comp_level) { + const int msg_len = 256; + char* msg = NULL; + if (!is_compile(comp_level)) { + msg = NEW_RESOURCE_ARRAY(char, msg_len); + jio_snprintf(msg, msg_len, "%d isn't compilation level", comp_level); + } else if (!TieredCompilation && (comp_level != CompLevel_highest_tier)) { + msg = NEW_RESOURCE_ARRAY(char, msg_len); + switch (comp_level) { + case CompLevel_simple: + jio_snprintf(msg, msg_len, "compilation level %d requires Client VM or TieredCompilation", comp_level); + break; + case CompLevel_full_optimization: + jio_snprintf(msg, msg_len, "compilation level %d requires Server VM", comp_level); + break; + default: + jio_snprintf(msg, msg_len, "compilation level %d requires TieredCompilation", comp_level); + } + } + if (msg != NULL) { + report_error(msg); + return false; + } + return true; + } + + // compile void process_compile(TRAPS) { // methodHandle method; Method* method = parse_method(CHECK); int entry_bci = parse_int("entry_bci"); + const char* comp_level_label = "comp_level"; + int comp_level = parse_int(comp_level_label); + // old version w/o comp_level + if (had_error() && (error_message() == comp_level_label)) { + comp_level = CompLevel_full_optimization; + } + if (!is_valid_comp_level(comp_level)) { + return; + } Klass* k = method->method_holder(); ((InstanceKlass*)k)->initialize(THREAD); if (HAS_PENDING_EXCEPTION) { @@ -389,12 +424,12 @@ } } // Make sure the existence of a prior compile doesn't stop this one - nmethod* nm = (entry_bci != InvocationEntryBci) ? method->lookup_osr_nmethod_for(entry_bci, CompLevel_full_optimization, true) : method->code(); + nmethod* nm = (entry_bci != InvocationEntryBci) ? method->lookup_osr_nmethod_for(entry_bci, comp_level, true) : method->code(); if (nm != NULL) { nm->make_not_entrant(); } replay_state = this; - CompileBroker::compile_method(method, entry_bci, CompLevel_full_optimization, + CompileBroker::compile_method(method, entry_bci, comp_level, methodHandle(), 0, "replay", THREAD); replay_state = NULL; reset(); @@ -551,7 +586,7 @@ if (parsed_two_word == i) continue; default: - ShouldNotReachHere(); + fatal(err_msg_res("Unexpected tag: %d", cp->tag_at(i).value())); break; } @@ -819,6 +854,11 @@ ReplaySuppressInitializers = 1; } + if (FLAG_IS_DEFAULT(ReplayDataFile)) { + tty->print_cr("ERROR: no compiler replay data file specified (use -XX:ReplayDataFile=replay_pid12345.txt)."); + return 1; + } + // Load and parse the replay data CompileReplay rp(ReplayDataFile, THREAD); int exit_code = 0; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/classfile/classFileParser.cpp --- a/hotspot/src/share/vm/classfile/classFileParser.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/classfile/classFileParser.cpp Wed May 08 23:54:45 2013 -0700 @@ -2027,7 +2027,6 @@ u2 method_parameters_length = 0; u1* method_parameters_data = NULL; bool method_parameters_seen = false; - bool method_parameters_four_byte_flags; bool parsed_code_attribute = false; bool parsed_checked_exceptions_attribute = false; bool parsed_stackmap_attribute = false; @@ -2241,26 +2240,14 @@ } method_parameters_seen = true; method_parameters_length = cfs->get_u1_fast(); - // Track the actual size (note: this is written for clarity; a - // decent compiler will CSE and constant-fold this into a single - // expression) - // Use the attribute length to figure out the size of flags - if (method_attribute_length == (method_parameters_length * 6u) + 1u) { - method_parameters_four_byte_flags = true; - } else if (method_attribute_length == (method_parameters_length * 4u) + 1u) { - method_parameters_four_byte_flags = false; - } else { + if (method_attribute_length != (method_parameters_length * 4u) + 1u) { classfile_parse_error( "Invalid MethodParameters method attribute length %u in class file", method_attribute_length, CHECK_(nullHandle)); } method_parameters_data = cfs->get_u1_buffer(); cfs->skip_u2_fast(method_parameters_length); - if (method_parameters_four_byte_flags) { - cfs->skip_u4_fast(method_parameters_length); - } else { - cfs->skip_u2_fast(method_parameters_length); - } + cfs->skip_u2_fast(method_parameters_length); // ignore this attribute if it cannot be reflected if (!SystemDictionary::Parameter_klass_loaded()) method_parameters_length = 0; @@ -2423,13 +2410,8 @@ for (int i = 0; i < method_parameters_length; i++) { elem[i].name_cp_index = Bytes::get_Java_u2(method_parameters_data); method_parameters_data += 2; - if (method_parameters_four_byte_flags) { - elem[i].flags = Bytes::get_Java_u4(method_parameters_data); - method_parameters_data += 4; - } else { - elem[i].flags = Bytes::get_Java_u2(method_parameters_data); - method_parameters_data += 2; - } + elem[i].flags = Bytes::get_Java_u2(method_parameters_data); + method_parameters_data += 2; } } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/classfile/classFileParser.hpp --- a/hotspot/src/share/vm/classfile/classFileParser.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/classfile/classFileParser.hpp Wed May 08 23:54:45 2013 -0700 @@ -304,7 +304,19 @@ inline void assert_property(bool b, const char* msg, TRAPS) { #ifdef ASSERT - if (!b) { fatal(msg); } + if (!b) { + ResourceMark rm(THREAD); + fatal(err_msg(msg, _class_name->as_C_string())); + } +#endif + } + + inline void assert_property(bool b, const char* msg, int index, TRAPS) { +#ifdef ASSERT + if (!b) { + ResourceMark rm(THREAD); + fatal(err_msg(msg, index, _class_name->as_C_string())); + } #endif } @@ -312,7 +324,7 @@ if (_need_verify) { guarantee_property(property, msg, index, CHECK); } else { - assert_property(property, msg, CHECK); + assert_property(property, msg, index, CHECK); } } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/classfile/classLoader.cpp --- a/hotspot/src/share/vm/classfile/classLoader.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/classfile/classLoader.cpp Wed May 08 23:54:45 2013 -0700 @@ -1345,9 +1345,10 @@ tty->print_cr("CompileTheWorld (%d) : %s", _compile_the_world_class_counter, buffer); // Preload all classes to get around uncommon traps // Iterate over all methods in class + int comp_level = CompilationPolicy::policy()->initial_compile_level(); for (int n = 0; n < k->methods()->length(); n++) { methodHandle m (THREAD, k->methods()->at(n)); - if (CompilationPolicy::can_be_compiled(m)) { + if (CompilationPolicy::can_be_compiled(m, comp_level)) { if (++_codecache_sweep_counter == CompileTheWorldSafepointInterval) { // Give sweeper a chance to keep up with CTW @@ -1356,7 +1357,7 @@ _codecache_sweep_counter = 0; } // Force compilation - CompileBroker::compile_method(m, InvocationEntryBci, CompilationPolicy::policy()->initial_compile_level(), + CompileBroker::compile_method(m, InvocationEntryBci, comp_level, methodHandle(), 0, "CTW", THREAD); if (HAS_PENDING_EXCEPTION) { clear_pending_exception_if_not_oom(CHECK); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/classfile/classLoaderData.cpp --- a/hotspot/src/share/vm/classfile/classLoaderData.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp Wed May 08 23:54:45 2013 -0700 @@ -53,6 +53,7 @@ #include "classfile/metadataOnStackMark.hpp" #include "classfile/systemDictionary.hpp" #include "code/codeCache.hpp" +#include "memory/gcLocker.hpp" #include "memory/metadataFactory.hpp" #include "memory/metaspaceShared.hpp" #include "memory/oopFactory.hpp" @@ -65,17 +66,19 @@ ClassLoaderData * ClassLoaderData::_the_null_class_loader_data = NULL; -ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool is_anonymous) : +ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool is_anonymous, Dependencies dependencies) : _class_loader(h_class_loader()), _is_anonymous(is_anonymous), _keep_alive(is_anonymous), // initially _metaspace(NULL), _unloading(false), _klasses(NULL), _claimed(0), _jmethod_ids(NULL), _handles(NULL), _deallocate_list(NULL), - _next(NULL), _dependencies(), + _next(NULL), _dependencies(dependencies), _metaspace_lock(new Mutex(Monitor::leaf+1, "Metaspace allocation lock", true)) { // empty } void ClassLoaderData::init_dependencies(TRAPS) { + assert(!Universe::is_fully_initialized(), "should only be called when initializing"); + assert(is_the_null_class_loader_data(), "should only call this for the null class loader"); _dependencies.init(CHECK); } @@ -277,6 +280,9 @@ void ClassLoaderData::unload() { _unloading = true; + // Tell serviceability tools these classes are unloading + classes_do(InstanceKlass::notify_unload_class); + if (TraceClassLoaderData) { ResourceMark rm; tty->print("[ClassLoaderData: unload loader data "PTR_FORMAT, this); @@ -300,6 +306,9 @@ ClassLoaderData::~ClassLoaderData() { + // Release C heap structures for all the classes. + classes_do(InstanceKlass::release_C_heap_structures); + Metaspace *m = _metaspace; if (m != NULL) { _metaspace = NULL; @@ -423,7 +432,7 @@ // These anonymous class loaders are to contain classes used for JSR292 ClassLoaderData* ClassLoaderData::anonymous_class_loader_data(oop loader, TRAPS) { // Add a new class loader data to the graph. - return ClassLoaderDataGraph::add(NULL, loader, CHECK_NULL); + return ClassLoaderDataGraph::add(loader, true, CHECK_NULL); } const char* ClassLoaderData::loader_name() { @@ -495,19 +504,22 @@ ClassLoaderData* ClassLoaderDataGraph::_unloading = NULL; ClassLoaderData* ClassLoaderDataGraph::_saved_head = NULL; - // Add a new class loader data node to the list. Assign the newly created // ClassLoaderData into the java/lang/ClassLoader object as a hidden field -ClassLoaderData* ClassLoaderDataGraph::add(ClassLoaderData** cld_addr, Handle loader, TRAPS) { - // Not assigned a class loader data yet. - // Create one. - ClassLoaderData* *list_head = &_head; - ClassLoaderData* next = _head; +ClassLoaderData* ClassLoaderDataGraph::add(Handle loader, bool is_anonymous, TRAPS) { + // We need to allocate all the oops for the ClassLoaderData before allocating the + // actual ClassLoaderData object. + ClassLoaderData::Dependencies dependencies(CHECK_NULL); - bool is_anonymous = (cld_addr == NULL); - ClassLoaderData* cld = new ClassLoaderData(loader, is_anonymous); + No_Safepoint_Verifier no_safepoints; // we mustn't GC until we've installed the + // ClassLoaderData in the graph since the CLD + // contains unhandled oops - if (cld_addr != NULL) { + ClassLoaderData* cld = new ClassLoaderData(loader, is_anonymous, dependencies); + + + if (!is_anonymous) { + ClassLoaderData** cld_addr = java_lang_ClassLoader::loader_data_addr(loader()); // First, Atomically set it ClassLoaderData* old = (ClassLoaderData*) Atomic::cmpxchg_ptr(cld, cld_addr, NULL); if (old != NULL) { @@ -519,6 +531,9 @@ // We won the race, and therefore the task of adding the data to the list of // class loader data + ClassLoaderData** list_head = &_head; + ClassLoaderData* next = _head; + do { cld->set_next(next); ClassLoaderData* exchanged = (ClassLoaderData*)Atomic::cmpxchg_ptr(cld, list_head, next); @@ -531,10 +546,6 @@ cld->loader_name()); tty->print_cr("]"); } - // Create dependencies after the CLD is added to the list. Otherwise, - // the GC GC will not find the CLD and the _class_loader field will - // not be updated. - cld->init_dependencies(CHECK_NULL); return cld; } next = exchanged; @@ -665,6 +676,8 @@ dead->unload(); data = data->next(); // Remove from loader list. + // This class loader data will no longer be found + // in the ClassLoaderDataGraph. if (prev != NULL) { prev->set_next(data); } else { @@ -686,6 +699,7 @@ next = purge_me->next(); delete purge_me; } + Metaspace::purge(); } // CDS support diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/classfile/classLoaderData.hpp --- a/hotspot/src/share/vm/classfile/classLoaderData.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/classfile/classLoaderData.hpp Wed May 08 23:54:45 2013 -0700 @@ -62,7 +62,7 @@ // CMS support. static ClassLoaderData* _saved_head; - static ClassLoaderData* add(ClassLoaderData** loader_data_addr, Handle class_loader, TRAPS); + static ClassLoaderData* add(Handle class_loader, bool anonymous, TRAPS); public: static ClassLoaderData* find_or_create(Handle class_loader, TRAPS); static void purge(); @@ -100,6 +100,9 @@ Thread* THREAD); public: Dependencies() : _list_head(NULL) {} + Dependencies(TRAPS) : _list_head(NULL) { + init(CHECK); + } void add(Handle dependency, TRAPS); void init(TRAPS); void oops_do(OopClosure* f); @@ -150,7 +153,7 @@ void set_next(ClassLoaderData* next) { _next = next; } ClassLoaderData* next() const { return _next; } - ClassLoaderData(Handle h_class_loader, bool is_anonymous); + ClassLoaderData(Handle h_class_loader, bool is_anonymous, Dependencies dependencies); ~ClassLoaderData(); void set_metaspace(Metaspace* m) { _metaspace = m; } @@ -190,7 +193,9 @@ static void init_null_class_loader_data() { assert(_the_null_class_loader_data == NULL, "cannot initialize twice"); assert(ClassLoaderDataGraph::_head == NULL, "cannot initialize twice"); - _the_null_class_loader_data = new ClassLoaderData((oop)NULL, false); + + // We explicitly initialize the Dependencies object at a later phase in the initialization + _the_null_class_loader_data = new ClassLoaderData((oop)NULL, false, Dependencies()); ClassLoaderDataGraph::_head = _the_null_class_loader_data; assert(_the_null_class_loader_data->is_the_null_class_loader_data(), "Must be"); if (DumpSharedSpaces) { diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/classfile/classLoaderData.inline.hpp --- a/hotspot/src/share/vm/classfile/classLoaderData.inline.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/classfile/classLoaderData.inline.hpp Wed May 08 23:54:45 2013 -0700 @@ -43,10 +43,9 @@ assert(loader() != NULL,"Must be a class loader"); // Gets the class loader data out of the java/lang/ClassLoader object, if non-null // it's already in the loader_data, so no need to add - ClassLoaderData** loader_data_addr = java_lang_ClassLoader::loader_data_addr(loader()); - ClassLoaderData* loader_data_id = *loader_data_addr; - if (loader_data_id) { - return loader_data_id; + ClassLoaderData* loader_data= java_lang_ClassLoader::loader_data(loader()); + if (loader_data) { + return loader_data; } - return ClassLoaderDataGraph::add(loader_data_addr, loader, THREAD); + return ClassLoaderDataGraph::add(loader, false, THREAD); } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/classfile/dictionary.cpp --- a/hotspot/src/share/vm/classfile/dictionary.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/classfile/dictionary.cpp Wed May 08 23:54:45 2013 -0700 @@ -27,7 +27,6 @@ #include "classfile/systemDictionary.hpp" #include "oops/oop.inline.hpp" #include "prims/jvmtiRedefineClassesTrace.hpp" -#include "services/classLoadingService.hpp" #include "utilities/hashtable.inline.hpp" @@ -156,19 +155,7 @@ if (k_def_class_loader_data == loader_data) { // This is the defining entry, so the referred class is about // to be unloaded. - // Notify the debugger and clean up the class. class_was_unloaded = true; - // notify the debugger - if (JvmtiExport::should_post_class_unload()) { - JvmtiExport::post_class_unload(ik); - } - - // notify ClassLoadingService of class unload - ClassLoadingService::notify_class_unloaded(ik); - - // Clean up C heap - ik->release_C_heap_structures(); - ik->constants()->release_C_heap_structures(); } // Also remove this system dictionary entry. purge_entry = true; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/classfile/javaClasses.cpp --- a/hotspot/src/share/vm/classfile/javaClasses.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/classfile/javaClasses.cpp Wed May 08 23:54:45 2013 -0700 @@ -315,14 +315,18 @@ return string; } -jchar* java_lang_String::as_unicode_string(oop java_string, int& length) { +jchar* java_lang_String::as_unicode_string(oop java_string, int& length, TRAPS) { typeArrayOop value = java_lang_String::value(java_string); int offset = java_lang_String::offset(java_string); length = java_lang_String::length(java_string); - jchar* result = NEW_RESOURCE_ARRAY(jchar, length); - for (int index = 0; index < length; index++) { - result[index] = value->char_at(index + offset); + jchar* result = NEW_RESOURCE_ARRAY_RETURN_NULL(jchar, length); + if (result != NULL) { + for (int index = 0; index < length; index++) { + result[index] = value->char_at(index + offset); + } + } else { + THROW_MSG_0(vmSymbols::java_lang_OutOfMemoryError(), "could not allocate Unicode string"); } return result; } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/classfile/javaClasses.hpp --- a/hotspot/src/share/vm/classfile/javaClasses.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/classfile/javaClasses.hpp Wed May 08 23:54:45 2013 -0700 @@ -153,7 +153,7 @@ static char* as_utf8_string(oop java_string, char* buf, int buflen); static char* as_utf8_string(oop java_string, int start, int len); static char* as_platform_dependent_str(Handle java_string, TRAPS); - static jchar* as_unicode_string(oop java_string, int& length); + static jchar* as_unicode_string(oop java_string, int& length, TRAPS); // produce an ascii string with all other values quoted using \u#### static char* as_quoted_ascii(oop java_string); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/classfile/symbolTable.cpp --- a/hotspot/src/share/vm/classfile/symbolTable.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/classfile/symbolTable.cpp Wed May 08 23:54:45 2013 -0700 @@ -735,7 +735,7 @@ ResourceMark rm(THREAD); int length; Handle h_string (THREAD, string); - jchar* chars = java_lang_String::as_unicode_string(string, length); + jchar* chars = java_lang_String::as_unicode_string(string, length, CHECK_NULL); oop result = intern(h_string, chars, length, CHECK_NULL); return result; } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/code/codeCache.cpp --- a/hotspot/src/share/vm/code/codeCache.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/code/codeCache.cpp Wed May 08 23:54:45 2013 -0700 @@ -463,8 +463,10 @@ } #endif //PRODUCT - -nmethod* CodeCache::find_and_remove_saved_code(Method* m) { +/** + * Remove and return nmethod from the saved code list in order to reanimate it. + */ +nmethod* CodeCache::reanimate_saved_code(Method* m) { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); nmethod* saved = _saved_nmethods; nmethod* prev = NULL; @@ -479,7 +481,7 @@ saved->set_speculatively_disconnected(false); saved->set_saved_nmethod_link(NULL); if (PrintMethodFlushing) { - saved->print_on(tty, " ### nmethod is reconnected\n"); + saved->print_on(tty, " ### nmethod is reconnected"); } if (LogCompilation && (xtty != NULL)) { ttyLocker ttyl; @@ -496,6 +498,9 @@ return NULL; } +/** + * Remove nmethod from the saved code list in order to discard it permanently + */ void CodeCache::remove_saved_code(nmethod* nm) { // For conc swpr this will be called with CodeCache_lock taken by caller assert_locked_or_safepoint(CodeCache_lock); @@ -529,7 +534,7 @@ nm->set_saved_nmethod_link(_saved_nmethods); _saved_nmethods = nm; if (PrintMethodFlushing) { - nm->print_on(tty, " ### nmethod is speculatively disconnected\n"); + nm->print_on(tty, " ### nmethod is speculatively disconnected"); } if (LogCompilation && (xtty != NULL)) { ttyLocker ttyl; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/code/codeCache.hpp --- a/hotspot/src/share/vm/code/codeCache.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/code/codeCache.hpp Wed May 08 23:54:45 2013 -0700 @@ -57,7 +57,7 @@ static int _number_of_nmethods_with_dependencies; static bool _needs_cache_clean; static nmethod* _scavenge_root_nmethods; // linked via nm->scavenge_root_link() - static nmethod* _saved_nmethods; // linked via nm->saved_nmethod_look() + static nmethod* _saved_nmethods; // Linked list of speculatively disconnected nmethods. static void verify_if_often() PRODUCT_RETURN; @@ -168,7 +168,7 @@ static void set_needs_cache_clean(bool v) { _needs_cache_clean = v; } static void clear_inline_caches(); // clear all inline caches - static nmethod* find_and_remove_saved_code(Method* m); + static nmethod* reanimate_saved_code(Method* m); static void remove_saved_code(nmethod* nm); static void speculatively_disconnect(nmethod* nm); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/code/compiledIC.cpp --- a/hotspot/src/share/vm/code/compiledIC.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/code/compiledIC.cpp Wed May 08 23:54:45 2013 -0700 @@ -45,25 +45,6 @@ // Every time a compiled IC is changed or its type is being accessed, // either the CompiledIC_lock must be set or we must be at a safe point. - -// Release the CompiledICHolder* associated with this call site is there is one. -void CompiledIC::cleanup_call_site(virtual_call_Relocation* call_site) { - // This call site might have become stale so inspect it carefully. - NativeCall* call = nativeCall_at(call_site->addr()); - if (is_icholder_entry(call->destination())) { - NativeMovConstReg* value = nativeMovConstReg_at(call_site->cached_value()); - InlineCacheBuffer::queue_for_release((CompiledICHolder*)value->data()); - } -} - - -bool CompiledIC::is_icholder_call_site(virtual_call_Relocation* call_site) { - // This call site might have become stale so inspect it carefully. - NativeCall* call = nativeCall_at(call_site->addr()); - return is_icholder_entry(call->destination()); -} - - //----------------------------------------------------------------------------- // Low-level access to an inline cache. Private, since they might not be // MT-safe to use. @@ -488,33 +469,6 @@ return (cb != NULL && cb->is_adapter_blob()); } - -CompiledIC::CompiledIC(nmethod* nm, NativeCall* call) - : _ic_call(call) -{ - address ic_call = call->instruction_address(); - - assert(ic_call != NULL, "ic_call address must be set"); - assert(nm != NULL, "must pass nmethod"); - assert(nm->contains(ic_call), "must be in nmethod"); - - // search for the ic_call at the given address - RelocIterator iter(nm, ic_call, ic_call+1); - bool ret = iter.next(); - assert(ret == true, "relocInfo must exist at this address"); - assert(iter.addr() == ic_call, "must find ic_call"); - if (iter.type() == relocInfo::virtual_call_type) { - virtual_call_Relocation* r = iter.virtual_call_reloc(); - _is_optimized = false; - _value = nativeMovConstReg_at(r->cached_value()); - } else { - assert(iter.type() == relocInfo::opt_virtual_call_type, "must be a virtual call"); - _is_optimized = true; - _value = NULL; -} -} - - // ---------------------------------------------------------------------------- void CompiledStaticCall::set_to_clean() { @@ -549,33 +503,6 @@ return nm->stub_contains(destination()); } - -void CompiledStaticCall::set_to_interpreted(methodHandle callee, address entry) { - address stub=find_stub(); - guarantee(stub != NULL, "stub not found"); - - if (TraceICs) { - ResourceMark rm; - tty->print_cr("CompiledStaticCall@" INTPTR_FORMAT ": set_to_interpreted %s", - instruction_address(), - callee->name_and_sig_as_C_string()); - } - - NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); // creation also verifies the object - NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); - - assert(method_holder->data() == 0 || method_holder->data() == (intptr_t)callee(), "a) MT-unsafe modification of inline cache"); - assert(jump->jump_destination() == (address)-1 || jump->jump_destination() == entry, "b) MT-unsafe modification of inline cache"); - - // Update stub - method_holder->set_data((intptr_t)callee()); - jump->set_jump_destination(entry); - - // Update jump to call - set_destination_mt_safe(stub); -} - - void CompiledStaticCall::set(const StaticCallInfo& info) { assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), "mt unsafe call"); MutexLockerEx pl(Patching_lock, Mutex::_no_safepoint_check_flag); @@ -618,19 +545,6 @@ } } - -void CompiledStaticCall::set_stub_to_clean(static_stub_Relocation* static_stub) { - assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), "mt unsafe call"); - // Reset stub - address stub = static_stub->addr(); - assert(stub!=NULL, "stub not found"); - NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); // creation also verifies the object - NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); - method_holder->set_data(0); - jump->set_jump_destination((address)-1); -} - - address CompiledStaticCall::find_stub() { // Find reloc. information containing this call-site RelocIterator iter((nmethod*)NULL, instruction_address()); @@ -668,19 +582,16 @@ || is_optimized() || is_megamorphic(), "sanity check"); } - void CompiledIC::print() { print_compiled_ic(); tty->cr(); } - void CompiledIC::print_compiled_ic() { tty->print("Inline cache at " INTPTR_FORMAT ", calling %s " INTPTR_FORMAT " cached_value " INTPTR_FORMAT, instruction_address(), is_call_to_interpreted() ? "interpreted " : "", ic_destination(), is_optimized() ? NULL : cached_value()); } - void CompiledStaticCall::print() { tty->print("static call at " INTPTR_FORMAT " -> ", instruction_address()); if (is_clean()) { @@ -693,21 +604,4 @@ tty->cr(); } -void CompiledStaticCall::verify() { - // Verify call - NativeCall::verify(); - if (os::is_MP()) { - verify_alignment(); - } - - // Verify stub - address stub = find_stub(); - assert(stub != NULL, "no stub found for static call"); - NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); // creation also verifies the object - NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); - - // Verify state - assert(is_clean() || is_call_to_compiled() || is_call_to_interpreted(), "sanity check"); -} - -#endif +#endif // !PRODUCT diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/code/compiledIC.hpp --- a/hotspot/src/share/vm/code/compiledIC.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/code/compiledIC.hpp Wed May 08 23:54:45 2013 -0700 @@ -304,6 +304,11 @@ friend CompiledStaticCall* compiledStaticCall_at(address native_call); friend CompiledStaticCall* compiledStaticCall_at(Relocation* call_site); + // Code + static void emit_to_interp_stub(CodeBuffer &cbuf); + static int to_interp_stub_size(); + static int reloc_to_interp_stub(); + // State bool is_clean() const; bool is_call_to_compiled() const; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/code/stubs.cpp --- a/hotspot/src/share/vm/code/stubs.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/code/stubs.cpp Wed May 08 23:54:45 2013 -0700 @@ -67,7 +67,7 @@ intptr_t size = round_to(buffer_size, 2*BytesPerWord); BufferBlob* blob = BufferBlob::create(name, size); if( blob == NULL) { - vm_exit_out_of_memory(size, err_msg("CodeCache: no room for %s", name)); + vm_exit_out_of_memory(size, OOM_MALLOC_ERROR, err_msg("CodeCache: no room for %s", name)); } _stub_interface = stub_interface; _buffer_size = blob->content_size(); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/code/vtableStubs.cpp --- a/hotspot/src/share/vm/code/vtableStubs.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/code/vtableStubs.cpp Wed May 08 23:54:45 2013 -0700 @@ -60,7 +60,7 @@ const int bytes = chunk_factor * real_size + pd_code_alignment(); BufferBlob* blob = BufferBlob::create("vtable chunks", bytes); if (blob == NULL) { - vm_exit_out_of_memory(bytes, "CodeCache: no room for vtable chunks"); + vm_exit_out_of_memory(bytes, OOM_MALLOC_ERROR, "CodeCache: no room for vtable chunks"); } _chunk = blob->content_begin(); _chunk_end = _chunk + bytes; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/compiler/compileBroker.cpp --- a/hotspot/src/share/vm/compiler/compileBroker.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/compiler/compileBroker.cpp Wed May 08 23:54:45 2013 -0700 @@ -65,7 +65,7 @@ HS_DTRACE_PROBE_DECL9(hotspot, method__compile__end, char*, intptr_t, char*, intptr_t, char*, intptr_t, char*, intptr_t, bool); -#define DTRACE_METHOD_COMPILE_BEGIN_PROBE(compiler, method, comp_name) \ +#define DTRACE_METHOD_COMPILE_BEGIN_PROBE(method, comp_name) \ { \ Symbol* klass_name = (method)->klass_name(); \ Symbol* name = (method)->name(); \ @@ -77,8 +77,7 @@ signature->bytes(), signature->utf8_length()); \ } -#define DTRACE_METHOD_COMPILE_END_PROBE(compiler, method, \ - comp_name, success) \ +#define DTRACE_METHOD_COMPILE_END_PROBE(method, comp_name, success) \ { \ Symbol* klass_name = (method)->klass_name(); \ Symbol* name = (method)->name(); \ @@ -92,7 +91,7 @@ #else /* USDT2 */ -#define DTRACE_METHOD_COMPILE_BEGIN_PROBE(compiler, method, comp_name) \ +#define DTRACE_METHOD_COMPILE_BEGIN_PROBE(method, comp_name) \ { \ Symbol* klass_name = (method)->klass_name(); \ Symbol* name = (method)->name(); \ @@ -104,8 +103,7 @@ (char *) signature->bytes(), signature->utf8_length()); \ } -#define DTRACE_METHOD_COMPILE_END_PROBE(compiler, method, \ - comp_name, success) \ +#define DTRACE_METHOD_COMPILE_END_PROBE(method, comp_name, success) \ { \ Symbol* klass_name = (method)->klass_name(); \ Symbol* name = (method)->name(); \ @@ -120,8 +118,8 @@ #else // ndef DTRACE_ENABLED -#define DTRACE_METHOD_COMPILE_BEGIN_PROBE(compiler, method, comp_name) -#define DTRACE_METHOD_COMPILE_END_PROBE(compiler, method, comp_name, success) +#define DTRACE_METHOD_COMPILE_BEGIN_PROBE(method, comp_name) +#define DTRACE_METHOD_COMPILE_END_PROBE(method, comp_name, success) #endif // ndef DTRACE_ENABLED @@ -1229,7 +1227,7 @@ if (method->is_not_compilable(comp_level)) return NULL; if (UseCodeCacheFlushing) { - nmethod* saved = CodeCache::find_and_remove_saved_code(method()); + nmethod* saved = CodeCache::reanimate_saved_code(method()); if (saved != NULL) { method->set_code(method, saved); return saved; @@ -1288,9 +1286,9 @@ method->jmethod_id(); } - // If the compiler is shut off due to code cache flushing or otherwise, + // If the compiler is shut off due to code cache getting full // fail out now so blocking compiles dont hang the java thread - if (!should_compile_new_jobs() || (UseCodeCacheFlushing && CodeCache::needs_flushing())) { + if (!should_compile_new_jobs()) { CompilationPolicy::policy()->delay_compilation(method()); return NULL; } @@ -1766,8 +1764,7 @@ // Save information about this method in case of failure. set_last_compile(thread, method, is_osr, task_level); - DTRACE_METHOD_COMPILE_BEGIN_PROBE(compiler(task_level), method, - compiler_name(task_level)); + DTRACE_METHOD_COMPILE_BEGIN_PROBE(method, compiler_name(task_level)); } // Allocate a new set of JNI handles. @@ -1842,13 +1839,14 @@ } } } + // simulate crash during compilation + assert(task->compile_id() != CICrashAt, "just as planned"); } pop_jni_handle_block(); methodHandle method(thread, task->method()); - DTRACE_METHOD_COMPILE_END_PROBE(compiler(task_level), method, - compiler_name(task_level), task->is_success()); + DTRACE_METHOD_COMPILE_END_PROBE(method, compiler_name(task_level), task->is_success()); collect_statistics(thread, time, task); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Wed May 08 23:54:45 2013 -0700 @@ -2444,8 +2444,7 @@ // initial marking in checkpointRootsInitialWork has been completed if (VerifyDuringGC && GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) { - gclog_or_tty->print("Verify before initial mark: "); - Universe::verify(); + Universe::verify("Verify before initial mark: "); } { bool res = markFromRoots(false); @@ -2456,8 +2455,7 @@ case FinalMarking: if (VerifyDuringGC && GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) { - gclog_or_tty->print("Verify before re-mark: "); - Universe::verify(); + Universe::verify("Verify before re-mark: "); } checkpointRootsFinal(false, clear_all_soft_refs, init_mark_was_synchronous); @@ -2468,8 +2466,7 @@ // final marking in checkpointRootsFinal has been completed if (VerifyDuringGC && GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) { - gclog_or_tty->print("Verify before sweep: "); - Universe::verify(); + Universe::verify("Verify before sweep: "); } sweep(false); assert(_collectorState == Resizing, "Incorrect state"); @@ -2484,8 +2481,7 @@ // The heap has been resized. if (VerifyDuringGC && GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) { - gclog_or_tty->print("Verify before reset: "); - Universe::verify(); + Universe::verify("Verify before reset: "); } reset(false); assert(_collectorState == Idling, "Collector state should " @@ -2853,8 +2849,8 @@ bool failed() { return _failed; } }; -bool CMSCollector::verify_after_remark() { - gclog_or_tty->print(" [Verifying CMS Marking... "); +bool CMSCollector::verify_after_remark(bool silent) { + if (!silent) gclog_or_tty->print(" [Verifying CMS Marking... "); MutexLockerEx ml(verification_mark_bm()->lock(), Mutex::_no_safepoint_check_flag); static bool init = false; @@ -2915,7 +2911,7 @@ warning("Unrecognized value %d for CMSRemarkVerifyVariant", CMSRemarkVerifyVariant); } - gclog_or_tty->print(" done] "); + if (!silent) gclog_or_tty->print(" done] "); return true; } @@ -3426,8 +3422,9 @@ void ConcurrentMarkSweepGeneration::shrink_free_list_by(size_t bytes) { assert_locked_or_safepoint(Heap_lock); assert_lock_strong(freelistLock()); - // XXX Fix when compaction is implemented. - warning("Shrinking of CMS not yet implemented"); + if (PrintGCDetails && Verbose) { + warning("Shrinking of CMS not yet implemented"); + } return; } @@ -6010,26 +6007,23 @@ &cmsDrainMarkingStackClosure, NULL); } - verify_work_stacks_empty(); - } + } + + // This is the point where the entire marking should have completed. + verify_work_stacks_empty(); if (should_unload_classes()) { { TraceTime t("class unloading", PrintGCDetails, false, gclog_or_tty); - // Follow SystemDictionary roots and unload classes + // Unload classes and purge the SystemDictionary. bool purged_class = SystemDictionary::do_unloading(&_is_alive_closure); - // Follow CodeCache roots and unload any methods marked for unloading + // Unload nmethods. CodeCache::do_unloading(&_is_alive_closure, purged_class); - cmsDrainMarkingStackClosure.do_void(); - verify_work_stacks_empty(); - - // Update subklass/sibling/implementor links in KlassKlass descendants + // Prune dead klasses from subklass/sibling/implementor lists. Klass::clean_weak_klass_links(&_is_alive_closure); - // Nothing should have been pushed onto the working stacks. - verify_work_stacks_empty(); } { @@ -6043,11 +6037,10 @@ // Need to check if we really scanned the StringTable. if ((roots_scanning_options() & SharedHeap::SO_Strings) == 0) { TraceTime t("scrub string table", PrintGCDetails, false, gclog_or_tty); - // Now clean up stale oops in StringTable + // Delete entries for dead interned strings. StringTable::unlink(&_is_alive_closure); } - verify_work_stacks_empty(); // Restore any preserved marks as a result of mark stack or // work queue overflow restore_preserved_marks_if_any(); // done single-threaded for now diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp Wed May 08 23:54:45 2013 -0700 @@ -990,7 +990,7 @@ // debugging void verify(); - bool verify_after_remark(); + bool verify_after_remark(bool silent = VerifySilently); void verify_ok_to_terminate() const PRODUCT_RETURN; void verify_work_stacks_empty() const PRODUCT_RETURN; void verify_overflow_empty() const PRODUCT_RETURN; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp Wed May 08 23:54:45 2013 -0700 @@ -1273,10 +1273,9 @@ if (VerifyDuringGC) { HandleMark hm; // handle scope - gclog_or_tty->print(" VerifyDuringGC:(before)"); Universe::heap()->prepare_for_verify(); - Universe::verify(/* silent */ false, - /* option */ VerifyOption_G1UsePrevMarking); + Universe::verify(VerifyOption_G1UsePrevMarking, + " VerifyDuringGC:(before)"); } G1CollectorPolicy* g1p = g1h->g1_policy(); @@ -1300,10 +1299,9 @@ // Verify the heap w.r.t. the previous marking bitmap. if (VerifyDuringGC) { HandleMark hm; // handle scope - gclog_or_tty->print(" VerifyDuringGC:(overflow)"); Universe::heap()->prepare_for_verify(); - Universe::verify(/* silent */ false, - /* option */ VerifyOption_G1UsePrevMarking); + Universe::verify(VerifyOption_G1UsePrevMarking, + " VerifyDuringGC:(overflow)"); } // Clear the marking state because we will be restarting @@ -1323,10 +1321,9 @@ if (VerifyDuringGC) { HandleMark hm; // handle scope - gclog_or_tty->print(" VerifyDuringGC:(after)"); Universe::heap()->prepare_for_verify(); - Universe::verify(/* silent */ false, - /* option */ VerifyOption_G1UseNextMarking); + Universe::verify(VerifyOption_G1UseNextMarking, + " VerifyDuringGC:(after)"); } assert(!restart_for_overflow(), "sanity"); // Completely reset the marking state since marking completed @@ -1972,10 +1969,9 @@ if (VerifyDuringGC) { HandleMark hm; // handle scope - gclog_or_tty->print(" VerifyDuringGC:(before)"); Universe::heap()->prepare_for_verify(); - Universe::verify(/* silent */ false, - /* option */ VerifyOption_G1UsePrevMarking); + Universe::verify(VerifyOption_G1UsePrevMarking, + " VerifyDuringGC:(before)"); } G1CollectorPolicy* g1p = G1CollectedHeap::heap()->g1_policy(); @@ -2127,10 +2123,9 @@ if (VerifyDuringGC) { HandleMark hm; // handle scope - gclog_or_tty->print(" VerifyDuringGC:(after)"); Universe::heap()->prepare_for_verify(); - Universe::verify(/* silent */ false, - /* option */ VerifyOption_G1UsePrevMarking); + Universe::verify(VerifyOption_G1UsePrevMarking, + " VerifyDuringGC:(after)"); } g1h->verify_region_sets_optional(); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.cpp Wed May 08 23:54:45 2013 -0700 @@ -77,7 +77,7 @@ assert(delta > 0, "just checking"); if (!_vs.expand_by(delta)) { // Do better than this for Merlin - vm_exit_out_of_memory(delta, "offset table expansion"); + vm_exit_out_of_memory(delta, OOM_MMAP_ERROR, "offset table expansion"); } assert(_vs.high() == high + delta, "invalid expansion"); // Initialization of the contents is left to the diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Wed May 08 23:54:45 2013 -0700 @@ -1271,9 +1271,8 @@ if (guard && total_collections() >= VerifyGCStartAt) { double verify_start = os::elapsedTime(); HandleMark hm; // Discard invalid handles created during verification - gclog_or_tty->print(msg); prepare_for_verify(); - Universe::verify(false /* silent */, VerifyOption_G1UsePrevMarking); + Universe::verify(VerifyOption_G1UsePrevMarking, msg); verify_time_ms = (os::elapsedTime() - verify_start) * 1000; } @@ -1304,7 +1303,7 @@ print_heap_before_gc(); - size_t metadata_prev_used = MetaspaceAux::used_in_bytes(); + size_t metadata_prev_used = MetaspaceAux::allocated_used_bytes(); HRSPhaseSetter x(HRSPhaseFullGC); verify_region_sets_optional(); @@ -1425,6 +1424,7 @@ // Delete metaspaces for unloaded class loaders and clean up loader_data graph ClassLoaderDataGraph::purge(); + MetaspaceAux::verify_metrics(); // Note: since we've just done a full GC, concurrent // marking is no longer active. Therefore we need not @@ -1831,7 +1831,7 @@ if (G1ExitOnExpansionFailure && _g1_storage.uncommitted_size() >= aligned_expand_bytes) { // We had head room... - vm_exit_out_of_memory(aligned_expand_bytes, "G1 heap expansion"); + vm_exit_out_of_memory(aligned_expand_bytes, OOM_MMAP_ERROR, "G1 heap expansion"); } } return successful; @@ -1955,13 +1955,6 @@ int n_rem_sets = HeapRegionRemSet::num_par_rem_sets(); assert(n_rem_sets > 0, "Invariant."); - HeapRegionRemSetIterator** iter_arr = - NEW_C_HEAP_ARRAY(HeapRegionRemSetIterator*, n_queues, mtGC); - for (int i = 0; i < n_queues; i++) { - iter_arr[i] = new HeapRegionRemSetIterator(); - } - _rem_set_iterator = iter_arr; - _worker_cset_start_region = NEW_C_HEAP_ARRAY(HeapRegion*, n_queues, mtGC); _worker_cset_start_region_time_stamp = NEW_C_HEAP_ARRAY(unsigned int, n_queues, mtGC); @@ -3614,7 +3607,7 @@ uint array_length = g1_policy()->young_cset_region_length(); _surviving_young_words = NEW_C_HEAP_ARRAY(size_t, (size_t) array_length, mtGC); if (_surviving_young_words == NULL) { - vm_exit_out_of_memory(sizeof(size_t) * array_length, + vm_exit_out_of_memory(sizeof(size_t) * array_length, OOM_MALLOC_ERROR, "Not enough space for young surv words summary."); } memset(_surviving_young_words, 0, (size_t) array_length * sizeof(size_t)); @@ -4397,7 +4390,7 @@ PADDING_ELEM_NUM; _surviving_young_words_base = NEW_C_HEAP_ARRAY(size_t, array_length, mtGC); if (_surviving_young_words_base == NULL) - vm_exit_out_of_memory(array_length * sizeof(size_t), + vm_exit_out_of_memory(array_length * sizeof(size_t), OOM_MALLOC_ERROR, "Not enough space for young surv histo."); _surviving_young_words = _surviving_young_words_base + PADDING_ELEM_NUM; memset(_surviving_young_words, 0, (size_t) real_length * sizeof(size_t)); @@ -5079,10 +5072,9 @@ } void -G1CollectedHeap::g1_process_weak_roots(OopClosure* root_closure, - OopClosure* non_root_closure) { +G1CollectedHeap::g1_process_weak_roots(OopClosure* root_closure) { CodeBlobToOopClosure roots_in_blobs(root_closure, /*do_marking=*/ false); - SharedHeap::process_weak_roots(root_closure, &roots_in_blobs, non_root_closure); + SharedHeap::process_weak_roots(root_closure, &roots_in_blobs); } // Weak Reference Processing support diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Wed May 08 23:54:45 2013 -0700 @@ -786,9 +786,6 @@ // concurrently after the collection. DirtyCardQueueSet _dirty_card_queue_set; - // The Heap Region Rem Set Iterator. - HeapRegionRemSetIterator** _rem_set_iterator; - // The closure used to refine a single card. RefineCardTableEntryClosure* _refine_cte_cl; @@ -827,8 +824,7 @@ // Apply "blk" to all the weak roots of the system. These include // JNI weak roots, the code cache, system dictionary, symbol table, // string table, and referents of reachable weak refs. - void g1_process_weak_roots(OopClosure* root_closure, - OopClosure* non_root_closure); + void g1_process_weak_roots(OopClosure* root_closure); // Frees a non-humongous region by initializing its contents and // adding it to the free list that's passed as a parameter (this is @@ -1114,15 +1110,6 @@ G1RemSet* g1_rem_set() const { return _g1_rem_set; } ModRefBarrierSet* mr_bs() const { return _mr_bs; } - // The rem set iterator. - HeapRegionRemSetIterator* rem_set_iterator(int i) { - return _rem_set_iterator[i]; - } - - HeapRegionRemSetIterator* rem_set_iterator() { - return _rem_set_iterator[0]; - } - unsigned get_gc_time_stamp() { return _gc_time_stamp; } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp Wed May 08 23:54:45 2013 -0700 @@ -144,33 +144,28 @@ &GenMarkSweep::follow_stack_closure, NULL); - // Follow system dictionary roots and unload classes + + // This is the point where the entire marking should have completed. + assert(GenMarkSweep::_marking_stack.is_empty(), "Marking should have completed"); + + // Unload classes and purge the SystemDictionary. bool purged_class = SystemDictionary::do_unloading(&GenMarkSweep::is_alive); - assert(GenMarkSweep::_marking_stack.is_empty(), - "stack should be empty by now"); - // Follow code cache roots (has to be done after system dictionary, - // assumes all live klasses are marked) + // Unload nmethods. CodeCache::do_unloading(&GenMarkSweep::is_alive, purged_class); - GenMarkSweep::follow_stack(); - // Update subklass/sibling/implementor links of live klasses + // Prune dead klasses from subklass/sibling/implementor lists. Klass::clean_weak_klass_links(&GenMarkSweep::is_alive); - assert(GenMarkSweep::_marking_stack.is_empty(), - "stack should be empty by now"); - // Visit interned string tables and delete unmarked oops + // Delete entries for dead interned strings. StringTable::unlink(&GenMarkSweep::is_alive); + // Clean up unreferenced symbols in symbol table. SymbolTable::unlink(); - assert(GenMarkSweep::_marking_stack.is_empty(), - "stack should be empty by now"); - if (VerifyDuringGC) { HandleMark hm; // handle scope COMPILER2_PRESENT(DerivedPointerTableDeactivate dpt_deact); - gclog_or_tty->print(" VerifyDuringGC:(full)[Verifying "); Universe::heap()->prepare_for_verify(); // Note: we can verify only the heap here. When an object is // marked, the previous value of the mark word (including @@ -182,11 +177,13 @@ // fail. At the end of the GC, the orginal mark word values // (including hash values) are restored to the appropriate // objects. - Universe::heap()->verify(/* silent */ false, - /* option */ VerifyOption_G1UseMarkWord); - - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - gclog_or_tty->print_cr("]"); + if (!VerifySilently) { + gclog_or_tty->print(" VerifyDuringGC:(full)[Verifying "); + } + Universe::heap()->verify(VerifySilently, VerifyOption_G1UseMarkWord); + if (!VerifySilently) { + gclog_or_tty->print_cr("]"); + } } } @@ -308,17 +305,16 @@ sh->process_strong_roots(true, // activate StrongRootsScope false, // not scavenging. SharedHeap::SO_AllClasses, - &GenMarkSweep::adjust_root_pointer_closure, + &GenMarkSweep::adjust_pointer_closure, NULL, // do not touch code cache here &GenMarkSweep::adjust_klass_closure); assert(GenMarkSweep::ref_processor() == g1h->ref_processor_stw(), "Sanity"); - g1h->ref_processor_stw()->weak_oops_do(&GenMarkSweep::adjust_root_pointer_closure); + g1h->ref_processor_stw()->weak_oops_do(&GenMarkSweep::adjust_pointer_closure); // Now adjust pointers in remaining weak roots. (All of which should // have been cleared if they pointed to non-surviving objects.) - g1h->g1_process_weak_roots(&GenMarkSweep::adjust_root_pointer_closure, - &GenMarkSweep::adjust_pointer_closure); + g1h->g1_process_weak_roots(&GenMarkSweep::adjust_pointer_closure); GenMarkSweep::adjust_marks(); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp Wed May 08 23:54:45 2013 -0700 @@ -169,14 +169,13 @@ // _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 = _g1h->rem_set_iterator(_worker_i); - hrrs->init_iterator(iter); + HeapRegionRemSetIterator iter(hrrs); size_t card_index; // We claim cards in block so as to recude 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++) { + 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); } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp Wed May 08 23:54:45 2013 -0700 @@ -53,14 +53,14 @@ NumSeqTasks = 1 }; - CardTableModRefBS* _ct_bs; - SubTasksDone* _seq_task; - G1CollectorPolicy* _g1p; + CardTableModRefBS* _ct_bs; + SubTasksDone* _seq_task; + G1CollectorPolicy* _g1p; - ConcurrentG1Refine* _cg1r; + ConcurrentG1Refine* _cg1r; - size_t* _cards_scanned; - size_t _total_cards_scanned; + size_t* _cards_scanned; + size_t _total_cards_scanned; // Used for caching the closure that is responsible for scanning // references into the collection set. diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Wed May 08 23:54:45 2013 -0700 @@ -285,7 +285,7 @@ _fine_grain_regions = new PerRegionTablePtr[_max_fine_entries]; if (_fine_grain_regions == NULL) { - vm_exit_out_of_memory(sizeof(void*)*_max_fine_entries, + vm_exit_out_of_memory(sizeof(void*)*_max_fine_entries, OOM_MALLOC_ERROR, "Failed to allocate _fine_grain_entries."); } @@ -877,14 +877,9 @@ return _iter_state == Complete; } -void HeapRegionRemSet::init_iterator(HeapRegionRemSetIterator* iter) const { - iter->initialize(this); -} - #ifndef PRODUCT void HeapRegionRemSet::print() const { - HeapRegionRemSetIterator iter; - init_iterator(&iter); + HeapRegionRemSetIterator iter(this); size_t card_index; while (iter.has_next(card_index)) { HeapWord* card_start = @@ -928,35 +923,23 @@ //-------------------- Iteration -------------------- -HeapRegionRemSetIterator:: -HeapRegionRemSetIterator() : - _hrrs(NULL), +HeapRegionRemSetIterator:: HeapRegionRemSetIterator(const HeapRegionRemSet* hrrs) : + _hrrs(hrrs), _g1h(G1CollectedHeap::heap()), - _bosa(NULL), - _sparse_iter() { } - -void HeapRegionRemSetIterator::initialize(const HeapRegionRemSet* hrrs) { - _hrrs = hrrs; - _coarse_map = &_hrrs->_other_regions._coarse_map; - _fine_grain_regions = _hrrs->_other_regions._fine_grain_regions; - _bosa = _hrrs->bosa(); - - _is = Sparse; + _coarse_map(&hrrs->_other_regions._coarse_map), + _fine_grain_regions(hrrs->_other_regions._fine_grain_regions), + _bosa(hrrs->bosa()), + _is(Sparse), // Set these values so that we increment to the first region. - _coarse_cur_region_index = -1; - _coarse_cur_region_cur_card = (HeapRegion::CardsPerRegion-1); - - _cur_region_cur_card = 0; - - _fine_array_index = -1; - _fine_cur_prt = NULL; - - _n_yielded_coarse = 0; - _n_yielded_fine = 0; - _n_yielded_sparse = 0; - - _sparse_iter.init(&hrrs->_other_regions._sparse_table); -} + _coarse_cur_region_index(-1), + _coarse_cur_region_cur_card(HeapRegion::CardsPerRegion-1), + _cur_region_cur_card(0), + _fine_array_index(-1), + _fine_cur_prt(NULL), + _n_yielded_coarse(0), + _n_yielded_fine(0), + _n_yielded_sparse(0), + _sparse_iter(&hrrs->_other_regions._sparse_table) {} bool HeapRegionRemSetIterator::coarse_has_next(size_t& card_index) { if (_hrrs->_other_regions._n_coarse_entries == 0) return false; @@ -1209,8 +1192,7 @@ hrrs->add_reference((OopOrNarrowOopStar)hr5->bottom()); // Now, does iteration yield these three? - HeapRegionRemSetIterator iter; - hrrs->init_iterator(&iter); + HeapRegionRemSetIterator iter(hrrs); size_t sum = 0; size_t card_index; while (iter.has_next(card_index)) { diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp Wed May 08 23:54:45 2013 -0700 @@ -281,9 +281,6 @@ return (_iter_state == Unclaimed) && (_iter_claimed == 0); } - // Initialize the given iterator to iterate over this rem set. - void init_iterator(HeapRegionRemSetIterator* iter) const; - // The actual # of bytes this hr_remset takes up. size_t mem_size() { return _other_regions.mem_size() @@ -345,9 +342,9 @@ #endif }; -class HeapRegionRemSetIterator : public CHeapObj { +class HeapRegionRemSetIterator : public StackObj { - // The region over which we're iterating. + // The region RSet over which we're iterating. const HeapRegionRemSet* _hrrs; // Local caching of HRRS fields. @@ -362,8 +359,10 @@ size_t _n_yielded_coarse; size_t _n_yielded_sparse; - // If true we're iterating over the coarse table; if false the fine - // table. + // Indicates what granularity of table that we're currently iterating over. + // We start iterating over the sparse table, progress to the fine grain + // table, and then finish with the coarse table. + // See HeapRegionRemSetIterator::has_next(). enum IterState { Sparse, Fine, @@ -403,9 +402,7 @@ public: // We require an iterator to be initialized before use, so the // constructor does little. - HeapRegionRemSetIterator(); - - void initialize(const HeapRegionRemSet* hrrs); + HeapRegionRemSetIterator(const HeapRegionRemSet* hrrs); // If there remains one or more cards to be yielded, returns true and // sets "card_index" to one of those cards (which is then considered diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp Wed May 08 23:54:45 2013 -0700 @@ -35,10 +35,6 @@ #define UNROLL_CARD_LOOPS 1 -void SparsePRT::init_iterator(SparsePRTIter* sprt_iter) { - sprt_iter->init(this); -} - void SparsePRTEntry::init(RegionIdx_t region_ind) { _region_ind = region_ind; _next_index = NullEntry; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/g1/sparsePRT.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.hpp Wed May 08 23:54:45 2013 -0700 @@ -192,18 +192,11 @@ size_t compute_card_ind(CardIdx_t ci); public: - RSHashTableIter() : - _tbl_ind(RSHashTable::NullEntry), + RSHashTableIter(RSHashTable* rsht) : + _tbl_ind(RSHashTable::NullEntry), // So that first increment gets to 0. _bl_ind(RSHashTable::NullEntry), _card_ind((SparsePRTEntry::cards_num() - 1)), - _rsht(NULL) {} - - void init(RSHashTable* rsht) { - _rsht = rsht; - _tbl_ind = -1; // So that first increment gets to 0. - _bl_ind = RSHashTable::NullEntry; - _card_ind = (SparsePRTEntry::cards_num() - 1); - } + _rsht(rsht) {} bool has_next(size_t& card_index); }; @@ -284,8 +277,6 @@ static void cleanup_all(); RSHashTable* cur() const { return _cur; } - void init_iterator(SparsePRTIter* sprt_iter); - static void add_to_expanded_list(SparsePRT* sprt); static SparsePRT* get_from_expanded_list(); @@ -321,9 +312,9 @@ class SparsePRTIter: public RSHashTableIter { public: - void init(const SparsePRT* sprt) { - RSHashTableIter::init(sprt->cur()); - } + SparsePRTIter(const SparsePRT* sprt) : + RSHashTableIter(sprt->cur()) {} + bool has_next(size_t& card_index) { return RSHashTableIter::has_next(card_index); } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp Wed May 08 23:54:45 2013 -0700 @@ -567,7 +567,7 @@ MemRegion(new_start_aligned, new_end_for_commit); if (!os::commit_memory((char*)new_committed.start(), new_committed.byte_size())) { - vm_exit_out_of_memory(new_committed.byte_size(), + vm_exit_out_of_memory(new_committed.byte_size(), OOM_MMAP_ERROR, "card table expansion"); } } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskThread.cpp --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskThread.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskThread.cpp Wed May 08 23:54:45 2013 -0700 @@ -43,7 +43,7 @@ _time_stamp_index(0) { if (!os::create_thread(this, os::pgc_thread)) - vm_exit_out_of_memory(0, "Cannot create GC thread. Out of system resources."); + vm_exit_out_of_memory(0, OOM_MALLOC_ERROR, "Cannot create GC thread. Out of system resources."); if (PrintGCTaskTimeStamps) { _time_stamps = NEW_C_HEAP_ARRAY(GCTaskTimeStamp, GCTaskTimeStampEntries, mtGC); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/parallelScavenge/objectStartArray.cpp --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/objectStartArray.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/objectStartArray.cpp Wed May 08 23:54:45 2013 -0700 @@ -99,7 +99,7 @@ // Expand size_t expand_by = requested_blocks_size_in_bytes - current_blocks_size_in_bytes; if (!_virtual_space.expand_by(expand_by)) { - vm_exit_out_of_memory(expand_by, "object start array expansion"); + vm_exit_out_of_memory(expand_by, OOM_MMAP_ERROR, "object start array expansion"); } // Clear *only* the newly allocated region memset(_blocks_region.end(), clean_block, expand_by); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Wed May 08 23:54:45 2013 -0700 @@ -138,8 +138,7 @@ if (VerifyBeforeGC && heap->total_collections() >= VerifyGCStartAt) { HandleMark hm; // Discard invalid handles created during verification - gclog_or_tty->print(" VerifyBeforeGC:"); - Universe::verify(); + Universe::verify(" VerifyBeforeGC:"); } // Verify object start arrays @@ -177,7 +176,7 @@ size_t prev_used = heap->used(); // Capture metadata size before collection for sizing. - size_t metadata_prev_used = MetaspaceAux::used_in_bytes(); + size_t metadata_prev_used = MetaspaceAux::allocated_used_bytes(); // For PrintGCDetails size_t old_gen_prev_used = old_gen->used_in_bytes(); @@ -238,6 +237,7 @@ // Delete metaspaces for unloaded class loaders and clean up loader_data graph ClassLoaderDataGraph::purge(); + MetaspaceAux::verify_metrics(); BiasedLocking::restore_marks(); Threads::gc_epilogue(); @@ -340,8 +340,7 @@ if (VerifyAfterGC && heap->total_collections() >= VerifyGCStartAt) { HandleMark hm; // Discard invalid handles created during verification - gclog_or_tty->print(" VerifyAfterGC:"); - Universe::verify(); + Universe::verify(" VerifyAfterGC:"); } // Re-verify object start arrays @@ -518,23 +517,23 @@ is_alive_closure(), mark_and_push_closure(), follow_stack_closure(), NULL); } - // Follow system dictionary roots and unload classes + // This is the point where the entire marking should have completed. + assert(_marking_stack.is_empty(), "Marking should have completed"); + + // Unload classes and purge the SystemDictionary. bool purged_class = SystemDictionary::do_unloading(is_alive_closure()); - // Follow code cache roots + // Unload nmethods. CodeCache::do_unloading(is_alive_closure(), purged_class); - follow_stack(); // Flush marking stack - // Update subklass/sibling/implementor links of live klasses - Klass::clean_weak_klass_links(&is_alive); - assert(_marking_stack.is_empty(), "just drained"); + // Prune dead klasses from subklass/sibling/implementor lists. + Klass::clean_weak_klass_links(is_alive_closure()); - // Visit interned string tables and delete unmarked oops + // Delete entries for dead interned strings. StringTable::unlink(is_alive_closure()); + // Clean up unreferenced symbols in symbol table. SymbolTable::unlink(); - - assert(_marking_stack.is_empty(), "stack should be empty by now"); } @@ -583,28 +582,27 @@ ClassLoaderDataGraph::clear_claimed_marks(); // General strong roots. - Universe::oops_do(adjust_root_pointer_closure()); - JNIHandles::oops_do(adjust_root_pointer_closure()); // Global (strong) JNI handles - CLDToOopClosure adjust_from_cld(adjust_root_pointer_closure()); - Threads::oops_do(adjust_root_pointer_closure(), &adjust_from_cld, NULL); - ObjectSynchronizer::oops_do(adjust_root_pointer_closure()); - FlatProfiler::oops_do(adjust_root_pointer_closure()); - Management::oops_do(adjust_root_pointer_closure()); - JvmtiExport::oops_do(adjust_root_pointer_closure()); + Universe::oops_do(adjust_pointer_closure()); + JNIHandles::oops_do(adjust_pointer_closure()); // Global (strong) JNI handles + CLDToOopClosure adjust_from_cld(adjust_pointer_closure()); + Threads::oops_do(adjust_pointer_closure(), &adjust_from_cld, NULL); + ObjectSynchronizer::oops_do(adjust_pointer_closure()); + FlatProfiler::oops_do(adjust_pointer_closure()); + Management::oops_do(adjust_pointer_closure()); + JvmtiExport::oops_do(adjust_pointer_closure()); // SO_AllClasses - SystemDictionary::oops_do(adjust_root_pointer_closure()); - ClassLoaderDataGraph::oops_do(adjust_root_pointer_closure(), adjust_klass_closure(), true); - //CodeCache::scavenge_root_nmethods_oops_do(adjust_root_pointer_closure()); + SystemDictionary::oops_do(adjust_pointer_closure()); + ClassLoaderDataGraph::oops_do(adjust_pointer_closure(), adjust_klass_closure(), true); // Now adjust pointers in remaining weak roots. (All of which should // have been cleared if they pointed to non-surviving objects.) // Global (weak) JNI handles - JNIHandles::weak_oops_do(&always_true, adjust_root_pointer_closure()); + JNIHandles::weak_oops_do(&always_true, adjust_pointer_closure()); CodeCache::oops_do(adjust_pointer_closure()); - StringTable::oops_do(adjust_root_pointer_closure()); - ref_processor()->weak_oops_do(adjust_root_pointer_closure()); - PSScavenge::reference_processor()->weak_oops_do(adjust_root_pointer_closure()); + StringTable::oops_do(adjust_pointer_closure()); + ref_processor()->weak_oops_do(adjust_pointer_closure()); + PSScavenge::reference_processor()->weak_oops_do(adjust_pointer_closure()); adjust_marks(); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp Wed May 08 23:54:45 2013 -0700 @@ -44,7 +44,6 @@ static KlassClosure* follow_klass_closure() { return &MarkSweep::follow_klass_closure; } static VoidClosure* follow_stack_closure() { return (VoidClosure*)&MarkSweep::follow_stack_closure; } static OopClosure* adjust_pointer_closure() { return (OopClosure*)&MarkSweep::adjust_pointer_closure; } - static OopClosure* adjust_root_pointer_closure() { return (OopClosure*)&MarkSweep::adjust_root_pointer_closure; } static KlassClosure* adjust_klass_closure() { return &MarkSweep::adjust_klass_closure; } static BoolObjectClosure* is_alive_closure() { return (BoolObjectClosure*)&MarkSweep::is_alive; } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Wed May 08 23:54:45 2013 -0700 @@ -787,12 +787,11 @@ void PSParallelCompact::KeepAliveClosure::do_oop(oop* p) { PSParallelCompact::KeepAliveClosure::do_oop_work(p); } void PSParallelCompact::KeepAliveClosure::do_oop(narrowOop* p) { PSParallelCompact::KeepAliveClosure::do_oop_work(p); } -PSParallelCompact::AdjustPointerClosure PSParallelCompact::_adjust_root_pointer_closure(true); -PSParallelCompact::AdjustPointerClosure PSParallelCompact::_adjust_pointer_closure(false); +PSParallelCompact::AdjustPointerClosure PSParallelCompact::_adjust_pointer_closure; PSParallelCompact::AdjustKlassClosure PSParallelCompact::_adjust_klass_closure; -void PSParallelCompact::AdjustPointerClosure::do_oop(oop* p) { adjust_pointer(p, _is_root); } -void PSParallelCompact::AdjustPointerClosure::do_oop(narrowOop* p) { adjust_pointer(p, _is_root); } +void PSParallelCompact::AdjustPointerClosure::do_oop(oop* p) { adjust_pointer(p); } +void PSParallelCompact::AdjustPointerClosure::do_oop(narrowOop* p) { adjust_pointer(p); } void PSParallelCompact::FollowStackClosure::do_void() { _compaction_manager->follow_marking_stacks(); } @@ -805,7 +804,7 @@ klass->oops_do(_mark_and_push_closure); } void PSParallelCompact::AdjustKlassClosure::do_klass(Klass* klass) { - klass->oops_do(&PSParallelCompact::_adjust_root_pointer_closure); + klass->oops_do(&PSParallelCompact::_adjust_pointer_closure); } void PSParallelCompact::post_initialize() { @@ -892,7 +891,7 @@ _heap_used = heap->used(); _young_gen_used = heap->young_gen()->used_in_bytes(); _old_gen_used = heap->old_gen()->used_in_bytes(); - _metadata_used = MetaspaceAux::used_in_bytes(); + _metadata_used = MetaspaceAux::allocated_used_bytes(); }; size_t heap_used() const { return _heap_used; } @@ -967,8 +966,7 @@ if (VerifyBeforeGC && heap->total_collections() >= VerifyGCStartAt) { HandleMark hm; // Discard invalid handles created during verification - gclog_or_tty->print(" VerifyBeforeGC:"); - Universe::verify(); + Universe::verify(" VerifyBeforeGC:"); } // Verify object start arrays @@ -1027,6 +1025,7 @@ // Delete metaspaces for unloaded class loaders and clean up loader_data graph ClassLoaderDataGraph::purge(); + MetaspaceAux::verify_metrics(); Threads::gc_epilogue(); CodeCache::gc_epilogue(); @@ -2168,8 +2167,7 @@ if (VerifyAfterGC && heap->total_collections() >= VerifyGCStartAt) { HandleMark hm; // Discard invalid handles created during verification - gclog_or_tty->print(" VerifyAfterGC:"); - Universe::verify(); + Universe::verify(" VerifyAfterGC:"); } // Re-verify object start arrays @@ -2356,22 +2354,24 @@ } TraceTime tm_c("class unloading", print_phases(), true, gclog_or_tty); + + // This is the point where the entire marking should have completed. + assert(cm->marking_stacks_empty(), "Marking should have completed"); + // Follow system dictionary roots and unload classes. bool purged_class = SystemDictionary::do_unloading(is_alive_closure()); - // Follow code cache roots. + // Unload nmethods. CodeCache::do_unloading(is_alive_closure(), purged_class); - cm->follow_marking_stacks(); // Flush marking stack. - - // Update subklass/sibling/implementor links of live klasses + + // Prune dead klasses from subklass/sibling/implementor lists. Klass::clean_weak_klass_links(is_alive_closure()); - // Visit interned string tables and delete unmarked oops + // Delete entries for dead interned strings. StringTable::unlink(is_alive_closure()); + // Clean up unreferenced symbols in symbol table. SymbolTable::unlink(); - - assert(cm->marking_stacks_empty(), "marking stacks should be empty"); } void PSParallelCompact::follow_klass(ParCompactionManager* cm, Klass* klass) { @@ -2398,7 +2398,7 @@ void PSParallelCompact::adjust_class_loader(ParCompactionManager* cm, ClassLoaderData* cld) { - cld->oops_do(PSParallelCompact::adjust_root_pointer_closure(), + cld->oops_do(PSParallelCompact::adjust_pointer_closure(), PSParallelCompact::adjust_klass_closure(), true); } @@ -2419,32 +2419,31 @@ ClassLoaderDataGraph::clear_claimed_marks(); // General strong roots. - Universe::oops_do(adjust_root_pointer_closure()); - JNIHandles::oops_do(adjust_root_pointer_closure()); // Global (strong) JNI handles - CLDToOopClosure adjust_from_cld(adjust_root_pointer_closure()); - Threads::oops_do(adjust_root_pointer_closure(), &adjust_from_cld, NULL); - ObjectSynchronizer::oops_do(adjust_root_pointer_closure()); - FlatProfiler::oops_do(adjust_root_pointer_closure()); - Management::oops_do(adjust_root_pointer_closure()); - JvmtiExport::oops_do(adjust_root_pointer_closure()); + Universe::oops_do(adjust_pointer_closure()); + JNIHandles::oops_do(adjust_pointer_closure()); // Global (strong) JNI handles + CLDToOopClosure adjust_from_cld(adjust_pointer_closure()); + Threads::oops_do(adjust_pointer_closure(), &adjust_from_cld, NULL); + ObjectSynchronizer::oops_do(adjust_pointer_closure()); + FlatProfiler::oops_do(adjust_pointer_closure()); + Management::oops_do(adjust_pointer_closure()); + JvmtiExport::oops_do(adjust_pointer_closure()); // SO_AllClasses - SystemDictionary::oops_do(adjust_root_pointer_closure()); - ClassLoaderDataGraph::oops_do(adjust_root_pointer_closure(), adjust_klass_closure(), true); + SystemDictionary::oops_do(adjust_pointer_closure()); + ClassLoaderDataGraph::oops_do(adjust_pointer_closure(), adjust_klass_closure(), true); // Now adjust pointers in remaining weak roots. (All of which should // have been cleared if they pointed to non-surviving objects.) // Global (weak) JNI handles - JNIHandles::weak_oops_do(&always_true, adjust_root_pointer_closure()); + JNIHandles::weak_oops_do(&always_true, adjust_pointer_closure()); CodeCache::oops_do(adjust_pointer_closure()); - StringTable::oops_do(adjust_root_pointer_closure()); - ref_processor()->weak_oops_do(adjust_root_pointer_closure()); + StringTable::oops_do(adjust_pointer_closure()); + ref_processor()->weak_oops_do(adjust_pointer_closure()); // Roots were visited so references into the young gen in roots // may have been scanned. Process them also. // Should the reference processor have a span that excludes // young gen objects? - PSScavenge::reference_processor()->weak_oops_do( - adjust_root_pointer_closure()); + PSScavenge::reference_processor()->weak_oops_do(adjust_pointer_closure()); } void PSParallelCompact::enqueue_region_draining_tasks(GCTaskQueue* q, diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp Wed May 08 23:54:45 2013 -0700 @@ -799,16 +799,6 @@ virtual void do_oop(narrowOop* p); }; - // Current unused - class FollowRootClosure: public OopsInGenClosure { - private: - ParCompactionManager* _compaction_manager; - public: - FollowRootClosure(ParCompactionManager* cm) : _compaction_manager(cm) { } - virtual void do_oop(oop* p); - virtual void do_oop(narrowOop* p); - }; - class FollowStackClosure: public VoidClosure { private: ParCompactionManager* _compaction_manager; @@ -818,10 +808,7 @@ }; class AdjustPointerClosure: public OopClosure { - private: - bool _is_root; public: - AdjustPointerClosure(bool is_root) : _is_root(is_root) { } virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); // do not walk from thread stacks to the code cache on this phase @@ -838,7 +825,6 @@ friend class AdjustPointerClosure; friend class AdjustKlassClosure; friend class FollowKlassClosure; - friend class FollowRootClosure; friend class InstanceClassLoaderKlass; friend class RefProcTaskProxy; @@ -853,7 +839,6 @@ static IsAliveClosure _is_alive_closure; static SpaceInfo _space_info[last_space_id]; static bool _print_phases; - static AdjustPointerClosure _adjust_root_pointer_closure; static AdjustPointerClosure _adjust_pointer_closure; static AdjustKlassClosure _adjust_klass_closure; @@ -889,9 +874,6 @@ static void marking_phase(ParCompactionManager* cm, bool maximum_heap_compaction); - template static inline void adjust_pointer(T* p, bool is_root); - static void adjust_root_pointer(oop* p) { adjust_pointer(p, true); } - template static inline void follow_root(ParCompactionManager* cm, T* p); @@ -1046,7 +1028,6 @@ // Closure accessors static OopClosure* adjust_pointer_closure() { return (OopClosure*)&_adjust_pointer_closure; } - static OopClosure* adjust_root_pointer_closure() { return (OopClosure*)&_adjust_root_pointer_closure; } static KlassClosure* adjust_klass_closure() { return (KlassClosure*)&_adjust_klass_closure; } static BoolObjectClosure* is_alive_closure() { return (BoolObjectClosure*)&_is_alive_closure; } @@ -1067,6 +1048,7 @@ // Check mark and maybe push on marking stack template static inline void mark_and_push(ParCompactionManager* cm, T* p); + template static inline void adjust_pointer(T* p); static void follow_klass(ParCompactionManager* cm, Klass* klass); static void adjust_klass(ParCompactionManager* cm, Klass* klass); @@ -1151,9 +1133,6 @@ static ParMarkBitMap* mark_bitmap() { return &_mark_bitmap; } static ParallelCompactData& summary_data() { return _summary_data; } - static inline void adjust_pointer(oop* p) { adjust_pointer(p, false); } - static inline void adjust_pointer(narrowOop* p) { adjust_pointer(p, false); } - // Reference Processing static ReferenceProcessor* const ref_processor() { return _ref_processor; } @@ -1230,7 +1209,7 @@ } template -inline void PSParallelCompact::adjust_pointer(T* p, bool isroot) { +inline void PSParallelCompact::adjust_pointer(T* p) { T heap_oop = oopDesc::load_heap_oop(p); if (!oopDesc::is_null(heap_oop)) { oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp Wed May 08 23:54:45 2013 -0700 @@ -314,8 +314,7 @@ if (VerifyBeforeGC && heap->total_collections() >= VerifyGCStartAt) { HandleMark hm; // Discard invalid handles created during verification - gclog_or_tty->print(" VerifyBeforeGC:"); - Universe::verify(); + Universe::verify(" VerifyBeforeGC:"); } { @@ -638,8 +637,7 @@ if (VerifyAfterGC && heap->total_collections() >= VerifyGCStartAt) { HandleMark hm; // Discard invalid handles created during verification - gclog_or_tty->print(" VerifyAfterGC:"); - Universe::verify(); + Universe::verify(" VerifyAfterGC:"); } heap->print_heap_after_gc(); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/shared/markSweep.cpp --- a/hotspot/src/share/vm/gc_implementation/shared/markSweep.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/shared/markSweep.cpp Wed May 08 23:54:45 2013 -0700 @@ -81,7 +81,7 @@ } void MarkSweep::adjust_class_loader(ClassLoaderData* cld) { - cld->oops_do(&MarkSweep::adjust_root_pointer_closure, &MarkSweep::adjust_klass_closure, true); + cld->oops_do(&MarkSweep::adjust_pointer_closure, &MarkSweep::adjust_klass_closure, true); } @@ -121,11 +121,10 @@ } } -MarkSweep::AdjustPointerClosure MarkSweep::adjust_root_pointer_closure(true); -MarkSweep::AdjustPointerClosure MarkSweep::adjust_pointer_closure(false); +MarkSweep::AdjustPointerClosure MarkSweep::adjust_pointer_closure; -void MarkSweep::AdjustPointerClosure::do_oop(oop* p) { adjust_pointer(p, _is_root); } -void MarkSweep::AdjustPointerClosure::do_oop(narrowOop* p) { adjust_pointer(p, _is_root); } +void MarkSweep::AdjustPointerClosure::do_oop(oop* p) { adjust_pointer(p); } +void MarkSweep::AdjustPointerClosure::do_oop(narrowOop* p) { adjust_pointer(p); } void MarkSweep::adjust_marks() { assert( _preserved_oop_stack.size() == _preserved_mark_stack.size(), diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/shared/markSweep.hpp --- a/hotspot/src/share/vm/gc_implementation/shared/markSweep.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/shared/markSweep.hpp Wed May 08 23:54:45 2013 -0700 @@ -80,10 +80,7 @@ }; class AdjustPointerClosure: public OopsInGenClosure { - private: - bool _is_root; public: - AdjustPointerClosure(bool is_root) : _is_root(is_root) {} virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); }; @@ -146,7 +143,6 @@ static MarkAndPushClosure mark_and_push_closure; static FollowKlassClosure follow_klass_closure; static FollowStackClosure follow_stack_closure; - static AdjustPointerClosure adjust_root_pointer_closure; static AdjustPointerClosure adjust_pointer_closure; static AdjustKlassClosure adjust_klass_closure; @@ -179,12 +175,7 @@ static void adjust_marks(); // Adjust the pointers in the preserved marks table static void restore_marks(); // Restore the marks that we saved in preserve_mark - template static inline void adjust_pointer(T* p, bool isroot); - - static void adjust_root_pointer(oop* p) { adjust_pointer(p, true); } - static void adjust_pointer(oop* p) { adjust_pointer(p, false); } - static void adjust_pointer(narrowOop* p) { adjust_pointer(p, false); } - + template static inline void adjust_pointer(T* p); }; class PreservedMark VALUE_OBJ_CLASS_SPEC { diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/shared/markSweep.inline.hpp --- a/hotspot/src/share/vm/gc_implementation/shared/markSweep.inline.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/shared/markSweep.inline.hpp Wed May 08 23:54:45 2013 -0700 @@ -76,7 +76,7 @@ _objarray_stack.push(task); } -template inline void MarkSweep::adjust_pointer(T* p, bool isroot) { +template inline void MarkSweep::adjust_pointer(T* p) { T heap_oop = oopDesc::load_heap_oop(p); if (!oopDesc::is_null(heap_oop)) { oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp --- a/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp Wed May 08 23:54:45 2013 -0700 @@ -225,7 +225,10 @@ gclog_or_tty->print_cr("\nCMS full GC for Metaspace"); } heap->collect_as_vm_thread(GCCause::_metadata_GC_threshold); - _result = _loader_data->metaspace_non_null()->allocate(_size, _mdtype); + // After a GC try to allocate without expanding. Could fail + // and expansion will be tried below. + _result = + _loader_data->metaspace_non_null()->allocate(_size, _mdtype); } if (_result == NULL && !UseConcMarkSweepGC /* CMS already tried */) { // If still failing, allow the Metaspace to expand. diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/interpreter/interpreterRuntime.cpp --- a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp Wed May 08 23:54:45 2013 -0700 @@ -1052,7 +1052,7 @@ return; } if (set_handler_blob() == NULL) { - vm_exit_out_of_memory(blob_size, "native signature handlers"); + vm_exit_out_of_memory(blob_size, OOM_MALLOC_ERROR, "native signature handlers"); } BufferBlob* bb = BufferBlob::create("Signature Handler Temp Buffer", diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/allocation.cpp --- a/hotspot/src/share/vm/memory/allocation.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/allocation.cpp Wed May 08 23:54:45 2013 -0700 @@ -259,7 +259,7 @@ } if (p == NULL) p = os::malloc(bytes, mtChunk, CURRENT_PC); if (p == NULL) - vm_exit_out_of_memory(bytes, "ChunkPool::allocate"); + vm_exit_out_of_memory(bytes, OOM_MALLOC_ERROR, "ChunkPool::allocate"); return p; } @@ -371,7 +371,7 @@ default: { void *p = os::malloc(bytes, mtChunk, CALLER_PC); if (p == NULL) - vm_exit_out_of_memory(bytes, "Chunk::new"); + vm_exit_out_of_memory(bytes, OOM_MALLOC_ERROR, "Chunk::new"); return p; } } @@ -531,7 +531,7 @@ } void Arena::signal_out_of_memory(size_t sz, const char* whence) const { - vm_exit_out_of_memory(sz, whence); + vm_exit_out_of_memory(sz, OOM_MALLOC_ERROR, whence); } // Grow a new Chunk diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/allocation.hpp --- a/hotspot/src/share/vm/memory/allocation.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/allocation.hpp Wed May 08 23:54:45 2013 -0700 @@ -539,6 +539,9 @@ #define NEW_RESOURCE_ARRAY(type, size)\ (type*) resource_allocate_bytes((size) * sizeof(type)) +#define NEW_RESOURCE_ARRAY_RETURN_NULL(type, size)\ + (type*) resource_allocate_bytes((size) * sizeof(type), AllocFailStrategy::RETURN_NULL) + #define NEW_RESOURCE_ARRAY_IN_THREAD(thread, type, size)\ (type*) resource_allocate_bytes(thread, (size) * sizeof(type)) diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/allocation.inline.hpp --- a/hotspot/src/share/vm/memory/allocation.inline.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/allocation.inline.hpp Wed May 08 23:54:45 2013 -0700 @@ -58,7 +58,9 @@ #ifdef ASSERT if (PrintMallocFree) trace_heap_malloc(size, "AllocateHeap", p); #endif - if (p == NULL && alloc_failmode == AllocFailStrategy::EXIT_OOM) vm_exit_out_of_memory(size, "AllocateHeap"); + if (p == NULL && alloc_failmode == AllocFailStrategy::EXIT_OOM) { + vm_exit_out_of_memory(size, OOM_MALLOC_ERROR, "AllocateHeap"); + } return p; } @@ -68,7 +70,9 @@ #ifdef ASSERT if (PrintMallocFree) trace_heap_malloc(size, "ReallocateHeap", p); #endif - if (p == NULL && alloc_failmode == AllocFailStrategy::EXIT_OOM) vm_exit_out_of_memory(size, "ReallocateHeap"); + if (p == NULL && alloc_failmode == AllocFailStrategy::EXIT_OOM) { + vm_exit_out_of_memory(size, OOM_MALLOC_ERROR, "ReallocateHeap"); + } return p; } @@ -130,12 +134,12 @@ _addr = os::reserve_memory(_size, NULL, alignment); if (_addr == NULL) { - vm_exit_out_of_memory(_size, "Allocator (reserve)"); + vm_exit_out_of_memory(_size, OOM_MMAP_ERROR, "Allocator (reserve)"); } bool success = os::commit_memory(_addr, _size, false /* executable */); if (!success) { - vm_exit_out_of_memory(_size, "Allocator (commit)"); + vm_exit_out_of_memory(_size, OOM_MMAP_ERROR, "Allocator (commit)"); } return (E*)_addr; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/blockOffsetTable.cpp --- a/hotspot/src/share/vm/memory/blockOffsetTable.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/blockOffsetTable.cpp Wed May 08 23:54:45 2013 -0700 @@ -80,7 +80,7 @@ assert(delta > 0, "just checking"); if (!_vs.expand_by(delta)) { // Do better than this for Merlin - vm_exit_out_of_memory(delta, "offset table expansion"); + vm_exit_out_of_memory(delta, OOM_MMAP_ERROR, "offset table expansion"); } assert(_vs.high() == high + delta, "invalid expansion"); } else { diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/cardTableModRefBS.cpp --- a/hotspot/src/share/vm/memory/cardTableModRefBS.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/cardTableModRefBS.cpp Wed May 08 23:54:45 2013 -0700 @@ -116,7 +116,7 @@ _guard_region = MemRegion((HeapWord*)guard_page, _page_size); if (!os::commit_memory((char*)guard_page, _page_size, _page_size)) { // Do better than this for Merlin - vm_exit_out_of_memory(_page_size, "card table last card"); + vm_exit_out_of_memory(_page_size, OOM_MMAP_ERROR, "card table last card"); } *guard_card = last_card; @@ -292,7 +292,7 @@ if (!os::commit_memory((char*)new_committed.start(), new_committed.byte_size(), _page_size)) { // Do better than this for Merlin - vm_exit_out_of_memory(new_committed.byte_size(), + vm_exit_out_of_memory(new_committed.byte_size(), OOM_MMAP_ERROR, "card table expansion"); } // Use new_end_aligned (as opposed to new_end_for_commit) because diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/filemap.cpp --- a/hotspot/src/share/vm/memory/filemap.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/filemap.cpp Wed May 08 23:54:45 2013 -0700 @@ -238,8 +238,8 @@ void FileMapInfo::write_space(int i, Metaspace* space, bool read_only) { align_file_position(); - size_t used = space->used_words(Metaspace::NonClassType) * BytesPerWord; - size_t capacity = space->capacity_words(Metaspace::NonClassType) * BytesPerWord; + size_t used = space->used_bytes_slow(Metaspace::NonClassType); + size_t capacity = space->capacity_bytes_slow(Metaspace::NonClassType); struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[i]; write_region(i, (char*)space->bottom(), used, capacity, read_only, false); } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/genCollectedHeap.cpp --- a/hotspot/src/share/vm/memory/genCollectedHeap.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/genCollectedHeap.cpp Wed May 08 23:54:45 2013 -0700 @@ -377,7 +377,7 @@ ClearedAllSoftRefs casr(do_clear_all_soft_refs, collector_policy()); - const size_t metadata_prev_used = MetaspaceAux::used_in_bytes(); + const size_t metadata_prev_used = MetaspaceAux::allocated_used_bytes(); print_heap_before_gc(); @@ -447,8 +447,7 @@ prepare_for_verify(); prepared_for_verification = true; } - gclog_or_tty->print(" VerifyBeforeGC:"); - Universe::verify(); + Universe::verify(" VerifyBeforeGC:"); } COMPILER2_PRESENT(DerivedPointerTable::clear()); @@ -519,8 +518,7 @@ if (VerifyAfterGC && i >= VerifyGCLevel && total_collections() >= VerifyGCStartAt) { HandleMark hm; // Discard invalid handles created during verification - gclog_or_tty->print(" VerifyAfterGC:"); - Universe::verify(); + Universe::verify(" VerifyAfterGC:"); } if (PrintGCDetails) { @@ -556,6 +554,7 @@ if (complete) { // Delete metaspaces for unloaded class loaders and clean up loader_data graph ClassLoaderDataGraph::purge(); + MetaspaceAux::verify_metrics(); // Resize the metaspace capacity after full collections MetaspaceGC::compute_new_size(); update_full_collections_completed(); @@ -633,9 +632,8 @@ } void GenCollectedHeap::gen_process_weak_roots(OopClosure* root_closure, - CodeBlobClosure* code_roots, - OopClosure* non_root_closure) { - SharedHeap::process_weak_roots(root_closure, code_roots, non_root_closure); + CodeBlobClosure* code_roots) { + SharedHeap::process_weak_roots(root_closure, code_roots); // "Local" "weak" refs for (int i = 0; i < _n_gens; i++) { _gens[i]->ref_processor()->weak_oops_do(root_closure); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/genCollectedHeap.hpp --- a/hotspot/src/share/vm/memory/genCollectedHeap.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/genCollectedHeap.hpp Wed May 08 23:54:45 2013 -0700 @@ -432,8 +432,7 @@ // JNI weak roots, the code cache, system dictionary, symbol table, // string table, and referents of reachable weak refs. void gen_process_weak_roots(OopClosure* root_closure, - CodeBlobClosure* code_roots, - OopClosure* non_root_closure); + CodeBlobClosure* code_roots); // Set the saved marks of generations, if that makes sense. // In particular, if any generation might iterate over the oops diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/genMarkSweep.cpp --- a/hotspot/src/share/vm/memory/genMarkSweep.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/genMarkSweep.cpp Wed May 08 23:54:45 2013 -0700 @@ -223,23 +223,23 @@ &is_alive, &keep_alive, &follow_stack_closure, NULL); } - // Follow system dictionary roots and unload classes + // This is the point where the entire marking should have completed. + assert(_marking_stack.is_empty(), "Marking should have completed"); + + // Unload classes and purge the SystemDictionary. bool purged_class = SystemDictionary::do_unloading(&is_alive); - // Follow code cache roots + // Unload nmethods. CodeCache::do_unloading(&is_alive, purged_class); - follow_stack(); // Flush marking stack - // Update subklass/sibling/implementor links of live klasses + // Prune dead klasses from subklass/sibling/implementor lists. Klass::clean_weak_klass_links(&is_alive); - assert(_marking_stack.is_empty(), "just drained"); - // Visit interned string tables and delete unmarked oops + // Delete entries for dead interned strings. StringTable::unlink(&is_alive); + // Clean up unreferenced symbols in symbol table. SymbolTable::unlink(); - - assert(_marking_stack.is_empty(), "stack should be empty by now"); } @@ -282,11 +282,10 @@ // Need new claim bits for the pointer adjustment tracing. ClassLoaderDataGraph::clear_claimed_marks(); - // Because the two closures below are created statically, cannot + // Because the closure below is created statically, we cannot // use OopsInGenClosure constructor which takes a generation, // as the Universe has not been created when the static constructors // are run. - adjust_root_pointer_closure.set_orig_generation(gch->get_gen(level)); adjust_pointer_closure.set_orig_generation(gch->get_gen(level)); gch->gen_process_strong_roots(level, @@ -294,18 +293,17 @@ true, // activate StrongRootsScope false, // not scavenging SharedHeap::SO_AllClasses, - &adjust_root_pointer_closure, + &adjust_pointer_closure, false, // do not walk code - &adjust_root_pointer_closure, + &adjust_pointer_closure, &adjust_klass_closure); // Now adjust pointers in remaining weak roots. (All of which should // have been cleared if they pointed to non-surviving objects.) CodeBlobToOopClosure adjust_code_pointer_closure(&adjust_pointer_closure, /*do_marking=*/ false); - gch->gen_process_weak_roots(&adjust_root_pointer_closure, - &adjust_code_pointer_closure, - &adjust_pointer_closure); + gch->gen_process_weak_roots(&adjust_pointer_closure, + &adjust_code_pointer_closure); adjust_marks(); GenAdjustPointersClosure blk; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/metachunk.cpp --- a/hotspot/src/share/vm/memory/metachunk.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/metachunk.cpp Wed May 08 23:54:45 2013 -0700 @@ -28,6 +28,7 @@ #include "utilities/copy.hpp" #include "utilities/debug.hpp" +class VirtualSpaceNode; // // Future modification // @@ -45,27 +46,30 @@ // Metachunk methods -Metachunk* Metachunk::initialize(MetaWord* ptr, size_t word_size) { - // Set bottom, top, and end. Allow space for the Metachunk itself - Metachunk* chunk = (Metachunk*) ptr; - - MetaWord* chunk_bottom = ptr + _overhead; - chunk->set_bottom(ptr); - chunk->set_top(chunk_bottom); - MetaWord* chunk_end = ptr + word_size; - assert(chunk_end > chunk_bottom, "Chunk must be too small"); - chunk->set_end(chunk_end); - chunk->set_next(NULL); - chunk->set_prev(NULL); - chunk->set_word_size(word_size); +Metachunk::Metachunk(size_t word_size, + VirtualSpaceNode* container) : + _word_size(word_size), + _bottom(NULL), + _end(NULL), + _top(NULL), + _next(NULL), + _prev(NULL), + _container(container) +{ + _bottom = (MetaWord*)this; + _top = (MetaWord*)this + _overhead; + _end = (MetaWord*)this + word_size; #ifdef ASSERT - size_t data_word_size = pointer_delta(chunk_end, chunk_bottom, sizeof(MetaWord)); - Copy::fill_to_words((HeapWord*) chunk_bottom, data_word_size, metadata_chunk_initialize); + set_is_free(false); + size_t data_word_size = pointer_delta(end(), + top(), + sizeof(MetaWord)); + Copy::fill_to_words((HeapWord*) top(), + data_word_size, + metadata_chunk_initialize); #endif - return chunk; } - MetaWord* Metachunk::allocate(size_t word_size) { MetaWord* result = NULL; // If available, bump the pointer to allocate. diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/metachunk.hpp --- a/hotspot/src/share/vm/memory/metachunk.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/metachunk.hpp Wed May 08 23:54:45 2013 -0700 @@ -41,10 +41,13 @@ // | | | | // +--------------+ <- bottom ---+ ---+ +class VirtualSpaceNode; + class Metachunk VALUE_OBJ_CLASS_SPEC { // link to support lists of chunks Metachunk* _next; Metachunk* _prev; + VirtualSpaceNode* _container; MetaWord* _bottom; MetaWord* _end; @@ -61,29 +64,20 @@ // the space. static size_t _overhead; - void set_bottom(MetaWord* v) { _bottom = v; } - void set_end(MetaWord* v) { _end = v; } - void set_top(MetaWord* v) { _top = v; } - void set_word_size(size_t v) { _word_size = v; } public: -#ifdef ASSERT - Metachunk() : _bottom(NULL), _end(NULL), _top(NULL), _is_free(false), - _next(NULL), _prev(NULL) {} -#else - Metachunk() : _bottom(NULL), _end(NULL), _top(NULL), - _next(NULL), _prev(NULL) {} -#endif + Metachunk(size_t word_size , VirtualSpaceNode* container); // Used to add a Metachunk to a list of Metachunks void set_next(Metachunk* v) { _next = v; assert(v != this, "Boom");} void set_prev(Metachunk* v) { _prev = v; assert(v != this, "Boom");} + void set_container(VirtualSpaceNode* v) { _container = v; } MetaWord* allocate(size_t word_size); - static Metachunk* initialize(MetaWord* ptr, size_t word_size); // Accessors Metachunk* next() const { return _next; } Metachunk* prev() const { return _prev; } + VirtualSpaceNode* container() const { return _container; } MetaWord* bottom() const { return _bottom; } MetaWord* end() const { return _end; } MetaWord* top() const { return _top; } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/metaspace.cpp --- a/hotspot/src/share/vm/memory/metaspace.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/metaspace.cpp Wed May 08 23:54:45 2013 -0700 @@ -47,7 +47,6 @@ // the free chunk lists const bool metaspace_slow_verify = false; - // Parameters for stress mode testing const uint metadata_deallocate_a_lot_block = 10; const uint metadata_deallocate_a_lock_chunk = 3; @@ -112,6 +111,7 @@ class ChunkManager VALUE_OBJ_CLASS_SPEC { // Free list of chunks of different sizes. + // SpecializedChunk // SmallChunk // MediumChunk // HumongousChunk @@ -165,6 +165,10 @@ // for special, small, medium, and humongous chunks. static ChunkIndex list_index(size_t size); + // Remove the chunk from its freelist. It is + // expected to be on one of the _free_chunks[] lists. + void remove_chunk(Metachunk* chunk); + // Add the simple linked list of chunks to the freelist of chunks // of type index. void return_chunks(ChunkIndex index, Metachunk* chunks); @@ -215,7 +219,6 @@ void print_on(outputStream* st); }; - // Used to manage the free list of Metablocks (a block corresponds // to the allocation of a quantum of metadata). class BlockFreelist VALUE_OBJ_CLASS_SPEC { @@ -255,6 +258,8 @@ ReservedSpace _rs; VirtualSpace _virtual_space; MetaWord* _top; + // count of chunks contained in this VirtualSpace + uintx _container_count; // Convenience functions for logical bottom and end MetaWord* bottom() const { return (MetaWord*) _virtual_space.low(); } @@ -264,10 +269,19 @@ char* low() const { return virtual_space()->low(); } char* high() const { return virtual_space()->high(); } + // The first Metachunk will be allocated at the bottom of the + // VirtualSpace + Metachunk* first_chunk() { return (Metachunk*) bottom(); } + + void inc_container_count(); +#ifdef ASSERT + uint container_count_slow(); +#endif + public: VirtualSpaceNode(size_t byte_size); - VirtualSpaceNode(ReservedSpace rs) : _top(NULL), _next(NULL), _rs(rs) {} + VirtualSpaceNode(ReservedSpace rs) : _top(NULL), _next(NULL), _rs(rs), _container_count(0) {} ~VirtualSpaceNode(); // address of next available space in _virtual_space; @@ -282,15 +296,22 @@ MemRegion* reserved() { return &_reserved; } VirtualSpace* virtual_space() const { return (VirtualSpace*) &_virtual_space; } - // Returns true if "word_size" is available in the virtual space + // Returns true if "word_size" is available in the VirtualSpace bool is_available(size_t word_size) { return _top + word_size <= end(); } MetaWord* top() const { return _top; } void inc_top(size_t word_size) { _top += word_size; } + uintx container_count() { return _container_count; } + void dec_container_count(); +#ifdef ASSERT + void verify_container_count(); +#endif + // used and capacity in this single entry in the list size_t used_words_in_vs() const; size_t capacity_words_in_vs() const; + size_t free_words_in_vs() const; bool initialize(); @@ -306,6 +327,10 @@ bool expand_by(size_t words, bool pre_touch = false); bool shrink_by(size_t words); + // In preparation for deleting this node, remove all the chunks + // in the node from any freelist. + void purge(ChunkManager* chunk_manager); + #ifdef ASSERT // Debug support static void verify_virtual_space_total(); @@ -317,7 +342,7 @@ }; // byte_size is the size of the associated virtualspace. -VirtualSpaceNode::VirtualSpaceNode(size_t byte_size) : _top(NULL), _next(NULL), _rs(0) { +VirtualSpaceNode::VirtualSpaceNode(size_t byte_size) : _top(NULL), _next(NULL), _rs(0), _container_count(0) { // align up to vm allocation granularity byte_size = align_size_up(byte_size, os::vm_allocation_granularity()); @@ -341,6 +366,39 @@ MemTracker::record_virtual_memory_type((address)_rs.base(), mtClass); } +void VirtualSpaceNode::purge(ChunkManager* chunk_manager) { + Metachunk* chunk = first_chunk(); + Metachunk* invalid_chunk = (Metachunk*) top(); + while (chunk < invalid_chunk ) { + assert(chunk->is_free(), "Should be marked free"); + MetaWord* next = ((MetaWord*)chunk) + chunk->word_size(); + chunk_manager->remove_chunk(chunk); + assert(chunk->next() == NULL && + chunk->prev() == NULL, + "Was not removed from its list"); + chunk = (Metachunk*) next; + } +} + +#ifdef ASSERT +uint VirtualSpaceNode::container_count_slow() { + uint count = 0; + Metachunk* chunk = first_chunk(); + Metachunk* invalid_chunk = (Metachunk*) top(); + while (chunk < invalid_chunk ) { + MetaWord* next = ((MetaWord*)chunk) + chunk->word_size(); + // Don't count the chunks on the free lists. Those are + // still part of the VirtualSpaceNode but not currently + // counted. + if (!chunk->is_free()) { + count++; + } + chunk = (Metachunk*) next; + } + return count; +} +#endif + // List of VirtualSpaces for metadata allocation. // It has a _next link for singly linked list and a MemRegion // for total space in the VirtualSpace. @@ -390,6 +448,8 @@ VirtualSpaceList(size_t word_size); VirtualSpaceList(ReservedSpace rs); + size_t free_bytes(); + Metachunk* get_new_chunk(size_t word_size, size_t grow_chunks_by_words, size_t medium_chunk_bunch); @@ -410,14 +470,14 @@ void initialize(size_t word_size); size_t virtual_space_total() { return _virtual_space_total; } - void inc_virtual_space_total(size_t v) { - Atomic::add_ptr(v, &_virtual_space_total); - } - - size_t virtual_space_count() { return _virtual_space_count; } - void inc_virtual_space_count() { - Atomic::inc_ptr(&_virtual_space_count); - } + + void inc_virtual_space_total(size_t v); + void dec_virtual_space_total(size_t v); + void inc_virtual_space_count(); + void dec_virtual_space_count(); + + // Unlink empty VirtualSpaceNodes and free it. + void purge(); // Used and capacity in the entire list of virtual spaces. // These are global values shared by all Metaspaces @@ -520,7 +580,11 @@ bool has_small_chunk_limit() { return !vs_list()->is_class(); } // Sum of all space in allocated chunks - size_t _allocation_total; + size_t _allocated_blocks_words; + + // Sum of all allocated chunks + size_t _allocated_chunks_words; + size_t _allocated_chunks_count; // Free lists of blocks are per SpaceManager since they // are assumed to be in chunks in use by the SpaceManager @@ -576,12 +640,27 @@ size_t medium_chunk_size() { return (size_t) vs_list()->is_class() ? ClassMediumChunk : MediumChunk; } size_t medium_chunk_bunch() { return medium_chunk_size() * MediumChunkMultiple; } - size_t allocation_total() const { return _allocation_total; } - void inc_allocation_total(size_t v) { Atomic::add_ptr(v, &_allocation_total); } + size_t allocated_blocks_words() const { return _allocated_blocks_words; } + size_t allocated_blocks_bytes() const { return _allocated_blocks_words * BytesPerWord; } + size_t allocated_chunks_words() const { return _allocated_chunks_words; } + size_t allocated_chunks_count() const { return _allocated_chunks_count; } + bool is_humongous(size_t word_size) { return word_size > medium_chunk_size(); } static Mutex* expand_lock() { return _expand_lock; } + // Increment the per Metaspace and global running sums for Metachunks + // by the given size. This is used when a Metachunk to added to + // the in-use list. + void inc_size_metrics(size_t words); + // Increment the per Metaspace and global running sums Metablocks by the given + // size. This is used when a Metablock is allocated. + void inc_used_metrics(size_t words); + // Delete the portion of the running sums for this SpaceManager. That is, + // the globals running sums for the Metachunks and Metablocks are + // decremented for all the Metachunks in-use by this SpaceManager. + void dec_total_from_size_metrics(); + // Set the sizes for the initial chunks. void get_initial_chunk_sizes(Metaspace::MetaspaceType type, size_t* chunk_word_size, @@ -627,7 +706,7 @@ void verify_chunk_size(Metachunk* chunk); NOT_PRODUCT(void mangle_freed_chunks();) #ifdef ASSERT - void verify_allocation_total(); + void verify_allocated_blocks_words(); #endif }; @@ -641,6 +720,28 @@ SpaceManager::_expand_lock_name, Mutex::_allow_vm_block_flag); +void VirtualSpaceNode::inc_container_count() { + assert_lock_strong(SpaceManager::expand_lock()); + _container_count++; + assert(_container_count == container_count_slow(), + err_msg("Inconsistency in countainer_count _container_count " SIZE_FORMAT + "container_count_slow() " SIZE_FORMAT, + _container_count, container_count_slow())); +} + +void VirtualSpaceNode::dec_container_count() { + assert_lock_strong(SpaceManager::expand_lock()); + _container_count--; +} + +#ifdef ASSERT +void VirtualSpaceNode::verify_container_count() { + assert(_container_count == container_count_slow(), + err_msg("Inconsistency in countainer_count _container_count " SIZE_FORMAT + "container_count_slow() " SIZE_FORMAT, _container_count, container_count_slow())); +} +#endif + // BlockFreelist methods BlockFreelist::BlockFreelist() : _dictionary(NULL) {} @@ -701,6 +802,10 @@ VirtualSpaceNode::~VirtualSpaceNode() { _rs.release(); +#ifdef ASSERT + size_t word_size = sizeof(*this) / BytesPerWord; + Copy::fill_to_words((HeapWord*) this, word_size, 0xf1f1f1f1); +#endif } size_t VirtualSpaceNode::used_words_in_vs() const { @@ -712,6 +817,9 @@ return pointer_delta(end(), bottom(), sizeof(MetaWord)); } +size_t VirtualSpaceNode::free_words_in_vs() const { + return pointer_delta(end(), top(), sizeof(MetaWord)); +} // Allocates the chunk from the virtual space only. // This interface is also used internally for debugging. Not all @@ -733,8 +841,8 @@ // Take the space (bump top on the current virtual space). inc_top(chunk_word_size); - // Point the chunk at the space - Metachunk* result = Metachunk::initialize(chunk_limit, chunk_word_size); + // Initialize the chunk + Metachunk* result = ::new (chunk_limit) Metachunk(chunk_word_size, this); return result; } @@ -762,9 +870,11 @@ Metachunk* VirtualSpaceNode::get_chunk_vs(size_t chunk_word_size) { assert_lock_strong(SpaceManager::expand_lock()); - Metachunk* result = NULL; - - return take_from_committed(chunk_word_size); + Metachunk* result = take_from_committed(chunk_word_size); + if (result != NULL) { + inc_container_count(); + } + return result; } Metachunk* VirtualSpaceNode::get_chunk_vs_with_expand(size_t chunk_word_size) { @@ -843,6 +953,83 @@ } } +void VirtualSpaceList::inc_virtual_space_total(size_t v) { + assert_lock_strong(SpaceManager::expand_lock()); + _virtual_space_total = _virtual_space_total + v; +} +void VirtualSpaceList::dec_virtual_space_total(size_t v) { + assert_lock_strong(SpaceManager::expand_lock()); + _virtual_space_total = _virtual_space_total - v; +} + +void VirtualSpaceList::inc_virtual_space_count() { + assert_lock_strong(SpaceManager::expand_lock()); + _virtual_space_count++; +} +void VirtualSpaceList::dec_virtual_space_count() { + assert_lock_strong(SpaceManager::expand_lock()); + _virtual_space_count--; +} + +void ChunkManager::remove_chunk(Metachunk* chunk) { + size_t word_size = chunk->word_size(); + ChunkIndex index = list_index(word_size); + if (index != HumongousIndex) { + free_chunks(index)->remove_chunk(chunk); + } else { + humongous_dictionary()->remove_chunk(chunk); + } + + // Chunk is being removed from the chunks free list. + dec_free_chunks_total(chunk->capacity_word_size()); +} + +// Walk the list of VirtualSpaceNodes and delete +// nodes with a 0 container_count. Remove Metachunks in +// the node from their respective freelists. +void VirtualSpaceList::purge() { + assert_lock_strong(SpaceManager::expand_lock()); + // Don't use a VirtualSpaceListIterator because this + // list is being changed and a straightforward use of an iterator is not safe. + VirtualSpaceNode* purged_vsl = NULL; + VirtualSpaceNode* prev_vsl = virtual_space_list(); + VirtualSpaceNode* next_vsl = prev_vsl; + while (next_vsl != NULL) { + VirtualSpaceNode* vsl = next_vsl; + next_vsl = vsl->next(); + // Don't free the current virtual space since it will likely + // be needed soon. + if (vsl->container_count() == 0 && vsl != current_virtual_space()) { + // Unlink it from the list + if (prev_vsl == vsl) { + // This is the case of the current note being the first note. + assert(vsl == virtual_space_list(), "Expected to be the first note"); + set_virtual_space_list(vsl->next()); + } else { + prev_vsl->set_next(vsl->next()); + } + + vsl->purge(chunk_manager()); + dec_virtual_space_total(vsl->reserved()->word_size()); + dec_virtual_space_count(); + purged_vsl = vsl; + delete vsl; + } else { + prev_vsl = vsl; + } + } +#ifdef ASSERT + if (purged_vsl != NULL) { + // List should be stable enough to use an iterator here. + VirtualSpaceListIterator iter(virtual_space_list()); + while (iter.repeat()) { + VirtualSpaceNode* vsl = iter.get_next(); + assert(vsl != purged_vsl, "Purge of vsl failed"); + } + } +#endif +} + size_t VirtualSpaceList::used_words_sum() { size_t allocated_by_vs = 0; VirtualSpaceListIterator iter(virtual_space_list()); @@ -907,6 +1094,10 @@ link_vs(class_entry, rs.size()/BytesPerWord); } +size_t VirtualSpaceList::free_bytes() { + return virtual_space_list()->free_words_in_vs() * BytesPerWord; +} + // Allocate another meta virtual space and add it to the list. bool VirtualSpaceList::grow_vs(size_t vs_word_size) { assert_lock_strong(SpaceManager::expand_lock()); @@ -955,8 +1146,10 @@ // Get a chunk from the chunk freelist Metachunk* next = chunk_manager()->chunk_freelist_allocate(grow_chunks_by_words); - // Allocate a chunk out of the current virtual space. - if (next == NULL) { + if (next != NULL) { + next->container()->inc_container_count(); + } else { + // Allocate a chunk out of the current virtual space. next = current_virtual_space()->get_chunk_vs(grow_chunks_by_words); } @@ -1045,9 +1238,9 @@ // // After the GC the compute_new_size() for MetaspaceGC is called to // resize the capacity of the metaspaces. The current implementation -// is based on the flags MinMetaspaceFreeRatio and MaxHeapFreeRatio used +// is based on the flags MinMetaspaceFreeRatio and MaxMetaspaceFreeRatio used // to resize the Java heap by some GC's. New flags can be implemented -// if really needed. MinHeapFreeRatio is used to calculate how much +// if really needed. MinMetaspaceFreeRatio is used to calculate how much // free space is desirable in the metaspace capacity to decide how much // to increase the HWM. MaxMetaspaceFreeRatio is used to decide how much // free space is desirable in the metaspace capacity before decreasing @@ -1082,7 +1275,11 @@ } bool MetaspaceGC::should_expand(VirtualSpaceList* vsl, size_t word_size) { + + size_t committed_capacity_bytes = MetaspaceAux::allocated_capacity_bytes(); // If the user wants a limit, impose one. + size_t max_metaspace_size_bytes = MaxMetaspaceSize; + size_t metaspace_size_bytes = MetaspaceSize; if (!FLAG_IS_DEFAULT(MaxMetaspaceSize) && MetaspaceAux::reserved_in_bytes() >= MaxMetaspaceSize) { return false; @@ -1094,57 +1291,48 @@ // If this is part of an allocation after a GC, expand // unconditionally. - if(MetaspaceGC::expand_after_GC()) { + if (MetaspaceGC::expand_after_GC()) { return true; } - size_t metaspace_size_words = MetaspaceSize / BytesPerWord; + // If the capacity is below the minimum capacity, allow the // expansion. Also set the high-water-mark (capacity_until_GC) // to that minimum capacity so that a GC will not be induced // until that minimum capacity is exceeded. - if (vsl->capacity_words_sum() < metaspace_size_words || + if (committed_capacity_bytes < metaspace_size_bytes || capacity_until_GC() == 0) { - set_capacity_until_GC(metaspace_size_words); + set_capacity_until_GC(metaspace_size_bytes); return true; } else { - if (vsl->capacity_words_sum() < capacity_until_GC()) { + if (committed_capacity_bytes < capacity_until_GC()) { return true; } else { if (TraceMetadataChunkAllocation && Verbose) { gclog_or_tty->print_cr(" allocation request size " SIZE_FORMAT " capacity_until_GC " SIZE_FORMAT - " capacity_words_sum " SIZE_FORMAT - " used_words_sum " SIZE_FORMAT - " free chunks " SIZE_FORMAT - " free chunks count %d", + " allocated_capacity_bytes " SIZE_FORMAT, word_size, capacity_until_GC(), - vsl->capacity_words_sum(), - vsl->used_words_sum(), - vsl->chunk_manager()->free_chunks_total(), - vsl->chunk_manager()->free_chunks_count()); + MetaspaceAux::allocated_capacity_bytes()); } return false; } } } -// Variables are in bytes + void MetaspaceGC::compute_new_size() { assert(_shrink_factor <= 100, "invalid shrink factor"); uint current_shrink_factor = _shrink_factor; _shrink_factor = 0; - VirtualSpaceList *vsl = Metaspace::space_list(); - - size_t capacity_after_gc = vsl->capacity_bytes_sum(); - // Check to see if these two can be calculated without walking the CLDG - size_t used_after_gc = vsl->used_bytes_sum(); - size_t capacity_until_GC = vsl->capacity_bytes_sum(); - size_t free_after_gc = capacity_until_GC - used_after_gc; + // Until a faster way of calculating the "used" quantity is implemented, + // use "capacity". + const size_t used_after_gc = MetaspaceAux::allocated_capacity_bytes(); + const size_t capacity_until_GC = MetaspaceGC::capacity_until_GC(); const double minimum_free_percentage = MinMetaspaceFreeRatio / 100.0; const double maximum_used_percentage = 1.0 - minimum_free_percentage; @@ -1157,45 +1345,34 @@ MetaspaceSize); if (PrintGCDetails && Verbose) { - const double free_percentage = ((double)free_after_gc) / capacity_until_GC; gclog_or_tty->print_cr("\nMetaspaceGC::compute_new_size: "); gclog_or_tty->print_cr(" " " minimum_free_percentage: %6.2f" " maximum_used_percentage: %6.2f", minimum_free_percentage, maximum_used_percentage); - double d_free_after_gc = free_after_gc / (double) K; gclog_or_tty->print_cr(" " - " free_after_gc : %6.1fK" - " used_after_gc : %6.1fK" - " capacity_after_gc : %6.1fK" - " metaspace HWM : %6.1fK", - free_after_gc / (double) K, - used_after_gc / (double) K, - capacity_after_gc / (double) K, - capacity_until_GC / (double) K); - gclog_or_tty->print_cr(" " - " free_percentage: %6.2f", - free_percentage); + " used_after_gc : %6.1fKB", + used_after_gc / (double) K); } + size_t shrink_bytes = 0; if (capacity_until_GC < minimum_desired_capacity) { // If we have less capacity below the metaspace HWM, then // increment the HWM. size_t expand_bytes = minimum_desired_capacity - capacity_until_GC; // Don't expand unless it's significant if (expand_bytes >= MinMetaspaceExpansion) { - size_t expand_words = expand_bytes / BytesPerWord; - MetaspaceGC::inc_capacity_until_GC(expand_words); + MetaspaceGC::set_capacity_until_GC(capacity_until_GC + expand_bytes); } if (PrintGCDetails && Verbose) { - size_t new_capacity_until_GC = MetaspaceGC::capacity_until_GC_in_bytes(); + size_t new_capacity_until_GC = capacity_until_GC; gclog_or_tty->print_cr(" expanding:" - " minimum_desired_capacity: %6.1fK" - " expand_words: %6.1fK" - " MinMetaspaceExpansion: %6.1fK" - " new metaspace HWM: %6.1fK", + " minimum_desired_capacity: %6.1fKB" + " expand_bytes: %6.1fKB" + " MinMetaspaceExpansion: %6.1fKB" + " new metaspace HWM: %6.1fKB", minimum_desired_capacity / (double) K, expand_bytes / (double) K, MinMetaspaceExpansion / (double) K, @@ -1205,11 +1382,10 @@ } // No expansion, now see if we want to shrink - size_t shrink_words = 0; // We would never want to shrink more than this - size_t max_shrink_words = capacity_until_GC - minimum_desired_capacity; - assert(max_shrink_words >= 0, err_msg("max_shrink_words " SIZE_FORMAT, - max_shrink_words)); + size_t max_shrink_bytes = capacity_until_GC - minimum_desired_capacity; + assert(max_shrink_bytes >= 0, err_msg("max_shrink_bytes " SIZE_FORMAT, + max_shrink_bytes)); // Should shrinking be considered? if (MaxMetaspaceFreeRatio < 100) { @@ -1219,17 +1395,15 @@ size_t maximum_desired_capacity = (size_t)MIN2(max_tmp, double(max_uintx)); maximum_desired_capacity = MAX2(maximum_desired_capacity, MetaspaceSize); - if (PrintGC && Verbose) { + if (PrintGCDetails && Verbose) { gclog_or_tty->print_cr(" " " maximum_free_percentage: %6.2f" " minimum_used_percentage: %6.2f", maximum_free_percentage, minimum_used_percentage); gclog_or_tty->print_cr(" " - " capacity_until_GC: %6.1fK" - " minimum_desired_capacity: %6.1fK" - " maximum_desired_capacity: %6.1fK", - capacity_until_GC / (double) K, + " minimum_desired_capacity: %6.1fKB" + " maximum_desired_capacity: %6.1fKB", minimum_desired_capacity / (double) K, maximum_desired_capacity / (double) K); } @@ -1239,17 +1413,17 @@ if (capacity_until_GC > maximum_desired_capacity) { // Capacity too large, compute shrinking size - shrink_words = capacity_until_GC - maximum_desired_capacity; + shrink_bytes = capacity_until_GC - maximum_desired_capacity; // We don't want shrink all the way back to initSize if people call // System.gc(), because some programs do that between "phases" and then // we'd just have to grow the heap up again for the next phase. So we // damp the shrinking: 0% on the first call, 10% on the second call, 40% // on the third call, and 100% by the fourth call. But if we recompute // size without shrinking, it goes back to 0%. - shrink_words = shrink_words / 100 * current_shrink_factor; - assert(shrink_words <= max_shrink_words, + shrink_bytes = shrink_bytes / 100 * current_shrink_factor; + assert(shrink_bytes <= max_shrink_bytes, err_msg("invalid shrink size " SIZE_FORMAT " not <= " SIZE_FORMAT, - shrink_words, max_shrink_words)); + shrink_bytes, max_shrink_bytes)); if (current_shrink_factor == 0) { _shrink_factor = 10; } else { @@ -1263,11 +1437,11 @@ MetaspaceSize / (double) K, maximum_desired_capacity / (double) K); gclog_or_tty->print_cr(" " - " shrink_words: %.1fK" + " shrink_bytes: %.1fK" " current_shrink_factor: %d" " new shrink factor: %d" " MinMetaspaceExpansion: %.1fK", - shrink_words / (double) K, + shrink_bytes / (double) K, current_shrink_factor, _shrink_factor, MinMetaspaceExpansion / (double) K); @@ -1275,23 +1449,11 @@ } } - // Don't shrink unless it's significant - if (shrink_words >= MinMetaspaceExpansion) { - VirtualSpaceNode* csp = vsl->current_virtual_space(); - size_t available_to_shrink = csp->capacity_words_in_vs() - - csp->used_words_in_vs(); - shrink_words = MIN2(shrink_words, available_to_shrink); - csp->shrink_by(shrink_words); - MetaspaceGC::dec_capacity_until_GC(shrink_words); - if (PrintGCDetails && Verbose) { - size_t new_capacity_until_GC = MetaspaceGC::capacity_until_GC_in_bytes(); - gclog_or_tty->print_cr(" metaspace HWM: %.1fK", new_capacity_until_GC / (double) K); - } + if (shrink_bytes >= MinMetaspaceExpansion && + ((capacity_until_GC - shrink_bytes) >= MetaspaceSize)) { + MetaspaceGC::set_capacity_until_GC(capacity_until_GC - shrink_bytes); } - assert(used_after_gc <= vsl->capacity_bytes_sum(), - "sanity check"); - } // Metadebug methods @@ -1567,9 +1729,6 @@ } // Chunk is being removed from the chunks free list. dec_free_chunks_total(chunk->capacity_word_size()); -#ifdef ASSERT - chunk->set_is_free(false); -#endif } else { return NULL; } @@ -1578,6 +1737,11 @@ // Remove it from the links to this freelist chunk->set_next(NULL); chunk->set_prev(NULL); +#ifdef ASSERT + // Chunk is no longer on any freelist. Setting to false make container_count_slow() + // work. + chunk->set_is_free(false); +#endif slow_locked_verify(); return chunk; } @@ -1692,18 +1856,28 @@ } size_t SpaceManager::sum_capacity_in_chunks_in_use() const { - MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); - size_t sum = 0; - for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { - Metachunk* chunk = chunks_in_use(i); - while (chunk != NULL) { - // Just changed this sum += chunk->capacity_word_size(); - // sum += chunk->word_size() - Metachunk::overhead(); - sum += chunk->capacity_word_size(); - chunk = chunk->next(); + // For CMS use "allocated_chunks_words()" which does not need the + // Metaspace lock. For the other collectors sum over the + // lists. Use both methods as a check that "allocated_chunks_words()" + // is correct. That is, sum_capacity_in_chunks() is too expensive + // to use in the product and allocated_chunks_words() should be used + // but allow for checking that allocated_chunks_words() returns the same + // value as sum_capacity_in_chunks_in_use() which is the definitive + // answer. + if (UseConcMarkSweepGC) { + return allocated_chunks_words(); + } else { + MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); + size_t sum = 0; + for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { + Metachunk* chunk = chunks_in_use(i); + while (chunk != NULL) { + sum += chunk->capacity_word_size(); + chunk = chunk->next(); + } } + return sum; } - return sum; } size_t SpaceManager::sum_count_in_chunks_in_use() { @@ -1861,12 +2035,44 @@ SpaceManager::SpaceManager(Mutex* lock, VirtualSpaceList* vs_list) : _vs_list(vs_list), - _allocation_total(0), + _allocated_blocks_words(0), + _allocated_chunks_words(0), + _allocated_chunks_count(0), _lock(lock) { initialize(); } +void SpaceManager::inc_size_metrics(size_t words) { + assert_lock_strong(SpaceManager::expand_lock()); + // Total of allocated Metachunks and allocated Metachunks count + // for each SpaceManager + _allocated_chunks_words = _allocated_chunks_words + words; + _allocated_chunks_count++; + // Global total of capacity in allocated Metachunks + MetaspaceAux::inc_capacity(words); + // Global total of allocated Metablocks. + // used_words_slow() includes the overhead in each + // Metachunk so include it in the used when the + // Metachunk is first added (so only added once per + // Metachunk). + MetaspaceAux::inc_used(Metachunk::overhead()); +} + +void SpaceManager::inc_used_metrics(size_t words) { + // Add to the per SpaceManager total + Atomic::add_ptr(words, &_allocated_blocks_words); + // Add to the global total + MetaspaceAux::inc_used(words); +} + +void SpaceManager::dec_total_from_size_metrics() { + MetaspaceAux::dec_capacity(allocated_chunks_words()); + MetaspaceAux::dec_used(allocated_blocks_words()); + // Also deduct the overhead per Metachunk + MetaspaceAux::dec_used(allocated_chunks_count() * Metachunk::overhead()); +} + void SpaceManager::initialize() { Metadebug::init_allocation_fail_alot_count(); for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { @@ -1887,11 +2093,13 @@ assert_lock_strong(SpaceManager::expand_lock()); Metachunk* cur = chunks; - // This return chunks one at a time. If a new + // This returns chunks one at a time. If a new // class List can be created that is a base class // of FreeList then something like FreeList::prepend() // can be used in place of this loop while (cur != NULL) { + assert(cur->container() != NULL, "Container should have been set"); + cur->container()->dec_container_count(); // Capture the next link before it is changed // by the call to return_chunk_at_head(); Metachunk* next = cur->next(); @@ -1903,7 +2111,10 @@ SpaceManager::~SpaceManager() { // This call this->_lock which can't be done while holding expand_lock() - const size_t in_use_before = sum_capacity_in_chunks_in_use(); + assert(sum_capacity_in_chunks_in_use() == allocated_chunks_words(), + err_msg("sum_capacity_in_chunks_in_use() " SIZE_FORMAT + " allocated_chunks_words() " SIZE_FORMAT, + sum_capacity_in_chunks_in_use(), allocated_chunks_words())); MutexLockerEx fcl(SpaceManager::expand_lock(), Mutex::_no_safepoint_check_flag); @@ -1912,17 +2123,19 @@ chunk_manager->slow_locked_verify(); + dec_total_from_size_metrics(); + if (TraceMetadataChunkAllocation && Verbose) { gclog_or_tty->print_cr("~SpaceManager(): " PTR_FORMAT, this); locked_print_chunks_in_use_on(gclog_or_tty); } - // Mangle freed memory. - NOT_PRODUCT(mangle_freed_chunks();) + // Do not mangle freed Metachunks. The chunk size inside Metachunks + // is during the freeing of a VirtualSpaceNodes. // Have to update before the chunks_in_use lists are emptied // below. - chunk_manager->inc_free_chunks_total(in_use_before, + chunk_manager->inc_free_chunks_total(allocated_chunks_words(), sum_count_in_chunks_in_use()); // Add all the chunks in use by this space manager @@ -1978,6 +2191,7 @@ " granularity %d", humongous_chunks->word_size(), HumongousChunkGranularity)); Metachunk* next_humongous_chunks = humongous_chunks->next(); + humongous_chunks->container()->dec_container_count(); chunk_manager->humongous_dictionary()->return_chunk(humongous_chunks); humongous_chunks = next_humongous_chunks; } @@ -1987,7 +2201,6 @@ chunk_manager->humongous_dictionary()->total_count(), chunk_size_name(HumongousIndex)); } - set_chunks_in_use(HumongousIndex, NULL); chunk_manager->slow_locked_verify(); } @@ -2067,12 +2280,17 @@ assert(new_chunk->word_size() > medium_chunk_size(), "List inconsistency"); } + // Add to the running sum of capacity + inc_size_metrics(new_chunk->word_size()); + assert(new_chunk->is_empty(), "Not ready for reuse"); if (TraceMetadataChunkAllocation && Verbose) { gclog_or_tty->print("SpaceManager::add_chunk: %d) ", sum_count_in_chunks_in_use()); new_chunk->print_on(gclog_or_tty); - vs_list()->chunk_manager()->locked_print_free_chunks(tty); + if (vs_list() != NULL) { + vs_list()->chunk_manager()->locked_print_free_chunks(tty); + } } } @@ -2143,7 +2361,7 @@ // of memory if this returns null. if (DumpSharedSpaces) { assert(current_chunk() != NULL, "should never happen"); - inc_allocation_total(word_size); + inc_used_metrics(word_size); return current_chunk()->allocate(word_size); // caller handles null result } if (current_chunk() != NULL) { @@ -2154,7 +2372,7 @@ result = grow_and_allocate(word_size); } if (result > 0) { - inc_allocation_total(word_size); + inc_used_metrics(word_size); assert(result != (MetaWord*) chunks_in_use(MediumIndex), "Head of the list is being allocated"); } @@ -2188,20 +2406,14 @@ } #ifdef ASSERT -void SpaceManager::verify_allocation_total() { +void SpaceManager::verify_allocated_blocks_words() { // Verification is only guaranteed at a safepoint. - if (SafepointSynchronize::is_at_safepoint()) { - gclog_or_tty->print_cr("Chunk " PTR_FORMAT " allocation_total " SIZE_FORMAT - " sum_used_in_chunks_in_use " SIZE_FORMAT, - this, - allocation_total(), - sum_used_in_chunks_in_use()); - } - MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); - assert(allocation_total() == sum_used_in_chunks_in_use(), + assert(SafepointSynchronize::is_at_safepoint() || !Universe::is_fully_initialized(), + "Verification can fail if the applications is running"); + assert(allocated_blocks_words() == sum_used_in_chunks_in_use(), err_msg("allocation total is not consistent " SIZE_FORMAT " vs " SIZE_FORMAT, - allocation_total(), sum_used_in_chunks_in_use())); + allocated_blocks_words(), sum_used_in_chunks_in_use())); } #endif @@ -2257,14 +2469,65 @@ // MetaspaceAux -size_t MetaspaceAux::used_in_bytes(Metaspace::MetadataType mdtype) { + +size_t MetaspaceAux::_allocated_capacity_words = 0; +size_t MetaspaceAux::_allocated_used_words = 0; + +size_t MetaspaceAux::free_bytes() { + size_t result = 0; + if (Metaspace::class_space_list() != NULL) { + result = result + Metaspace::class_space_list()->free_bytes(); + } + if (Metaspace::space_list() != NULL) { + result = result + Metaspace::space_list()->free_bytes(); + } + return result; +} + +void MetaspaceAux::dec_capacity(size_t words) { + assert_lock_strong(SpaceManager::expand_lock()); + assert(words <= _allocated_capacity_words, + err_msg("About to decrement below 0: words " SIZE_FORMAT + " is greater than _allocated_capacity_words " SIZE_FORMAT, + words, _allocated_capacity_words)); + _allocated_capacity_words = _allocated_capacity_words - words; +} + +void MetaspaceAux::inc_capacity(size_t words) { + assert_lock_strong(SpaceManager::expand_lock()); + // Needs to be atomic + _allocated_capacity_words = _allocated_capacity_words + words; +} + +void MetaspaceAux::dec_used(size_t words) { + assert(words <= _allocated_used_words, + err_msg("About to decrement below 0: words " SIZE_FORMAT + " is greater than _allocated_used_words " SIZE_FORMAT, + words, _allocated_used_words)); + // For CMS deallocation of the Metaspaces occurs during the + // sweep which is a concurrent phase. Protection by the expand_lock() + // is not enough since allocation is on a per Metaspace basis + // and protected by the Metaspace lock. + jlong minus_words = (jlong) - (jlong) words; + Atomic::add_ptr(minus_words, &_allocated_used_words); +} + +void MetaspaceAux::inc_used(size_t words) { + // _allocated_used_words tracks allocations for + // each piece of metadata. Those allocations are + // generally done concurrently by different application + // threads so must be done atomically. + Atomic::add_ptr(words, &_allocated_used_words); +} + +size_t MetaspaceAux::used_bytes_slow(Metaspace::MetadataType mdtype) { size_t used = 0; ClassLoaderDataGraphMetaspaceIterator iter; while (iter.repeat()) { Metaspace* msp = iter.get_next(); - // Sum allocation_total for each metaspace + // Sum allocated_blocks_words for each metaspace if (msp != NULL) { - used += msp->used_words(mdtype); + used += msp->used_words_slow(mdtype); } } return used * BytesPerWord; @@ -2282,13 +2545,15 @@ return free * BytesPerWord; } -size_t MetaspaceAux::capacity_in_bytes(Metaspace::MetadataType mdtype) { - size_t capacity = free_chunks_total(mdtype); +size_t MetaspaceAux::capacity_bytes_slow(Metaspace::MetadataType mdtype) { + // Don't count the space in the freelists. That space will be + // added to the capacity calculation as needed. + size_t capacity = 0; ClassLoaderDataGraphMetaspaceIterator iter; while (iter.repeat()) { Metaspace* msp = iter.get_next(); if (msp != NULL) { - capacity += msp->capacity_words(mdtype); + capacity += msp->capacity_words_slow(mdtype); } } return capacity * BytesPerWord; @@ -2315,23 +2580,30 @@ return free_chunks_total(mdtype) * BytesPerWord; } +size_t MetaspaceAux::free_chunks_total() { + return free_chunks_total(Metaspace::ClassType) + + free_chunks_total(Metaspace::NonClassType); +} + +size_t MetaspaceAux::free_chunks_total_in_bytes() { + return free_chunks_total() * BytesPerWord; +} + void MetaspaceAux::print_metaspace_change(size_t prev_metadata_used) { gclog_or_tty->print(", [Metaspace:"); if (PrintGCDetails && Verbose) { gclog_or_tty->print(" " SIZE_FORMAT "->" SIZE_FORMAT - "(" SIZE_FORMAT "/" SIZE_FORMAT ")", + "(" SIZE_FORMAT ")", prev_metadata_used, - used_in_bytes(), - capacity_in_bytes(), + allocated_capacity_bytes(), reserved_in_bytes()); } else { gclog_or_tty->print(" " SIZE_FORMAT "K" "->" SIZE_FORMAT "K" - "(" SIZE_FORMAT "K/" SIZE_FORMAT "K)", + "(" SIZE_FORMAT "K)", prev_metadata_used / K, - used_in_bytes()/ K, - capacity_in_bytes()/K, + allocated_capacity_bytes() / K, reserved_in_bytes()/ K); } @@ -2346,23 +2618,30 @@ out->print_cr(" Metaspace total " SIZE_FORMAT "K, used " SIZE_FORMAT "K," " reserved " SIZE_FORMAT "K", - capacity_in_bytes()/K, used_in_bytes()/K, reserved_in_bytes()/K); - out->print_cr(" data space " - SIZE_FORMAT "K, used " SIZE_FORMAT "K," - " reserved " SIZE_FORMAT "K", - capacity_in_bytes(nct)/K, used_in_bytes(nct)/K, reserved_in_bytes(nct)/K); - out->print_cr(" class space " - SIZE_FORMAT "K, used " SIZE_FORMAT "K," - " reserved " SIZE_FORMAT "K", - capacity_in_bytes(ct)/K, used_in_bytes(ct)/K, reserved_in_bytes(ct)/K); + allocated_capacity_bytes()/K, allocated_used_bytes()/K, reserved_in_bytes()/K); +#if 0 +// The calls to capacity_bytes_slow() and used_bytes_slow() cause +// lock ordering assertion failures with some collectors. Do +// not include this code until the lock ordering is fixed. + if (PrintGCDetails && Verbose) { + out->print_cr(" data space " + SIZE_FORMAT "K, used " SIZE_FORMAT "K," + " reserved " SIZE_FORMAT "K", + capacity_bytes_slow(nct)/K, used_bytes_slow(nct)/K, reserved_in_bytes(nct)/K); + out->print_cr(" class space " + SIZE_FORMAT "K, used " SIZE_FORMAT "K," + " reserved " SIZE_FORMAT "K", + capacity_bytes_slow(ct)/K, used_bytes_slow(ct)/K, reserved_in_bytes(ct)/K); + } +#endif } // Print information for class space and data space separately. // This is almost the same as above. void MetaspaceAux::print_on(outputStream* out, Metaspace::MetadataType mdtype) { size_t free_chunks_capacity_bytes = free_chunks_total_in_bytes(mdtype); - size_t capacity_bytes = capacity_in_bytes(mdtype); - size_t used_bytes = used_in_bytes(mdtype); + size_t capacity_bytes = capacity_bytes_slow(mdtype); + size_t used_bytes = used_bytes_slow(mdtype); size_t free_bytes = free_in_bytes(mdtype); size_t used_and_free = used_bytes + free_bytes + free_chunks_capacity_bytes; @@ -2435,6 +2714,36 @@ Metaspace::class_space_list()->chunk_manager()->verify(); } +void MetaspaceAux::verify_capacity() { +#ifdef ASSERT + size_t running_sum_capacity_bytes = allocated_capacity_bytes(); + // For purposes of the running sum of used, verify against capacity + size_t capacity_in_use_bytes = capacity_bytes_slow(); + assert(running_sum_capacity_bytes == capacity_in_use_bytes, + err_msg("allocated_capacity_words() * BytesPerWord " SIZE_FORMAT + " capacity_bytes_slow()" SIZE_FORMAT, + running_sum_capacity_bytes, capacity_in_use_bytes)); +#endif +} + +void MetaspaceAux::verify_used() { +#ifdef ASSERT + size_t running_sum_used_bytes = allocated_used_bytes(); + // For purposes of the running sum of used, verify against capacity + size_t used_in_use_bytes = used_bytes_slow(); + assert(allocated_used_bytes() == used_in_use_bytes, + err_msg("allocated_used_bytes() " SIZE_FORMAT + " used_bytes_slow()()" SIZE_FORMAT, + allocated_used_bytes(), used_in_use_bytes)); +#endif +} + +void MetaspaceAux::verify_metrics() { + verify_capacity(); + verify_used(); +} + + // Metaspace methods size_t Metaspace::_first_chunk_word_size = 0; @@ -2584,8 +2893,8 @@ MetaWord* result; MetaspaceGC::set_expand_after_GC(true); size_t before_inc = MetaspaceGC::capacity_until_GC(); - size_t delta_words = MetaspaceGC::delta_capacity_until_GC(word_size); - MetaspaceGC::inc_capacity_until_GC(delta_words); + size_t delta_bytes = MetaspaceGC::delta_capacity_until_GC(word_size) * BytesPerWord; + MetaspaceGC::inc_capacity_until_GC(delta_bytes); if (PrintGCDetails && Verbose) { gclog_or_tty->print_cr("Increase capacity to GC from " SIZE_FORMAT " to " SIZE_FORMAT, before_inc, MetaspaceGC::capacity_until_GC()); @@ -2603,8 +2912,8 @@ return (char*)vsm()->current_chunk()->bottom(); } -size_t Metaspace::used_words(MetadataType mdtype) const { - // return vsm()->allocation_total(); +size_t Metaspace::used_words_slow(MetadataType mdtype) const { + // return vsm()->allocated_used_words(); return mdtype == ClassType ? class_vsm()->sum_used_in_chunks_in_use() : vsm()->sum_used_in_chunks_in_use(); // includes overhead! } @@ -2619,16 +2928,24 @@ // have been made. Don't include space in the global freelist and // in the space available in the dictionary which // is already counted in some chunk. -size_t Metaspace::capacity_words(MetadataType mdtype) const { +size_t Metaspace::capacity_words_slow(MetadataType mdtype) const { return mdtype == ClassType ? class_vsm()->sum_capacity_in_chunks_in_use() : vsm()->sum_capacity_in_chunks_in_use(); } +size_t Metaspace::used_bytes_slow(MetadataType mdtype) const { + return used_words_slow(mdtype) * BytesPerWord; +} + +size_t Metaspace::capacity_bytes_slow(MetadataType mdtype) const { + return capacity_words_slow(mdtype) * BytesPerWord; +} + void Metaspace::deallocate(MetaWord* ptr, size_t word_size, bool is_class) { if (SafepointSynchronize::is_at_safepoint()) { assert(Thread::current()->is_VM_thread(), "should be the VM thread"); // Don't take Heap_lock - MutexLocker ml(vsm()->lock()); + MutexLockerEx ml(vsm()->lock(), Mutex::_no_safepoint_check_flag); if (word_size < TreeChunk::min_size()) { // Dark matter. Too small for dictionary. #ifdef ASSERT @@ -2642,7 +2959,7 @@ vsm()->deallocate(ptr, word_size); } } else { - MutexLocker ml(vsm()->lock()); + MutexLockerEx ml(vsm()->lock(), Mutex::_no_safepoint_check_flag); if (word_size < TreeChunk::min_size()) { // Dark matter. Too small for dictionary. @@ -2716,6 +3033,13 @@ return Metablock::initialize(result, word_size); } +void Metaspace::purge() { + MutexLockerEx cl(SpaceManager::expand_lock(), + Mutex::_no_safepoint_check_flag); + space_list()->purge(); + class_space_list()->purge(); +} + void Metaspace::print_on(outputStream* out) const { // Print both class virtual space counts and metaspace. if (Verbose) { @@ -2733,7 +3057,8 @@ // aren't deleted presently. When they are, some sort of locking might // be needed. Note, locking this can cause inversion problems with the // caller in MetaspaceObj::is_metadata() function. - return space_list()->contains(ptr) || class_space_list()->contains(ptr); + return space_list()->contains(ptr) || + class_space_list()->contains(ptr); } void Metaspace::verify() { @@ -2742,10 +3067,6 @@ } void Metaspace::dump(outputStream* const out) const { - if (UseMallocOnly) { - // Just print usage for now - out->print_cr("usage %d", used_words(Metaspace::NonClassType)); - } out->print_cr("\nVirtual space manager: " INTPTR_FORMAT, vsm()); vsm()->dump(out); out->print_cr("\nClass space manager: " INTPTR_FORMAT, class_vsm()); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/metaspace.hpp --- a/hotspot/src/share/vm/memory/metaspace.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/metaspace.hpp Wed May 08 23:54:45 2013 -0700 @@ -111,6 +111,10 @@ SpaceManager* _class_vsm; SpaceManager* class_vsm() const { return _class_vsm; } + // Allocate space for metadata of type mdtype. This is space + // within a Metachunk and is used by + // allocate(ClassLoaderData*, size_t, bool, MetadataType, TRAPS) + // which returns a Metablock. MetaWord* allocate(size_t word_size, MetadataType mdtype); // Virtual Space lists for both classes and other metadata @@ -133,11 +137,14 @@ static size_t first_class_chunk_word_size() { return _first_class_chunk_word_size; } char* bottom() const; - size_t used_words(MetadataType mdtype) const; + size_t used_words_slow(MetadataType mdtype) const; size_t free_words(MetadataType mdtype) const; - size_t capacity_words(MetadataType mdtype) const; + size_t capacity_words_slow(MetadataType mdtype) const; size_t waste_words(MetadataType mdtype) const; + size_t used_bytes_slow(MetadataType mdtype) const; + size_t capacity_bytes_slow(MetadataType mdtype) const; + static Metablock* allocate(ClassLoaderData* loader_data, size_t size, bool read_only, MetadataType mdtype, TRAPS); void deallocate(MetaWord* ptr, size_t byte_size, bool is_class); @@ -150,6 +157,9 @@ static bool contains(const void *ptr); void dump(outputStream* const out) const; + // Free empty virtualspaces + static void purge(); + void print_on(outputStream* st) const; // Debugging support void verify(); @@ -158,28 +168,81 @@ class MetaspaceAux : AllStatic { // Statistics for class space and data space in metaspace. - static size_t used_in_bytes(Metaspace::MetadataType mdtype); + + // These methods iterate over the classloader data graph + // for the given Metaspace type. These are slow. + static size_t used_bytes_slow(Metaspace::MetadataType mdtype); static size_t free_in_bytes(Metaspace::MetadataType mdtype); - static size_t capacity_in_bytes(Metaspace::MetadataType mdtype); + static size_t capacity_bytes_slow(Metaspace::MetadataType mdtype); + + // Iterates over the virtual space list. static size_t reserved_in_bytes(Metaspace::MetadataType mdtype); static size_t free_chunks_total(Metaspace::MetadataType mdtype); static size_t free_chunks_total_in_bytes(Metaspace::MetadataType mdtype); public: - // Total of space allocated to metadata in all Metaspaces - static size_t used_in_bytes() { - return used_in_bytes(Metaspace::ClassType) + - used_in_bytes(Metaspace::NonClassType); + // Running sum of space in all Metachunks that has been + // allocated to a Metaspace. This is used instead of + // iterating over all the classloaders + static size_t _allocated_capacity_words; + // Running sum of space in all Metachunks that have + // are being used for metadata. + static size_t _allocated_used_words; + + public: + // Decrement and increment _allocated_capacity_words + static void dec_capacity(size_t words); + static void inc_capacity(size_t words); + + // Decrement and increment _allocated_used_words + static void dec_used(size_t words); + static void inc_used(size_t words); + + // Total of space allocated to metadata in all Metaspaces. + // This sums the space used in each Metachunk by + // iterating over the classloader data graph + static size_t used_bytes_slow() { + return used_bytes_slow(Metaspace::ClassType) + + used_bytes_slow(Metaspace::NonClassType); } - // Total of available space in all Metaspaces - // Total of capacity allocated to all Metaspaces. This includes - // space in Metachunks not yet allocated and in the Metachunk - // freelist. - static size_t capacity_in_bytes() { - return capacity_in_bytes(Metaspace::ClassType) + - capacity_in_bytes(Metaspace::NonClassType); + // Used by MetaspaceCounters + static size_t free_chunks_total(); + static size_t free_chunks_total_in_bytes(); + + static size_t allocated_capacity_words() { + return _allocated_capacity_words; + } + static size_t allocated_capacity_bytes() { + return _allocated_capacity_words * BytesPerWord; + } + + static size_t allocated_used_words() { + return _allocated_used_words; + } + static size_t allocated_used_bytes() { + return _allocated_used_words * BytesPerWord; + } + + static size_t free_bytes(); + + // Total capacity in all Metaspaces + static size_t capacity_bytes_slow() { +#ifdef PRODUCT + // Use allocated_capacity_bytes() in PRODUCT instead of this function. + guarantee(false, "Should not call capacity_bytes_slow() in the PRODUCT"); +#endif + size_t class_capacity = capacity_bytes_slow(Metaspace::ClassType); + size_t non_class_capacity = capacity_bytes_slow(Metaspace::NonClassType); + assert(allocated_capacity_bytes() == class_capacity + non_class_capacity, + err_msg("bad accounting: allocated_capacity_bytes() " SIZE_FORMAT + " class_capacity + non_class_capacity " SIZE_FORMAT + " class_capacity " SIZE_FORMAT " non_class_capacity " SIZE_FORMAT, + allocated_capacity_bytes(), class_capacity + non_class_capacity, + class_capacity, non_class_capacity)); + + return class_capacity + non_class_capacity; } // Total space reserved in all Metaspaces @@ -198,6 +261,11 @@ static void print_waste(outputStream* out); static void dump(outputStream* out); static void verify_free_chunks(); + // Checks that the values returned by allocated_capacity_bytes() and + // capacity_bytes_slow() are the same. + static void verify_capacity(); + static void verify_used(); + static void verify_metrics(); }; // Metaspace are deallocated when their class loader are GC'ed. @@ -232,7 +300,6 @@ public: static size_t capacity_until_GC() { return _capacity_until_GC; } - static size_t capacity_until_GC_in_bytes() { return _capacity_until_GC * BytesPerWord; } static void inc_capacity_until_GC(size_t v) { _capacity_until_GC += v; } static void dec_capacity_until_GC(size_t v) { _capacity_until_GC = _capacity_until_GC > v ? _capacity_until_GC - v : 0; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/metaspaceCounters.cpp --- a/hotspot/src/share/vm/memory/metaspaceCounters.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/metaspaceCounters.cpp Wed May 08 23:54:45 2013 -0700 @@ -29,6 +29,16 @@ MetaspaceCounters* MetaspaceCounters::_metaspace_counters = NULL; +size_t MetaspaceCounters::calc_total_capacity() { + // The total capacity is the sum of + // 1) capacity of Metachunks in use by all Metaspaces + // 2) unused space at the end of each Metachunk + // 3) space in the freelist + size_t total_capacity = MetaspaceAux::allocated_capacity_bytes() + + MetaspaceAux::free_bytes() + MetaspaceAux::free_chunks_total_in_bytes(); + return total_capacity; +} + MetaspaceCounters::MetaspaceCounters() : _capacity(NULL), _used(NULL), @@ -36,8 +46,8 @@ if (UsePerfData) { size_t min_capacity = MetaspaceAux::min_chunk_size(); size_t max_capacity = MetaspaceAux::reserved_in_bytes(); - size_t curr_capacity = MetaspaceAux::capacity_in_bytes(); - size_t used = MetaspaceAux::used_in_bytes(); + size_t curr_capacity = calc_total_capacity(); + size_t used = MetaspaceAux::allocated_used_bytes(); initialize(min_capacity, max_capacity, curr_capacity, used); } @@ -82,15 +92,13 @@ void MetaspaceCounters::update_capacity() { assert(UsePerfData, "Should not be called unless being used"); - assert(_capacity != NULL, "Should be initialized"); - size_t capacity_in_bytes = MetaspaceAux::capacity_in_bytes(); - _capacity->set_value(capacity_in_bytes); + size_t total_capacity = calc_total_capacity(); + _capacity->set_value(total_capacity); } void MetaspaceCounters::update_used() { assert(UsePerfData, "Should not be called unless being used"); - assert(_used != NULL, "Should be initialized"); - size_t used_in_bytes = MetaspaceAux::used_in_bytes(); + size_t used_in_bytes = MetaspaceAux::allocated_used_bytes(); _used->set_value(used_in_bytes); } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/metaspaceCounters.hpp --- a/hotspot/src/share/vm/memory/metaspaceCounters.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/metaspaceCounters.hpp Wed May 08 23:54:45 2013 -0700 @@ -37,6 +37,7 @@ size_t max_capacity, size_t curr_capacity, size_t used); + size_t calc_total_capacity(); public: MetaspaceCounters(); ~MetaspaceCounters(); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/metaspaceShared.cpp --- a/hotspot/src/share/vm/memory/metaspaceShared.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp Wed May 08 23:54:45 2013 -0700 @@ -376,18 +376,17 @@ const char* fmt = "%s space: %9d [ %4.1f%% of total] out of %9d bytes [%4.1f%% used] at " PTR_FORMAT; Metaspace* ro_space = _loader_data->ro_metaspace(); Metaspace* rw_space = _loader_data->rw_metaspace(); - const size_t BPW = BytesPerWord; // Allocated size of each space (may not be all occupied) - const size_t ro_alloced = ro_space->capacity_words(Metaspace::NonClassType) * BPW; - const size_t rw_alloced = rw_space->capacity_words(Metaspace::NonClassType) * BPW; + const size_t ro_alloced = ro_space->capacity_bytes_slow(Metaspace::NonClassType); + const size_t rw_alloced = rw_space->capacity_bytes_slow(Metaspace::NonClassType); const size_t md_alloced = md_end-md_low; const size_t mc_alloced = mc_end-mc_low; const size_t total_alloced = ro_alloced + rw_alloced + md_alloced + mc_alloced; // Occupied size of each space. - const size_t ro_bytes = ro_space->used_words(Metaspace::NonClassType) * BPW; - const size_t rw_bytes = rw_space->used_words(Metaspace::NonClassType) * BPW; + const size_t ro_bytes = ro_space->used_bytes_slow(Metaspace::NonClassType); + const size_t rw_bytes = rw_space->used_bytes_slow(Metaspace::NonClassType); const size_t md_bytes = size_t(md_top - md_low); const size_t mc_bytes = size_t(mc_top - mc_low); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/sharedHeap.cpp --- a/hotspot/src/share/vm/memory/sharedHeap.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/sharedHeap.cpp Wed May 08 23:54:45 2013 -0700 @@ -218,14 +218,13 @@ static AlwaysTrueClosure always_true; void SharedHeap::process_weak_roots(OopClosure* root_closure, - CodeBlobClosure* code_roots, - OopClosure* non_root_closure) { + CodeBlobClosure* code_roots) { // Global (weak) JNI handles JNIHandles::weak_oops_do(&always_true, root_closure); CodeCache::blobs_do(code_roots); - StringTable::oops_do(root_closure); - } + StringTable::oops_do(root_closure); +} void SharedHeap::set_barrier_set(BarrierSet* bs) { _barrier_set = bs; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/sharedHeap.hpp --- a/hotspot/src/share/vm/memory/sharedHeap.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/sharedHeap.hpp Wed May 08 23:54:45 2013 -0700 @@ -249,8 +249,7 @@ // JNI weak roots, the code cache, system dictionary, symbol table, // string table. void process_weak_roots(OopClosure* root_closure, - CodeBlobClosure* code_roots, - OopClosure* non_root_closure); + CodeBlobClosure* code_roots); // The functions below are helper functions that a subclass of // "SharedHeap" can use in the implementation of its virtual diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/universe.cpp --- a/hotspot/src/share/vm/memory/universe.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/universe.cpp Wed May 08 23:54:45 2013 -0700 @@ -1270,7 +1270,7 @@ st->print_cr("}"); } -void Universe::verify(bool silent, VerifyOption option) { +void Universe::verify(VerifyOption option, const char* prefix, bool silent) { // The use of _verify_in_progress is a temporary work around for // 6320749. Don't bother with a creating a class to set and clear // it since it is only used in this method and the control flow is @@ -1287,11 +1287,12 @@ HandleMark hm; // Handles created during verification can be zapped _verify_count++; + if (!silent) gclog_or_tty->print(prefix); if (!silent) gclog_or_tty->print("[Verifying "); if (!silent) gclog_or_tty->print("threads "); Threads::verify(); + if (!silent) gclog_or_tty->print("heap "); heap()->verify(silent, option); - if (!silent) gclog_or_tty->print("syms "); SymbolTable::verify(); if (!silent) gclog_or_tty->print("strs "); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/memory/universe.hpp --- a/hotspot/src/share/vm/memory/universe.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/memory/universe.hpp Wed May 08 23:54:45 2013 -0700 @@ -445,12 +445,12 @@ // Debugging static bool verify_in_progress() { return _verify_in_progress; } - static void verify(bool silent, VerifyOption option); - static void verify(bool silent) { - verify(silent, VerifyOption_Default /* option */); + static void verify(VerifyOption option, const char* prefix, bool silent = VerifySilently); + static void verify(const char* prefix, bool silent = VerifySilently) { + verify(VerifyOption_Default, prefix, silent); } - static void verify() { - verify(false /* silent */); + static void verify(bool silent = VerifySilently) { + verify("", silent); } static int verify_count() { return _verify_count; } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/oops/constantPool.cpp --- a/hotspot/src/share/vm/oops/constantPool.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/oops/constantPool.cpp Wed May 08 23:54:45 2013 -0700 @@ -40,6 +40,7 @@ #include "runtime/init.hpp" #include "runtime/javaCalls.hpp" #include "runtime/signature.hpp" +#include "runtime/synchronizer.hpp" #include "runtime/vframe.hpp" ConstantPool* ConstantPool::allocate(ClassLoaderData* loader_data, int length, TRAPS) { @@ -69,7 +70,6 @@ // only set to non-zero if constant pool is merged by RedefineClasses set_version(0); - set_lock(new Monitor(Monitor::nonleaf + 2, "A constant pool lock")); // initialize tag array int length = tags->length(); @@ -95,9 +95,6 @@ void ConstantPool::release_C_heap_structures() { // walk constant pool and decrement symbol reference counts unreference_symbols(); - - delete _lock; - set_lock(NULL); } objArrayOop ConstantPool::resolved_references() const { @@ -154,9 +151,6 @@ ClassLoaderData* loader_data = pool_holder()->class_loader_data(); set_resolved_references(loader_data->add_handle(refs_handle)); } - - // Also need to recreate the mutex. Make sure this matches the constructor - set_lock(new Monitor(Monitor::nonleaf + 2, "A constant pool lock")); } } @@ -167,7 +161,23 @@ set_resolved_reference_length( resolved_references() != NULL ? resolved_references()->length() : 0); set_resolved_references(NULL); - set_lock(NULL); +} + +oop ConstantPool::lock() { + if (_pool_holder) { + // We re-use the _pool_holder's init_lock to reduce footprint. + // Notes on deadlocks: + // [1] This lock is a Java oop, so it can be recursively locked by + // the same thread without self-deadlocks. + // [2] Deadlock will happen if there is circular dependency between + // the of two Java classes. However, in this case, + // the deadlock would have happened long before we reach + // ConstantPool::lock(), so reusing init_lock does not + // increase the possibility of deadlock. + return _pool_holder->init_lock(); + } else { + return NULL; + } } int ConstantPool::cp_to_object_index(int cp_index) { @@ -208,7 +218,9 @@ Symbol* name = NULL; Handle loader; - { MonitorLockerEx ml(this_oop->lock()); + { + oop cplock = this_oop->lock(); + ObjectLocker ol(cplock , THREAD, cplock != NULL); if (this_oop->tag_at(which).is_unresolved_klass()) { if (this_oop->tag_at(which).is_unresolved_klass_in_error()) { @@ -255,7 +267,8 @@ bool throw_orig_error = false; { - MonitorLockerEx ml(this_oop->lock()); + oop cplock = this_oop->lock(); + ObjectLocker ol(cplock, THREAD, cplock != NULL); // some other thread has beaten us and has resolved the class. if (this_oop->tag_at(which).is_klass()) { @@ -323,7 +336,8 @@ } return k(); } else { - MonitorLockerEx ml(this_oop->lock()); + oop cplock = this_oop->lock(); + ObjectLocker ol(cplock, THREAD, cplock != NULL); // Only updated constant pool - if it is resolved. do_resolve = this_oop->tag_at(which).is_unresolved_klass(); if (do_resolve) { @@ -619,7 +633,8 @@ int tag, TRAPS) { ResourceMark rm; Symbol* error = PENDING_EXCEPTION->klass()->name(); - MonitorLockerEx ml(this_oop->lock()); // lock cpool to change tag. + oop cplock = this_oop->lock(); + ObjectLocker ol(cplock, THREAD, cplock != NULL); // lock cpool to change tag. int error_tag = (tag == JVM_CONSTANT_MethodHandle) ? JVM_CONSTANT_MethodHandleInError : JVM_CONSTANT_MethodTypeInError; @@ -780,7 +795,8 @@ if (cache_index >= 0) { // Cache the oop here also. Handle result_handle(THREAD, result_oop); - MonitorLockerEx ml(this_oop->lock()); // don't know if we really need this + oop cplock = this_oop->lock(); + ObjectLocker ol(cplock, THREAD, cplock != NULL); // don't know if we really need this oop result = this_oop->resolved_references()->obj_at(cache_index); // Benign race condition: resolved_references may already be filled in while we were trying to lock. // The important thing here is that all threads pick up the same result. @@ -1043,24 +1059,13 @@ case JVM_CONSTANT_InvokeDynamic: { - int k1 = invoke_dynamic_bootstrap_method_ref_index_at(index1); - int k2 = cp2->invoke_dynamic_bootstrap_method_ref_index_at(index2); - bool match = compare_entry_to(k1, cp2, k2, CHECK_false); - if (!match) return false; - k1 = invoke_dynamic_name_and_type_ref_index_at(index1); - k2 = cp2->invoke_dynamic_name_and_type_ref_index_at(index2); - match = compare_entry_to(k1, cp2, k2, CHECK_false); - if (!match) return false; - int argc = invoke_dynamic_argument_count_at(index1); - if (argc == cp2->invoke_dynamic_argument_count_at(index2)) { - for (int j = 0; j < argc; j++) { - k1 = invoke_dynamic_argument_index_at(index1, j); - k2 = cp2->invoke_dynamic_argument_index_at(index2, j); - match = compare_entry_to(k1, cp2, k2, CHECK_false); - if (!match) return false; - } - return true; // got through loop; all elements equal - } + int k1 = invoke_dynamic_name_and_type_ref_index_at(index1); + int k2 = cp2->invoke_dynamic_name_and_type_ref_index_at(index2); + int i1 = invoke_dynamic_bootstrap_specifier_index(index1); + int i2 = cp2->invoke_dynamic_bootstrap_specifier_index(index2); + bool match = compare_entry_to(k1, cp2, k2, CHECK_false) && + compare_operand_to(i1, cp2, i2, CHECK_false); + return match; } break; case JVM_CONSTANT_String: @@ -1095,6 +1100,80 @@ } // end compare_entry_to() +// Resize the operands array with delta_len and delta_size. +// Used in RedefineClasses for CP merge. +void ConstantPool::resize_operands(int delta_len, int delta_size, TRAPS) { + int old_len = operand_array_length(operands()); + int new_len = old_len + delta_len; + int min_len = (delta_len > 0) ? old_len : new_len; + + int old_size = operands()->length(); + int new_size = old_size + delta_size; + int min_size = (delta_size > 0) ? old_size : new_size; + + ClassLoaderData* loader_data = pool_holder()->class_loader_data(); + Array* new_ops = MetadataFactory::new_array(loader_data, new_size, CHECK); + + // Set index in the resized array for existing elements only + for (int idx = 0; idx < min_len; idx++) { + int offset = operand_offset_at(idx); // offset in original array + operand_offset_at_put(new_ops, idx, offset + 2*delta_len); // offset in resized array + } + // Copy the bootstrap specifiers only + Copy::conjoint_memory_atomic(operands()->adr_at(2*old_len), + new_ops->adr_at(2*new_len), + (min_size - 2*min_len) * sizeof(u2)); + // Explicitly deallocate old operands array. + // Note, it is not needed for 7u backport. + if ( operands() != NULL) { // the safety check + MetadataFactory::free_array(loader_data, operands()); + } + set_operands(new_ops); +} // end resize_operands() + + +// Extend the operands array with the length and size of the ext_cp operands. +// Used in RedefineClasses for CP merge. +void ConstantPool::extend_operands(constantPoolHandle ext_cp, TRAPS) { + int delta_len = operand_array_length(ext_cp->operands()); + if (delta_len == 0) { + return; // nothing to do + } + int delta_size = ext_cp->operands()->length(); + + assert(delta_len > 0 && delta_size > 0, "extended operands array must be bigger"); + + if (operand_array_length(operands()) == 0) { + ClassLoaderData* loader_data = pool_holder()->class_loader_data(); + Array* new_ops = MetadataFactory::new_array(loader_data, delta_size, CHECK); + // The first element index defines the offset of second part + operand_offset_at_put(new_ops, 0, 2*delta_len); // offset in new array + set_operands(new_ops); + } else { + resize_operands(delta_len, delta_size, CHECK); + } + +} // end extend_operands() + + +// Shrink the operands array to a smaller array with new_len length. +// Used in RedefineClasses for CP merge. +void ConstantPool::shrink_operands(int new_len, TRAPS) { + int old_len = operand_array_length(operands()); + if (new_len == old_len) { + return; // nothing to do + } + assert(new_len < old_len, "shrunken operands array must be smaller"); + + int free_base = operand_next_offset_at(new_len - 1); + int delta_len = new_len - old_len; + int delta_size = 2*delta_len + free_base - operands()->length(); + + resize_operands(delta_len, delta_size, CHECK); + +} // end shrink_operands() + + void ConstantPool::copy_operands(constantPoolHandle from_cp, constantPoolHandle to_cp, TRAPS) { @@ -1357,6 +1436,46 @@ } // end find_matching_entry() +// Compare this constant pool's bootstrap specifier at idx1 to the constant pool +// cp2's bootstrap specifier at idx2. +bool ConstantPool::compare_operand_to(int idx1, constantPoolHandle cp2, int idx2, TRAPS) { + int k1 = operand_bootstrap_method_ref_index_at(idx1); + int k2 = cp2->operand_bootstrap_method_ref_index_at(idx2); + bool match = compare_entry_to(k1, cp2, k2, CHECK_false); + + if (!match) { + return false; + } + int argc = operand_argument_count_at(idx1); + if (argc == cp2->operand_argument_count_at(idx2)) { + for (int j = 0; j < argc; j++) { + k1 = operand_argument_index_at(idx1, j); + k2 = cp2->operand_argument_index_at(idx2, j); + match = compare_entry_to(k1, cp2, k2, CHECK_false); + if (!match) { + return false; + } + } + return true; // got through loop; all elements equal + } + return false; +} // end compare_operand_to() + +// Search constant pool search_cp for a bootstrap specifier that matches +// this constant pool's bootstrap specifier at pattern_i index. +// Return the index of a matching bootstrap specifier or (-1) if there is no match. +int ConstantPool::find_matching_operand(int pattern_i, + constantPoolHandle search_cp, int search_len, TRAPS) { + for (int i = 0; i < search_len; i++) { + bool found = compare_operand_to(pattern_i, search_cp, i, CHECK_(-1)); + if (found) { + return i; + } + } + return -1; // bootstrap specifier not found; return unused index (-1) +} // end find_matching_operand() + + #ifndef PRODUCT const char* ConstantPool::printable_name_at(int which) { diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/oops/constantPool.hpp --- a/hotspot/src/share/vm/oops/constantPool.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/oops/constantPool.hpp Wed May 08 23:54:45 2013 -0700 @@ -111,7 +111,6 @@ int _version; } _saved; - Monitor* _lock; void set_tags(Array* tags) { _tags = tags; } void tag_at_put(int which, jbyte t) { tags()->at_put(which, t); } @@ -567,6 +566,47 @@ _indy_argc_offset = 1, // u2 argc _indy_argv_offset = 2 // u2 argv[argc] }; + + // These functions are used in RedefineClasses for CP merge + + int operand_offset_at(int bootstrap_specifier_index) { + assert(0 <= bootstrap_specifier_index && + bootstrap_specifier_index < operand_array_length(operands()), + "Corrupted CP operands"); + return operand_offset_at(operands(), bootstrap_specifier_index); + } + int operand_bootstrap_method_ref_index_at(int bootstrap_specifier_index) { + int offset = operand_offset_at(bootstrap_specifier_index); + return operands()->at(offset + _indy_bsm_offset); + } + int operand_argument_count_at(int bootstrap_specifier_index) { + int offset = operand_offset_at(bootstrap_specifier_index); + int argc = operands()->at(offset + _indy_argc_offset); + return argc; + } + int operand_argument_index_at(int bootstrap_specifier_index, int j) { + int offset = operand_offset_at(bootstrap_specifier_index); + return operands()->at(offset + _indy_argv_offset + j); + } + int operand_next_offset_at(int bootstrap_specifier_index) { + int offset = operand_offset_at(bootstrap_specifier_index) + _indy_argv_offset + + operand_argument_count_at(bootstrap_specifier_index); + return offset; + } + // Compare a bootsrap specifier in the operands arrays + bool compare_operand_to(int bootstrap_specifier_index1, constantPoolHandle cp2, + int bootstrap_specifier_index2, TRAPS); + // Find a bootsrap specifier in the operands array + int find_matching_operand(int bootstrap_specifier_index, constantPoolHandle search_cp, + int operands_cur_len, TRAPS); + // Resize the operands array with delta_len and delta_size + void resize_operands(int delta_len, int delta_size, TRAPS); + // Extend the operands array with the length and size of the ext_cp operands + void extend_operands(constantPoolHandle ext_cp, TRAPS); + // Shrink the operands array to a smaller array with new_len length + void shrink_operands(int new_len, TRAPS); + + int invoke_dynamic_bootstrap_method_ref_index_at(int which) { assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool"); int op_base = invoke_dynamic_operand_base(which); @@ -782,8 +822,17 @@ void set_resolved_reference_length(int length) { _saved._resolved_reference_length = length; } int resolved_reference_length() const { return _saved._resolved_reference_length; } - void set_lock(Monitor* lock) { _lock = lock; } - Monitor* lock() { return _lock; } + + // lock() may return null -- constant pool updates may happen before this lock is + // initialized, because the _pool_holder has not been fully initialized and + // has not been registered into the system dictionary. In this case, no other + // thread can be modifying this constantpool, so no synchronization is + // necessary. + // + // Use cplock() like this: + // oop cplock = cp->lock(); + // ObjectLocker ol(cplock , THREAD, cplock != NULL); + oop lock(); // Decrease ref counts of symbols that are in the constant pool // when the holder class is unloaded diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/oops/cpCache.cpp --- a/hotspot/src/share/vm/oops/cpCache.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/oops/cpCache.cpp Wed May 08 23:54:45 2013 -0700 @@ -266,7 +266,8 @@ // the lock, so that when the losing writer returns, he can use the linked // cache entry. - MonitorLockerEx ml(cpool->lock()); + oop cplock = cpool->lock(); + ObjectLocker ol(cplock, Thread::current(), cplock != NULL); if (!is_f1_null()) { return; } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/oops/instanceKlass.cpp --- a/hotspot/src/share/vm/oops/instanceKlass.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Wed May 08 23:54:45 2013 -0700 @@ -54,6 +54,7 @@ #include "runtime/javaCalls.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/thread.inline.hpp" +#include "services/classLoadingService.hpp" #include "services/threadService.hpp" #include "utilities/dtrace.hpp" #include "utilities/macros.hpp" @@ -418,25 +419,6 @@ set_annotations(NULL); } -volatile oop InstanceKlass::init_lock() const { - volatile oop lock = _init_lock; // read once - assert((oop)lock != NULL || !is_not_initialized(), // initialized or in_error state - "only fully initialized state can have a null lock"); - return lock; -} - -// Set the initialization lock to null so the object can be GC'ed. Any racing -// threads to get this lock will see a null lock and will not lock. -// That's okay because they all check for initialized state after getting -// the lock and return. -void InstanceKlass::fence_and_clear_init_lock() { - // make sure previous stores are all done, notably the init_state. - OrderAccess::storestore(); - klass_oop_store(&_init_lock, NULL); - assert(!is_not_initialized(), "class must be initialized now"); -} - - bool InstanceKlass::should_be_initialized() const { return !is_initialized(); } @@ -473,7 +455,7 @@ void InstanceKlass::eager_initialize_impl(instanceKlassHandle this_oop) { EXCEPTION_MARK; volatile oop init_lock = this_oop->init_lock(); - ObjectLocker ol(init_lock, THREAD, init_lock != NULL); + ObjectLocker ol(init_lock, THREAD); // abort if someone beat us to the initialization if (!this_oop->is_not_initialized()) return; // note: not equivalent to is_initialized() @@ -492,7 +474,6 @@ } else { // linking successfull, mark class as initialized this_oop->set_init_state (fully_initialized); - this_oop->fence_and_clear_init_lock(); // trace if (TraceClassInitialization) { ResourceMark rm(THREAD); @@ -619,7 +600,7 @@ // verification & rewriting { volatile oop init_lock = this_oop->init_lock(); - ObjectLocker ol(init_lock, THREAD, init_lock != NULL); + ObjectLocker ol(init_lock, THREAD); // rewritten will have been set if loader constraint error found // on an earlier link attempt // don't verify or rewrite if already rewritten @@ -742,7 +723,7 @@ // Step 1 { volatile oop init_lock = this_oop->init_lock(); - ObjectLocker ol(init_lock, THREAD, init_lock != NULL); + ObjectLocker ol(init_lock, THREAD); Thread *self = THREAD; // it's passed the current thread @@ -890,9 +871,8 @@ void InstanceKlass::set_initialization_state_and_notify_impl(instanceKlassHandle this_oop, ClassState state, TRAPS) { volatile oop init_lock = this_oop->init_lock(); - ObjectLocker ol(init_lock, THREAD, init_lock != NULL); + ObjectLocker ol(init_lock, THREAD); this_oop->set_init_state(state); - this_oop->fence_and_clear_init_lock(); ol.notify_all(CHECK); } @@ -2312,7 +2292,29 @@ m->clear_all_breakpoints(); } + +void InstanceKlass::notify_unload_class(InstanceKlass* ik) { + // notify the debugger + if (JvmtiExport::should_post_class_unload()) { + JvmtiExport::post_class_unload(ik); + } + + // notify ClassLoadingService of class unload + ClassLoadingService::notify_class_unloaded(ik); +} + +void InstanceKlass::release_C_heap_structures(InstanceKlass* ik) { + // Clean up C heap + ik->release_C_heap_structures(); + ik->constants()->release_C_heap_structures(); +} + void InstanceKlass::release_C_heap_structures() { + + // Can't release the constant pool here because the constant pool can be + // deallocated separately from the InstanceKlass for default methods and + // redefine classes. + // Deallocate oop map cache if (_oop_map_cache != NULL) { delete _oop_map_cache; @@ -2837,7 +2839,7 @@ st->print(BULLET"protection domain: "); ((InstanceKlass*)this)->protection_domain()->print_value_on(st); st->cr(); st->print(BULLET"host class: "); host_klass()->print_value_on_maybe_null(st); st->cr(); st->print(BULLET"signers: "); signers()->print_value_on(st); st->cr(); - st->print(BULLET"init_lock: "); ((oop)_init_lock)->print_value_on(st); st->cr(); + st->print(BULLET"init_lock: "); ((oop)_init_lock)->print_value_on(st); st->cr(); if (source_file_name() != NULL) { st->print(BULLET"source file: "); source_file_name()->print_value_on(st); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/oops/instanceKlass.hpp --- a/hotspot/src/share/vm/oops/instanceKlass.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/oops/instanceKlass.hpp Wed May 08 23:54:45 2013 -0700 @@ -184,8 +184,9 @@ oop _protection_domain; // Class signers. objArrayOop _signers; - // Initialization lock. Must be one per class and it has to be a VM internal - // object so java code cannot lock it (like the mirror) + // Lock for (1) initialization; (2) access to the ConstantPool of this class. + // Must be one per class and it has to be a VM internal object so java code + // cannot lock it (like the mirror). // It has to be an object not a Mutex because it's held through java calls. volatile oop _init_lock; @@ -236,7 +237,7 @@ _misc_rewritten = 1 << 0, // methods rewritten. _misc_has_nonstatic_fields = 1 << 1, // for sizing with UseCompressedOops _misc_should_verify_class = 1 << 2, // allow caching of preverification - _misc_is_anonymous = 1 << 3, // has embedded _inner_classes field + _misc_is_anonymous = 1 << 3, // has embedded _host_klass field _misc_is_contended = 1 << 4, // marked with contended annotation _misc_has_default_methods = 1 << 5 // class/superclass/implemented interfaces has default methods }; @@ -934,7 +935,9 @@ // referenced by handles. bool on_stack() const { return _constants->on_stack(); } - void release_C_heap_structures(); + // callbacks for actions during class unloading + static void notify_unload_class(InstanceKlass* ik); + static void release_C_heap_structures(InstanceKlass* ik); // Parallel Scavenge and Parallel Old PARALLEL_GC_DECLS @@ -968,6 +971,7 @@ #endif // INCLUDE_ALL_GCS u2 idnum_allocated_count() const { return _idnum_allocated_count; } + private: // initialization state #ifdef ASSERT @@ -994,9 +998,10 @@ { OrderAccess::release_store_ptr(&_methods_cached_itable_indices, indices); } // Lock during initialization - volatile oop init_lock() const; - void set_init_lock(oop value) { klass_oop_store(&_init_lock, value); } - void fence_and_clear_init_lock(); // after fully_initialized +public: + volatile oop init_lock() const {return _init_lock; } +private: + void set_init_lock(oop value) { klass_oop_store(&_init_lock, value); } // Offsets for memory management oop* adr_protection_domain() const { return (oop*)&this->_protection_domain;} @@ -1022,6 +1027,8 @@ // Returns the array class with this class as element type Klass* array_klass_impl(bool or_null, TRAPS); + // Free CHeap allocated fields. + void release_C_heap_structures(); public: // CDS support - remove and restore oops from metadata. Oops are not shared. virtual void remove_unshareable_info(); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/oops/klassVtable.cpp --- a/hotspot/src/share/vm/oops/klassVtable.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/oops/klassVtable.cpp Wed May 08 23:54:45 2013 -0700 @@ -519,6 +519,9 @@ // check if a method is a miranda method, given a class's methods table and it's super // the caller must make sure that the method belongs to an interface implemented by the class bool klassVtable::is_miranda(Method* m, Array* class_methods, Klass* super) { + if (m->is_static()) { + return false; + } Symbol* name = m->name(); Symbol* signature = m->signature(); if (InstanceKlass::find_method(class_methods, name, signature) == NULL) { diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/oops/method.cpp --- a/hotspot/src/share/vm/oops/method.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/oops/method.cpp Wed May 08 23:54:45 2013 -0700 @@ -877,7 +877,7 @@ debug_only(No_Safepoint_Verifier nsv;) nmethod *code = (nmethod *)OrderAccess::load_ptr_acquire(&_code); if (code == NULL && UseCodeCacheFlushing) { - nmethod *saved_code = CodeCache::find_and_remove_saved_code(this); + nmethod *saved_code = CodeCache::reanimate_saved_code(this); if (saved_code != NULL) { methodHandle method(this); assert( ! saved_code->is_osr_method(), "should not get here for osr" ); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/oops/oop.cpp --- a/hotspot/src/share/vm/oops/oop.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/oops/oop.cpp Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -103,11 +103,17 @@ // When String table needs to rehash unsigned int oopDesc::new_hash(jint seed) { + EXCEPTION_MARK; ResourceMark rm; int length; - jchar* chars = java_lang_String::as_unicode_string(this, length); - // Use alternate hashing algorithm on the string - return AltHashing::murmur3_32(seed, chars, length); + jchar* chars = java_lang_String::as_unicode_string(this, length, THREAD); + if (chars != NULL) { + // Use alternate hashing algorithm on the string + return AltHashing::murmur3_32(seed, chars, length); + } else { + vm_exit_out_of_memory(length, OOM_MALLOC_ERROR, "unable to create Unicode strings for String table rehash"); + return 0; + } } VerifyOopClosure VerifyOopClosure::verify_oop; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/opto/graphKit.cpp --- a/hotspot/src/share/vm/opto/graphKit.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/opto/graphKit.cpp Wed May 08 23:54:45 2013 -0700 @@ -3564,7 +3564,8 @@ Node* no_ctrl = NULL; Node* no_base = __ top(); - Node* zero = __ ConI(0); + Node* zero = __ ConI(0); + Node* zeroX = __ ConX(0); float likely = PROB_LIKELY(0.999); float unlikely = PROB_UNLIKELY(0.999); @@ -3590,7 +3591,9 @@ // if (!marking) __ if_then(marking, BoolTest::ne, zero); { - Node* index = __ load(__ ctrl(), index_adr, TypeInt::INT, T_INT, Compile::AliasIdxRaw); + BasicType index_bt = TypeX_X->basic_type(); + assert(sizeof(size_t) == type2aelembytes(index_bt), "Loading G1 PtrQueue::_index with wrong size."); + Node* index = __ load(__ ctrl(), index_adr, TypeX_X, index_bt, Compile::AliasIdxRaw); if (do_load) { // load original value @@ -3603,22 +3606,16 @@ Node* buffer = __ load(__ ctrl(), buffer_adr, TypeRawPtr::NOTNULL, T_ADDRESS, Compile::AliasIdxRaw); // is the queue for this thread full? - __ if_then(index, BoolTest::ne, zero, likely); { + __ if_then(index, BoolTest::ne, zeroX, likely); { // decrement the index - Node* next_index = __ SubI(index, __ ConI(sizeof(intptr_t))); - Node* next_indexX = next_index; -#ifdef _LP64 - // We could refine the type for what it's worth - // const TypeLong* lidxtype = TypeLong::make(CONST64(0), get_size_from_queue); - next_indexX = _gvn.transform( new (C) ConvI2LNode(next_index, TypeLong::make(0, max_jlong, Type::WidenMax)) ); -#endif + Node* next_index = _gvn.transform(new (C) SubXNode(index, __ ConX(sizeof(intptr_t)))); // Now get the buffer location we will log the previous value into and store it - Node *log_addr = __ AddP(no_base, buffer, next_indexX); + Node *log_addr = __ AddP(no_base, buffer, next_index); __ store(__ ctrl(), log_addr, pre_val, T_OBJECT, Compile::AliasIdxRaw); // update the index - __ store(__ ctrl(), index_adr, next_index, T_INT, Compile::AliasIdxRaw); + __ store(__ ctrl(), index_adr, next_index, index_bt, Compile::AliasIdxRaw); } __ else_(); { @@ -3645,26 +3642,21 @@ Node* buffer, const TypeFunc* tf) { - Node* zero = __ ConI(0); + Node* zero = __ ConI(0); + Node* zeroX = __ ConX(0); Node* no_base = __ top(); BasicType card_bt = T_BYTE; // Smash zero into card. MUST BE ORDERED WRT TO STORE __ storeCM(__ ctrl(), card_adr, zero, oop_store, oop_alias_idx, card_bt, Compile::AliasIdxRaw); // Now do the queue work - __ if_then(index, BoolTest::ne, zero); { - - Node* next_index = __ SubI(index, __ ConI(sizeof(intptr_t))); - Node* next_indexX = next_index; -#ifdef _LP64 - // We could refine the type for what it's worth - // const TypeLong* lidxtype = TypeLong::make(CONST64(0), get_size_from_queue); - next_indexX = _gvn.transform( new (C) ConvI2LNode(next_index, TypeLong::make(0, max_jlong, Type::WidenMax)) ); -#endif // _LP64 - Node* log_addr = __ AddP(no_base, buffer, next_indexX); + __ if_then(index, BoolTest::ne, zeroX); { + + Node* next_index = _gvn.transform(new (C) SubXNode(index, __ ConX(sizeof(intptr_t)))); + Node* log_addr = __ AddP(no_base, buffer, next_index); __ store(__ ctrl(), log_addr, card_adr, T_ADDRESS, Compile::AliasIdxRaw); - __ store(__ ctrl(), index_adr, next_index, T_INT, Compile::AliasIdxRaw); + __ store(__ ctrl(), index_adr, next_index, TypeX_X->basic_type(), Compile::AliasIdxRaw); } __ else_(); { __ make_leaf_call(tf, CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_post), "g1_wb_post", card_adr, __ thread()); @@ -3725,7 +3717,7 @@ // Now some values // Use ctrl to avoid hoisting these values past a safepoint, which could // potentially reset these fields in the JavaThread. - Node* index = __ load(__ ctrl(), index_adr, TypeInt::INT, T_INT, Compile::AliasIdxRaw); + Node* index = __ load(__ ctrl(), index_adr, TypeX_X, TypeX_X->basic_type(), Compile::AliasIdxRaw); Node* buffer = __ load(__ ctrl(), buffer_adr, TypeRawPtr::NOTNULL, T_ADDRESS, Compile::AliasIdxRaw); // Convert the store obj pointer to an int prior to doing math on it diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/opto/output.cpp --- a/hotspot/src/share/vm/opto/output.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/opto/output.cpp Wed May 08 23:54:45 2013 -0700 @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "asm/assembler.inline.hpp" +#include "code/compiledIC.hpp" #include "code/debugInfo.hpp" #include "code/debugInfoRec.hpp" #include "compiler/compileBroker.hpp" @@ -41,8 +42,6 @@ #include "runtime/handles.inline.hpp" #include "utilities/xmlstream.hpp" -extern uint size_java_to_interp(); -extern uint reloc_java_to_interp(); extern uint size_exception_handler(); extern uint size_deopt_handler(); @@ -389,15 +388,15 @@ MachNode *mach = nj->as_Mach(); blk_size += (mach->alignment_required() - 1) * relocInfo::addr_unit(); // assume worst case padding reloc_size += mach->reloc(); - if( mach->is_MachCall() ) { + if (mach->is_MachCall()) { MachCallNode *mcall = mach->as_MachCall(); // This destination address is NOT PC-relative mcall->method_set((intptr_t)mcall->entry_point()); - if( mcall->is_MachCallJava() && mcall->as_MachCallJava()->_method ) { - stub_size += size_java_to_interp(); - reloc_size += reloc_java_to_interp(); + if (mcall->is_MachCallJava() && mcall->as_MachCallJava()->_method) { + stub_size += CompiledStaticCall::to_interp_stub_size(); + reloc_size += CompiledStaticCall::reloc_to_interp_stub(); } } else if (mach->is_MachSafePoint()) { // If call/safepoint are adjacent, account for possible diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/prims/jvmtiEnv.cpp --- a/hotspot/src/share/vm/prims/jvmtiEnv.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/prims/jvmtiEnv.cpp Wed May 08 23:54:45 2013 -0700 @@ -259,7 +259,8 @@ // bytes to the InstanceKlass here because they have not been // validated and we're not at a safepoint. constantPoolHandle constants(current_thread, ikh->constants()); - MonitorLockerEx ml(constants->lock()); // lock constant pool while we query it + oop cplock = constants->lock(); + ObjectLocker ol(cplock, current_thread, cplock != NULL); // lock constant pool while we query it JvmtiClassFileReconstituter reconstituter(ikh); if (reconstituter.get_error() != JVMTI_ERROR_NONE) { @@ -2417,7 +2418,8 @@ instanceKlassHandle ikh(thread, k_oop); constantPoolHandle constants(thread, ikh->constants()); - MonitorLockerEx ml(constants->lock()); // lock constant pool while we query it + oop cplock = constants->lock(); + ObjectLocker ol(cplock, thread, cplock != NULL); // lock constant pool while we query it JvmtiConstantPoolReconstituter reconstituter(ikh); if (reconstituter.get_error() != JVMTI_ERROR_NONE) { diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp --- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp Wed May 08 23:54:45 2013 -0700 @@ -415,20 +415,26 @@ // this is an indirect CP entry so it needs special handling case JVM_CONSTANT_InvokeDynamic: { - // TBD: cross-checks and possible extra appends into CP and bsm operands - // are needed as well. This issue is tracked by a separate bug 8007037. - int bss_idx = scratch_cp->invoke_dynamic_bootstrap_specifier_index(scratch_i); - - int ref_i = scratch_cp->invoke_dynamic_name_and_type_ref_index_at(scratch_i); - int new_ref_i = find_or_append_indirect_entry(scratch_cp, ref_i, merge_cp_p, + // Index of the bootstrap specifier in the operands array + int old_bs_i = scratch_cp->invoke_dynamic_bootstrap_specifier_index(scratch_i); + int new_bs_i = find_or_append_operand(scratch_cp, old_bs_i, merge_cp_p, + merge_cp_length_p, THREAD); + // The bootstrap method NameAndType_info index + int old_ref_i = scratch_cp->invoke_dynamic_name_and_type_ref_index_at(scratch_i); + int new_ref_i = find_or_append_indirect_entry(scratch_cp, old_ref_i, merge_cp_p, merge_cp_length_p, THREAD); - if (new_ref_i != ref_i) { + if (new_bs_i != old_bs_i) { RC_TRACE(0x00080000, - ("InvokeDynamic entry@%d name_and_type ref_index change: %d to %d", - *merge_cp_length_p, ref_i, new_ref_i)); + ("InvokeDynamic entry@%d bootstrap_method_attr_index change: %d to %d", + *merge_cp_length_p, old_bs_i, new_bs_i)); } - - (*merge_cp_p)->invoke_dynamic_at_put(*merge_cp_length_p, bss_idx, new_ref_i); + if (new_ref_i != old_ref_i) { + RC_TRACE(0x00080000, + ("InvokeDynamic entry@%d name_and_type_index change: %d to %d", + *merge_cp_length_p, old_ref_i, new_ref_i)); + } + + (*merge_cp_p)->invoke_dynamic_at_put(*merge_cp_length_p, new_bs_i, new_ref_i); if (scratch_i != *merge_cp_length_p) { // The new entry in *merge_cp_p is at a different index than // the new entry in scratch_cp so we need to map the index values. @@ -492,6 +498,105 @@ } // end find_or_append_indirect_entry() +// Append a bootstrap specifier into the merge_cp operands that is semantically equal +// to the scratch_cp operands bootstrap specifier passed by the old_bs_i index. +// Recursively append new merge_cp entries referenced by the new bootstrap specifier. +void VM_RedefineClasses::append_operand(constantPoolHandle scratch_cp, int old_bs_i, + constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS) { + + int old_ref_i = scratch_cp->operand_bootstrap_method_ref_index_at(old_bs_i); + int new_ref_i = find_or_append_indirect_entry(scratch_cp, old_ref_i, merge_cp_p, + merge_cp_length_p, THREAD); + if (new_ref_i != old_ref_i) { + RC_TRACE(0x00080000, + ("operands entry@%d bootstrap method ref_index change: %d to %d", + _operands_cur_length, old_ref_i, new_ref_i)); + } + + Array* merge_ops = (*merge_cp_p)->operands(); + int new_bs_i = _operands_cur_length; + // We have _operands_cur_length == 0 when the merge_cp operands is empty yet. + // However, the operand_offset_at(0) was set in the extend_operands() call. + int new_base = (new_bs_i == 0) ? (*merge_cp_p)->operand_offset_at(0) + : (*merge_cp_p)->operand_next_offset_at(new_bs_i - 1); + int argc = scratch_cp->operand_argument_count_at(old_bs_i); + + ConstantPool::operand_offset_at_put(merge_ops, _operands_cur_length, new_base); + merge_ops->at_put(new_base++, new_ref_i); + merge_ops->at_put(new_base++, argc); + + for (int i = 0; i < argc; i++) { + int old_arg_ref_i = scratch_cp->operand_argument_index_at(old_bs_i, i); + int new_arg_ref_i = find_or_append_indirect_entry(scratch_cp, old_arg_ref_i, merge_cp_p, + merge_cp_length_p, THREAD); + merge_ops->at_put(new_base++, new_arg_ref_i); + if (new_arg_ref_i != old_arg_ref_i) { + RC_TRACE(0x00080000, + ("operands entry@%d bootstrap method argument ref_index change: %d to %d", + _operands_cur_length, old_arg_ref_i, new_arg_ref_i)); + } + } + if (old_bs_i != _operands_cur_length) { + // The bootstrap specifier in *merge_cp_p is at a different index than + // that in scratch_cp so we need to map the index values. + map_operand_index(old_bs_i, new_bs_i); + } + _operands_cur_length++; +} // end append_operand() + + +int VM_RedefineClasses::find_or_append_operand(constantPoolHandle scratch_cp, + int old_bs_i, constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS) { + + int new_bs_i = old_bs_i; // bootstrap specifier index + bool match = (old_bs_i < _operands_cur_length) && + scratch_cp->compare_operand_to(old_bs_i, *merge_cp_p, old_bs_i, THREAD); + + if (!match) { + // forward reference in *merge_cp_p or not a direct match + int found_i = scratch_cp->find_matching_operand(old_bs_i, *merge_cp_p, + _operands_cur_length, THREAD); + if (found_i != -1) { + guarantee(found_i != old_bs_i, "compare_operand_to() and find_matching_operand() disagree"); + // found a matching operand somewhere else in *merge_cp_p so just need a mapping + new_bs_i = found_i; + map_operand_index(old_bs_i, found_i); + } else { + // no match found so we have to append this bootstrap specifier to *merge_cp_p + append_operand(scratch_cp, old_bs_i, merge_cp_p, merge_cp_length_p, THREAD); + new_bs_i = _operands_cur_length - 1; + } + } + return new_bs_i; +} // end find_or_append_operand() + + +void VM_RedefineClasses::finalize_operands_merge(constantPoolHandle merge_cp, TRAPS) { + if (merge_cp->operands() == NULL) { + return; + } + // Shrink the merge_cp operands + merge_cp->shrink_operands(_operands_cur_length, CHECK); + + if (RC_TRACE_ENABLED(0x00040000)) { + // don't want to loop unless we are tracing + int count = 0; + for (int i = 1; i < _operands_index_map_p->length(); i++) { + int value = _operands_index_map_p->at(i); + if (value != -1) { + RC_TRACE_WITH_THREAD(0x00040000, THREAD, + ("operands_index_map[%d]: old=%d new=%d", count, i, value)); + count++; + } + } + } + // Clean-up + _operands_index_map_p = NULL; + _operands_cur_length = 0; + _operands_index_map_count = 0; +} // end finalize_operands_merge() + + jvmtiError VM_RedefineClasses::compare_and_normalize_class_versions( instanceKlassHandle the_class, instanceKlassHandle scratch_class) { @@ -765,6 +870,31 @@ } // end find_new_index() +// Find new bootstrap specifier index value for old bootstrap specifier index +// value by seaching the index map. Returns unused index (-1) if there is +// no mapped value for the old bootstrap specifier index. +int VM_RedefineClasses::find_new_operand_index(int old_index) { + if (_operands_index_map_count == 0) { + // map is empty so nothing can be found + return -1; + } + + if (old_index == -1 || old_index >= _operands_index_map_p->length()) { + // The old_index is out of range so it is not mapped. + // This should not happen in regular constant pool merging use. + return -1; + } + + int value = _operands_index_map_p->at(old_index); + if (value == -1) { + // the old_index is not mapped + return -1; + } + + return value; +} // end find_new_operand_index() + + // Returns true if the current mismatch is due to a resolved/unresolved // class pair. Otherwise, returns false. bool VM_RedefineClasses::is_unresolved_class_mismatch(constantPoolHandle cp1, @@ -1014,6 +1144,25 @@ } // end map_index() +// Map old_index to new_index as needed. +void VM_RedefineClasses::map_operand_index(int old_index, int new_index) { + if (find_new_operand_index(old_index) != -1) { + // old_index is already mapped + return; + } + + if (old_index == new_index) { + // no mapping is needed + return; + } + + _operands_index_map_p->at_put(old_index, new_index); + _operands_index_map_count++; + + RC_TRACE(0x00040000, ("mapped bootstrap specifier at index %d to %d", old_index, new_index)); +} // end map_index() + + // Merge old_cp and scratch_cp and return the results of the merge via // merge_cp_p. The number of entries in *merge_cp_p is returned via // merge_cp_length_p. The entries in old_cp occupy the same locations @@ -1086,6 +1235,7 @@ } // end for each old_cp entry ConstantPool::copy_operands(old_cp, *merge_cp_p, CHECK_0); + (*merge_cp_p)->extend_operands(scratch_cp, CHECK_0); // We don't need to sanity check that *merge_cp_length_p is within // *merge_cp_p bounds since we have the minimum on-entry check above. @@ -1198,6 +1348,8 @@ CHECK_0); } + finalize_operands_merge(*merge_cp_p, THREAD); + RC_TRACE_WITH_THREAD(0x00020000, THREAD, ("after pass 1b: merge_cp_len=%d, scratch_i=%d, index_map_len=%d", *merge_cp_length_p, scratch_i, _index_map_count)); @@ -1270,6 +1422,11 @@ _index_map_count = 0; _index_map_p = new intArray(scratch_cp->length(), -1); + _operands_cur_length = ConstantPool::operand_array_length(old_cp->operands()); + _operands_index_map_count = 0; + _operands_index_map_p = new intArray( + ConstantPool::operand_array_length(scratch_cp->operands()), -1); + // reference to the cp holder is needed for copy_operands() merge_cp->set_pool_holder(scratch_class()); bool result = merge_constant_pools(old_cp, scratch_cp, &merge_cp, @@ -1400,7 +1557,6 @@ return true; } // end rewrite_cp_refs() - // Rewrite constant pool references in the methods. bool VM_RedefineClasses::rewrite_cp_refs_in_methods( instanceKlassHandle scratch_class, TRAPS) { diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp --- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp Wed May 08 23:54:45 2013 -0700 @@ -359,8 +359,15 @@ // _index_map_p contains any entries. int _index_map_count; intArray * _index_map_p; + + // _operands_index_map_count is just an optimization for knowing if + // _operands_index_map_p contains any entries. + int _operands_cur_length; + int _operands_index_map_count; + intArray * _operands_index_map_p; + // ptr to _class_count scratch_classes - Klass** _scratch_classes; + Klass** _scratch_classes; jvmtiError _res; // Performance measurement support. These timers do not cover all @@ -422,12 +429,19 @@ // Support for constant pool merging (these routines are in alpha order): void append_entry(constantPoolHandle scratch_cp, int scratch_i, constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS); + void append_operand(constantPoolHandle scratch_cp, int scratch_bootstrap_spec_index, + constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS); + void finalize_operands_merge(constantPoolHandle merge_cp, TRAPS); int find_or_append_indirect_entry(constantPoolHandle scratch_cp, int scratch_i, constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS); + int find_or_append_operand(constantPoolHandle scratch_cp, int scratch_bootstrap_spec_index, + constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS); int find_new_index(int old_index); + int find_new_operand_index(int old_bootstrap_spec_index); bool is_unresolved_class_mismatch(constantPoolHandle cp1, int index1, constantPoolHandle cp2, int index2); void map_index(constantPoolHandle scratch_cp, int old_index, int new_index); + void map_operand_index(int old_bootstrap_spec_index, int new_bootstrap_spec_index); bool merge_constant_pools(constantPoolHandle old_cp, constantPoolHandle scratch_cp, constantPoolHandle *merge_cp_p, int *merge_cp_length_p, TRAPS); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/prims/jvmtiTagMap.cpp --- a/hotspot/src/share/vm/prims/jvmtiTagMap.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/prims/jvmtiTagMap.cpp Wed May 08 23:54:45 2013 -0700 @@ -153,7 +153,8 @@ size_t s = initial_size * sizeof(JvmtiTagHashmapEntry*); _table = (JvmtiTagHashmapEntry**)os::malloc(s, mtInternal); if (_table == NULL) { - vm_exit_out_of_memory(s, "unable to allocate initial hashtable for jvmti object tags"); + vm_exit_out_of_memory(s, OOM_MALLOC_ERROR, + "unable to allocate initial hashtable for jvmti object tags"); } for (int i=0; ilookup(name, len); - if (found_string == NULL) { - return false; - } - return true; + jchar* name = java_lang_String::as_unicode_string(JNIHandles::resolve(javaString), len, CHECK_false); + return (StringTable::lookup(name, len) != NULL); WB_END @@ -324,6 +320,11 @@ Universe::heap()->collect(GCCause::_last_ditch_collection); WB_END + +WB_ENTRY(jlong, WB_ReserveMemory(JNIEnv* env, jobject o, jlong size)) + return (jlong)os::reserve_memory(size, NULL, 0); +WB_END + //Some convenience methods to deal with objects from java int WhiteBox::offset_for_field(const char* field_name, oop object, Symbol* signature_symbol) { @@ -425,6 +426,8 @@ CC"(Ljava/lang/reflect/Executable;)V", (void*)&WB_ClearMethodState}, {CC"isInStringTable", CC"(Ljava/lang/String;)Z", (void*)&WB_IsInStringTable }, {CC"fullGC", CC"()V", (void*)&WB_FullGC }, + + {CC"reserveMemory", CC"(J)J", (void*)&WB_ReserveMemory }, }; #undef CC @@ -436,9 +439,29 @@ instanceKlassHandle ikh = instanceKlassHandle(JNIHandles::resolve(wbclass)->klass()); Handle loader(ikh->class_loader()); if (loader.is_null()) { + ResourceMark rm; ThreadToNativeFromVM ttnfv(thread); // can't be in VM when we call JNI - jint result = env->RegisterNatives(wbclass, methods, sizeof(methods)/sizeof(methods[0])); - if (result == 0) { + bool result = true; + // one by one registration natives for exception catching + jclass exceptionKlass = env->FindClass(vmSymbols::java_lang_NoSuchMethodError()->as_C_string()); + for (int i = 0, n = sizeof(methods) / sizeof(methods[0]); i < n; ++i) { + if (env->RegisterNatives(wbclass, methods + i, 1) != 0) { + result = false; + if (env->ExceptionCheck() && env->IsInstanceOf(env->ExceptionOccurred(), exceptionKlass)) { + // j.l.NoSuchMethodError is thrown when a method can't be found or a method is not native + // ignoring the exception + tty->print_cr("Warning: 'NoSuchMethodError' on register of sun.hotspot.WhiteBox::%s%s", methods[i].name, methods[i].signature); + env->ExceptionClear(); + } else { + // register is failed w/o exception or w/ unexpected exception + tty->print_cr("Warning: unexpected error on register of sun.hotspot.WhiteBox::%s%s. All methods will be unregistered", methods[i].name, methods[i].signature); + env->UnregisterNatives(wbclass); + break; + } + } + } + + if (result) { WhiteBox::set_used(); } } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/arguments.cpp --- a/hotspot/src/share/vm/runtime/arguments.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/arguments.cpp Wed May 08 23:54:45 2013 -0700 @@ -2224,6 +2224,55 @@ return JNI_OK; } +// Checks if name in command-line argument -agent{lib,path}:name[=options] +// represents a valid HPROF of JDWP agent. is_path==true denotes that we +// are dealing with -agentpath (case where name is a path), otherwise with +// -agentlib +bool valid_hprof_or_jdwp_agent(char *name, bool is_path) { + char *_name; + const char *_hprof = "hprof", *_jdwp = "jdwp"; + size_t _len_hprof, _len_jdwp, _len_prefix; + + if (is_path) { + if ((_name = strrchr(name, (int) *os::file_separator())) == NULL) { + return false; + } + + _name++; // skip past last path separator + _len_prefix = strlen(JNI_LIB_PREFIX); + + if (strncmp(_name, JNI_LIB_PREFIX, _len_prefix) != 0) { + return false; + } + + _name += _len_prefix; + _len_hprof = strlen(_hprof); + _len_jdwp = strlen(_jdwp); + + if (strncmp(_name, _hprof, _len_hprof) == 0) { + _name += _len_hprof; + } + else if (strncmp(_name, _jdwp, _len_jdwp) == 0) { + _name += _len_jdwp; + } + else { + return false; + } + + if (strcmp(_name, JNI_LIB_SUFFIX) != 0) { + return false; + } + + return true; + } + + if (strcmp(name, _hprof) == 0 || strcmp(name, _jdwp) == 0) { + return true; + } + + return false; +} + jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, SysClassPath* scp_p, bool* scp_assembly_required_p, @@ -2322,7 +2371,7 @@ options = strcpy(NEW_C_HEAP_ARRAY(char, strlen(pos + 1) + 1, mtInternal), pos + 1); } #if !INCLUDE_JVMTI - if ((strcmp(name, "hprof") == 0) || (strcmp(name, "jdwp") == 0)) { + if (valid_hprof_or_jdwp_agent(name, is_absolute_path)) { jio_fprintf(defaultStream::error_stream(), "Profiling and debugging agents are not supported in this VM\n"); return JNI_ERR; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/compilationPolicy.cpp --- a/hotspot/src/share/vm/runtime/compilationPolicy.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/compilationPolicy.cpp Wed May 08 23:54:45 2013 -0700 @@ -109,6 +109,9 @@ // Returns true if m is allowed to be compiled bool CompilationPolicy::can_be_compiled(methodHandle m, int comp_level) { + // allow any levels for WhiteBox + assert(WhiteBoxAPI || comp_level == CompLevel_all || is_compile(comp_level), "illegal compilation level"); + if (m->is_abstract()) return false; if (DontCompileHugeMethods && m->code_size() > HugeMethodLimit) return false; @@ -122,7 +125,13 @@ return false; } if (comp_level == CompLevel_all) { - return !m->is_not_compilable(CompLevel_simple) && !m->is_not_compilable(CompLevel_full_optimization); + if (TieredCompilation) { + // enough to be compilable at any level for tiered + return !m->is_not_compilable(CompLevel_simple) || !m->is_not_compilable(CompLevel_full_optimization); + } else { + // must be compilable at available level for non-tiered + return !m->is_not_compilable(CompLevel_highest_tier); + } } else if (is_compile(comp_level)) { return !m->is_not_compilable(comp_level); } @@ -436,7 +445,7 @@ reset_counter_for_invocation_event(m); const char* comment = "count"; - if (is_compilation_enabled() && can_be_compiled(m)) { + if (is_compilation_enabled() && can_be_compiled(m, comp_level)) { nmethod* nm = m->code(); if (nm == NULL ) { CompileBroker::compile_method(m, InvocationEntryBci, comp_level, m, hot_count, comment, thread); @@ -449,7 +458,7 @@ const int hot_count = m->backedge_count(); const char* comment = "backedge_count"; - if (is_compilation_enabled() && !m->is_not_osr_compilable(comp_level) && can_be_compiled(m)) { + if (is_compilation_enabled() && !m->is_not_osr_compilable(comp_level) && can_be_compiled(m, comp_level)) { CompileBroker::compile_method(m, bci, comp_level, m, hot_count, comment, thread); NOT_PRODUCT(trace_osr_completion(m->lookup_osr_nmethod_for(bci, comp_level, true));) } @@ -467,7 +476,7 @@ reset_counter_for_invocation_event(m); const char* comment = "count"; - if (is_compilation_enabled() && m->code() == NULL && can_be_compiled(m)) { + if (is_compilation_enabled() && m->code() == NULL && can_be_compiled(m, comp_level)) { ResourceMark rm(thread); frame fr = thread->last_frame(); assert(fr.is_interpreted_frame(), "must be interpreted"); @@ -505,7 +514,7 @@ const int hot_count = m->backedge_count(); const char* comment = "backedge_count"; - if (is_compilation_enabled() && !m->is_not_osr_compilable(comp_level) && can_be_compiled(m)) { + if (is_compilation_enabled() && !m->is_not_osr_compilable(comp_level) && can_be_compiled(m, comp_level)) { CompileBroker::compile_method(m, bci, comp_level, m, hot_count, comment, thread); NOT_PRODUCT(trace_osr_completion(m->lookup_osr_nmethod_for(bci, comp_level, true));) } @@ -600,7 +609,7 @@ // If the caller method is too big or something then we do not want to // compile it just to inline a method - if (!can_be_compiled(next_m)) { + if (!can_be_compiled(next_m, CompLevel_any)) { msg = "caller cannot be compiled"; break; } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/globals.hpp --- a/hotspot/src/share/vm/runtime/globals.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/globals.hpp Wed May 08 23:54:45 2013 -0700 @@ -2123,6 +2123,9 @@ product(intx, PrefetchFieldsAhead, -1, \ "How many fields ahead to prefetch in oop scan (<= 0 means off)") \ \ + diagnostic(bool, VerifySilently, false, \ + "Don't print print the verification progress") \ + \ diagnostic(bool, VerifyDuringStartup, false, \ "Verify memory system before executing any Java code " \ "during VM initialization") \ @@ -3179,6 +3182,9 @@ product(uintx, CodeCacheFlushingMinimumFreeSpace, 1500*K, \ "When less than X space left, start code cache cleaning") \ \ + product(uintx, CodeCacheFlushingFraction, 2, \ + "Fraction of the code cache that is flushed when full") \ + \ /* interpreter debugging */ \ develop(intx, BinarySwitchThreshold, 5, \ "Minimal number of lookupswitch entries for rewriting to binary " \ @@ -3223,8 +3229,9 @@ develop(bool, ReplayCompiles, false, \ "Enable replay of compilations from ReplayDataFile") \ \ - develop(ccstr, ReplayDataFile, "replay.txt", \ - "file containing compilation replay information") \ + product(ccstr, ReplayDataFile, NULL, \ + "File containing compilation replay information" \ + "[default: ./replay_pid%p.log] (%p replaced with pid)") \ \ develop(intx, ReplaySuppressInitializers, 2, \ "Controls handling of class initialization during replay" \ @@ -3237,8 +3244,8 @@ develop(bool, ReplayIgnoreInitErrors, false, \ "Ignore exceptions thrown during initialization for replay") \ \ - develop(bool, DumpReplayDataOnError, true, \ - "record replay data for crashing compiler threads") \ + product(bool, DumpReplayDataOnError, true, \ + "Record replay data for crashing compiler threads") \ \ product(bool, CICompilerCountPerCPU, false, \ "1 compiler thread for log(N CPUs)") \ @@ -3247,7 +3254,9 @@ "Fire OutOfMemoryErrors throughout CI for testing the compiler " \ "(non-negative value throws OOM after this many CI accesses " \ "in each compile)") \ - \ + notproduct(intx, CICrashAt, -1, \ + "id of compilation to trigger assert in compiler thread for " \ + "the purpose of testing, e.g. generation of replay data") \ notproduct(bool, CIObjectFactoryVerify, false, \ "enable potentially expensive verification in ciObjectFactory") \ \ diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/objectMonitor.cpp --- a/hotspot/src/share/vm/runtime/objectMonitor.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/objectMonitor.cpp Wed May 08 23:54:45 2013 -0700 @@ -2404,7 +2404,7 @@ size_t sz = strlen (SyncKnobs) ; char * knobs = (char *) malloc (sz + 2) ; if (knobs == NULL) { - vm_exit_out_of_memory (sz + 2, "Parse SyncKnobs") ; + vm_exit_out_of_memory (sz + 2, OOM_MALLOC_ERROR, "Parse SyncKnobs") ; guarantee (0, "invariant") ; } strcpy (knobs, SyncKnobs) ; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/os.hpp --- a/hotspot/src/share/vm/runtime/os.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/os.hpp Wed May 08 23:54:45 2013 -0700 @@ -454,6 +454,7 @@ // File i/o operations static const int default_file_open_flags(); static int open(const char *path, int oflag, int mode); + static FILE* open(int fd, const char* mode); static int close(int fd); static jlong lseek(int fd, jlong offset, int whence); static char* native_path(char *path); @@ -477,7 +478,7 @@ static const char* dll_file_extension(); static const char* get_temp_directory(); - static const char* get_current_directory(char *buf, int buflen); + static const char* get_current_directory(char *buf, size_t buflen); // Builds a platform-specific full library path given a ld path and lib name // Returns true if buffer contains full path to existing file, false otherwise diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/sharedRuntime.cpp --- a/hotspot/src/share/vm/runtime/sharedRuntime.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp Wed May 08 23:54:45 2013 -0700 @@ -1316,12 +1316,6 @@ assert(stub_frame.is_runtime_frame(), "sanity check"); frame caller_frame = stub_frame.sender(®_map); - // MethodHandle invokes don't have a CompiledIC and should always - // simply redispatch to the callee_target. - address sender_pc = caller_frame.pc(); - CodeBlob* sender_cb = caller_frame.cb(); - nmethod* sender_nm = sender_cb->as_nmethod_or_null(); - if (caller_frame.is_interpreted_frame() || caller_frame.is_entry_frame()) { Method* callee = thread->callee_target(); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp --- a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp Wed May 08 23:54:45 2013 -0700 @@ -154,9 +154,10 @@ // Set carry flags on the counters if necessary void SimpleThresholdPolicy::handle_counter_overflow(Method* method) { MethodCounters *mcs = method->method_counters(); - assert(mcs != NULL, ""); - set_carry_if_necessary(mcs->invocation_counter()); - set_carry_if_necessary(mcs->backedge_counter()); + if (mcs != NULL) { + set_carry_if_necessary(mcs->invocation_counter()); + set_carry_if_necessary(mcs->backedge_counter()); + } MethodData* mdo = method->method_data(); if (mdo != NULL) { set_carry_if_necessary(mdo->invocation_counter()); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/stubRoutines.cpp --- a/hotspot/src/share/vm/runtime/stubRoutines.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/stubRoutines.cpp Wed May 08 23:54:45 2013 -0700 @@ -147,7 +147,7 @@ TraceTime timer("StubRoutines generation 1", TraceStartupTime); _code1 = BufferBlob::create("StubRoutines (1)", code_size1); if (_code1 == NULL) { - vm_exit_out_of_memory(code_size1, "CodeCache: no room for StubRoutines (1)"); + vm_exit_out_of_memory(code_size1, OOM_MALLOC_ERROR, "CodeCache: no room for StubRoutines (1)"); } CodeBuffer buffer(_code1); StubGenerator_generate(&buffer, false); @@ -199,7 +199,7 @@ TraceTime timer("StubRoutines generation 2", TraceStartupTime); _code2 = BufferBlob::create("StubRoutines (2)", code_size2); if (_code2 == NULL) { - vm_exit_out_of_memory(code_size2, "CodeCache: no room for StubRoutines (2)"); + vm_exit_out_of_memory(code_size2, OOM_MALLOC_ERROR, "CodeCache: no room for StubRoutines (2)"); } CodeBuffer buffer(_code2); StubGenerator_generate(&buffer, true); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/sweeper.cpp --- a/hotspot/src/share/vm/runtime/sweeper.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/sweeper.cpp Wed May 08 23:54:45 2013 -0700 @@ -136,13 +136,12 @@ jint NMethodSweeper::_locked_seen = 0; jint NMethodSweeper::_not_entrant_seen_on_stack = 0; -bool NMethodSweeper::_rescan = false; -bool NMethodSweeper::_do_sweep = false; -bool NMethodSweeper::_was_full = false; -jint NMethodSweeper::_advise_to_sweep = 0; -jlong NMethodSweeper::_last_was_full = 0; -uint NMethodSweeper::_highest_marked = 0; -long NMethodSweeper::_was_full_traversal = 0; +bool NMethodSweeper::_resweep = false; +jint NMethodSweeper::_flush_token = 0; +jlong NMethodSweeper::_last_full_flush_time = 0; +int NMethodSweeper::_highest_marked = 0; +int NMethodSweeper::_dead_compile_ids = 0; +long NMethodSweeper::_last_flush_traversal_id = 0; class MarkActivationClosure: public CodeBlobClosure { public: @@ -155,20 +154,16 @@ }; static MarkActivationClosure mark_activation_closure; +bool NMethodSweeper::sweep_in_progress() { + return (_current != NULL); +} + void NMethodSweeper::scan_stacks() { assert(SafepointSynchronize::is_at_safepoint(), "must be executed at a safepoint"); if (!MethodFlushing) return; - _do_sweep = true; // No need to synchronize access, since this is always executed at a - // safepoint. If we aren't in the middle of scan and a rescan - // hasn't been requested then just return. If UseCodeCacheFlushing is on and - // code cache flushing is in progress, don't skip sweeping to help make progress - // clearing space in the code cache. - if ((_current == NULL && !_rescan) && !(UseCodeCacheFlushing && !CompileBroker::should_compile_new_jobs())) { - _do_sweep = false; - return; - } + // safepoint. // Make sure CompiledIC_lock in unlocked, since we might update some // inline caches. If it is, we just bail-out and try later. @@ -176,7 +171,7 @@ // Check for restart assert(CodeCache::find_blob_unsafe(_current) == _current, "Sweeper nmethod cached state invalid"); - if (_current == NULL) { + if (!sweep_in_progress() && _resweep) { _seen = 0; _invocations = NmethodSweepFraction; _current = CodeCache::first_nmethod(); @@ -187,39 +182,30 @@ Threads::nmethods_do(&mark_activation_closure); // reset the flags since we started a scan from the beginning. - _rescan = false; + _resweep = false; _locked_seen = 0; _not_entrant_seen_on_stack = 0; } if (UseCodeCacheFlushing) { - if (!CodeCache::needs_flushing()) { - // scan_stacks() runs during a safepoint, no race with setters - _advise_to_sweep = 0; + // only allow new flushes after the interval is complete. + jlong now = os::javaTimeMillis(); + jlong max_interval = (jlong)MinCodeCacheFlushingInterval * (jlong)1000; + jlong curr_interval = now - _last_full_flush_time; + if (curr_interval > max_interval) { + _flush_token = 0; } - if (was_full()) { - // There was some progress so attempt to restart the compiler - jlong now = os::javaTimeMillis(); - jlong max_interval = (jlong)MinCodeCacheFlushingInterval * (jlong)1000; - jlong curr_interval = now - _last_was_full; - if ((!CodeCache::needs_flushing()) && (curr_interval > max_interval)) { - CompileBroker::set_should_compile_new_jobs(CompileBroker::run_compilation); - set_was_full(false); - - // Update the _last_was_full time so we can tell how fast the - // code cache is filling up - _last_was_full = os::javaTimeMillis(); - - log_sweep("restart_compiler"); - } + if (!CodeCache::needs_flushing() && !CompileBroker::should_compile_new_jobs()) { + CompileBroker::set_should_compile_new_jobs(CompileBroker::run_compilation); + log_sweep("restart_compiler"); } } } void NMethodSweeper::possibly_sweep() { assert(JavaThread::current()->thread_state() == _thread_in_vm, "must run in vm mode"); - if ((!MethodFlushing) || (!_do_sweep)) return; + if (!MethodFlushing || !sweep_in_progress()) return; if (_invocations > 0) { // Only one thread at a time will sweep @@ -253,6 +239,14 @@ tty->print_cr("### Sweep at %d out of %d. Invocations left: %d", _seen, CodeCache::nof_nmethods(), _invocations); } + if (!CompileBroker::should_compile_new_jobs()) { + // If we have turned off compilations we might as well do full sweeps + // in order to reach the clean state faster. Otherwise the sleeping compiler + // threads will slow down sweeping. After a few iterations the cache + // will be clean and sweeping stops (_resweep will not be set) + _invocations = 1; + } + // We want to visit all nmethods after NmethodSweepFraction // invocations so divide the remaining number of nmethods by the // remaining number of invocations. This is only an estimate since @@ -296,7 +290,7 @@ assert(_invocations > 1 || _current == NULL, "must have scanned the whole cache"); - if (_current == NULL && !_rescan && (_locked_seen || _not_entrant_seen_on_stack)) { + if (!sweep_in_progress() && !_resweep && (_locked_seen || _not_entrant_seen_on_stack)) { // we've completed a scan without making progress but there were // nmethods we were unable to process either because they were // locked or were still on stack. We don't have to aggresively @@ -318,6 +312,13 @@ if (_invocations == 1) { log_sweep("finished"); } + + // Sweeper is the only case where memory is released, + // check here if it is time to restart the compiler. + if (UseCodeCacheFlushing && !CompileBroker::should_compile_new_jobs() && !CodeCache::needs_flushing()) { + CompileBroker::set_should_compile_new_jobs(CompileBroker::run_compilation); + log_sweep("restart_compiler"); + } } class NMethodMarker: public StackObj { @@ -392,7 +393,7 @@ tty->print_cr("### Nmethod %3d/" PTR_FORMAT " (zombie) being marked for reclamation", nm->compile_id(), nm); } nm->mark_for_reclamation(); - _rescan = true; + _resweep = true; SWEEP(nm); } } else if (nm->is_not_entrant()) { @@ -403,7 +404,7 @@ tty->print_cr("### Nmethod %3d/" PTR_FORMAT " (not entrant) being made zombie", nm->compile_id(), nm); } nm->make_zombie(); - _rescan = true; + _resweep = true; SWEEP(nm); } else { // Still alive, clean up its inline caches @@ -425,16 +426,15 @@ release_nmethod(nm); } else { nm->make_zombie(); - _rescan = true; + _resweep = true; SWEEP(nm); } } else { assert(nm->is_alive(), "should be alive"); if (UseCodeCacheFlushing) { - if ((nm->method()->code() != nm) && !(nm->is_locked_by_vm()) && !(nm->is_osr_method()) && - (_traversals > _was_full_traversal+2) && (((uint)nm->compile_id()) < _highest_marked) && - CodeCache::needs_flushing()) { + if (nm->is_speculatively_disconnected() && !nm->is_locked_by_vm() && !nm->is_osr_method() && + (_traversals > _last_flush_traversal_id + 2) && (nm->compile_id() < _highest_marked)) { // This method has not been called since the forced cleanup happened nm->make_not_entrant(); } @@ -457,41 +457,27 @@ // _code field is restored and the Method*/nmethod // go back to their normal state. void NMethodSweeper::handle_full_code_cache(bool is_full) { - // Only the first one to notice can advise us to start early cleaning - if (!is_full){ - jint old = Atomic::cmpxchg( 1, &_advise_to_sweep, 0 ); - if (old != 0) { - return; - } - } if (is_full) { // Since code cache is full, immediately stop new compiles - bool did_set = CompileBroker::set_should_compile_new_jobs(CompileBroker::stop_compilation); - if (!did_set) { - // only the first to notice can start the cleaning, - // others will go back and block - return; + if (CompileBroker::set_should_compile_new_jobs(CompileBroker::stop_compilation)) { + log_sweep("disable_compiler"); } - set_was_full(true); + } - // If we run out within MinCodeCacheFlushingInterval of the last unload time, give up - jlong now = os::javaTimeMillis(); - jlong max_interval = (jlong)MinCodeCacheFlushingInterval * (jlong)1000; - jlong curr_interval = now - _last_was_full; - if (curr_interval < max_interval) { - _rescan = true; - log_sweep("disable_compiler", "flushing_interval='" UINT64_FORMAT "'", - curr_interval/1000); - return; - } + // Make sure only one thread can flush + // The token is reset after CodeCacheMinimumFlushInterval in scan stacks, + // no need to check the timeout here. + jint old = Atomic::cmpxchg( 1, &_flush_token, 0 ); + if (old != 0) { + return; } VM_HandleFullCodeCache op(is_full); VMThread::execute(&op); - // rescan again as soon as possible - _rescan = true; + // resweep again as soon as possible + _resweep = true; } void NMethodSweeper::speculative_disconnect_nmethods(bool is_full) { @@ -500,62 +486,64 @@ debug_only(jlong start = os::javaTimeMillis();) - if ((!was_full()) && (is_full)) { - if (!CodeCache::needs_flushing()) { - log_sweep("restart_compiler"); - CompileBroker::set_should_compile_new_jobs(CompileBroker::run_compilation); - return; - } - } + // Traverse the code cache trying to dump the oldest nmethods + int curr_max_comp_id = CompileBroker::get_compilation_id(); + int flush_target = ((curr_max_comp_id - _dead_compile_ids) / CodeCacheFlushingFraction) + _dead_compile_ids; - // Traverse the code cache trying to dump the oldest nmethods - uint curr_max_comp_id = CompileBroker::get_compilation_id(); - uint flush_target = ((curr_max_comp_id - _highest_marked) >> 1) + _highest_marked; log_sweep("start_cleaning"); nmethod* nm = CodeCache::alive_nmethod(CodeCache::first()); jint disconnected = 0; jint made_not_entrant = 0; + jint nmethod_count = 0; + while ((nm != NULL)){ - uint curr_comp_id = nm->compile_id(); + int curr_comp_id = nm->compile_id(); // OSR methods cannot be flushed like this. Also, don't flush native methods // since they are part of the JDK in most cases - if (nm->is_in_use() && (!nm->is_osr_method()) && (!nm->is_locked_by_vm()) && - (!nm->is_native_method()) && ((curr_comp_id < flush_target))) { + if (!nm->is_osr_method() && !nm->is_locked_by_vm() && !nm->is_native_method()) { + + // only count methods that can be speculatively disconnected + nmethod_count++; - if ((nm->method()->code() == nm)) { - // This method has not been previously considered for - // unloading or it was restored already - CodeCache::speculatively_disconnect(nm); - disconnected++; - } else if (nm->is_speculatively_disconnected()) { - // This method was previously considered for preemptive unloading and was not called since then - CompilationPolicy::policy()->delay_compilation(nm->method()); - nm->make_not_entrant(); - made_not_entrant++; - } + if (nm->is_in_use() && (curr_comp_id < flush_target)) { + if ((nm->method()->code() == nm)) { + // This method has not been previously considered for + // unloading or it was restored already + CodeCache::speculatively_disconnect(nm); + disconnected++; + } else if (nm->is_speculatively_disconnected()) { + // This method was previously considered for preemptive unloading and was not called since then + CompilationPolicy::policy()->delay_compilation(nm->method()); + nm->make_not_entrant(); + made_not_entrant++; + } - if (curr_comp_id > _highest_marked) { - _highest_marked = curr_comp_id; + if (curr_comp_id > _highest_marked) { + _highest_marked = curr_comp_id; + } } } nm = CodeCache::alive_nmethod(CodeCache::next(nm)); } + // remember how many compile_ids wheren't seen last flush. + _dead_compile_ids = curr_max_comp_id - nmethod_count; + log_sweep("stop_cleaning", "disconnected='" UINT32_FORMAT "' made_not_entrant='" UINT32_FORMAT "'", disconnected, made_not_entrant); // Shut off compiler. Sweeper will start over with a new stack scan and // traversal cycle and turn it back on if it clears enough space. - if (was_full()) { - _last_was_full = os::javaTimeMillis(); - CompileBroker::set_should_compile_new_jobs(CompileBroker::stop_compilation); + if (is_full) { + _last_full_flush_time = os::javaTimeMillis(); } // After two more traversals the sweeper will get rid of unrestored nmethods - _was_full_traversal = _traversals; + _last_flush_traversal_id = _traversals; + _resweep = true; #ifdef ASSERT jlong end = os::javaTimeMillis(); if(PrintMethodFlushing && Verbose) { diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/sweeper.hpp --- a/hotspot/src/share/vm/runtime/sweeper.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/sweeper.hpp Wed May 08 23:54:45 2013 -0700 @@ -35,26 +35,29 @@ static nmethod* _current; // Current nmethod static int _seen; // Nof. nmethod we have currently processed in current pass of CodeCache - static volatile int _invocations; // No. of invocations left until we are completed with this pass - static volatile int _sweep_started; // Flag to control conc sweeper + static volatile int _invocations; // No. of invocations left until we are completed with this pass + static volatile int _sweep_started; // Flag to control conc sweeper - static bool _rescan; // Indicates that we should do a full rescan of the - // of the code cache looking for work to do. - static bool _do_sweep; // Flag to skip the conc sweep if no stack scan happened - static int _locked_seen; // Number of locked nmethods encountered during the scan + //The following are reset in scan_stacks and synchronized by the safepoint + static bool _resweep; // Indicates that a change has happend and we want another sweep, + // always checked and reset at a safepoint so memory will be in sync. + static int _locked_seen; // Number of locked nmethods encountered during the scan static int _not_entrant_seen_on_stack; // Number of not entrant nmethod were are still on stack + static jint _flush_token; // token that guards method flushing, making sure it is executed only once. - static bool _was_full; // remember if we did emergency unloading - static jint _advise_to_sweep; // flag to indicate code cache getting full - static jlong _last_was_full; // timestamp of last emergency unloading - static uint _highest_marked; // highest compile id dumped at last emergency unloading - static long _was_full_traversal; // trav number at last emergency unloading + // These are set during a flush, a VM-operation + static long _last_flush_traversal_id; // trav number at last flush unloading + static jlong _last_full_flush_time; // timestamp of last emergency unloading + + // These are synchronized by the _sweep_started token + static int _highest_marked; // highest compile id dumped at last emergency unloading + static int _dead_compile_ids; // number of compile ids that where not in the cache last flush static void process_nmethod(nmethod *nm); - static void release_nmethod(nmethod* nm); static void log_sweep(const char* msg, const char* format = NULL, ...); + static bool sweep_in_progress(); public: static long traversal_count() { return _traversals; } @@ -71,17 +74,14 @@ static void possibly_sweep(); // Compiler threads call this to sweep static void notify(nmethod* nm) { - // Perform a full scan of the code cache from the beginning. No + // Request a new sweep of the code cache from the beginning. No // need to synchronize the setting of this flag since it only // changes to false at safepoint so we can never overwrite it with false. - _rescan = true; + _resweep = true; } static void handle_full_code_cache(bool is_full); // Called by compilers who fail to allocate static void speculative_disconnect_nmethods(bool was_full); // Called by vm op to deal with alloc failure - - static void set_was_full(bool state) { _was_full = state; } - static bool was_full() { return _was_full; } }; #endif // SHARE_VM_RUNTIME_SWEEPER_HPP diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/synchronizer.cpp --- a/hotspot/src/share/vm/runtime/synchronizer.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/synchronizer.cpp Wed May 08 23:54:45 2013 -0700 @@ -1018,7 +1018,8 @@ // We might be able to induce a STW safepoint and scavenge enough // objectMonitors to permit progress. if (temp == NULL) { - vm_exit_out_of_memory (sizeof (ObjectMonitor[_BLOCKSIZE]), "Allocate ObjectMonitors") ; + vm_exit_out_of_memory (sizeof (ObjectMonitor[_BLOCKSIZE]), OOM_MALLOC_ERROR, + "Allocate ObjectMonitors"); } // Format the block. diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/thread.cpp --- a/hotspot/src/share/vm/runtime/thread.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/thread.cpp Wed May 08 23:54:45 2013 -0700 @@ -3447,7 +3447,8 @@ assert (Universe::is_fully_initialized(), "not initialized"); if (VerifyDuringStartup) { - VM_Verify verify_op(false /* silent */); // make sure we're starting with a clean slate + // Make sure we're starting with a clean slate. + VM_Verify verify_op; VMThread::execute(&verify_op); } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/virtualspace.cpp --- a/hotspot/src/share/vm/runtime/virtualspace.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/virtualspace.cpp Wed May 08 23:54:45 2013 -0700 @@ -60,72 +60,6 @@ initialize(size, alignment, large, NULL, 0, executable); } -char * -ReservedSpace::align_reserved_region(char* addr, const size_t len, - const size_t prefix_size, - const size_t prefix_align, - const size_t suffix_size, - const size_t suffix_align) -{ - assert(addr != NULL, "sanity"); - const size_t required_size = prefix_size + suffix_size; - assert(len >= required_size, "len too small"); - - const size_t s = size_t(addr); - const size_t beg_ofs = (s + prefix_size) & (suffix_align - 1); - const size_t beg_delta = beg_ofs == 0 ? 0 : suffix_align - beg_ofs; - - if (len < beg_delta + required_size) { - return NULL; // Cannot do proper alignment. - } - const size_t end_delta = len - (beg_delta + required_size); - - if (beg_delta != 0) { - os::release_memory(addr, beg_delta); - } - - if (end_delta != 0) { - char* release_addr = (char*) (s + beg_delta + required_size); - os::release_memory(release_addr, end_delta); - } - - return (char*) (s + beg_delta); -} - -char* ReservedSpace::reserve_and_align(const size_t reserve_size, - const size_t prefix_size, - const size_t prefix_align, - const size_t suffix_size, - const size_t suffix_align) -{ - assert(reserve_size > prefix_size + suffix_size, "should not be here"); - - char* raw_addr = os::reserve_memory(reserve_size, NULL, prefix_align); - if (raw_addr == NULL) return NULL; - - char* result = align_reserved_region(raw_addr, reserve_size, prefix_size, - prefix_align, suffix_size, - suffix_align); - if (result == NULL && !os::release_memory(raw_addr, reserve_size)) { - fatal("os::release_memory failed"); - } - -#ifdef ASSERT - if (result != NULL) { - const size_t raw = size_t(raw_addr); - const size_t res = size_t(result); - assert(res >= raw, "alignment decreased start addr"); - assert(res + prefix_size + suffix_size <= raw + reserve_size, - "alignment increased end addr"); - assert((res & (prefix_align - 1)) == 0, "bad alignment of prefix"); - assert(((res + prefix_size) & (suffix_align - 1)) == 0, - "bad alignment of suffix"); - } -#endif - - return result; -} - // Helper method. static bool failed_to_reserve_as_requested(char* base, char* requested_address, const size_t size, bool special) @@ -155,92 +89,6 @@ return true; } -ReservedSpace::ReservedSpace(const size_t suffix_size, - const size_t suffix_align, - char* requested_address, - const size_t noaccess_prefix) -{ - assert(suffix_size != 0, "sanity"); - assert(suffix_align != 0, "sanity"); - assert((suffix_size & (suffix_align - 1)) == 0, - "suffix_size not divisible by suffix_align"); - - // Assert that if noaccess_prefix is used, it is the same as prefix_align. - // Add in noaccess_prefix to prefix - const size_t adjusted_prefix_size = noaccess_prefix; - const size_t size = adjusted_prefix_size + suffix_size; - - // On systems where the entire region has to be reserved and committed up - // front, the compound alignment normally done by this method is unnecessary. - const bool try_reserve_special = UseLargePages && - suffix_align == os::large_page_size(); - if (!os::can_commit_large_page_memory() && try_reserve_special) { - initialize(size, suffix_align, true, requested_address, noaccess_prefix, - false); - return; - } - - _base = NULL; - _size = 0; - _alignment = 0; - _special = false; - _noaccess_prefix = 0; - _executable = false; - - // Optimistically try to reserve the exact size needed. - char* addr; - if (requested_address != 0) { - requested_address -= noaccess_prefix; // adjust address - assert(requested_address != NULL, "huge noaccess prefix?"); - addr = os::attempt_reserve_memory_at(size, requested_address); - if (failed_to_reserve_as_requested(addr, requested_address, size, false)) { - // OS ignored requested address. Try different address. - addr = NULL; - } - } else { - addr = os::reserve_memory(size, NULL, suffix_align); - } - if (addr == NULL) return; - - // Check whether the result has the needed alignment - const size_t ofs = (size_t(addr) + adjusted_prefix_size) & (suffix_align - 1); - if (ofs != 0) { - // Wrong alignment. Release, allocate more space and do manual alignment. - // - // On most operating systems, another allocation with a somewhat larger size - // will return an address "close to" that of the previous allocation. The - // result is often the same address (if the kernel hands out virtual - // addresses from low to high), or an address that is offset by the increase - // in size. Exploit that to minimize the amount of extra space requested. - if (!os::release_memory(addr, size)) { - fatal("os::release_memory failed"); - } - - const size_t extra = MAX2(ofs, suffix_align - ofs); - addr = reserve_and_align(size + extra, adjusted_prefix_size, suffix_align, - suffix_size, suffix_align); - if (addr == NULL) { - // Try an even larger region. If this fails, address space is exhausted. - addr = reserve_and_align(size + suffix_align, adjusted_prefix_size, - suffix_align, suffix_size, suffix_align); - } - - if (requested_address != 0 && - failed_to_reserve_as_requested(addr, requested_address, size, false)) { - // As a result of the alignment constraints, the allocated addr differs - // from the requested address. Return back to the caller who can - // take remedial action (like try again without a requested address). - assert(_base == NULL, "should be"); - return; - } - } - - _base = addr; - _size = size; - _alignment = suffix_align; - _noaccess_prefix = noaccess_prefix; -} - void ReservedSpace::initialize(size_t size, size_t alignment, bool large, char* requested_address, const size_t noaccess_prefix, @@ -476,20 +324,6 @@ protect_noaccess_prefix(size); } -ReservedHeapSpace::ReservedHeapSpace(const size_t heap_space_size, - const size_t alignment, - char* requested_address) : - ReservedSpace(heap_space_size, alignment, - requested_address, - (UseCompressedOops && (Universe::narrow_oop_base() != NULL) && - Universe::narrow_oop_use_implicit_null_checks()) ? - lcm(os::vm_page_size(), alignment) : 0) { - if (base() > 0) { - MemTracker::record_virtual_memory_type((address)base(), mtJavaHeap); - } - protect_noaccess_prefix(heap_space_size); -} - // Reserve space for code segment. Same as Java heap only we mark this as // executable. ReservedCodeSpace::ReservedCodeSpace(size_t r_size, diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/virtualspace.hpp --- a/hotspot/src/share/vm/runtime/virtualspace.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/virtualspace.hpp Wed May 08 23:54:45 2013 -0700 @@ -47,28 +47,6 @@ const size_t noaccess_prefix, bool executable); - // Release parts of an already-reserved memory region [addr, addr + len) to - // get a new region that has "compound alignment." Return the start of the - // resulting region, or NULL on failure. - // - // The region is logically divided into a prefix and a suffix. The prefix - // starts at the result address, which is aligned to prefix_align. The suffix - // starts at result address + prefix_size, which is aligned to suffix_align. - // The total size of the result region is size prefix_size + suffix_size. - char* align_reserved_region(char* addr, const size_t len, - const size_t prefix_size, - const size_t prefix_align, - const size_t suffix_size, - const size_t suffix_align); - - // Reserve memory, call align_reserved_region() to alignment it and return the - // result. - char* reserve_and_align(const size_t reserve_size, - const size_t prefix_size, - const size_t prefix_align, - const size_t suffix_size, - const size_t suffix_align); - protected: // Create protection page at the beginning of the space. void protect_noaccess_prefix(const size_t size); @@ -79,9 +57,6 @@ ReservedSpace(size_t size, size_t alignment, bool large, char* requested_address = NULL, const size_t noaccess_prefix = 0); - ReservedSpace(const size_t suffix_size, const size_t suffix_align, - char* requested_address, - const size_t noaccess_prefix = 0); ReservedSpace(size_t size, size_t alignment, bool large, bool executable); // Accessors @@ -128,8 +103,6 @@ // Constructor ReservedHeapSpace(size_t size, size_t forced_base_alignment, bool large, char* requested_address); - ReservedHeapSpace(const size_t prefix_size, const size_t prefix_align, - char* requested_address); }; // Class encapsulating behavior specific memory space for Code diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/vmStructs.cpp --- a/hotspot/src/share/vm/runtime/vmStructs.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp Wed May 08 23:54:45 2013 -0700 @@ -828,6 +828,7 @@ nonstatic_field(nmethod, _lock_count, jint) \ nonstatic_field(nmethod, _stack_traversal_mark, long) \ nonstatic_field(nmethod, _compile_id, int) \ + nonstatic_field(nmethod, _comp_level, int) \ nonstatic_field(nmethod, _exception_cache, ExceptionCache*) \ nonstatic_field(nmethod, _marked_for_deoptimization, bool) \ \ diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/vmThread.cpp --- a/hotspot/src/share/vm/runtime/vmThread.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/vmThread.cpp Wed May 08 23:54:45 2013 -0700 @@ -293,7 +293,7 @@ os::check_heap(); // Silent verification so as not to pollute normal output, // unless we really asked for it. - Universe::verify(!(PrintGCDetails || Verbose)); + Universe::verify(!(PrintGCDetails || Verbose) || VerifySilently); } CompileBroker::set_should_block(); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/runtime/vm_operations.hpp --- a/hotspot/src/share/vm/runtime/vm_operations.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/runtime/vm_operations.hpp Wed May 08 23:54:45 2013 -0700 @@ -302,7 +302,7 @@ private: bool _silent; public: - VM_Verify(bool silent) : _silent(silent) {} + VM_Verify(bool silent = VerifySilently) : _silent(silent) {} VMOp_Type type() const { return VMOp_Verify; } void doit(); }; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/services/memBaseline.cpp --- a/hotspot/src/share/vm/services/memBaseline.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/services/memBaseline.cpp Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -23,9 +23,12 @@ */ #include "precompiled.hpp" #include "memory/allocation.hpp" +#include "runtime/safepoint.hpp" +#include "runtime/thread.inline.hpp" #include "services/memBaseline.hpp" #include "services/memTracker.hpp" + MemType2Name MemBaseline::MemType2NameMap[NUMBER_OF_MEMORY_TYPE] = { {mtJavaHeap, "Java Heap"}, {mtClass, "Class"}, @@ -149,6 +152,15 @@ return true; } +// check if there is a safepoint in progress, if so, block the thread +// for the safepoint +void MemBaseline::check_safepoint(JavaThread* thr) { + if (SafepointSynchronize::is_synchronizing()) { + // grab and drop the SR_lock to honor the safepoint protocol + MutexLocker ml(thr->SR_lock()); + } +} + // baseline mmap'd memory records, generate overall summary and summaries by // memory types bool MemBaseline::baseline_vm_summary(const MemPointerArray* vm_records) { @@ -306,7 +318,7 @@ committed_rec->pc() != vm_ptr->pc()) { if (!_vm_map->append(vm_ptr)) { return false; - } + } committed_rec = (VMMemRegionEx*)_vm_map->at(_vm_map->length() - 1); } else { committed_rec->expand_region(vm_ptr->addr(), vm_ptr->size()); @@ -344,16 +356,27 @@ // baseline a snapshot. If summary_only = false, memory usages aggregated by // callsites are also baselined. +// The method call can be lengthy, especially when detail tracking info is +// requested. So the method checks for safepoint explicitly. bool MemBaseline::baseline(MemSnapshot& snapshot, bool summary_only) { - MutexLockerEx snapshot_locker(snapshot._lock, true); + Thread* THREAD = Thread::current(); + assert(THREAD->is_Java_thread(), "must be a JavaThread"); + MutexLocker snapshot_locker(snapshot._lock); reset(); - _baselined = baseline_malloc_summary(snapshot._alloc_ptrs) && - baseline_vm_summary(snapshot._vm_ptrs); + _baselined = baseline_malloc_summary(snapshot._alloc_ptrs); + if (_baselined) { + check_safepoint((JavaThread*)THREAD); + _baselined = baseline_vm_summary(snapshot._vm_ptrs); + } _number_of_classes = snapshot.number_of_classes(); if (!summary_only && MemTracker::track_callsite() && _baselined) { - _baselined = baseline_malloc_details(snapshot._alloc_ptrs) && - baseline_vm_details(snapshot._vm_ptrs); + check_safepoint((JavaThread*)THREAD); + _baselined = baseline_malloc_details(snapshot._alloc_ptrs); + if (_baselined) { + check_safepoint((JavaThread*)THREAD); + _baselined = baseline_vm_details(snapshot._vm_ptrs); + } } return _baselined; } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/services/memBaseline.hpp --- a/hotspot/src/share/vm/services/memBaseline.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/services/memBaseline.hpp Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -330,6 +330,9 @@ // should not use copy constructor MemBaseline(MemBaseline& copy) { ShouldNotReachHere(); } + // check and block at a safepoint + static inline void check_safepoint(JavaThread* thr); + public: // create a memory baseline MemBaseline(); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/services/memTracker.cpp --- a/hotspot/src/share/vm/services/memTracker.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/services/memTracker.cpp Wed May 08 23:54:45 2013 -0700 @@ -573,7 +573,7 @@ // baseline current memory snapshot bool MemTracker::baseline() { - MutexLockerEx lock(_query_lock, true); + MutexLocker lock(_query_lock); MemSnapshot* snapshot = get_snapshot(); if (snapshot != NULL) { return _baseline.baseline(*snapshot, false); @@ -584,7 +584,7 @@ // print memory usage from current snapshot bool MemTracker::print_memory_usage(BaselineOutputer& out, size_t unit, bool summary_only) { MemBaseline baseline; - MutexLockerEx lock(_query_lock, true); + MutexLocker lock(_query_lock); MemSnapshot* snapshot = get_snapshot(); if (snapshot != NULL && baseline.baseline(*snapshot, summary_only)) { BaselineReporter reporter(out, unit); @@ -597,7 +597,7 @@ // Whitebox API for blocking until the current generation of NMT data has been merged bool MemTracker::wbtest_wait_for_data_merge() { // NMT can't be shutdown while we're holding _query_lock - MutexLockerEx lock(_query_lock, true); + MutexLocker lock(_query_lock); assert(_worker_thread != NULL, "Invalid query"); // the generation at query time, so NMT will spin till this generation is processed unsigned long generation_at_query_time = SequenceGenerator::current_generation(); @@ -641,7 +641,7 @@ // compare memory usage between current snapshot and baseline bool MemTracker::compare_memory_usage(BaselineOutputer& out, size_t unit, bool summary_only) { - MutexLockerEx lock(_query_lock, true); + MutexLocker lock(_query_lock); if (_baseline.baselined()) { MemBaseline baseline; MemSnapshot* snapshot = get_snapshot(); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/utilities/debug.cpp --- a/hotspot/src/share/vm/utilities/debug.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/utilities/debug.cpp Wed May 08 23:54:45 2013 -0700 @@ -229,11 +229,11 @@ } void report_vm_out_of_memory(const char* file, int line, size_t size, - const char* message) { + VMErrorType vm_err_type, const char* message) { if (Debugging) return; Thread* thread = ThreadLocalStorage::get_thread_slow(); - VMError(thread, file, line, size, message).report_and_die(); + VMError(thread, file, line, size, vm_err_type, message).report_and_die(); // The UseOSErrorReporting option in report_and_die() may allow a return // to here. If so then we'll have to figure out how to handle it. @@ -344,7 +344,7 @@ msg, eol, msg, eol, msg, eol, msg, eol, msg, eol, msg, eol, msg, eol, msg, eol, msg, eol, msg, eol, msg, eol, msg, eol, msg, eol, msg, eol, msg)); - case 8: vm_exit_out_of_memory(num, "ChunkPool::allocate"); + case 8: vm_exit_out_of_memory(num, OOM_MALLOC_ERROR, "ChunkPool::allocate"); case 9: ShouldNotCallThis(); case 10: ShouldNotReachHere(); case 11: Unimplemented(); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/utilities/debug.hpp --- a/hotspot/src/share/vm/utilities/debug.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/utilities/debug.hpp Wed May 08 23:54:45 2013 -0700 @@ -174,9 +174,9 @@ } while (0) // out of memory -#define vm_exit_out_of_memory(size, msg) \ +#define vm_exit_out_of_memory(size, vm_err_type, msg) \ do { \ - report_vm_out_of_memory(__FILE__, __LINE__, size, msg); \ + report_vm_out_of_memory(__FILE__, __LINE__, size, vm_err_type, msg); \ BREAKPOINT; \ } while (0) @@ -204,12 +204,20 @@ BREAKPOINT; \ } while (0); + +// types of VM error - originally in vmError.hpp +enum VMErrorType { + INTERNAL_ERROR = 0xe0000000, + OOM_MALLOC_ERROR = 0xe0000001, + OOM_MMAP_ERROR = 0xe0000002 +}; + // error reporting helper functions void report_vm_error(const char* file, int line, const char* error_msg, const char* detail_msg = NULL); void report_fatal(const char* file, int line, const char* message); void report_vm_out_of_memory(const char* file, int line, size_t size, - const char* message); + VMErrorType vm_err_type, const char* message); void report_should_not_call(const char* file, int line); void report_should_not_reach_here(const char* file, int line); void report_unimplemented(const char* file, int line); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/utilities/ostream.hpp --- a/hotspot/src/share/vm/utilities/ostream.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/utilities/ostream.hpp Wed May 08 23:54:45 2013 -0700 @@ -196,7 +196,7 @@ fileStream() { _file = NULL; _need_close = false; } fileStream(const char* file_name); fileStream(const char* file_name, const char* opentype); - fileStream(FILE* file) { _file = file; _need_close = false; } + fileStream(FILE* file, bool need_close = false) { _file = file; _need_close = need_close; } ~fileStream(); bool is_open() const { return _file != NULL; } void set_need_close(bool b) { _need_close = b;} diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/utilities/vmError.cpp --- a/hotspot/src/share/vm/utilities/vmError.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/utilities/vmError.cpp Wed May 08 23:54:45 2013 -0700 @@ -100,7 +100,7 @@ const char* message, const char * detail_msg) { _thread = thread; - _id = internal_error; // Value that's not an OS exception/signal + _id = INTERNAL_ERROR; // Value that's not an OS exception/signal _filename = filename; _lineno = lineno; _message = message; @@ -119,9 +119,9 @@ // Constructor for OOM errors VMError::VMError(Thread* thread, const char* filename, int lineno, size_t size, - const char* message) { + VMErrorType vm_err_type, const char* message) { _thread = thread; - _id = oom_error; // Value that's not an OS exception/signal + _id = vm_err_type; // Value that's not an OS exception/signal _filename = filename; _lineno = lineno; _message = message; @@ -142,7 +142,7 @@ // Constructor for non-fatal errors VMError::VMError(const char* message) { _thread = NULL; - _id = internal_error; // Value that's not an OS exception/signal + _id = INTERNAL_ERROR; // Value that's not an OS exception/signal _filename = NULL; _lineno = 0; _message = message; @@ -351,9 +351,12 @@ STEP(15, "(printing type of error)") switch(_id) { - case oom_error: + case OOM_MALLOC_ERROR: + case OOM_MMAP_ERROR: if (_size) { - st->print("# Native memory allocation (malloc) failed to allocate "); + st->print("# Native memory allocation "); + st->print((_id == (int)OOM_MALLOC_ERROR) ? "(malloc) failed to allocate " : + "(mmap) failed to map "); jio_snprintf(buf, sizeof(buf), SIZE_FORMAT, _size); st->print(buf); st->print(" bytes"); @@ -386,7 +389,7 @@ return; // that's enough for the screen } break; - case internal_error: + case INTERNAL_ERROR: default: break; } @@ -796,6 +799,56 @@ VMError* volatile VMError::first_error = NULL; volatile jlong VMError::first_error_tid = -1; +/** Expand a pattern into a buffer starting at pos and open a file using constructed path */ +static int expand_and_open(const char* pattern, char* buf, size_t buflen, size_t pos) { + int fd = -1; + if (Arguments::copy_expand_pid(pattern, strlen(pattern), &buf[pos], buflen - pos)) { + fd = open(buf, O_RDWR | O_CREAT | O_TRUNC, 0666); + } + return fd; +} + +/** + * Construct file name for a log file and return it's file descriptor. + * Name and location depends on pattern, default_pattern params and access + * permissions. + */ +static int prepare_log_file(const char* pattern, const char* default_pattern, char* buf, size_t buflen) { + int fd = -1; + + // If possible, use specified pattern to construct log file name + if (pattern != NULL) { + fd = expand_and_open(pattern, buf, buflen, 0); + } + + // Either user didn't specify, or the user's location failed, + // so use the default name in the current directory + if (fd == -1) { + const char* cwd = os::get_current_directory(buf, buflen); + if (cwd != NULL) { + size_t pos = strlen(cwd); + int fsep_len = jio_snprintf(&buf[pos], buflen-pos, "%s", os::file_separator()); + pos += fsep_len; + if (fsep_len > 0) { + fd = expand_and_open(default_pattern, buf, buflen, pos); + } + } + } + + // try temp directory if it exists. + if (fd == -1) { + const char* tmpdir = os::get_temp_directory(); + if (tmpdir != NULL && strlen(tmpdir) > 0) { + int pos = jio_snprintf(buf, buflen, "%s%s", tmpdir, os::file_separator()); + if (pos > 0) { + fd = expand_and_open(default_pattern, buf, buflen, pos); + } + } + } + + return fd; +} + void VMError::report_and_die() { // Don't allocate large buffer on stack static char buffer[O_BUFLEN]; @@ -905,36 +958,7 @@ // see if log file is already open if (!log.is_open()) { // open log file - int fd = -1; - - if (ErrorFile != NULL) { - bool copy_ok = - Arguments::copy_expand_pid(ErrorFile, strlen(ErrorFile), buffer, sizeof(buffer)); - if (copy_ok) { - fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0666); - } - } - - if (fd == -1) { - const char *cwd = os::get_current_directory(buffer, sizeof(buffer)); - size_t len = strlen(cwd); - // either user didn't specify, or the user's location failed, - // so use the default name in the current directory - jio_snprintf(&buffer[len], sizeof(buffer)-len, "%shs_err_pid%u.log", - os::file_separator(), os::current_process_id()); - fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0666); - } - - if (fd == -1) { - const char * tmpdir = os::get_temp_directory(); - // try temp directory if it exists. - if (tmpdir != NULL && tmpdir[0] != '\0') { - jio_snprintf(buffer, sizeof(buffer), "%s%shs_err_pid%u.log", - tmpdir, os::file_separator(), os::current_process_id()); - fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0666); - } - } - + int fd = prepare_log_file(ErrorFile, "hs_err_pid%p.log", buffer, sizeof(buffer)); if (fd != -1) { out.print_raw("# An error report file with more information is saved as:\n# "); out.print_raw_cr(buffer); @@ -958,7 +982,7 @@ // Run error reporting to determine whether or not to report the crash. if (!transmit_report_done && should_report_bug(first_error->_id)) { transmit_report_done = true; - FILE* hs_err = ::fdopen(log.fd(), "r"); + FILE* hs_err = os::open(log.fd(), "r"); if (NULL != hs_err) { ErrorReporter er; er.call(hs_err, buffer, O_BUFLEN); @@ -1008,7 +1032,19 @@ skip_replay = true; ciEnv* env = ciEnv::current(); if (env != NULL) { - env->dump_replay_data(); + int fd = prepare_log_file(ReplayDataFile, "replay_pid%p.log", buffer, sizeof(buffer)); + if (fd != -1) { + FILE* replay_data_file = os::open(fd, "w"); + if (replay_data_file != NULL) { + fileStream replay_data_stream(replay_data_file, /*need_close=*/true); + env->dump_replay_data(&replay_data_stream); + out.print_raw("#\n# Compiler replay data is saved as:\n# "); + out.print_raw_cr(buffer); + } else { + out.print_raw("#\n# Can't open file to dump replay data. Error: "); + out.print_raw_cr(strerror(os::get_last_error())); + } + } } } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/utilities/vmError.hpp --- a/hotspot/src/share/vm/utilities/vmError.hpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/utilities/vmError.hpp Wed May 08 23:54:45 2013 -0700 @@ -34,10 +34,6 @@ friend class VM_ReportJavaOutOfMemory; friend class Decoder; - enum ErrorType { - internal_error = 0xe0000000, - oom_error = 0xe0000001 - }; int _id; // Solaris/Linux signals: 0 - SIGRTMAX // Windows exceptions: 0xCxxxxxxx system errors // 0x8xxxxxxx system warnings @@ -96,9 +92,12 @@ // accessor const char* message() const { return _message; } const char* detail_msg() const { return _detail_msg; } - bool should_report_bug(unsigned int id) { return id != oom_error; } + bool should_report_bug(unsigned int id) { + return (id != OOM_MALLOC_ERROR) && (id != OOM_MMAP_ERROR); + } public: + // Constructor for crashes VMError(Thread* thread, unsigned int sig, address pc, void* siginfo, void* context); @@ -108,7 +107,7 @@ // Constructor for VM OOM errors VMError(Thread* thread, const char* filename, int lineno, size_t size, - const char* message); + VMErrorType vm_err_type, const char* message); // Constructor for non-fatal errors VMError(const char* message); diff -r 241d13ccb08b -r 4ea383c26563 hotspot/src/share/vm/utilities/workgroup.cpp --- a/hotspot/src/share/vm/utilities/workgroup.cpp Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/src/share/vm/utilities/workgroup.cpp Wed May 08 23:54:45 2013 -0700 @@ -79,7 +79,7 @@ } _gang_workers = NEW_C_HEAP_ARRAY(GangWorker*, total_workers(), mtInternal); if (gang_workers() == NULL) { - vm_exit_out_of_memory(0, "Cannot create GangWorker array."); + vm_exit_out_of_memory(0, OOM_MALLOC_ERROR, "Cannot create GangWorker array."); return false; } os::ThreadType worker_type; @@ -93,7 +93,8 @@ assert(new_worker != NULL, "Failed to allocate GangWorker"); _gang_workers[worker] = new_worker; if (new_worker == NULL || !os::create_thread(new_worker, worker_type)) { - vm_exit_out_of_memory(0, "Cannot create worker GC thread. Out of system resources."); + vm_exit_out_of_memory(0, OOM_MALLOC_ERROR, + "Cannot create worker GC thread. Out of system resources."); return false; } if (!DisableStartThread) { diff -r 241d13ccb08b -r 4ea383c26563 hotspot/test/compiler/ciReplay/TestSA.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/compiler/ciReplay/TestSA.sh Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,92 @@ +#!/bin/sh +# +# Copyright (c) 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. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please 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 8011675 +## @summary testing of ciReplay with using generated by SA replay.txt +## @author igor.ignatyev@oracle.com +## @run shell TestSA.sh +## + +if [ "${TESTSRC}" = "" ] +then + TESTSRC=${PWD} + echo "TESTSRC not set. Using "${TESTSRC}" as default" +fi +echo "TESTSRC=${TESTSRC}" + +## Adding common setup Variables for running shell tests. +. ${TESTSRC}/../../test_env.sh + +. ${TESTSRC}/common.sh + +generate_replay + +${MV} ${replay_data} replay_vm.txt + +if [ -z "${core_file}" -o ! -r "${core_file}" ] +then + # skip test if MacOS host isn't configured for core dumping + if [ "$OS" = "Darwin" ] + then + if [ ! -d "/cores" ] + then + echo TEST SKIPPED: \'/cores\' dir doens\'t exist + exit 0 + fi + if [ ! -w "/cores" ] + then + echo TEST SKIPPED: \'/cores\' dir exists but is not writable + exit 0 + fi + fi + test_fail 2 "CHECK :: CORE GENERATION" "core wasn't generated on $OS" +fi + +echo "dumpreplaydata -a > ${replay_data}" | \ + ${JAVA} ${TESTVMOPTS} \ + -cp ${TESTJAVA}${FS}lib${FS}sa-jdi.jar \ + sun.jvm.hotspot.CLHSDB ${JAVA} ${core_file} + +if [ ! -s ${replay_data} ] +then + test_fail 1 "CHECK :: REPLAY DATA GENERATION" \ + "replay data wasn't generated by SA" +fi + +diff --brief ${replay_data} replay_vm.txt +if [ $? -ne 0 ] +then + echo WARNING: replay.txt from SA != replay.txt from VM +fi + +common_tests 10 +${VM_TYPE}_tests 20 + +cleanup + +echo TEST PASSED + diff -r 241d13ccb08b -r 4ea383c26563 hotspot/test/compiler/ciReplay/TestVM.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/compiler/ciReplay/TestVM.sh Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,86 @@ +#!/bin/sh +# +# Copyright (c) 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. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please 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 8011675 +## @summary testing of ciReplay with using generated by VM replay.txt +## @author igor.ignatyev@oracle.com +## @run shell TestVM.sh +## + +if [ "${TESTSRC}" = "" ] +then + TESTSRC=${PWD} + echo "TESTSRC not set. Using "${TESTSRC}" as default" +fi +echo "TESTSRC=${TESTSRC}" + +## Adding common setup Variables for running shell tests. +. ${TESTSRC}/../../test_env.sh + +. ${TESTSRC}/common.sh + +generate_replay + +if [ ! -s ${replay_data} ] +then + test_fail 1 "CHECK :: REPLAY DATA GENERATION" \ + "replay data wasn't generated by VM" +fi + +common_tests 10 +${VM_TYPE}_tests 20 + +cleanup + +if [ $is_tiered -eq 1 ] +then + stop_level=1 + while [ $stop_level -le $server_level ] + do + generate_replay "-XX:TieredStopAtLevel=$stop_level" + if [ ! -s ${replay_data} ] + then + test_fail `expr $stop_level + 30` \ + "TIERED LEVEL $stop_level :: REPLAY DATA GENERATION" \ + "replay data wasn't generated by VM with stop_level=$stop_level" + fi + level=`grep "^compile " $replay_data | awk '{print $6}'` + if [ $level -gt $stop_level ] + then + test_fail `expr $stop_level + 40` \ + "TIERED LEVEL $stop_level :: COMP_LEVEL VERIFICATION" \ + "comp_level in replay[$level] is greater than stop_level[$stop_level]" + fi + positive_test `expr $stop_level + 50` "TIERED LEVEL $stop_level :: REPLAY" \ + "-XX:TieredStopAtLevel=$stop_level" + stop_level=`expr $stop_level + 1` + done + cleanup +fi + +echo TEST PASSED + diff -r 241d13ccb08b -r 4ea383c26563 hotspot/test/compiler/ciReplay/TestVM_no_comp_level.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/compiler/ciReplay/TestVM_no_comp_level.sh Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,74 @@ +#!/bin/sh +# +# Copyright (c) 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. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please 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 8011675 +## @summary testing of ciReplay with using generated by VM replay.txt w/o comp_level +## @author igor.ignatyev@oracle.com +## @run shell TestVM_no_comp_level.sh +## + +if [ "${TESTSRC}" = "" ] +then + TESTSRC=${PWD} + echo "TESTSRC not set. Using "${TESTSRC}" as default" +fi +echo "TESTSRC=${TESTSRC}" + +## Adding common setup Variables for running shell tests. +. ${TESTSRC}/../../test_env.sh + +. ${TESTSRC}/common.sh + +generate_replay + +if [ ! -s ${replay_data} ] +then + test_fail 1 "CHECK :: REPLAY DATA GENERATION" \ + "replay data wasn't generated by VM" +fi + +${CP} ${replay_data} replay_vm.txt + +sed 's/^\(compile *[^ ][^ ]* *[^ ][^ ]* [^ ][^ ]* [^ ][^ ]*\).*$/\1/' \ + replay_vm.txt > ${replay_data} + +if [ $client_available -eq 1 ] +then + # tiered is unavailable in client vm, so results w/ flags will be the same as w/o flags + negative_test 10 "CLIENT" -client +fi + +if [ $server_available -eq 1 ] +then + positive_test 21 "SERVER :: NON-TIERED" -XX:-TieredCompilation -server + positive_test 22 "SERVER :: TIERED" -XX:+TieredCompilation -server +fi + +cleanup + +echo TEST PASSED + diff -r 241d13ccb08b -r 4ea383c26563 hotspot/test/compiler/ciReplay/common.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/compiler/ciReplay/common.sh Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,253 @@ +#!/bin/sh +# +# Copyright (c) 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. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +# + +# $1 - error code +# $2 - test name +# $3,.. - decription +test_fail() { + error=$1 + shift + name=$1 + shift + echo "TEST [$name] FAILED:" + echo "$@" + exit $error +} + +# $@ - additional vm opts +start_test() { + # disable core dump on *nix + ulimit -S -c 0 + # disable core dump on windows + VMOPTS="$@ -XX:-CreateMinidumpOnCrash" + cmd="${JAVA} ${VMOPTS} -XX:+ReplayCompiles -XX:ReplayDataFile=${replay_data}" + echo $cmd + $cmd + return $? +} + +# $1 - error_code +# $2 - test name +# $3,.. - additional vm opts +positive_test() { + error=$1 + shift + name=$1 + shift + VMOPTS="${TESTVMOPTS} $@" + echo "POSITIVE TEST [$name]" + start_test ${VMOPTS} + exit_code=$? + if [ ${exit_code} -ne 0 ] + then + test_fail $error "$name" "exit_code[${exit_code}] != 0 during replay "\ + "w/ vmopts: ${VMOPTS}" + fi +} + +# $1 - error_code +# $2 - test name +# $2,.. - additional vm opts +negative_test() { + error=$1 + shift + name=$1 + shift + VMOPTS="${TESTVMOPTS} $@" + echo "NEGATIVE TEST [$name]" + start_test ${VMOPTS} + exit_code=$? + if [ ${exit_code} -eq 0 ] + then + test_fail $error "$name" "exit_code[${exit_code}] == 0 during replay "\ + "w/ vmopts: ${VMOPTS}" + fi +} + +# $1 - initial error_code +common_tests() { + positive_test $1 "COMMON :: THE SAME FLAGS" + positive_test `expr $1 + 1` "COMMON :: TIERED" -XX:+TieredCompilation +} + +# $1 - initial error_code +# $2 - non-tiered comp_level +nontiered_tests() { + level=`grep "^compile " $replay_data | awk '{print $6}'` + # is level available in non-tiere + if [ "$level" -eq $2 ] + then + positive_test $1 "NON-TIERED :: AVAILABLE COMP_LEVEL" \ + -XX:-TieredCompilation + else + negative_test `expr $1 + 1` "NON-TIERED :: UNAVAILABLE COMP_LEVEL" \ + negative_test `expr $1 + 1` "NON-TIERED :: UNAVAILABLE COMP_LEVEL" \ + -XX:-TieredCompilation + fi +} + +# $1 - initial error_code +client_tests() { + # testing in opposite VM + if [ $server_available -eq 1 ] + then + negative_test $1 "SERVER :: NON-TIERED" -XX:-TieredCompilation \ + -server + positive_test `expr $1 + 1` "SERVER :: TIERED" -XX:+TieredCompilation \ + -server + fi + nontiered_tests `expr $1 + 2` $client_level +} + +# $1 - initial error_code +server_tests() { + # testing in opposite VM + if [ $client_available -eq 1 ] + then + # tiered is unavailable in client vm, so results w/ flags will be the same as w/o flags + negative_test $1 "CLIENT" -client + fi + nontiered_tests `expr $1 + 2` $server_level +} + +cleanup() { + ${RM} -f core* + ${RM} -f replay*.txt + ${RM} -f hs_err_pid*.log + ${RM} -f test_core + ${RM} -f test_replay.txt +} + +JAVA=${TESTJAVA}${FS}bin${FS}java + +replay_data=test_replay.txt + +${JAVA} ${TESTVMOPTS} -Xinternalversion 2>&1 | grep debug + +# Only test fastdebug +if [ $? -ne 0 ] +then + echo TEST SKIPPED: product build + exit 0 +fi + +is_int=`${JAVA} ${TESTVMOPTS} -version 2>&1 | grep -c "interpreted mode"` +# Not applicable for Xint +if [ $is_int -ne 0 ] +then + echo TEST SKIPPED: interpreted mode + exit 0 +fi + +cleanup + +client_available=`${JAVA} ${TESTVMOPTS} -client -Xinternalversion 2>&1 | \ + grep -c Client` +server_available=`${JAVA} ${TESTVMOPTS} -server -Xinternalversion 2>&1 | \ + grep -c Server` +is_tiered=`${JAVA} ${TESTVMOPTS} -XX:+PrintFlagsFinal -version | \ + grep TieredCompilation | \ + grep -c true` +# CompLevel_simple -- C1 +client_level=1 +# CompLevel_full_optimization -- C2 or Shark +server_level=4 + +echo "client_available=$client_available" +echo "server_available=$server_available" +echo "is_tiered=$is_tiered" + +# crash vm in compiler thread with generation replay data and 'small' dump-file +# $@ - additional vm opts +generate_replay() { + # enable core dump + ulimit -c unlimited + + cmd="${JAVA} ${TESTVMOPTS} $@ \ + -Xms8m \ + -Xmx32m \ + -XX:MetaspaceSize=4m \ + -XX:MaxMetaspaceSize=16m \ + -XX:InitialCodeCacheSize=512k \ + -XX:ReservedCodeCacheSize=4m \ + -XX:ThreadStackSize=512 \ + -XX:VMThreadStackSize=512 \ + -XX:CompilerThreadStackSize=512 \ + -XX:ParallelGCThreads=1 \ + -XX:CICompilerCount=1 \ + -Xcomp \ + -XX:CICrashAt=1 \ + -XX:+CreateMinidumpOnCrash \ + -XX:+DumpReplayDataOnError \ + -XX:ReplayDataFile=${replay_data} \ + -version" + echo GENERATION OF REPLAY.TXT: + echo $cmd + + ${cmd} 2>&1 > crash.out + + core_locations=`grep -i core crash.out | grep "location:" | \ + sed -e 's/.*location: //'` + rm crash.out + # processing core locations for *nix + if [ $OS != "windows" ] + then + # remove 'or' between '/core.' and 'core' + core_locations=`echo $core_locations | \ + sed -e 's/\([^ ]*\) or \([^ ]*\)/\1 \2/'` + # add /core. core. + core=`echo $core_locations | awk '{print $1}'` + dir=`dirname $core` + core=`basename $core` + if [ -n ${core} ] + then + core_locations="$core_locations $dir${FS}$core" + fi + core=`echo $core_locations | awk '{print $2}'` + if [ -n ${core} ] + then + core_locations="$core_locations $dir${FS}$core" + fi + fi + + echo "LOOKING FOR CORE IN ${core_locations}" + for core in $core_locations + do + if [ -r "$core" ] + then + core_file=$core + fi + done + + # core-file was found + if [ -n "$core_file" ] + then + ${MV} "${core_file}" test_core + core_file=test_core + fi + + ${RM} -f hs_err_pid*.log +} + diff -r 241d13ccb08b -r 4ea383c26563 hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java --- a/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java Wed May 08 23:54:45 2013 -0700 @@ -42,6 +42,11 @@ protected static int COMP_LEVEL_NONE = 0; /** {@code CompLevel::CompLevel_any}, {@code CompLevel::CompLevel_all} */ protected static int COMP_LEVEL_ANY = -1; + /** {@code CompLevel::CompLevel_simple} -- C1 */ + protected static int COMP_LEVEL_SIMPLE = 1; + /** {@code CompLevel::CompLevel_full_optimization} -- C2 or Shark */ + protected static int COMP_LEVEL_FULL_OPTIMIZATION = 4; + /** Instance of WhiteBox */ protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); /** Value of {@code -XX:CompileThreshold} */ @@ -91,6 +96,17 @@ return result == null ? defaultValue : result; } + /** copy of is_c1_compile(int) from utilities/globalDefinitions.hpp */ + protected static boolean isC1Compile(int compLevel) { + return (compLevel > COMP_LEVEL_NONE) + && (compLevel < COMP_LEVEL_FULL_OPTIMIZATION); + } + + /** copy of is_c2_compile(int) from utilities/globalDefinitions.hpp */ + protected static boolean isC2Compile(int compLevel) { + return compLevel == COMP_LEVEL_FULL_OPTIMIZATION; + } + /** tested method */ protected final Executable method; private final Callable callable; diff -r 241d13ccb08b -r 4ea383c26563 hotspot/test/compiler/whitebox/MakeMethodNotCompilableTest.java --- a/hotspot/test/compiler/whitebox/MakeMethodNotCompilableTest.java Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/test/compiler/whitebox/MakeMethodNotCompilableTest.java Wed May 08 23:54:45 2013 -0700 @@ -23,6 +23,7 @@ /* * @test MakeMethodNotCompilableTest + * @bug 8012322 * @library /testlibrary /testlibrary/whitebox * @build MakeMethodNotCompilableTest * @run main ClassFileInstaller sun.hotspot.WhiteBox @@ -67,28 +68,69 @@ } if (TIERED_COMPILATION) { - for (int i = 1, n = TIERED_STOP_AT_LEVEL + 1; i < n; ++i) { - WHITE_BOX.makeMethodNotCompilable(method, i); - if (WHITE_BOX.isMethodCompilable(method, i)) { + final int tierLimit = TIERED_STOP_AT_LEVEL + 1; + for (int testedTier = 1; testedTier < tierLimit; ++testedTier) { + testTier(testedTier); + } + for (int testedTier = 1; testedTier < tierLimit; ++testedTier) { + WHITE_BOX.makeMethodNotCompilable(method, testedTier); + if (WHITE_BOX.isMethodCompilable(method, testedTier)) { throw new RuntimeException(method - + " must be not compilable at level" + i); + + " must be not compilable at level" + testedTier); } - WHITE_BOX.enqueueMethodForCompilation(method, i); + WHITE_BOX.enqueueMethodForCompilation(method, testedTier); checkNotCompiled(); if (!WHITE_BOX.isMethodCompilable(method)) { System.out.println(method - + " is not compilable after level " + i); + + " is not compilable after level " + testedTier); } } + } else { + compile(); + checkCompiled(); + int compLevel = WHITE_BOX.getMethodCompilationLevel(method); + WHITE_BOX.deoptimizeMethod(method); + WHITE_BOX.makeMethodNotCompilable(method, compLevel); + if (WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY)) { + throw new RuntimeException(method + + " must be not compilable at CompLevel::CompLevel_any," + + " after it is not compilable at " + compLevel); + } + WHITE_BOX.clearMethodState(method); + + // nocompilable at opposite level must make no sense + int oppositeLevel; + if (isC1Compile(compLevel)) { + oppositeLevel = COMP_LEVEL_FULL_OPTIMIZATION; + } else { + oppositeLevel = COMP_LEVEL_SIMPLE; + } + WHITE_BOX.makeMethodNotCompilable(method, oppositeLevel); + + if (!WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY)) { + throw new RuntimeException(method + + " must be compilable at CompLevel::CompLevel_any," + + " even it is not compilable at opposite level [" + + compLevel + "]"); + } - // WB.clearMethodState() must reset no-compilable flags - WHITE_BOX.clearMethodState(method); - if (!WHITE_BOX.isMethodCompilable(method)) { - throw new RuntimeException(method - + " is not compilable after clearMethodState()"); + if (!WHITE_BOX.isMethodCompilable(method, compLevel)) { + throw new RuntimeException(method + + " must be compilable at level " + compLevel + + ", even it is not compilable at opposite level [" + + compLevel + "]"); } } + + // clearing after tiered/non-tiered tests + // WB.clearMethodState() must reset no-compilable flags + WHITE_BOX.clearMethodState(method); + if (!WHITE_BOX.isMethodCompilable(method)) { + throw new RuntimeException(method + + " is not compilable after clearMethodState()"); + } + WHITE_BOX.makeMethodNotCompilable(method); if (WHITE_BOX.isMethodCompilable(method)) { throw new RuntimeException(method + " must be not compilable"); @@ -108,4 +150,65 @@ compile(); checkCompiled(); } + + // separately tests each tier + private void testTier(int testedTier) { + if (!WHITE_BOX.isMethodCompilable(method, testedTier)) { + throw new RuntimeException(method + + " is not compilable on start"); + } + WHITE_BOX.makeMethodNotCompilable(method, testedTier); + + // tests for all other tiers + for (int anotherTier = 1, tierLimit = TIERED_STOP_AT_LEVEL + 1; + anotherTier < tierLimit; ++anotherTier) { + boolean isCompilable = WHITE_BOX.isMethodCompilable(method, + anotherTier); + if (sameCompile(testedTier, anotherTier)) { + if (isCompilable) { + throw new RuntimeException(method + + " must be not compilable at level " + anotherTier + + ", if it is not compilable at " + testedTier); + } + WHITE_BOX.enqueueMethodForCompilation(method, anotherTier); + checkNotCompiled(); + } else { + if (!isCompilable) { + throw new RuntimeException(method + + " must be compilable at level " + anotherTier + + ", even if it is not compilable at " + + testedTier); + } + WHITE_BOX.enqueueMethodForCompilation(method, anotherTier); + checkCompiled(); + WHITE_BOX.deoptimizeMethod(method); + } + + if (!WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY)) { + throw new RuntimeException(method + + " must be compilable at 'CompLevel::CompLevel_any'" + + ", if it is not compilable only at " + testedTier); + } + } + + // clear state after test + WHITE_BOX.clearMethodState(method); + if (!WHITE_BOX.isMethodCompilable(method, testedTier)) { + throw new RuntimeException(method + + " is not compilable after clearMethodState()"); + } + } + + private boolean sameCompile(int level1, int level2) { + if (level1 == level2) { + return true; + } + if (isC1Compile(level1) && isC1Compile(level2)) { + return true; + } + if (isC2Compile(level1) && isC2Compile(level2)) { + return true; + } + return false; + } } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/test/gc/7072527/TestFullGCCount.java --- a/hotspot/test/gc/7072527/TestFullGCCount.java Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/test/gc/7072527/TestFullGCCount.java Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -25,71 +25,67 @@ * @test TestFullGCount.java * @bug 7072527 * @summary CMS: JMM GC counters overcount in some cases - * @run main/othervm -XX:+UseConcMarkSweepGC TestFullGCCount - * + * @run main/othervm -XX:+PrintGC TestFullGCCount */ import java.util.*; import java.lang.management.*; +/* + * Originally for a specific failure in CMS, this test now monitors all + * collectors for double-counting of collections. + */ public class TestFullGCCount { - public String collectorName = "ConcurrentMarkSweep"; - - public static void main(String [] args) { + static List collectors = ManagementFactory.getGarbageCollectorMXBeans(); - TestFullGCCount t = null; - if (args.length==2) { - t = new TestFullGCCount(args[0], args[1]); - } else { - t = new TestFullGCCount(); + public static void main(String[] args) { + int iterations = 20; + boolean failed = false; + String errorMessage = ""; + HashMap counts = new HashMap(); + + // Prime the collection of count lists for all collectors. + for (int i = 0; i < collectors.size(); i++) { + GarbageCollectorMXBean collector = collectors.get(i); + counts.put(collector.getName(), new ArrayList(iterations)); } - System.out.println("Monitoring collector: " + t.collectorName); - t.run(); - } - public TestFullGCCount(String pool, String collector) { - collectorName = collector; - } - - public TestFullGCCount() { - } + // Perform some gc, record collector counts. + for (int i = 0; i < iterations; i++) { + System.gc(); + addCollectionCount(counts, i); + } - public void run() { - int count = 0; - int iterations = 20; - long counts[] = new long[iterations]; - boolean diffAlways2 = true; // assume we will fail + // Check the increments: + // Old gen collectors should increase by one, + // New collectors may or may not increase. + // Any increase >=2 is unexpected. + for (String collector : counts.keySet()) { + System.out.println("Checking: " + collector); - for (int i=0; i0) { - if (counts[i] - counts[i-1] != 2) { - diffAlways2 = false; + for (int i = 0; i < iterations - 1; i++) { + List theseCounts = counts.get(collector); + long a = theseCounts.get(i); + long b = theseCounts.get(i + 1); + if (b - a >= 2) { + failed = true; + errorMessage += "Collector '" + collector + "' has increment " + (b - a) + + " at iteration " + i + "\n"; } } } - if (diffAlways2) { - throw new RuntimeException("FAILED: System.gc must be incrementing count twice."); + if (failed) { + System.err.println(errorMessage); + throw new RuntimeException("FAILED: System.gc collections miscounted."); } System.out.println("Passed."); } - private long getCollectionCount() { - long count = 0; - List pools = ManagementFactory.getMemoryPoolMXBeans(); - List collectors = ManagementFactory.getGarbageCollectorMXBeans(); - for (int i=0; i counts, int iteration) { + for (int i = 0; i < collectors.size(); i++) { GarbageCollectorMXBean collector = collectors.get(i); - String name = collector.getName(); - if (name.contains(collectorName)) { - System.out.println(name + ": collection count = " - + collector.getCollectionCount()); - count = collector.getCollectionCount(); - } + List thisList = counts.get(collector.getName()); + thisList.add(collector.getCollectionCount()); } - return count; } - } - diff -r 241d13ccb08b -r 4ea383c26563 hotspot/test/gc/TestVerifyDuringStartup.java --- a/hotspot/test/gc/TestVerifyDuringStartup.java Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/test/gc/TestVerifyDuringStartup.java Wed May 08 23:54:45 2013 -0700 @@ -23,22 +23,43 @@ /* @test TestVerifyDuringStartup.java * @key gc - * @bug 8010463 + * @bug 8010463 8011343 8011898 * @summary Simple test run with -XX:+VerifyDuringStartup -XX:-UseTLAB to verify 8010463 * @library /testlibrary */ +import com.oracle.java.testlibrary.JDKToolFinder; import com.oracle.java.testlibrary.OutputAnalyzer; import com.oracle.java.testlibrary.ProcessTools; +import java.util.ArrayList; +import java.util.Collections; public class TestVerifyDuringStartup { public static void main(String args[]) throws Exception { + ArrayList vmOpts = new ArrayList(); + + String testVmOptsStr = System.getProperty("test.java.opts"); + if (!testVmOptsStr.isEmpty()) { + String[] testVmOpts = testVmOptsStr.split(" "); + Collections.addAll(vmOpts, testVmOpts); + } + Collections.addAll(vmOpts, new String[] {"-XX:-UseTLAB", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+VerifyDuringStartup", + "-version"}); + + System.out.print("Testing:\n" + JDKToolFinder.getJDKTool("java")); + for (int i = 0; i < vmOpts.size(); i += 1) { + System.out.print(" " + vmOpts.get(i)); + } + System.out.println(); + ProcessBuilder pb = - ProcessTools.createJavaProcessBuilder(System.getProperty("test.vm.opts"), - "-XX:-UseTLAB", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+VerifyDuringStartup", "-version"); + ProcessTools.createJavaProcessBuilder(vmOpts.toArray(new String[vmOpts.size()])); OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + System.out.println("Output:\n" + output.getOutput()); + output.shouldContain("[Verifying"); output.shouldHaveExitValue(0); } diff -r 241d13ccb08b -r 4ea383c26563 hotspot/test/gc/concurrentMarkSweep/GuardShrinkWarning.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/concurrentMarkSweep/GuardShrinkWarning.java Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 GuardShrinkWarning + * @summary Remove warning about CMS generation shrinking. + * @bug 8012111 + * @key gc + * @key regression + * @library /testlibrary + * @run main/othervm GuardShrinkWarning + * @author jon.masamitsu@oracle.com + */ + +import com.oracle.java.testlibrary.*; + +public class GuardShrinkWarning { + public static void main(String args[]) throws Exception { + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-showversion", + "-XX:+UseConcMarkSweepGC", + "-XX:+ExplicitGCInvokesConcurrent", + "GuardShrinkWarning$SystemGCCaller" + ); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + output.shouldNotContain("Shrinking of CMS not yet implemented"); + + output.shouldNotContain("error"); + + output.shouldHaveExitValue(0); + } + static class SystemGCCaller { + public static void main(String [] args) { + System.gc(); + } + } +} diff -r 241d13ccb08b -r 4ea383c26563 hotspot/test/runtime/lambda-features/PublicStaticInterfaceMethodHandling.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/lambda-features/PublicStaticInterfaceMethodHandling.java Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 8013418 + * @summary [JDK 8] Test correct handling of static public interface methods + * @run main/othervm -Xverify:all PublicStaticInterfaceMethodHandling + */ + +class TestClass implements InterfaceWithStaticAndDefaultMethods { +} + +interface InterfaceWithStaticAndDefaultMethods { + public static String get() { + return "Hello from StaticMethodInInterface.get()"; + } + default void default_method() { + System.out.println("Default method FunctionalInterface:default_method()"); + } +} + +public class PublicStaticInterfaceMethodHandling { + public static void main(String[] args) { + TestClass tc = new TestClass(); + tc.default_method(); + } +} diff -r 241d13ccb08b -r 4ea383c26563 hotspot/test/runtime/memory/ReserveMemory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/memory/ReserveMemory.java Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 + * @key regression + * @bug 8012015 + * @summary Make sure reserved (but uncommitted) memory is not accessible + * @library /testlibrary /testlibrary/whitebox + * @build ReserveMemory + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main ReserveMemory + */ + +import com.oracle.java.testlibrary.*; + +import java.lang.reflect.Field; +import sun.hotspot.WhiteBox; +import sun.misc.Unsafe; + +public class ReserveMemory { + private static Unsafe getUnsafe() throws Exception { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (Unsafe)f.get(null); + } + + private static boolean isWindows() { + return System.getProperty("os.name").toLowerCase().startsWith("win"); + } + + public static void main(String args[]) throws Exception { + if (args.length > 0) { + long address = WhiteBox.getWhiteBox().reserveMemory(4096); + + System.out.println("Reserved memory at address: 0x" + Long.toHexString(address)); + System.out.println("Will now read from the address, expecting a crash!"); + + int x = getUnsafe().getInt(address); + + throw new Exception("Read of reserved/uncommitted memory unexpectedly succeeded, expected crash!"); + } + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-Xbootclasspath/a:.", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "ReserveMemory", + "test"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + if (isWindows()) { + output.shouldContain("EXCEPTION_ACCESS_VIOLATION"); + } else { + output.shouldContain("SIGSEGV"); + } + } +} diff -r 241d13ccb08b -r 4ea383c26563 hotspot/test/sanity/WhiteBox.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/sanity/WhiteBox.java Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 WhiteBox + * @bug 8011675 + * @summary verify that whitebox can be used even if not all functions are declared in java-part + * @author igor.ignatyev@oracle.com + * @library /testlibrary + * @compile WhiteBox.java + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI sun.hotspot.WhiteBox + * @clean sun.hotspot.WhiteBox + */ + +package sun.hotspot; + +public class WhiteBox { + private static native void registerNatives(); + static { registerNatives(); } + public native int notExistedMethod(); + public native int getHeapOopSize(); + public static void main(String[] args) { + WhiteBox wb = new WhiteBox(); + if (wb.getHeapOopSize() < 0) { + throw new Error("wb.getHeapOopSize() < 0"); + } + boolean catched = false; + try { + wb.notExistedMethod(); + } catch (UnsatisfiedLinkError e) { + catched = true; + } + if (!catched) { + throw new Error("wb.notExistedMethod() was invoked"); + } + } +} diff -r 241d13ccb08b -r 4ea383c26563 hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java --- a/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java Wed May 08 14:10:53 2013 +0200 +++ b/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java Wed May 08 23:54:45 2013 -0700 @@ -111,6 +111,9 @@ // Intered strings public native boolean isInStringTable(String str); + // Memory + public native long reserveMemory(long size); + // force Full GC public native void fullGC(); } diff -r 241d13ccb08b -r 4ea383c26563 jaxp/.hgtags --- a/jaxp/.hgtags Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/.hgtags Wed May 08 23:54:45 2013 -0700 @@ -209,3 +209,4 @@ 41b50e2c5ea3f4aa1af729e1deb1678cb3e1ef9c jdk8-b85 ca71ec37b2efc9c3f0971ebabb3a6eb1213d76de jdk8-b86 eddbc8ad2435a89f64729512337c9f2669e4dd85 jdk8-b87 +7122f7bb0fcc8a39e5254402119b2ee3fa0ad313 jdk8-b88 diff -r 241d13ccb08b -r 4ea383c26563 jaxp/build.xml --- a/jaxp/build.xml Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/build.xml Wed May 08 23:54:45 2013 -0700 @@ -176,4 +176,42 @@ + + + + + + + +

Unofficial Javadoc generated from developer sources for preview purposes only]]>
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 241d13ccb08b -r 4ea383c26563 jaxp/nbproject/project.xml --- a/jaxp/nbproject/project.xml Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/nbproject/project.xml Wed May 08 23:54:45 2013 -0700 @@ -15,6 +15,12 @@ . UTF-8 + + + java + src + UTF-8 + @@ -27,9 +33,16 @@ clean build + + javadoc-nb + + + + src + build.xml @@ -38,11 +51,16 @@ + - + + + src + + diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/datatype/DatatypeFactory.java --- a/jaxp/src/javax/xml/datatype/DatatypeFactory.java Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/src/javax/xml/datatype/DatatypeFactory.java Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -25,8 +25,8 @@ package javax.xml.datatype; +import java.math.BigDecimal; import java.math.BigInteger; -import java.math.BigDecimal; import java.util.GregorianCalendar; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -34,12 +34,12 @@ /** *

Factory that creates new javax.xml.datatype Objects that map XML to/from Java Objects.

* - *

{@link #newInstance()} is used to create a new DatatypeFactory. - * The following implementation resolution mechanisms are used in the following order:

+ *

A new instance of the DatatypeFactory is created through the {@link #newInstance()} method + * that uses the following implementation resolution mechanisms to determine an implementation:

*
    *
  1. * If the system property specified by {@link #DATATYPEFACTORY_PROPERTY}, "javax.xml.datatype.DatatypeFactory", - * exists, a class with the name of the property's value is instantiated. + * exists, a class with the name of the property value is instantiated. * Any Exception thrown during the instantiation process is wrapped as a {@link DatatypeConfigurationException}. *
  2. *
  3. @@ -48,8 +48,12 @@ * and processed as documented in the prior step. *
  4. *
  5. - * The services resolution mechanism is used, e.g. META-INF/services/java.xml.datatype.DatatypeFactory. - * Any Exception thrown during the instantiation process is wrapped as a {@link DatatypeConfigurationException}. + * Uses the service-provider loading facilities, defined by the {@link java.util.ServiceLoader} class, to attempt + * to locate and load an implementation of the service. + *
    + * In case of {@link java.util.ServiceConfigurationError service + * configuration error} a {@link javax.xml.datatype.DatatypeConfigurationException} + * will be thrown. *
  6. *
  7. * The final mechanism is to attempt to instantiate the Class specified by @@ -67,26 +71,33 @@ */ public abstract class DatatypeFactory { - /** - *

    Default property name as defined in JSR 206: Java(TM) API for XML Processing (JAXP) 1.3.

    - * - *

    Default value is javax.xml.datatype.DatatypeFactory.

    - */ - public static final String DATATYPEFACTORY_PROPERTY = "javax.xml.datatype.DatatypeFactory"; + /** + *

    Default property name as defined in JSR 206: Java(TM) API for XML Processing (JAXP) 1.3.

    + * + *

    Default value is javax.xml.datatype.DatatypeFactory.

    + */ + public static final String DATATYPEFACTORY_PROPERTY = + // We use a String constant here, rather than calling + // DatatypeFactory.class.getName() - in order to make javadoc + // generate a See Also: Constant Field Value link. + "javax.xml.datatype.DatatypeFactory"; - /** - *

    Default implementation class name as defined in - * JSR 206: Java(TM) API for XML Processing (JAXP) 1.3.

    - * - *

    Implementers should specify the name of an appropriate class - * to be instantiated if no other implementation resolution mechanism - * succeeds.

    - * - *

    Users should not refer to this field; it is intended only to - * document a factory implementation detail. - *

    - */ - public static final String DATATYPEFACTORY_IMPLEMENTATION_CLASS = new String("com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl"); + /** + *

    Default implementation class name as defined in + * JSR 206: Java(TM) API for XML Processing (JAXP) 1.3.

    + * + *

    Implementers should specify the name of an appropriate class + * to be instantiated if no other implementation resolution mechanism + * succeeds.

    + * + *

    Users should not refer to this field; it is intended only to + * document a factory implementation detail. + *

    + */ + public static final String DATATYPEFACTORY_IMPLEMENTATION_CLASS = + // We use new String() here to prevent javadoc from generating + // a See Also: Constant Field Value link. + new String("com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl"); /** * http://www.w3.org/TR/xpath-datamodel/#xdtschema defines two regexps @@ -101,40 +112,36 @@ private static final Pattern XDTSCHEMA_DTD = Pattern.compile("[^YM]*[DT].*"); - /** - *

    Protected constructor to prevent instaniation outside of package.

    - * - *

    Use {@link #newInstance()} to create a DatatypeFactory.

    - */ - protected DatatypeFactory() { - } + /** + *

    Protected constructor to prevent instaniation outside of package.

    + * + *

    Use {@link #newInstance()} to create a DatatypeFactory.

    + */ + protected DatatypeFactory() { + } - /** - *

    Obtain a new instance of a DatatypeFactory.

    - * + /** + *

    Obtain a new instance of a DatatypeFactory.

    + * *

    The implementation resolution mechanisms are defined in this * Class's documentation.

    - * - * @return New instance of a DatatypeFactory - * - * @throws DatatypeConfigurationException If the implementation is not - * available or cannot be instantiated. + * + * @return New instance of a DatatypeFactory + * + * @throws DatatypeConfigurationException If the implementation is not + * available or cannot be instantiated. * * @see #newInstance(String factoryClassName, ClassLoader classLoader) - */ - public static DatatypeFactory newInstance() - throws DatatypeConfigurationException { + */ + public static DatatypeFactory newInstance() + throws DatatypeConfigurationException { - try { - return (DatatypeFactory) FactoryFinder.find( - /* The default property name according to the JAXP spec */ - DATATYPEFACTORY_PROPERTY, - /* The fallback implementation class name */ - DATATYPEFACTORY_IMPLEMENTATION_CLASS); - } catch (FactoryFinder.ConfigurationError e) { - throw new DatatypeConfigurationException(e.getMessage(), e.getException()); - } - } + return FactoryFinder.find( + /* The default property name according to the JAXP spec */ + DatatypeFactory.class, + /* The fallback implementation class name */ + DATATYPEFACTORY_IMPLEMENTATION_CLASS); + } /** *

    Obtain a new instance of a DatatypeFactory from class name. @@ -172,57 +179,54 @@ */ public static DatatypeFactory newInstance(String factoryClassName, ClassLoader classLoader) throws DatatypeConfigurationException { - try { - return (DatatypeFactory) FactoryFinder.newInstance(factoryClassName, classLoader, false); - } catch (FactoryFinder.ConfigurationError e) { - throw new DatatypeConfigurationException(e.getMessage(), e.getException()); - } - } + return FactoryFinder.newInstance(DatatypeFactory.class, + factoryClassName, classLoader, false); + } - /** - *

    Obtain a new instance of a Duration - * specifying the Duration as its string representation, "PnYnMnDTnHnMnS", - * as defined in XML Schema 1.0 section 3.2.6.1.

    - * - *

    XML Schema Part 2: Datatypes, 3.2.6 duration, defines duration as:

    - *
    - * duration represents a duration of time. - * The value space of duration is a six-dimensional space where the coordinates designate the - * Gregorian year, month, day, hour, minute, and second components defined in Section 5.5.3.2 of [ISO 8601], respectively. - * These components are ordered in their significance by their order of appearance i.e. as - * year, month, day, hour, minute, and second. - *
    - *

    All six values are set and availabe from the created {@link Duration}

    + /** + *

    Obtain a new instance of a Duration + * specifying the Duration as its string representation, "PnYnMnDTnHnMnS", + * as defined in XML Schema 1.0 section 3.2.6.1.

    + * + *

    XML Schema Part 2: Datatypes, 3.2.6 duration, defines duration as:

    + *
    + * duration represents a duration of time. + * The value space of duration is a six-dimensional space where the coordinates designate the + * Gregorian year, month, day, hour, minute, and second components defined in Section 5.5.3.2 of [ISO 8601], respectively. + * These components are ordered in their significance by their order of appearance i.e. as + * year, month, day, hour, minute, and second. + *
    + *

    All six values are set and available from the created {@link Duration}

    * *

    The XML Schema specification states that values can be of an arbitrary size. * Implementations may chose not to or be incapable of supporting arbitrarily large and/or small values. * An {@link UnsupportedOperationException} will be thrown with a message indicating implementation limits * if implementation capacities are exceeded.

    - * - * @param lexicalRepresentation String representation of a Duration. - * - * @return New Duration created from parsing the lexicalRepresentation. - * - * @throws IllegalArgumentException If lexicalRepresentation is not a valid representation of a Duration. - * @throws UnsupportedOperationException If implementation cannot support requested values. - * @throws NullPointerException if lexicalRepresentation is null. - */ - public abstract Duration newDuration(final String lexicalRepresentation); + * + * @param lexicalRepresentation String representation of a Duration. + * + * @return New Duration created from parsing the lexicalRepresentation. + * + * @throws IllegalArgumentException If lexicalRepresentation is not a valid representation of a Duration. + * @throws UnsupportedOperationException If implementation cannot support requested values. + * @throws NullPointerException if lexicalRepresentation is null. + */ + public abstract Duration newDuration(final String lexicalRepresentation); - /** - *

    Obtain a new instance of a Duration - * specifying the Duration as milliseconds.

    - * - *

    XML Schema Part 2: Datatypes, 3.2.6 duration, defines duration as:

    - *
    - * duration represents a duration of time. - * The value space of duration is a six-dimensional space where the coordinates designate the - * Gregorian year, month, day, hour, minute, and second components defined in Section 5.5.3.2 of [ISO 8601], respectively. - * These components are ordered in their significance by their order of appearance i.e. as - * year, month, day, hour, minute, and second. - *
    + /** + *

    Obtain a new instance of a Duration + * specifying the Duration as milliseconds.

    + * + *

    XML Schema Part 2: Datatypes, 3.2.6 duration, defines duration as:

    + *
    + * duration represents a duration of time. + * The value space of duration is a six-dimensional space where the coordinates designate the + * Gregorian year, month, day, hour, minute, and second components defined in Section 5.5.3.2 of [ISO 8601], respectively. + * These components are ordered in their significance by their order of appearance i.e. as + * year, month, day, hour, minute, and second. + *
    *

    All six values are set by computing their values from the specified milliseconds - * and are availabe using the get methods of the created {@link Duration}. + * and are available using the get methods of the created {@link Duration}. * The values conform to and are defined by:

    *
      *
    • ISO 8601:2000(E) Section 5.5.3.2 Alternative format
    • @@ -231,25 +235,25 @@ * *
    • {@link XMLGregorianCalendar} Date/Time Datatype Field Mapping Between XML Schema 1.0 and Java Representation
    • *
    - * - *

    The default start instance is defined by {@link GregorianCalendar}'s use of the start of the epoch: i.e., - * {@link java.util.Calendar#YEAR} = 1970, - * {@link java.util.Calendar#MONTH} = {@link java.util.Calendar#JANUARY}, - * {@link java.util.Calendar#DATE} = 1, etc. - * This is important as there are variations in the Gregorian Calendar, - * e.g. leap years have different days in the month = {@link java.util.Calendar#FEBRUARY} - * so the result of {@link Duration#getMonths()} and {@link Duration#getDays()} can be influenced.

    - * - * @param durationInMilliSeconds Duration in milliseconds to create. - * - * @return New Duration representing durationInMilliSeconds. - */ - public abstract Duration newDuration(final long durationInMilliSeconds); + * + *

    The default start instance is defined by {@link GregorianCalendar}'s use of the start of the epoch: i.e., + * {@link java.util.Calendar#YEAR} = 1970, + * {@link java.util.Calendar#MONTH} = {@link java.util.Calendar#JANUARY}, + * {@link java.util.Calendar#DATE} = 1, etc. + * This is important as there are variations in the Gregorian Calendar, + * e.g. leap years have different days in the month = {@link java.util.Calendar#FEBRUARY} + * so the result of {@link Duration#getMonths()} and {@link Duration#getDays()} can be influenced.

    + * + * @param durationInMilliSeconds Duration in milliseconds to create. + * + * @return New Duration representing durationInMilliSeconds. + */ + public abstract Duration newDuration(final long durationInMilliSeconds); - /** - *

    Obtain a new instance of a Duration - * specifying the Duration as isPositive, years, months, days, hours, minutes, seconds.

    - * + /** + *

    Obtain a new instance of a Duration + * specifying the Duration as isPositive, years, months, days, hours, minutes, seconds.

    + * *

    The XML Schema specification states that values can be of an arbitrary size. * Implementations may chose not to or be incapable of supporting arbitrarily large and/or small values. * An {@link UnsupportedOperationException} will be thrown with a message indicating implementation limits @@ -257,35 +261,35 @@ * *

    A null value indicates that field is not set.

    * - * @param isPositive Set to false to create a negative duration. When the length - * of the duration is zero, this parameter will be ignored. - * @param years of this Duration - * @param months of this Duration - * @param days of this Duration - * @param hours of this Duration - * @param minutes of this Duration - * @param seconds of this Duration - * - * @return New Duration created from the specified values. - * - * @throws IllegalArgumentException If the values are not a valid representation of a - * Duration: if all the fields (years, months, ...) are null or - * if any of the fields is negative. - * @throws UnsupportedOperationException If implementation cannot support requested values. - */ - public abstract Duration newDuration( - final boolean isPositive, - final BigInteger years, - final BigInteger months, - final BigInteger days, - final BigInteger hours, - final BigInteger minutes, - final BigDecimal seconds); + * @param isPositive Set to false to create a negative duration. When the length + * of the duration is zero, this parameter will be ignored. + * @param years of this Duration + * @param months of this Duration + * @param days of this Duration + * @param hours of this Duration + * @param minutes of this Duration + * @param seconds of this Duration + * + * @return New Duration created from the specified values. + * + * @throws IllegalArgumentException If the values are not a valid representation of a + * Duration: if all the fields (years, months, ...) are null or + * if any of the fields is negative. + * @throws UnsupportedOperationException If implementation cannot support requested values. + */ + public abstract Duration newDuration( + final boolean isPositive, + final BigInteger years, + final BigInteger months, + final BigInteger days, + final BigInteger hours, + final BigInteger minutes, + final BigDecimal seconds); - /** - *

    Obtain a new instance of a Duration - * specifying the Duration as isPositive, years, months, days, hours, minutes, seconds.

    - * + /** + *

    Obtain a new instance of a Duration + * specifying the Duration as isPositive, years, months, days, hours, minutes, seconds.

    + * *

    A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.

    * * @param isPositive Set to false to create a negative duration. When the length @@ -297,113 +301,113 @@ * @param minutes of this Duration * @param seconds of this Duration * - * @return New Duration created from the specified values. - * - * @throws IllegalArgumentException If the values are not a valid representation of a - * Duration: if any of the fields is negative. - * - * @see #newDuration( - * boolean isPositive, - * BigInteger years, - * BigInteger months, - * BigInteger days, - * BigInteger hours, - * BigInteger minutes, - * BigDecimal seconds) - */ - public Duration newDuration( - final boolean isPositive, - final int years, - final int months, - final int days, - final int hours, - final int minutes, - final int seconds) { + * @return New Duration created from the specified values. + * + * @throws IllegalArgumentException If the values are not a valid representation of a + * Duration: if any of the fields is negative. + * + * @see #newDuration( + * boolean isPositive, + * BigInteger years, + * BigInteger months, + * BigInteger days, + * BigInteger hours, + * BigInteger minutes, + * BigDecimal seconds) + */ + public Duration newDuration( + final boolean isPositive, + final int years, + final int months, + final int days, + final int hours, + final int minutes, + final int seconds) { - // years may not be set - BigInteger realYears = (years != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) years) : null; + // years may not be set + BigInteger realYears = (years != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) years) : null; - // months may not be set - BigInteger realMonths = (months != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) months) : null; + // months may not be set + BigInteger realMonths = (months != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) months) : null; - // days may not be set - BigInteger realDays = (days != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) days) : null; + // days may not be set + BigInteger realDays = (days != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) days) : null; - // hours may not be set - BigInteger realHours = (hours != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) hours) : null; + // hours may not be set + BigInteger realHours = (hours != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) hours) : null; - // minutes may not be set - BigInteger realMinutes = (minutes != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) minutes) : null; + // minutes may not be set + BigInteger realMinutes = (minutes != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) minutes) : null; - // seconds may not be set - BigDecimal realSeconds = (seconds != DatatypeConstants.FIELD_UNDEFINED) ? BigDecimal.valueOf((long) seconds) : null; + // seconds may not be set + BigDecimal realSeconds = (seconds != DatatypeConstants.FIELD_UNDEFINED) ? BigDecimal.valueOf((long) seconds) : null; - return newDuration( - isPositive, - realYears, - realMonths, - realDays, - realHours, - realMinutes, - realSeconds - ); - } + return newDuration( + isPositive, + realYears, + realMonths, + realDays, + realHours, + realMinutes, + realSeconds + ); + } - /** - *

    Create a Duration of type xdt:dayTimeDuration by parsing its String representation, - * "PnDTnHnMnS", - * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    - * - *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration - * whose lexical representation contains only day, hour, minute, and second components. - * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    - * - *

    All four values are set and availabe from the created {@link Duration}

    - * + /** + *

    Create a Duration of type xdt:dayTimeDuration by parsing its String representation, + * "PnDTnHnMnS", + * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    + * + *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration + * whose lexical representation contains only day, hour, minute, and second components. + * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    + * + *

    All four values are set and available from the created {@link Duration}

    + * *

    The XML Schema specification states that values can be of an arbitrary size. * Implementations may chose not to or be incapable of supporting arbitrarily large and/or small values. * An {@link UnsupportedOperationException} will be thrown with a message indicating implementation limits * if implementation capacities are exceeded.

    * - * @param lexicalRepresentation Lexical representation of a duration. - * - * @return New Duration created using the specified lexicalRepresentation. - * - * @throws IllegalArgumentException If lexicalRepresentation is not a valid representation of a Duration expressed only in terms of days and time. - * @throws UnsupportedOperationException If implementation cannot support requested values. - * @throws NullPointerException If lexicalRepresentation is null. - */ - public Duration newDurationDayTime(final String lexicalRepresentation) { - // lexicalRepresentation must be non-null - if (lexicalRepresentation == null) { - throw new NullPointerException( - "Trying to create an xdt:dayTimeDuration with an invalid" - + " lexical representation of \"null\""); - } - - // test lexicalRepresentation against spec regex - Matcher matcher = XDTSCHEMA_DTD.matcher(lexicalRepresentation); - if (!matcher.matches()) { - throw new IllegalArgumentException( - "Trying to create an xdt:dayTimeDuration with an invalid" - + " lexical representation of \"" + lexicalRepresentation - + "\", data model requires years and months only."); - } - - return newDuration(lexicalRepresentation); + * @param lexicalRepresentation Lexical representation of a duration. + * + * @return New Duration created using the specified lexicalRepresentation. + * + * @throws IllegalArgumentException If lexicalRepresentation is not a valid representation of a Duration expressed only in terms of days and time. + * @throws UnsupportedOperationException If implementation cannot support requested values. + * @throws NullPointerException If lexicalRepresentation is null. + */ + public Duration newDurationDayTime(final String lexicalRepresentation) { + // lexicalRepresentation must be non-null + if (lexicalRepresentation == null) { + throw new NullPointerException( + "Trying to create an xdt:dayTimeDuration with an invalid" + + " lexical representation of \"null\""); } - /** - *

    Create a Duration of type xdt:dayTimeDuration using the specified milliseconds as defined in - * - * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    - * - *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration - * whose lexical representation contains only day, hour, minute, and second components. - * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    - * + // test lexicalRepresentation against spec regex + Matcher matcher = XDTSCHEMA_DTD.matcher(lexicalRepresentation); + if (!matcher.matches()) { + throw new IllegalArgumentException( + "Trying to create an xdt:dayTimeDuration with an invalid" + + " lexical representation of \"" + lexicalRepresentation + + "\", data model requires years and months only."); + } + + return newDuration(lexicalRepresentation); + } + + /** + *

    Create a Duration of type xdt:dayTimeDuration using the specified milliseconds as defined in + * + * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    + * + *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration + * whose lexical representation contains only day, hour, minute, and second components. + * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    + * *

    All four values are set by computing their values from the specified milliseconds - * and are availabe using the get methods of the created {@link Duration}. + * and are available using the get methods of the created {@link Duration}. * The values conform to and are defined by:

    *
      *
    • ISO 8601:2000(E) Section 5.5.3.2 Alternative format
    • @@ -412,39 +416,39 @@ * *
    • {@link XMLGregorianCalendar} Date/Time Datatype Field Mapping Between XML Schema 1.0 and Java Representation
    • *
    - * - *

    The default start instance is defined by {@link GregorianCalendar}'s use of the start of the epoch: i.e., - * {@link java.util.Calendar#YEAR} = 1970, - * {@link java.util.Calendar#MONTH} = {@link java.util.Calendar#JANUARY}, - * {@link java.util.Calendar#DATE} = 1, etc. - * This is important as there are variations in the Gregorian Calendar, - * e.g. leap years have different days in the month = {@link java.util.Calendar#FEBRUARY} - * so the result of {@link Duration#getDays()} can be influenced.

    - * + * + *

    The default start instance is defined by {@link GregorianCalendar}'s use of the start of the epoch: i.e., + * {@link java.util.Calendar#YEAR} = 1970, + * {@link java.util.Calendar#MONTH} = {@link java.util.Calendar#JANUARY}, + * {@link java.util.Calendar#DATE} = 1, etc. + * This is important as there are variations in the Gregorian Calendar, + * e.g. leap years have different days in the month = {@link java.util.Calendar#FEBRUARY} + * so the result of {@link Duration#getDays()} can be influenced.

    + * *

    Any remaining milliseconds after determining the day, hour, minute and second are discarded.

    * - * @param durationInMilliseconds Milliseconds of Duration to create. - * - * @return New Duration created with the specified durationInMilliseconds. - * - * @see - * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration - */ - public Duration newDurationDayTime(final long durationInMilliseconds) { + * @param durationInMilliseconds Milliseconds of Duration to create. + * + * @return New Duration created with the specified durationInMilliseconds. + * + * @see + * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration + */ + public Duration newDurationDayTime(final long durationInMilliseconds) { - return newDuration(durationInMilliseconds); - } + return newDuration(durationInMilliseconds); + } - /** - *

    Create a Duration of type xdt:dayTimeDuration using the specified - * day, hour, minute and second as defined in - * - * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    - * - *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration - * whose lexical representation contains only day, hour, minute, and second components. - * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    - * + /** + *

    Create a Duration of type xdt:dayTimeDuration using the specified + * day, hour, minute and second as defined in + * + * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    + * + *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration + * whose lexical representation contains only day, hour, minute, and second components. + * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    + * *

    The XML Schema specification states that values can be of an arbitrary size. * Implementations may chose not to or be incapable of supporting arbitrarily large and/or small values. * An {@link UnsupportedOperationException} will be thrown with a message indicating implementation limits @@ -454,102 +458,102 @@ * * @param isPositive Set to false to create a negative duration. When the length * of the duration is zero, this parameter will be ignored. - * @param day Day of Duration. - * @param hour Hour of Duration. - * @param minute Minute of Duration. - * @param second Second of Duration. - * - * @return New Duration created with the specified day, hour, minute - * and second. - * - * @throws IllegalArgumentException If the values are not a valid representation of a - * Duration: if all the fields (day, hour, ...) are null or - * if any of the fields is negative. - * @throws UnsupportedOperationException If implementation cannot support requested values. - */ - public Duration newDurationDayTime( - final boolean isPositive, - final BigInteger day, - final BigInteger hour, - final BigInteger minute, - final BigInteger second) { + * @param day Day of Duration. + * @param hour Hour of Duration. + * @param minute Minute of Duration. + * @param second Second of Duration. + * + * @return New Duration created with the specified day, hour, minute + * and second. + * + * @throws IllegalArgumentException If the values are not a valid representation of a + * Duration: if all the fields (day, hour, ...) are null or + * if any of the fields is negative. + * @throws UnsupportedOperationException If implementation cannot support requested values. + */ + public Duration newDurationDayTime( + final boolean isPositive, + final BigInteger day, + final BigInteger hour, + final BigInteger minute, + final BigInteger second) { - return newDuration( - isPositive, - null, // years - null, // months - day, - hour, - minute, - (second != null)? new BigDecimal(second):null - ); - } + return newDuration( + isPositive, + null, // years + null, // months + day, + hour, + minute, + (second != null)? new BigDecimal(second):null + ); + } - /** - *

    Create a Duration of type xdt:dayTimeDuration using the specified - * day, hour, minute and second as defined in - * - * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    - * - *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration - * whose lexical representation contains only day, hour, minute, and second components. - * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    - * + /** + *

    Create a Duration of type xdt:dayTimeDuration using the specified + * day, hour, minute and second as defined in + * + * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration.

    + * + *

    The datatype xdt:dayTimeDuration is a subtype of xs:duration + * whose lexical representation contains only day, hour, minute, and second components. + * This datatype resides in the namespace http://www.w3.org/2003/11/xpath-datatypes.

    + * *

    A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.

    * * @param isPositive Set to false to create a negative duration. When the length * of the duration is zero, this parameter will be ignored. - * @param day Day of Duration. - * @param hour Hour of Duration. - * @param minute Minute of Duration. - * @param second Second of Duration. - * - * @return New Duration created with the specified day, hour, minute - * and second. - * - * @throws IllegalArgumentException If the values are not a valid representation of a - * Duration: if any of the fields (day, hour, ...) is negative. - */ - public Duration newDurationDayTime( - final boolean isPositive, - final int day, - final int hour, - final int minute, - final int second) { + * @param day Day of Duration. + * @param hour Hour of Duration. + * @param minute Minute of Duration. + * @param second Second of Duration. + * + * @return New Duration created with the specified day, hour, minute + * and second. + * + * @throws IllegalArgumentException If the values are not a valid representation of a + * Duration: if any of the fields (day, hour, ...) is negative. + */ + public Duration newDurationDayTime( + final boolean isPositive, + final int day, + final int hour, + final int minute, + final int second) { - return newDurationDayTime( - isPositive, - BigInteger.valueOf((long) day), - BigInteger.valueOf((long) hour), - BigInteger.valueOf((long) minute), - BigInteger.valueOf((long) second) - ); - } + return newDurationDayTime( + isPositive, + BigInteger.valueOf((long) day), + BigInteger.valueOf((long) hour), + BigInteger.valueOf((long) minute), + BigInteger.valueOf((long) second) + ); + } - /** - *

    Create a Duration of type xdt:yearMonthDuration by parsing its String representation, - * "PnYnM", - * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    - * - *

    The datatype xdt:yearMonthDuration is a subtype of xs:duration - * whose lexical representation contains only year and month components. - * This datatype resides in the namespace {@link javax.xml.XMLConstants#W3C_XPATH_DATATYPE_NS_URI}.

    - * - *

    Both values are set and availabe from the created {@link Duration}

    - * + /** + *

    Create a Duration of type xdt:yearMonthDuration by parsing its String representation, + * "PnYnM", + * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    + * + *

    The datatype xdt:yearMonthDuration is a subtype of xs:duration + * whose lexical representation contains only year and month components. + * This datatype resides in the namespace {@link javax.xml.XMLConstants#W3C_XPATH_DATATYPE_NS_URI}.

    + * + *

    Both values are set and available from the created {@link Duration}

    + * *

    The XML Schema specification states that values can be of an arbitrary size. * Implementations may chose not to or be incapable of supporting arbitrarily large and/or small values. * An {@link UnsupportedOperationException} will be thrown with a message indicating implementation limits * if implementation capacities are exceeded.

    * - * @param lexicalRepresentation Lexical representation of a duration. - * - * @return New Duration created using the specified lexicalRepresentation. - * - * @throws IllegalArgumentException If lexicalRepresentation is not a valid representation of a Duration expressed only in terms of years and months. - * @throws UnsupportedOperationException If implementation cannot support requested values. - * @throws NullPointerException If lexicalRepresentation is null. - */ + * @param lexicalRepresentation Lexical representation of a duration. + * + * @return New Duration created using the specified lexicalRepresentation. + * + * @throws IllegalArgumentException If lexicalRepresentation is not a valid representation of a Duration expressed only in terms of years and months. + * @throws UnsupportedOperationException If implementation cannot support requested values. + * @throws NullPointerException If lexicalRepresentation is null. + */ public Duration newDurationYearMonth( final String lexicalRepresentation) { @@ -572,17 +576,17 @@ return newDuration(lexicalRepresentation); } - /** - *

    Create a Duration of type xdt:yearMonthDuration using the specified milliseconds as defined in - * - * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    - * - *

    The datatype xdt:yearMonthDuration is a subtype of xs:duration - * whose lexical representation contains only year and month components. - * This datatype resides in the namespace {@link javax.xml.XMLConstants#W3C_XPATH_DATATYPE_NS_URI}.

    - * + /** + *

    Create a Duration of type xdt:yearMonthDuration using the specified milliseconds as defined in + * + * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    + * + *

    The datatype xdt:yearMonthDuration is a subtype of xs:duration + * whose lexical representation contains only year and month components. + * This datatype resides in the namespace {@link javax.xml.XMLConstants#W3C_XPATH_DATATYPE_NS_URI}.

    + * *

    Both values are set by computing their values from the specified milliseconds - * and are availabe using the get methods of the created {@link Duration}. + * and are available using the get methods of the created {@link Duration}. * The values conform to and are defined by:

    *
      *
    • ISO 8601:2000(E) Section 5.5.3.2 Alternative format
    • @@ -592,20 +596,20 @@ *
    • {@link XMLGregorianCalendar} Date/Time Datatype Field Mapping Between XML Schema 1.0 and Java Representation
    • *
    * - *

    The default start instance is defined by {@link GregorianCalendar}'s use of the start of the epoch: i.e., - * {@link java.util.Calendar#YEAR} = 1970, - * {@link java.util.Calendar#MONTH} = {@link java.util.Calendar#JANUARY}, - * {@link java.util.Calendar#DATE} = 1, etc. - * This is important as there are variations in the Gregorian Calendar, - * e.g. leap years have different days in the month = {@link java.util.Calendar#FEBRUARY} - * so the result of {@link Duration#getMonths()} can be influenced.

    - * + *

    The default start instance is defined by {@link GregorianCalendar}'s use of the start of the epoch: i.e., + * {@link java.util.Calendar#YEAR} = 1970, + * {@link java.util.Calendar#MONTH} = {@link java.util.Calendar#JANUARY}, + * {@link java.util.Calendar#DATE} = 1, etc. + * This is important as there are variations in the Gregorian Calendar, + * e.g. leap years have different days in the month = {@link java.util.Calendar#FEBRUARY} + * so the result of {@link Duration#getMonths()} can be influenced.

    + * *

    Any remaining milliseconds after determining the year and month are discarded.

    - * - * @param durationInMilliseconds Milliseconds of Duration to create. - * - * @return New Duration created using the specified durationInMilliseconds. - */ + * + * @param durationInMilliseconds Milliseconds of Duration to create. + * + * @return New Duration created using the specified durationInMilliseconds. + */ public Duration newDurationYearMonth( final long durationInMilliseconds) { @@ -624,12 +628,12 @@ return newDurationYearMonth(isPositive, years, months); } - /** - *

    Create a Duration of type xdt:yearMonthDuration using the specified - * year and month as defined in - * - * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    - * + /** + *

    Create a Duration of type xdt:yearMonthDuration using the specified + * year and month as defined in + * + * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    + * *

    The XML Schema specification states that values can be of an arbitrary size. * Implementations may chose not to or be incapable of supporting arbitrarily large and/or small values. * An {@link UnsupportedOperationException} will be thrown with a message indicating implementation limits @@ -639,74 +643,74 @@ * * @param isPositive Set to false to create a negative duration. When the length * of the duration is zero, this parameter will be ignored. - * @param year Year of Duration. - * @param month Month of Duration. - * - * @return New Duration created using the specified year and month. - * - * @throws IllegalArgumentException If the values are not a valid representation of a - * Duration: if all of the fields (year, month) are null or - * if any of the fields is negative. - * @throws UnsupportedOperationException If implementation cannot support requested values. - */ - public Duration newDurationYearMonth( - final boolean isPositive, - final BigInteger year, - final BigInteger month) { + * @param year Year of Duration. + * @param month Month of Duration. + * + * @return New Duration created using the specified year and month. + * + * @throws IllegalArgumentException If the values are not a valid representation of a + * Duration: if all of the fields (year, month) are null or + * if any of the fields is negative. + * @throws UnsupportedOperationException If implementation cannot support requested values. + */ + public Duration newDurationYearMonth( + final boolean isPositive, + final BigInteger year, + final BigInteger month) { - return newDuration( - isPositive, - year, - month, - null, // days - null, // hours - null, // minutes - null // seconds - ); - } + return newDuration( + isPositive, + year, + month, + null, // days + null, // hours + null, // minutes + null // seconds + ); + } - /** - *

    Create a Duration of type xdt:yearMonthDuration using the specified - * year and month as defined in - * - * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    - * + /** + *

    Create a Duration of type xdt:yearMonthDuration using the specified + * year and month as defined in + * + * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration.

    + * *

    A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.

    * * @param isPositive Set to false to create a negative duration. When the length * of the duration is zero, this parameter will be ignored. - * @param year Year of Duration. - * @param month Month of Duration. - * - * @return New Duration created using the specified year and month. - * - * @throws IllegalArgumentException If the values are not a valid representation of a - * Duration: if any of the fields (year, month) is negative. - */ - public Duration newDurationYearMonth( - final boolean isPositive, - final int year, - final int month) { + * @param year Year of Duration. + * @param month Month of Duration. + * + * @return New Duration created using the specified year and month. + * + * @throws IllegalArgumentException If the values are not a valid representation of a + * Duration: if any of the fields (year, month) is negative. + */ + public Duration newDurationYearMonth( + final boolean isPositive, + final int year, + final int month) { - return newDurationYearMonth( - isPositive, - BigInteger.valueOf((long) year), - BigInteger.valueOf((long) month)); - } + return newDurationYearMonth( + isPositive, + BigInteger.valueOf((long) year), + BigInteger.valueOf((long) month)); + } - /** - *

    Create a new instance of an XMLGregorianCalendar.

    - * + /** + *

    Create a new instance of an XMLGregorianCalendar.

    + * *

    All date/time datatype fields set to {@link DatatypeConstants#FIELD_UNDEFINED} or null.

    * * @return New XMLGregorianCalendar with all date/time datatype fields set to * {@link DatatypeConstants#FIELD_UNDEFINED} or null. - */ - public abstract XMLGregorianCalendar newXMLGregorianCalendar(); + */ + public abstract XMLGregorianCalendar newXMLGregorianCalendar(); - /** - *

    Create a new XMLGregorianCalendar by parsing the String as a lexical representation.

    - * + /** + *

    Create a new XMLGregorianCalendar by parsing the String as a lexical representation.

    + * *

    Parsing the lexical string representation is defined in * XML Schema 1.0 Part 2, Section 3.2.[7-14].1, * Lexical Representation.

    @@ -721,344 +725,344 @@ *

    Except for the noted lexical/canonical representation mismatches * listed in * XML Schema 1.0 errata, Section 3.2.7.2.

    - * - * @param lexicalRepresentation Lexical representation of one the eight XML Schema date/time datatypes. - * - * @return XMLGregorianCalendar created from the lexicalRepresentation. - * - * @throws IllegalArgumentException If the lexicalRepresentation is not a valid XMLGregorianCalendar. - * @throws NullPointerException If lexicalRepresentation is null. - */ - public abstract XMLGregorianCalendar newXMLGregorianCalendar(final String lexicalRepresentation); + * + * @param lexicalRepresentation Lexical representation of one the eight XML Schema date/time datatypes. + * + * @return XMLGregorianCalendar created from the lexicalRepresentation. + * + * @throws IllegalArgumentException If the lexicalRepresentation is not a valid XMLGregorianCalendar. + * @throws NullPointerException If lexicalRepresentation is null. + */ + public abstract XMLGregorianCalendar newXMLGregorianCalendar(final String lexicalRepresentation); - /** - *

    Create an XMLGregorianCalendar from a {@link GregorianCalendar}.

    - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
    - * Field by Field Conversion from - * {@link GregorianCalendar} to an {@link XMLGregorianCalendar} - *
    java.util.GregorianCalendar fieldjavax.xml.datatype.XMLGregorianCalendar field
    ERA == GregorianCalendar.BC ? -YEAR : YEAR{@link XMLGregorianCalendar#setYear(int year)}
    MONTH + 1{@link XMLGregorianCalendar#setMonth(int month)}
    DAY_OF_MONTH{@link XMLGregorianCalendar#setDay(int day)}
    HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND{@link XMLGregorianCalendar#setTime(int hour, int minute, int second, BigDecimal fractional)}
    - * (ZONE_OFFSET + DST_OFFSET) / (60*1000)
    - * (in minutes) - *
    {@link XMLGregorianCalendar#setTimezone(int offset)}* - *
    - *

    *conversion loss of information. It is not possible to represent - * a java.util.GregorianCalendar daylight savings timezone id in the - * XML Schema 1.0 date/time datatype representation.

    - * - *

    To compute the return value's TimeZone field, - *

      - *
    • when this.getTimezone() != FIELD_UNDEFINED, - * create a java.util.TimeZone with a custom timezone id - * using the this.getTimezone().
    • - *
    • else use the GregorianCalendar default timezone value - * for the host is defined as specified by - * java.util.TimeZone.getDefault().
    • - * - * @param cal java.util.GregorianCalendar used to create XMLGregorianCalendar - * - * @return XMLGregorianCalendar created from java.util.GregorianCalendar - * - * @throws NullPointerException If cal is null. - */ - public abstract XMLGregorianCalendar newXMLGregorianCalendar(final GregorianCalendar cal); + /** + *

      Create an XMLGregorianCalendar from a {@link GregorianCalendar}.

      + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
      + * Field by Field Conversion from + * {@link GregorianCalendar} to an {@link XMLGregorianCalendar} + *
      java.util.GregorianCalendar fieldjavax.xml.datatype.XMLGregorianCalendar field
      ERA == GregorianCalendar.BC ? -YEAR : YEAR{@link XMLGregorianCalendar#setYear(int year)}
      MONTH + 1{@link XMLGregorianCalendar#setMonth(int month)}
      DAY_OF_MONTH{@link XMLGregorianCalendar#setDay(int day)}
      HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND{@link XMLGregorianCalendar#setTime(int hour, int minute, int second, BigDecimal fractional)}
      + * (ZONE_OFFSET + DST_OFFSET) / (60*1000)
      + * (in minutes) + *
      {@link XMLGregorianCalendar#setTimezone(int offset)}* + *
      + *

      *conversion loss of information. It is not possible to represent + * a java.util.GregorianCalendar daylight savings timezone id in the + * XML Schema 1.0 date/time datatype representation.

      + * + *

      To compute the return value's TimeZone field, + *

        + *
      • when this.getTimezone() != FIELD_UNDEFINED, + * create a java.util.TimeZone with a custom timezone id + * using the this.getTimezone().
      • + *
      • else use the GregorianCalendar default timezone value + * for the host is defined as specified by + * java.util.TimeZone.getDefault().
      • + * + * @param cal java.util.GregorianCalendar used to create XMLGregorianCalendar + * + * @return XMLGregorianCalendar created from java.util.GregorianCalendar + * + * @throws NullPointerException If cal is null. + */ + public abstract XMLGregorianCalendar newXMLGregorianCalendar(final GregorianCalendar cal); - /** - *

        Constructor allowing for complete value spaces allowed by - * W3C XML Schema 1.0 recommendation for xsd:dateTime and related - * builtin datatypes. Note that year parameter supports - * arbitrarily large numbers and fractionalSecond has infinite - * precision.

        - * + /** + *

        Constructor allowing for complete value spaces allowed by + * W3C XML Schema 1.0 recommendation for xsd:dateTime and related + * builtin datatypes. Note that year parameter supports + * arbitrarily large numbers and fractionalSecond has infinite + * precision.

        + * *

        A null value indicates that field is not set.

        * - * @param year of XMLGregorianCalendar to be created. - * @param month of XMLGregorianCalendar to be created. - * @param day of XMLGregorianCalendar to be created. - * @param hour of XMLGregorianCalendar to be created. - * @param minute of XMLGregorianCalendar to be created. - * @param second of XMLGregorianCalendar to be created. - * @param fractionalSecond of XMLGregorianCalendar to be created. - * @param timezone of XMLGregorianCalendar to be created. - * - * @return XMLGregorianCalendar created from specified values. - * - * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field - * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar} - * or if the composite values constitute an invalid XMLGregorianCalendar instance - * as determined by {@link XMLGregorianCalendar#isValid()}. - */ - public abstract XMLGregorianCalendar newXMLGregorianCalendar( - final BigInteger year, - final int month, - final int day, - final int hour, - final int minute, - final int second, - final BigDecimal fractionalSecond, - final int timezone); + * @param year of XMLGregorianCalendar to be created. + * @param month of XMLGregorianCalendar to be created. + * @param day of XMLGregorianCalendar to be created. + * @param hour of XMLGregorianCalendar to be created. + * @param minute of XMLGregorianCalendar to be created. + * @param second of XMLGregorianCalendar to be created. + * @param fractionalSecond of XMLGregorianCalendar to be created. + * @param timezone of XMLGregorianCalendar to be created. + * + * @return XMLGregorianCalendar created from specified values. + * + * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field + * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar} + * or if the composite values constitute an invalid XMLGregorianCalendar instance + * as determined by {@link XMLGregorianCalendar#isValid()}. + */ + public abstract XMLGregorianCalendar newXMLGregorianCalendar( + final BigInteger year, + final int month, + final int day, + final int hour, + final int minute, + final int second, + final BigDecimal fractionalSecond, + final int timezone); - /** - *

        Constructor of value spaces that a - * java.util.GregorianCalendar instance would need to convert to an - * XMLGregorianCalendar instance.

        - * - *

        XMLGregorianCalendar eon and - * fractionalSecond are set to null

        - * + /** + *

        Constructor of value spaces that a + * java.util.GregorianCalendar instance would need to convert to an + * XMLGregorianCalendar instance.

        + * + *

        XMLGregorianCalendar eon and + * fractionalSecond are set to null

        + * *

        A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.

        * - * @param year of XMLGregorianCalendar to be created. - * @param month of XMLGregorianCalendar to be created. - * @param day of XMLGregorianCalendar to be created. - * @param hour of XMLGregorianCalendar to be created. - * @param minute of XMLGregorianCalendar to be created. - * @param second of XMLGregorianCalendar to be created. - * @param millisecond of XMLGregorianCalendar to be created. - * @param timezone of XMLGregorianCalendar to be created. - * - * @return XMLGregorianCalendar created from specified values. - * - * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field - * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar} - * or if the composite values constitute an invalid XMLGregorianCalendar instance - * as determined by {@link XMLGregorianCalendar#isValid()}. - */ - public XMLGregorianCalendar newXMLGregorianCalendar( - final int year, - final int month, - final int day, - final int hour, - final int minute, - final int second, - final int millisecond, - final int timezone) { + * @param year of XMLGregorianCalendar to be created. + * @param month of XMLGregorianCalendar to be created. + * @param day of XMLGregorianCalendar to be created. + * @param hour of XMLGregorianCalendar to be created. + * @param minute of XMLGregorianCalendar to be created. + * @param second of XMLGregorianCalendar to be created. + * @param millisecond of XMLGregorianCalendar to be created. + * @param timezone of XMLGregorianCalendar to be created. + * + * @return XMLGregorianCalendar created from specified values. + * + * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field + * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar} + * or if the composite values constitute an invalid XMLGregorianCalendar instance + * as determined by {@link XMLGregorianCalendar#isValid()}. + */ + public XMLGregorianCalendar newXMLGregorianCalendar( + final int year, + final int month, + final int day, + final int hour, + final int minute, + final int second, + final int millisecond, + final int timezone) { - // year may be undefined - BigInteger realYear = (year != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) year) : null; + // year may be undefined + BigInteger realYear = (year != DatatypeConstants.FIELD_UNDEFINED) ? BigInteger.valueOf((long) year) : null; - // millisecond may be undefined - // millisecond must be >= 0 millisecond <= 1000 - BigDecimal realMillisecond = null; // undefined value - if (millisecond != DatatypeConstants.FIELD_UNDEFINED) { - if (millisecond < 0 || millisecond > 1000) { - throw new IllegalArgumentException( - "javax.xml.datatype.DatatypeFactory#newXMLGregorianCalendar(" - + "int year, int month, int day, int hour, int minute, int second, int millisecond, int timezone)" - + "with invalid millisecond: " + millisecond - ); - } + // millisecond may be undefined + // millisecond must be >= 0 millisecond <= 1000 + BigDecimal realMillisecond = null; // undefined value + if (millisecond != DatatypeConstants.FIELD_UNDEFINED) { + if (millisecond < 0 || millisecond > 1000) { + throw new IllegalArgumentException( + "javax.xml.datatype.DatatypeFactory#newXMLGregorianCalendar(" + + "int year, int month, int day, int hour, int minute, int second, int millisecond, int timezone)" + + "with invalid millisecond: " + millisecond + ); + } - realMillisecond = BigDecimal.valueOf((long) millisecond).movePointLeft(3); - } + realMillisecond = BigDecimal.valueOf((long) millisecond).movePointLeft(3); + } - return newXMLGregorianCalendar( - realYear, - month, - day, - hour, - minute, - second, - realMillisecond, - timezone - ); - } + return newXMLGregorianCalendar( + realYear, + month, + day, + hour, + minute, + second, + realMillisecond, + timezone + ); + } - /** - *

        Create a Java representation of XML Schema builtin datatype date or g*.

        - * - *

        For example, an instance of gYear can be created invoking this factory - * with month and day parameters set to - * {@link DatatypeConstants#FIELD_UNDEFINED}.

        - * + /** + *

        Create a Java representation of XML Schema builtin datatype date or g*.

        + * + *

        For example, an instance of gYear can be created invoking this factory + * with month and day parameters set to + * {@link DatatypeConstants#FIELD_UNDEFINED}.

        + * *

        A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.

        * - * @param year of XMLGregorianCalendar to be created. - * @param month of XMLGregorianCalendar to be created. - * @param day of XMLGregorianCalendar to be created. - * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set. - * - * @return XMLGregorianCalendar created from parameter values. - * - * @see DatatypeConstants#FIELD_UNDEFINED - * - * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field - * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar} - * or if the composite values constitute an invalid XMLGregorianCalendar instance - * as determined by {@link XMLGregorianCalendar#isValid()}. - */ - public XMLGregorianCalendar newXMLGregorianCalendarDate( - final int year, - final int month, - final int day, - final int timezone) { + * @param year of XMLGregorianCalendar to be created. + * @param month of XMLGregorianCalendar to be created. + * @param day of XMLGregorianCalendar to be created. + * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set. + * + * @return XMLGregorianCalendar created from parameter values. + * + * @see DatatypeConstants#FIELD_UNDEFINED + * + * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field + * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar} + * or if the composite values constitute an invalid XMLGregorianCalendar instance + * as determined by {@link XMLGregorianCalendar#isValid()}. + */ + public XMLGregorianCalendar newXMLGregorianCalendarDate( + final int year, + final int month, + final int day, + final int timezone) { - return newXMLGregorianCalendar( - year, - month, - day, - DatatypeConstants.FIELD_UNDEFINED, // hour - DatatypeConstants.FIELD_UNDEFINED, // minute - DatatypeConstants.FIELD_UNDEFINED, // second - DatatypeConstants.FIELD_UNDEFINED, // millisecond - timezone); - } + return newXMLGregorianCalendar( + year, + month, + day, + DatatypeConstants.FIELD_UNDEFINED, // hour + DatatypeConstants.FIELD_UNDEFINED, // minute + DatatypeConstants.FIELD_UNDEFINED, // second + DatatypeConstants.FIELD_UNDEFINED, // millisecond + timezone); + } - /** - *

        Create a Java instance of XML Schema builtin datatype time.

        - * + /** + *

        Create a Java instance of XML Schema builtin datatype time.

        + * *

        A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.

        * - * @param hours number of hours - * @param minutes number of minutes - * @param seconds number of seconds - * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set. - * - * @return XMLGregorianCalendar created from parameter values. - * - * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field - * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar} - * or if the composite values constitute an invalid XMLGregorianCalendar instance - * as determined by {@link XMLGregorianCalendar#isValid()}. - * - * @see DatatypeConstants#FIELD_UNDEFINED - */ - public XMLGregorianCalendar newXMLGregorianCalendarTime( - final int hours, - final int minutes, - final int seconds, - final int timezone) { + * @param hours number of hours + * @param minutes number of minutes + * @param seconds number of seconds + * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set. + * + * @return XMLGregorianCalendar created from parameter values. + * + * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field + * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar} + * or if the composite values constitute an invalid XMLGregorianCalendar instance + * as determined by {@link XMLGregorianCalendar#isValid()}. + * + * @see DatatypeConstants#FIELD_UNDEFINED + */ + public XMLGregorianCalendar newXMLGregorianCalendarTime( + final int hours, + final int minutes, + final int seconds, + final int timezone) { - return newXMLGregorianCalendar( - DatatypeConstants.FIELD_UNDEFINED, // Year - DatatypeConstants.FIELD_UNDEFINED, // Month - DatatypeConstants.FIELD_UNDEFINED, // Day - hours, - minutes, - seconds, - DatatypeConstants.FIELD_UNDEFINED, //Millisecond - timezone); - } + return newXMLGregorianCalendar( + DatatypeConstants.FIELD_UNDEFINED, // Year + DatatypeConstants.FIELD_UNDEFINED, // Month + DatatypeConstants.FIELD_UNDEFINED, // Day + hours, + minutes, + seconds, + DatatypeConstants.FIELD_UNDEFINED, //Millisecond + timezone); + } - /** - *

        Create a Java instance of XML Schema builtin datatype time.

        - * + /** + *

        Create a Java instance of XML Schema builtin datatype time.

        + * *

        A null value indicates that field is not set.

        *

        A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.

        * - * @param hours number of hours - * @param minutes number of minutes - * @param seconds number of seconds - * @param fractionalSecond value of null indicates that this optional field is not set. - * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set. - * - * @return XMLGregorianCalendar created from parameter values. - * - * @see DatatypeConstants#FIELD_UNDEFINED - * - * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field - * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar} - * or if the composite values constitute an invalid XMLGregorianCalendar instance - * as determined by {@link XMLGregorianCalendar#isValid()}. - */ - public XMLGregorianCalendar newXMLGregorianCalendarTime( - final int hours, - final int minutes, - final int seconds, - final BigDecimal fractionalSecond, - final int timezone) { + * @param hours number of hours + * @param minutes number of minutes + * @param seconds number of seconds + * @param fractionalSecond value of null indicates that this optional field is not set. + * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set. + * + * @return XMLGregorianCalendar created from parameter values. + * + * @see DatatypeConstants#FIELD_UNDEFINED + * + * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field + * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar} + * or if the composite values constitute an invalid XMLGregorianCalendar instance + * as determined by {@link XMLGregorianCalendar#isValid()}. + */ + public XMLGregorianCalendar newXMLGregorianCalendarTime( + final int hours, + final int minutes, + final int seconds, + final BigDecimal fractionalSecond, + final int timezone) { - return newXMLGregorianCalendar( - null, // year - DatatypeConstants.FIELD_UNDEFINED, // month - DatatypeConstants.FIELD_UNDEFINED, // day - hours, - minutes, - seconds, - fractionalSecond, - timezone); - } + return newXMLGregorianCalendar( + null, // year + DatatypeConstants.FIELD_UNDEFINED, // month + DatatypeConstants.FIELD_UNDEFINED, // day + hours, + minutes, + seconds, + fractionalSecond, + timezone); + } - /** - *

        Create a Java instance of XML Schema builtin datatype time.

        - * + /** + *

        Create a Java instance of XML Schema builtin datatype time.

        + * *

        A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.

        * - * @param hours number of hours - * @param minutes number of minutes - * @param seconds number of seconds - * @param milliseconds number of milliseconds - * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set. - * - * @return XMLGregorianCalendar created from parameter values. - * - * @see DatatypeConstants#FIELD_UNDEFINED - * - * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field - * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar} - * or if the composite values constitute an invalid XMLGregorianCalendar instance - * as determined by {@link XMLGregorianCalendar#isValid()}. - */ - public XMLGregorianCalendar newXMLGregorianCalendarTime( - final int hours, - final int minutes, - final int seconds, - final int milliseconds, - final int timezone) { + * @param hours number of hours + * @param minutes number of minutes + * @param seconds number of seconds + * @param milliseconds number of milliseconds + * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set. + * + * @return XMLGregorianCalendar created from parameter values. + * + * @see DatatypeConstants#FIELD_UNDEFINED + * + * @throws IllegalArgumentException If any individual parameter's value is outside the maximum value constraint for the field + * as determined by the Date/Time Data Mapping table in {@link XMLGregorianCalendar} + * or if the composite values constitute an invalid XMLGregorianCalendar instance + * as determined by {@link XMLGregorianCalendar#isValid()}. + */ + public XMLGregorianCalendar newXMLGregorianCalendarTime( + final int hours, + final int minutes, + final int seconds, + final int milliseconds, + final int timezone) { - // millisecond may be undefined - // millisecond must be >= 0 millisecond <= 1000 - BigDecimal realMilliseconds = null; // undefined value - if (milliseconds != DatatypeConstants.FIELD_UNDEFINED) { - if (milliseconds < 0 || milliseconds > 1000) { - throw new IllegalArgumentException( - "javax.xml.datatype.DatatypeFactory#newXMLGregorianCalendarTime(" - + "int hours, int minutes, int seconds, int milliseconds, int timezone)" - + "with invalid milliseconds: " + milliseconds - ); - } + // millisecond may be undefined + // millisecond must be >= 0 millisecond <= 1000 + BigDecimal realMilliseconds = null; // undefined value + if (milliseconds != DatatypeConstants.FIELD_UNDEFINED) { + if (milliseconds < 0 || milliseconds > 1000) { + throw new IllegalArgumentException( + "javax.xml.datatype.DatatypeFactory#newXMLGregorianCalendarTime(" + + "int hours, int minutes, int seconds, int milliseconds, int timezone)" + + "with invalid milliseconds: " + milliseconds + ); + } - realMilliseconds = BigDecimal.valueOf((long) milliseconds).movePointLeft(3); - } + realMilliseconds = BigDecimal.valueOf((long) milliseconds).movePointLeft(3); + } - return newXMLGregorianCalendarTime( - hours, - minutes, - seconds, - realMilliseconds, - timezone - ); - } + return newXMLGregorianCalendarTime( + hours, + minutes, + seconds, + realMilliseconds, + timezone + ); + } } diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/datatype/FactoryFinder.java --- a/jaxp/src/javax/xml/datatype/FactoryFinder.java Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/src/javax/xml/datatype/FactoryFinder.java Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -26,14 +26,12 @@ package javax.xml.datatype; import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Iterator; import java.util.Properties; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.URL; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; /** *

        Implements pluggable Datatypes.

        @@ -54,19 +52,19 @@ /** * Cache for properties in java.home/lib/jaxp.properties */ - static Properties cacheProps = new Properties(); + private final static Properties cacheProps = new Properties(); /** * Flag indicating if properties from java.home/lib/jaxp.properties * have been cached. */ - static volatile boolean firstTime = true; + private static volatile boolean firstTime = true; /** * Security support class use to check access control before * getting certain system resources. */ - static SecuritySupport ss = new SecuritySupport(); + private final static SecuritySupport ss = new SecuritySupport(); // Define system property "jaxp.debug" to get output static { @@ -99,31 +97,31 @@ * * Use bootstrap classLoader if cl = null and useBSClsLoader is true */ - static private Class getProviderClass(String className, ClassLoader cl, + static private Class getProviderClass(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException { try { if (cl == null) { if (useBSClsLoader) { - return Class.forName(className, true, FactoryFinder.class.getClassLoader()); + return Class.forName(className, false, FactoryFinder.class.getClassLoader()); } else { cl = ss.getContextClassLoader(); if (cl == null) { throw new ClassNotFoundException(); } else { - return cl.loadClass(className); + return Class.forName(className, false, cl); } } } else { - return cl.loadClass(className); + return Class.forName(className, false, cl); } } catch (ClassNotFoundException e1) { if (doFallback) { // Use current class loader - should always be bootstrap CL - return Class.forName(className, true, FactoryFinder.class.getClassLoader()); + return Class.forName(className, false, FactoryFinder.class.getClassLoader()); } else { throw e1; @@ -135,6 +133,9 @@ * Create an instance of a class. Delegates to method * getProviderClass() in order to load the class. * + * @param type Base class / Service interface of the factory to + * instantiate. + * * @param className Name of the concrete class corresponding to the * service provider * @@ -144,16 +145,19 @@ * @param doFallback True if the current ClassLoader should be tried as * a fallback if the class is not found using cl */ - static Object newInstance(String className, ClassLoader cl, boolean doFallback) - throws ConfigurationError + static T newInstance(Class type, String className, ClassLoader cl, boolean doFallback) + throws DatatypeConfigurationException { - return newInstance(className, cl, doFallback, false); + return newInstance(type, className, cl, doFallback, false); } /** * Create an instance of a class. Delegates to method * getProviderClass() in order to load the class. * + * @param type Base class / Service interface of the factory to + * instantiate. + * * @param className Name of the concrete class corresponding to the * service provider * @@ -166,9 +170,12 @@ * @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter * is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader. */ - static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader) - throws ConfigurationError + static T newInstance(Class type, String className, ClassLoader cl, + boolean doFallback, boolean useBSClsLoader) + throws DatatypeConfigurationException { + assert type != null; + // make sure we have access to restricted packages if (System.getSecurityManager() != null) { if (className != null && className.startsWith(DEFAULT_PACKAGE)) { @@ -178,20 +185,23 @@ } try { - Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); + Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); + if (!type.isAssignableFrom(providerClass)) { + throw new ClassCastException(className + " cannot be cast to " + type.getName()); + } Object instance = providerClass.newInstance(); if (debug) { // Extra check to avoid computing cl strings dPrint("created new instance of " + providerClass + " using ClassLoader: " + cl); } - return instance; + return type.cast(instance); } catch (ClassNotFoundException x) { - throw new ConfigurationError( + throw new DatatypeConfigurationException( "Provider " + className + " not found", x); } catch (Exception x) { - throw new ConfigurationError( + throw new DatatypeConfigurationException( "Provider " + className + " could not be instantiated: " + x, x); } @@ -202,16 +212,17 @@ * entry point. * @return Class object of factory, never null * - * @param factoryId Name of the factory to find, same as - * a property name + * @param type Base class / Service interface of the + * factory to find. * @param fallbackClassName Implementation class name, if nothing else * is found. Use null to mean no fallback. * * Package private so this code can be shared. */ - static Object find(String factoryId, String fallbackClassName) - throws ConfigurationError + static T find(Class type, String fallbackClassName) + throws DatatypeConfigurationException { + final String factoryId = type.getName(); dPrint("find factoryId =" + factoryId); // Use the system property first @@ -219,7 +230,7 @@ String systemProp = ss.getSystemProperty(factoryId); if (systemProp != null) { dPrint("found system property, value=" + systemProp); - return newInstance(systemProp, null, true); + return newInstance(type, systemProp, null, true); } } catch (SecurityException se) { @@ -228,7 +239,6 @@ // try to read from $java.home/lib/jaxp.properties try { - String factoryClassName = null; if (firstTime) { synchronized (cacheProps) { if (firstTime) { @@ -243,11 +253,11 @@ } } } - factoryClassName = cacheProps.getProperty(factoryId); + final String factoryClassName = cacheProps.getProperty(factoryId); if (factoryClassName != null) { dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName); - return newInstance(factoryClassName, null, true); + return newInstance(type, factoryClassName, null, true); } } catch (Exception ex) { @@ -255,112 +265,46 @@ } // Try Jar Service Provider Mechanism - Object provider = findJarServiceProvider(factoryId); + final T provider = findServiceProvider(type); if (provider != null) { return provider; } if (fallbackClassName == null) { - throw new ConfigurationError( - "Provider for " + factoryId + " cannot be found", null); + throw new DatatypeConfigurationException( + "Provider for " + factoryId + " cannot be found"); } dPrint("loaded from fallback value: " + fallbackClassName); - return newInstance(fallbackClassName, null, true); + return newInstance(type, fallbackClassName, null, true); } /* - * Try to find provider using Jar Service Provider Mechanism + * Try to find provider using the ServiceLoader API + * + * @param type Base class / Service interface of the factory to find. * * @return instance of provider class if found or null */ - private static Object findJarServiceProvider(String factoryId) - throws ConfigurationError + private static T findServiceProvider(final Class type) + throws DatatypeConfigurationException { - String serviceId = "META-INF/services/" + factoryId; - InputStream is = null; - - // First try the Context ClassLoader - ClassLoader cl = ss.getContextClassLoader(); - boolean useBSClsLoader = false; - if (cl != null) { - is = ss.getResourceAsStream(cl, serviceId); - - // If no provider found then try the current ClassLoader - if (is == null) { - cl = FactoryFinder.class.getClassLoader(); - is = ss.getResourceAsStream(cl, serviceId); - useBSClsLoader = true; - } - } else { - // No Context ClassLoader, try the current ClassLoader - cl = FactoryFinder.class.getClassLoader(); - is = ss.getResourceAsStream(cl, serviceId); - useBSClsLoader = true; - } - - if (is == null) { - // No provider found - return null; - } - - if (debug) { // Extra check to avoid computing cl strings - dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl); - } - - BufferedReader rd; - try { - rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); - } - catch (java.io.UnsupportedEncodingException e) { - rd = new BufferedReader(new InputStreamReader(is)); - } - - String factoryClassName = null; try { - // XXX Does not handle all possible input as specified by the - // Jar Service Provider specification - factoryClassName = rd.readLine(); - rd.close(); - } catch (IOException x) { - // No provider found - return null; - } - - if (factoryClassName != null && !"".equals(factoryClassName)) { - dPrint("found in resource, value=" + factoryClassName); - - // Note: here we do not want to fall back to the current - // ClassLoader because we want to avoid the case where the - // resource file was found using one ClassLoader and the - // provider class was instantiated using a different one. - return newInstance(factoryClassName, cl, false, useBSClsLoader); - } - - // No provider found - return null; - } - - static class ConfigurationError extends Error { - private Exception exception; - - /** - * Construct a new instance with the specified detail string and - * exception. - */ - ConfigurationError(String msg, Exception x) { - super(msg); - this.exception = x; - } - - Exception getException() { - return exception; - } - /** - * use the exception chaining mechanism of JDK1.4 - */ - @Override - public Throwable getCause() { - return exception; + return AccessController.doPrivileged(new PrivilegedAction() { + public T run() { + final ServiceLoader serviceLoader = ServiceLoader.load(type); + final Iterator iterator = serviceLoader.iterator(); + if (iterator.hasNext()) { + return iterator.next(); + } else { + return null; + } + } + }); + } catch(ServiceConfigurationError e) { + final DatatypeConfigurationException error = + new DatatypeConfigurationException( + "Provider for " + type + " cannot be found", e); + throw error; } } diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/parsers/DocumentBuilderFactory.java --- a/jaxp/src/javax/xml/parsers/DocumentBuilderFactory.java Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/src/javax/xml/parsers/DocumentBuilderFactory.java Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -40,9 +40,6 @@ public abstract class DocumentBuilderFactory { - /** The default property name according to the JAXP spec */ - private static final String DEFAULT_PROPERTY_NAME = "javax.xml.parsers.DocumentBuilderFactory"; - private boolean validating = false; private boolean namespaceAware = false; private boolean whitespace = false; @@ -50,8 +47,6 @@ private boolean ignoreComments = false; private boolean coalescing = false; - private boolean canonicalState = false; - /** *

        Protected constructor to prevent instantiation. * Use {@link #newInstance()}.

        @@ -85,14 +80,12 @@ * of any property in jaxp.properties after it has been read for the first time. * *
      • - * Use the Services API (as detailed in the JAR specification), if - * available, to determine the classname. The Services API will look - * for a classname in the file - * META-INF/services/javax.xml.parsers.DocumentBuilderFactory - * in jars available to the runtime. + * Uses the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. *
      • *
      • - * Platform default DocumentBuilderFactory instance. + * Otherwise, the system-default implementation is returned. *
      • *
      * @@ -113,21 +106,16 @@ * * @return New instance of a DocumentBuilderFactory * - * @throws FactoryConfigurationError if the implementation is not - * available or cannot be instantiated. + * @throws FactoryConfigurationError in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. */ public static DocumentBuilderFactory newInstance() { - try { - return (DocumentBuilderFactory) FactoryFinder.find( + return FactoryFinder.find( /* The default property name according to the JAXP spec */ - "javax.xml.parsers.DocumentBuilderFactory", + DocumentBuilderFactory.class, // "javax.xml.parsers.DocumentBuilderFactory" /* The fallback implementation class name */ "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } - } /** @@ -165,13 +153,9 @@ * @since 1.6 */ public static DocumentBuilderFactory newInstance(String factoryClassName, ClassLoader classLoader){ - try { //do not fallback if given classloader can't find the class, throw exception - return (DocumentBuilderFactory) FactoryFinder.newInstance(factoryClassName, classLoader, false); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } + return FactoryFinder.newInstance(DocumentBuilderFactory.class, + factoryClassName, classLoader, false); } /** @@ -391,75 +375,64 @@ public abstract Object getAttribute(String name) throws IllegalArgumentException; - /** - *

      Set a feature for this DocumentBuilderFactory and DocumentBuilders created by this factory.

      - * - *

      - * Feature names are fully qualified {@link java.net.URI}s. - * Implementations may define their own features. - * A {@link ParserConfigurationException} is thrown if this DocumentBuilderFactory or the - * DocumentBuilders it creates cannot support the feature. - * It is possible for a DocumentBuilderFactory to expose a feature value but be unable to change its state. - *

      - * - *

      - * All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature. - * When the feature is:

      - *
        - *
      • - * true: the implementation will limit XML processing to conform to implementation limits. - * Examples include enity expansion limits and XML Schema constructs that would consume large amounts of resources. - * If XML processing is limited for security reasons, it will be reported via a call to the registered - * {@link org.xml.sax.ErrorHandler#fatalError(SAXParseException exception)}. - * See {@link DocumentBuilder#setErrorHandler(org.xml.sax.ErrorHandler errorHandler)}. - *
      • - *
      • - * false: the implementation will processing XML according to the XML specifications without - * regard to possible implementation limits. - *
      • - *
      - * - * @param name Feature name. - * @param value Is feature state true or false. - * - * @throws ParserConfigurationException if this DocumentBuilderFactory or the DocumentBuilders - * it creates cannot support this feature. + /** + *

      Set a feature for this DocumentBuilderFactory and DocumentBuilders created by this factory.

      + * + *

      + * Feature names are fully qualified {@link java.net.URI}s. + * Implementations may define their own features. + * A {@link ParserConfigurationException} is thrown if this DocumentBuilderFactory or the + * DocumentBuilders it creates cannot support the feature. + * It is possible for a DocumentBuilderFactory to expose a feature value but be unable to change its state. + *

      + * + *

      + * All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature. + * When the feature is:

      + *
        + *
      • + * true: the implementation will limit XML processing to conform to implementation limits. + * Examples include enity expansion limits and XML Schema constructs that would consume large amounts of resources. + * If XML processing is limited for security reasons, it will be reported via a call to the registered + * {@link org.xml.sax.ErrorHandler#fatalError(SAXParseException exception)}. + * See {@link DocumentBuilder#setErrorHandler(org.xml.sax.ErrorHandler errorHandler)}. + *
      • + *
      • + * false: the implementation will processing XML according to the XML specifications without + * regard to possible implementation limits. + *
      • + *
      + * + * @param name Feature name. + * @param value Is feature state true or false. + * + * @throws ParserConfigurationException if this DocumentBuilderFactory or the DocumentBuilders + * it creates cannot support this feature. * @throws NullPointerException If the name parameter is null. - */ - public abstract void setFeature(String name, boolean value) - throws ParserConfigurationException; + */ + public abstract void setFeature(String name, boolean value) + throws ParserConfigurationException; - /** - *

      Get the state of the named feature.

      - * - *

      - * Feature names are fully qualified {@link java.net.URI}s. - * Implementations may define their own features. - * An {@link ParserConfigurationException} is thrown if this DocumentBuilderFactory or the - * DocumentBuilders it creates cannot support the feature. - * It is possible for an DocumentBuilderFactory to expose a feature value but be unable to change its state. - *

      - * - * @param name Feature name. - * - * @return State of the named feature. - * - * @throws ParserConfigurationException if this DocumentBuilderFactory - * or the DocumentBuilders it creates cannot support this feature. - */ - public abstract boolean getFeature(String name) - throws ParserConfigurationException; - - - /**

      Get current state of canonicalization.

      + /** + *

      Get the state of the named feature.

      * - * @return current state canonicalization control + *

      + * Feature names are fully qualified {@link java.net.URI}s. + * Implementations may define their own features. + * An {@link ParserConfigurationException} is thrown if this DocumentBuilderFactory or the + * DocumentBuilders it creates cannot support the feature. + * It is possible for an DocumentBuilderFactory to expose a feature value but be unable to change its state. + *

      + * + * @param name Feature name. + * + * @return State of the named feature. + * + * @throws ParserConfigurationException if this DocumentBuilderFactory + * or the DocumentBuilders it creates cannot support this feature. */ - /* - public boolean getCanonicalization() { - return canonicalState; - } - */ + public abstract boolean getFeature(String name) + throws ParserConfigurationException; /** @@ -488,17 +461,6 @@ } - /*

      Set canonicalization control to true or - * false.

      - * - * @param state of canonicalization - */ - /* - public void setCanonicalization(boolean state) { - canonicalState = state; - } - */ - /** *

      Set the {@link Schema} to be used by parsers created * from this factory. diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/parsers/FactoryFinder.java --- a/jaxp/src/javax/xml/parsers/FactoryFinder.java Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/src/javax/xml/parsers/FactoryFinder.java Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -25,15 +25,16 @@ package javax.xml.parsers; -import java.io.BufferedReader; import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Iterator; import java.util.Properties; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; /** - *

      Implements pluggable Datatypes.

      + *

      Implements pluggable Parsers.

      * *

      This class is duplicated for each JAXP subpackage so keep it in * sync. It is package private for secure class loading.

      @@ -51,7 +52,7 @@ /** * Cache for properties in java.home/lib/jaxp.properties */ - static Properties cacheProps = new Properties(); + private static final Properties cacheProps = new Properties(); /** * Flag indicating if properties from java.home/lib/jaxp.properties @@ -63,7 +64,7 @@ * Security support class use to check access control before * getting certain system resources. */ - static SecuritySupport ss = new SecuritySupport(); + private static final SecuritySupport ss = new SecuritySupport(); // Define system property "jaxp.debug" to get output static { @@ -96,31 +97,31 @@ * * Use bootstrap classLoader if cl = null and useBSClsLoader is true */ - static private Class getProviderClass(String className, ClassLoader cl, + static private Class getProviderClass(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException { try { if (cl == null) { if (useBSClsLoader) { - return Class.forName(className, true, FactoryFinder.class.getClassLoader()); + return Class.forName(className, false, FactoryFinder.class.getClassLoader()); } else { cl = ss.getContextClassLoader(); if (cl == null) { throw new ClassNotFoundException(); } else { - return cl.loadClass(className); + return Class.forName(className, false, cl); } } } else { - return cl.loadClass(className); + return Class.forName(className, false, cl); } } catch (ClassNotFoundException e1) { if (doFallback) { // Use current class loader - should always be bootstrap CL - return Class.forName(className, true, FactoryFinder.class.getClassLoader()); + return Class.forName(className, false, FactoryFinder.class.getClassLoader()); } else { throw e1; @@ -132,6 +133,9 @@ * Create an instance of a class. Delegates to method * getProviderClass() in order to load the class. * + * @param type Base class / Service interface of the factory to + * instantiate. + * * @param className Name of the concrete class corresponding to the * service provider * @@ -141,16 +145,20 @@ * @param doFallback True if the current ClassLoader should be tried as * a fallback if the class is not found using cl */ - static Object newInstance(String className, ClassLoader cl, boolean doFallback) - throws ConfigurationError + static T newInstance(Class type, String className, ClassLoader cl, + boolean doFallback) + throws FactoryConfigurationError { - return newInstance(className, cl, doFallback, false); + return newInstance(type, className, cl, doFallback, false); } /** * Create an instance of a class. Delegates to method * getProviderClass() in order to load the class. * + * @param type Base class / Service interface of the factory to + * instantiate. + * * @param className Name of the concrete class corresponding to the * service provider * @@ -163,9 +171,11 @@ * @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter * is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader. */ - static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader) - throws ConfigurationError + static T newInstance(Class type, String className, ClassLoader cl, + boolean doFallback, boolean useBSClsLoader) + throws FactoryConfigurationError { + assert type != null; // make sure we have access to restricted packages if (System.getSecurityManager() != null) { if (className != null && className.startsWith(DEFAULT_PACKAGE)) { @@ -175,22 +185,24 @@ } try { - Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); + Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); + if (!type.isAssignableFrom(providerClass)) { + throw new ClassCastException(className + " cannot be cast to " + type.getName()); + } Object instance = providerClass.newInstance(); if (debug) { // Extra check to avoid computing cl strings dPrint("created new instance of " + providerClass + " using ClassLoader: " + cl); } - return instance; + return type.cast(instance); } catch (ClassNotFoundException x) { - throw new ConfigurationError( - "Provider " + className + " not found", x); + throw new FactoryConfigurationError(x, + "Provider " + className + " not found"); } catch (Exception x) { - throw new ConfigurationError( - "Provider " + className + " could not be instantiated: " + x, - x); + throw new FactoryConfigurationError(x, + "Provider " + className + " could not be instantiated: " + x); } } @@ -199,16 +211,17 @@ * entry point. * @return Class object of factory, never null * - * @param factoryId Name of the factory to find, same as - * a property name + * @param type Base class / Service interface of the + * factory to find. * @param fallbackClassName Implementation class name, if nothing else * is found. Use null to mean no fallback. * * Package private so this code can be shared. */ - static Object find(String factoryId, String fallbackClassName) - throws ConfigurationError + static T find(Class type, String fallbackClassName) + throws FactoryConfigurationError { + final String factoryId = type.getName(); dPrint("find factoryId =" + factoryId); // Use the system property first @@ -216,7 +229,7 @@ String systemProp = ss.getSystemProperty(factoryId); if (systemProp != null) { dPrint("found system property, value=" + systemProp); - return newInstance(systemProp, null, true); + return newInstance(type, systemProp, null, true); } } catch (SecurityException se) { @@ -225,7 +238,6 @@ // try to read from $java.home/lib/jaxp.properties try { - String factoryClassName = null; if (firstTime) { synchronized (cacheProps) { if (firstTime) { @@ -240,11 +252,11 @@ } } } - factoryClassName = cacheProps.getProperty(factoryId); + final String factoryClassName = cacheProps.getProperty(factoryId); if (factoryClassName != null) { dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName); - return newInstance(factoryClassName, null, true); + return newInstance(type, factoryClassName, null, true); } } catch (Exception ex) { @@ -252,112 +264,52 @@ } // Try Jar Service Provider Mechanism - Object provider = findJarServiceProvider(factoryId); + T provider = findServiceProvider(type); if (provider != null) { return provider; } if (fallbackClassName == null) { - throw new ConfigurationError( - "Provider for " + factoryId + " cannot be found", null); + throw new FactoryConfigurationError( + "Provider for " + factoryId + " cannot be found"); } dPrint("loaded from fallback value: " + fallbackClassName); - return newInstance(fallbackClassName, null, true); + return newInstance(type, fallbackClassName, null, true); } /* - * Try to find provider using Jar Service Provider Mechanism + * Try to find provider using the ServiceLoader API + * + * @param type Base class / Service interface of the factory to find. * * @return instance of provider class if found or null */ - private static Object findJarServiceProvider(String factoryId) - throws ConfigurationError - { - String serviceId = "META-INF/services/" + factoryId; - InputStream is = null; - - // First try the Context ClassLoader - ClassLoader cl = ss.getContextClassLoader(); - boolean useBSClsLoader = false; - if (cl != null) { - is = ss.getResourceAsStream(cl, serviceId); - - // If no provider found then try the current ClassLoader - if (is == null) { - cl = FactoryFinder.class.getClassLoader(); - is = ss.getResourceAsStream(cl, serviceId); - useBSClsLoader = true; - } - } else { - // No Context ClassLoader, try the current ClassLoader - cl = FactoryFinder.class.getClassLoader(); - is = ss.getResourceAsStream(cl, serviceId); - useBSClsLoader = true; - } - - if (is == null) { - // No provider found - return null; - } - - if (debug) { // Extra check to avoid computing cl strings - dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl); - } - - BufferedReader rd; - try { - rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); - } - catch (java.io.UnsupportedEncodingException e) { - rd = new BufferedReader(new InputStreamReader(is)); - } - - String factoryClassName = null; + private static T findServiceProvider(final Class type) { try { - // XXX Does not handle all possible input as specified by the - // Jar Service Provider specification - factoryClassName = rd.readLine(); - rd.close(); - } catch (IOException x) { - // No provider found - return null; - } - - if (factoryClassName != null && !"".equals(factoryClassName)) { - dPrint("found in resource, value=" + factoryClassName); - - // Note: here we do not want to fall back to the current - // ClassLoader because we want to avoid the case where the - // resource file was found using one ClassLoader and the - // provider class was instantiated using a different one. - return newInstance(factoryClassName, cl, false, useBSClsLoader); - } - - // No provider found - return null; - } - - static class ConfigurationError extends Error { - private Exception exception; - - /** - * Construct a new instance with the specified detail string and - * exception. - */ - ConfigurationError(String msg, Exception x) { - super(msg); - this.exception = x; - } - - Exception getException() { - return exception; - } - /** - * use the exception chaining mechanism of JDK1.4 - */ - @Override - public Throwable getCause() { - return exception; + return AccessController.doPrivileged(new PrivilegedAction() { + public T run() { + final ServiceLoader serviceLoader = ServiceLoader.load(type); + final Iterator iterator = serviceLoader.iterator(); + if (iterator.hasNext()) { + return iterator.next(); + } else { + return null; + } + } + }); + } catch(ServiceConfigurationError e) { + // It is not possible to wrap an error directly in + // FactoryConfigurationError - so we need to wrap the + // ServiceConfigurationError in a RuntimeException. + // The alternative would be to modify the logic in + // FactoryConfigurationError to allow setting a + // Throwable as the cause, but that could cause + // compatibility issues down the road. + final RuntimeException x = new RuntimeException( + "Provider for " + type + " cannot be created", e); + final FactoryConfigurationError error = + new FactoryConfigurationError(x, x.getMessage()); + throw error; } } diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/parsers/SAXParserFactory.java --- a/jaxp/src/javax/xml/parsers/SAXParserFactory.java Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/src/javax/xml/parsers/SAXParserFactory.java Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -26,7 +26,6 @@ package javax.xml.parsers; import javax.xml.validation.Schema; - import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; @@ -42,8 +41,6 @@ * */ public abstract class SAXParserFactory { - /** The default property name according to the JAXP spec */ - private static final String DEFAULT_PROPERTY_NAME = "javax.xml.parsers.SAXParserFactory"; /** *

      Should Parsers be validating?

      @@ -87,14 +84,12 @@ * of any property in jaxp.properties after it has been read for the first time. * *
    • - * Use the Services API (as detailed in the JAR specification), if - * available, to determine the classname. The Services API will look - * for a classname in the file - * META-INF/services/javax.xml.parsers.SAXParserFactory - * in jars available to the runtime. + * Use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. *
    • *
    • - * Platform default SAXParserFactory instance. + * Otherwise the system-default implementation is returned. *
    • *
    * @@ -109,7 +104,7 @@ * this method to print a lot of debug messages * to System.err about what it is doing and where it is looking at.

    * - *

    If you have problems loading {@link DocumentBuilder}s, try:

    + *

    If you have problems loading {@link SAXParser}s, try:

    *
          * java -Djaxp.debug=1 YourProgram ....
          * 
    @@ -117,21 +112,17 @@ * * @return A new instance of a SAXParserFactory. * - * @throws FactoryConfigurationError if the implementation is - * not available or cannot be instantiated. + * @throws FactoryConfigurationError in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. */ public static SAXParserFactory newInstance() { - try { - return (SAXParserFactory) FactoryFinder.find( + return FactoryFinder.find( /* The default property name according to the JAXP spec */ - "javax.xml.parsers.SAXParserFactory", + SAXParserFactory.class, /* The fallback implementation class name */ "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } } /** @@ -169,13 +160,9 @@ * @since 1.6 */ public static SAXParserFactory newInstance(String factoryClassName, ClassLoader classLoader){ - try { //do not fallback if given classloader can't find the class, throw exception - return (SAXParserFactory) FactoryFinder.newInstance(factoryClassName, classLoader, false); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } + return FactoryFinder.newInstance(SAXParserFactory.class, + factoryClassName, classLoader, false); } /** @@ -266,22 +253,22 @@ * A list of the core features and properties can be found at * http://www.saxproject.org/

    * - *

    All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature. - * When the feature is

    - *
      - *
    • - * true: the implementation will limit XML processing to conform to implementation limits. - * Examples include enity expansion limits and XML Schema constructs that would consume large amounts of resources. - * If XML processing is limited for security reasons, it will be reported via a call to the registered - * {@link org.xml.sax.ErrorHandler#fatalError(SAXParseException exception)}. - * See {@link SAXParser} parse methods for handler specification. - *
    • - *
    • - * When the feature is false, the implementation will processing XML according to the XML specifications without - * regard to possible implementation limits. - *
    • - *
    - * + *

    All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature. + * When the feature is

    + *
      + *
    • + * true: the implementation will limit XML processing to conform to implementation limits. + * Examples include entity expansion limits and XML Schema constructs that would consume large amounts of resources. + * If XML processing is limited for security reasons, it will be reported via a call to the registered + * {@link org.xml.sax.ErrorHandler#fatalError(SAXParseException exception)}. + * See {@link SAXParser} parse methods for handler specification. + *
    • + *
    • + * When the feature is false, the implementation will processing XML according to the XML specifications without + * regard to possible implementation limits. + *
    • + *
    + * * @param name The name of the feature to be set. * @param value The value of the feature to be set. * @@ -320,17 +307,6 @@ SAXNotSupportedException; - - /*

    Get current state of canonicalization.

    - * - * @return current state canonicalization control - */ - /* - public boolean getCanonicalization() { - return canonicalState; - } - */ - /** * Gets the {@link Schema} object specified through * the {@link #setSchema(Schema schema)} method. @@ -357,17 +333,6 @@ ); } - /**

    Set canonicalization control to true or - * false.

    - * - * @param state of canonicalization - */ - /* - public void setCanonicalization(boolean state) { - canonicalState = state; - } - */ - /** *

    Set the {@link Schema} to be used by parsers created * from this factory.

    @@ -400,7 +365,7 @@ * Such configuration will cause a {@link SAXException} * exception when those properties are set on a {@link SAXParser}.

    * - *

    Note for implmentors

    + *

    Note for implementors

    *

    * A parser must be able to work with any {@link Schema} * implementation. However, parsers and schemas are allowed diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/stream/FactoryFinder.java --- a/jaxp/src/javax/xml/stream/FactoryFinder.java Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/src/javax/xml/stream/FactoryFinder.java Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -25,15 +25,16 @@ package javax.xml.stream; -import java.io.BufferedReader; import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Iterator; import java.util.Properties; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; /** - *

    Implements pluggable Datatypes.

    + *

    Implements pluggable streams.

    * *

    This class is duplicated for each JAXP subpackage so keep it in * sync. It is package private for secure class loading.

    @@ -52,19 +53,19 @@ /** * Cache for properties in java.home/lib/jaxp.properties */ - static Properties cacheProps = new Properties(); + final private static Properties cacheProps = new Properties(); /** * Flag indicating if properties from java.home/lib/jaxp.properties * have been cached. */ - static volatile boolean firstTime = true; + private static volatile boolean firstTime = true; /** * Security support class use to check access control before * getting certain system resources. */ - static SecuritySupport ss = new SecuritySupport(); + final private static SecuritySupport ss = new SecuritySupport(); // Define system property "jaxp.debug" to get output static { @@ -103,25 +104,25 @@ try { if (cl == null) { if (useBSClsLoader) { - return Class.forName(className, true, FactoryFinder.class.getClassLoader()); + return Class.forName(className, false, FactoryFinder.class.getClassLoader()); } else { cl = ss.getContextClassLoader(); if (cl == null) { throw new ClassNotFoundException(); } else { - return cl.loadClass(className); + return Class.forName(className, false, cl); } } } else { - return cl.loadClass(className); + return Class.forName(className, false, cl); } } catch (ClassNotFoundException e1) { if (doFallback) { // Use current class loader - should always be bootstrap CL - return Class.forName(className, true, FactoryFinder.class.getClassLoader()); + return Class.forName(className, false, FactoryFinder.class.getClassLoader()); } else { throw e1; @@ -133,6 +134,9 @@ * Create an instance of a class. Delegates to method * getProviderClass() in order to load the class. * + * @param type Base class / Service interface of the factory to + * instantiate. + * * @param className Name of the concrete class corresponding to the * service provider * @@ -142,16 +146,19 @@ * @param doFallback True if the current ClassLoader should be tried as * a fallback if the class is not found using cl */ - static Object newInstance(String className, ClassLoader cl, boolean doFallback) - throws ConfigurationError + static T newInstance(Class type, String className, ClassLoader cl, boolean doFallback) + throws FactoryConfigurationError { - return newInstance(className, cl, doFallback, false); + return newInstance(type, className, cl, doFallback, false); } /** * Create an instance of a class. Delegates to method * getProviderClass() in order to load the class. * + * @param type Base class / Service interface of the factory to + * instantiate. + * * @param className Name of the concrete class corresponding to the * service provider * @@ -164,9 +171,12 @@ * @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter * is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader. */ - static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader) - throws ConfigurationError + static T newInstance(Class type, String className, ClassLoader cl, + boolean doFallback, boolean useBSClsLoader) + throws FactoryConfigurationError { + assert type != null; + // make sure we have access to restricted packages if (System.getSecurityManager() != null) { if (className != null && className.startsWith(DEFAULT_PACKAGE)) { @@ -176,20 +186,23 @@ } try { - Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); + Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); + if (!type.isAssignableFrom(providerClass)) { + throw new ClassCastException(className + " cannot be cast to " + type.getName()); + } Object instance = providerClass.newInstance(); if (debug) { // Extra check to avoid computing cl strings dPrint("created new instance of " + providerClass + " using ClassLoader: " + cl); } - return instance; + return type.cast(instance); } catch (ClassNotFoundException x) { - throw new ConfigurationError( + throw new FactoryConfigurationError( "Provider " + className + " not found", x); } catch (Exception x) { - throw new ConfigurationError( + throw new FactoryConfigurationError( "Provider " + className + " could not be instantiated: " + x, x); } @@ -200,17 +213,18 @@ * * @return Class object of factory, never null * - * @param factoryId Name of the factory to find, same as - * a property name + * @param type Base class / Service interface of the + * factory to find. + * * @param fallbackClassName Implementation class name, if nothing else * is found. Use null to mean no fallback. * * Package private so this code can be shared. */ - static Object find(String factoryId, String fallbackClassName) - throws ConfigurationError + static T find(Class type, String fallbackClassName) + throws FactoryConfigurationError { - return find(factoryId, null, fallbackClassName); + return find(type, type.getName(), null, fallbackClassName); } /** @@ -218,6 +232,9 @@ * entry point. * @return Class object of factory, never null * + * @param type Base class / Service interface of the + * factory to find. + * * @param factoryId Name of the factory to find, same as * a property name * @@ -229,8 +246,8 @@ * * Package private so this code can be shared. */ - static Object find(String factoryId, ClassLoader cl, String fallbackClassName) - throws ConfigurationError + static T find(Class type, String factoryId, ClassLoader cl, String fallbackClassName) + throws FactoryConfigurationError { dPrint("find factoryId =" + factoryId); @@ -239,7 +256,9 @@ String systemProp = ss.getSystemProperty(factoryId); if (systemProp != null) { dPrint("found system property, value=" + systemProp); - return newInstance(systemProp, null, true); + // There's a bug here - because 'cl' is ignored. + // This will be handled separately. + return newInstance(type, systemProp, null, true); } } catch (SecurityException se) { @@ -250,7 +269,6 @@ // $java.home/lib/jaxp.properties if former not present String configFile = null; try { - String factoryClassName = null; if (firstTime) { synchronized (cacheProps) { if (firstTime) { @@ -269,130 +287,80 @@ if (ss.doesFileExist(f)) { dPrint("Read properties file "+f); cacheProps.load(ss.getFileInputStream(f)); + } + } } } } - } - } - factoryClassName = cacheProps.getProperty(factoryId); + final String factoryClassName = cacheProps.getProperty(factoryId); if (factoryClassName != null) { dPrint("found in " + configFile + " value=" + factoryClassName); - return newInstance(factoryClassName, null, true); + // There's a bug here - because 'cl' is ignored. + // This will be handled separately. + return newInstance(type, factoryClassName, null, true); } } catch (Exception ex) { if (debug) ex.printStackTrace(); } - // Try Jar Service Provider Mechanism - Object provider = findJarServiceProvider(factoryId); - if (provider != null) { - return provider; + if (type.getName().equals(factoryId)) { + // Try Jar Service Provider Mechanism + final T provider = findServiceProvider(type); + if (provider != null) { + return provider; + } + } else { + // We're in the case where a 'custom' factoryId was provided, + // and in every case where that happens, we expect that + // fallbackClassName will be null. + assert fallbackClassName == null; } if (fallbackClassName == null) { - throw new ConfigurationError( + throw new FactoryConfigurationError( "Provider for " + factoryId + " cannot be found", null); } dPrint("loaded from fallback value: " + fallbackClassName); - return newInstance(fallbackClassName, cl, true); + return newInstance(type, fallbackClassName, cl, true); } /* - * Try to find provider using Jar Service Provider Mechanism + * Try to find provider using the ServiceLoader API + * + * @param type Base class / Service interface of the factory to find. * * @return instance of provider class if found or null */ - private static Object findJarServiceProvider(String factoryId) - throws ConfigurationError - { - String serviceId = "META-INF/services/" + factoryId; - InputStream is = null; - - // First try the Context ClassLoader - ClassLoader cl = ss.getContextClassLoader(); - boolean useBSClsLoader = false; - if (cl != null) { - is = ss.getResourceAsStream(cl, serviceId); - - // If no provider found then try the current ClassLoader - if (is == null) { - cl = FactoryFinder.class.getClassLoader(); - is = ss.getResourceAsStream(cl, serviceId); - useBSClsLoader = true; - } - } else { - // No Context ClassLoader, try the current ClassLoader - cl = FactoryFinder.class.getClassLoader(); - is = ss.getResourceAsStream(cl, serviceId); - useBSClsLoader = true; - } - - if (is == null) { - // No provider found - return null; - } - - if (debug) { // Extra check to avoid computing cl strings - dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl); - } - - BufferedReader rd; - try { - rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); - } - catch (java.io.UnsupportedEncodingException e) { - rd = new BufferedReader(new InputStreamReader(is)); - } - - String factoryClassName = null; + private static T findServiceProvider(final Class type) { try { - // XXX Does not handle all possible input as specified by the - // Jar Service Provider specification - factoryClassName = rd.readLine(); - rd.close(); - } catch (IOException x) { - // No provider found - return null; - } - - if (factoryClassName != null && !"".equals(factoryClassName)) { - dPrint("found in resource, value=" + factoryClassName); - - // Note: here we do not want to fall back to the current - // ClassLoader because we want to avoid the case where the - // resource file was found using one ClassLoader and the - // provider class was instantiated using a different one. - return newInstance(factoryClassName, cl, false, useBSClsLoader); - } - - // No provider found - return null; - } - - static class ConfigurationError extends Error { - private Exception exception; - - /** - * Construct a new instance with the specified detail string and - * exception. - */ - ConfigurationError(String msg, Exception x) { - super(msg); - this.exception = x; - } - - Exception getException() { - return exception; - } - /** - * use the exception chaining mechanism of JDK1.4 - */ - @Override - public Throwable getCause() { - return exception; - } - } + return AccessController.doPrivileged(new PrivilegedAction() { + @Override + public T run() { + final ServiceLoader serviceLoader = ServiceLoader.load(type); + final Iterator iterator = serviceLoader.iterator(); + if (iterator.hasNext()) { + return iterator.next(); + } else { + return null; + } + } + }); + } catch(ServiceConfigurationError e) { + // It is not possible to wrap an error directly in + // FactoryConfigurationError - so we need to wrap the + // ServiceConfigurationError in a RuntimeException. + // The alternative would be to modify the logic in + // FactoryConfigurationError to allow setting a + // Throwable as the cause, but that could cause + // compatibility issues down the road. + final RuntimeException x = new RuntimeException( + "Provider for " + type + " cannot be created", e); + final FactoryConfigurationError error = + new FactoryConfigurationError(x, x.getMessage()); + throw error; + } + } } diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/stream/XMLEventFactory.java --- a/jaxp/src/javax/xml/stream/XMLEventFactory.java Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/src/javax/xml/stream/XMLEventFactory.java Wed May 08 23:54:45 2013 -0700 @@ -23,14 +23,14 @@ */ /* - * Copyright (c) 2009 by Oracle Corporation. All Rights Reserved. + * Copyright (c) 2009, 2013, by Oracle Corporation. All Rights Reserved. */ package javax.xml.stream; -import javax.xml.stream.events.*; +import java.util.Iterator; import javax.xml.namespace.NamespaceContext; import javax.xml.namespace.QName; -import java.util.Iterator; +import javax.xml.stream.events.*; /** * This interface defines a utility class for creating instances of * XMLEvents @@ -54,48 +54,59 @@ /** - * Create a new instance of the factory + * Creates a new instance of the factory in exactly the same manner as the + * {@link #newFactory()} method. * @throws FactoryConfigurationError if an instance of this factory cannot be loaded */ public static XMLEventFactory newInstance() throws FactoryConfigurationError { - return (XMLEventFactory) FactoryFinder.find( - JAXPFACTORYID, - DEFAULIMPL); + return FactoryFinder.find(XMLEventFactory.class, DEFAULIMPL); } /** * Create a new instance of the factory. + *

    * This static method creates a new factory instance. * This method uses the following ordered lookup procedure to determine * the XMLEventFactory implementation class to load: + *

    + *
      + *
    • * Use the javax.xml.stream.XMLEventFactory system property. + *
    • + *
    • * Use the properties file "lib/stax.properties" in the JRE directory. * This configuration file is in standard java.util.Properties format * and contains the fully qualified name of the implementation class * with the key being the system property defined above. - * Use the Services API (as detailed in the JAR specification), if available, - * to determine the classname. The Services API will look for a classname - * in the file META-INF/services/javax.xml.stream.XMLEventFactory in jars - * available to the runtime. - * Platform default XMLEventFactory instance. - * + *
    • + *
    • + * Use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. + *
    • + *
    • + * Otherwise, the system-default implementation is returned. + *
    • + *
    + *

    * Once an application has obtained a reference to a XMLEventFactory it * can use the factory to configure and obtain stream instances. - * + *

    + *

    * Note that this is a new method that replaces the deprecated newInstance() method. * No changes in behavior are defined by this replacement method relative to * the deprecated method. - * - * @throws FactoryConfigurationError if an instance of this factory cannot be loaded + *

    + * @throws FactoryConfigurationError in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. */ public static XMLEventFactory newFactory() throws FactoryConfigurationError { - return (XMLEventFactory) FactoryFinder.find( - JAXPFACTORYID, - DEFAULIMPL); + return FactoryFinder.find(XMLEventFactory.class, DEFAULIMPL); } /** @@ -116,40 +127,59 @@ public static XMLEventFactory newInstance(String factoryId, ClassLoader classLoader) throws FactoryConfigurationError { - try { - //do not fallback if given classloader can't find the class, throw exception - return (XMLEventFactory) FactoryFinder.find(factoryId, classLoader, null); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } + //do not fallback if given classloader can't find the class, throw exception + return FactoryFinder.find(XMLEventFactory.class, factoryId, classLoader, null); } /** * Create a new instance of the factory. * If the classLoader argument is null, then the ContextClassLoader is used. + *

    + * This method uses the following ordered lookup procedure to determine + * the XMLEventFactory implementation class to load: + *

    + *
      + *
    • + * Use the value of the system property identified by {@code factoryId}. + *
    • + *
    • + * Use the properties file "lib/stax.properties" in the JRE directory. + * This configuration file is in standard java.util.Properties format + * and contains the fully qualified name of the implementation class + * with the key being the given {@code factoryId}. + *
    • + *
    • + * If {@code factoryId} is "javax.xml.stream.XMLEventFactory", + * use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. + *
    • + *
    • + * Otherwise, throws a {@link FactoryConfigurationError}. + *
    • + *
    * + *

    * Note that this is a new method that replaces the deprecated - * newInstance(String factoryId, ClassLoader classLoader) method. + * {@link #newInstance(java.lang.String, java.lang.ClassLoader) + * newInstance(String factoryId, ClassLoader classLoader)} method. * No changes in behavior are defined by this replacement method relative * to the deprecated method. + *

    * * @param factoryId Name of the factory to find, same as * a property name * @param classLoader classLoader to use * @return the factory implementation - * @throws FactoryConfigurationError if an instance of this factory cannot be loaded + * @throws FactoryConfigurationError in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. */ public static XMLEventFactory newFactory(String factoryId, - ClassLoader classLoader) + ClassLoader classLoader) throws FactoryConfigurationError { - try { - //do not fallback if given classloader can't find the class, throw exception - return (XMLEventFactory) FactoryFinder.find(factoryId, classLoader, null); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } + //do not fallback if given classloader can't find the class, throw exception + return FactoryFinder.find(XMLEventFactory.class, factoryId, classLoader, null); } /** diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/stream/XMLInputFactory.java --- a/jaxp/src/javax/xml/stream/XMLInputFactory.java Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/src/javax/xml/stream/XMLInputFactory.java Wed May 08 23:54:45 2013 -0700 @@ -23,13 +23,13 @@ */ /* - * Copyright (c) 2009 by Oracle Corporation. All Rights Reserved. + * Copyright (c) 2009, 2013, by Oracle Corporation. All Rights Reserved. */ package javax.xml.stream; +import javax.xml.stream.util.XMLEventAllocator; import javax.xml.transform.Source; -import javax.xml.stream.util.XMLEventAllocator; /** * Defines an abstract implementation of a factory for getting streams. @@ -144,48 +144,59 @@ protected XMLInputFactory(){} /** - * Create a new instance of the factory. + * Creates a new instance of the factory in exactly the same manner as the + * {@link #newFactory()} method. * @throws FactoryConfigurationError if an instance of this factory cannot be loaded */ public static XMLInputFactory newInstance() throws FactoryConfigurationError { - return (XMLInputFactory) FactoryFinder.find( - "javax.xml.stream.XMLInputFactory", - DEFAULIMPL); + return FactoryFinder.find(XMLInputFactory.class, DEFAULIMPL); } /** * Create a new instance of the factory. + *

    * This static method creates a new factory instance. * This method uses the following ordered lookup procedure to determine * the XMLInputFactory implementation class to load: + *

    + *
      + *
    • * Use the javax.xml.stream.XMLInputFactory system property. + *
    • + *
    • * Use the properties file "lib/stax.properties" in the JRE directory. * This configuration file is in standard java.util.Properties format * and contains the fully qualified name of the implementation class * with the key being the system property defined above. - * Use the Services API (as detailed in the JAR specification), if available, - * to determine the classname. The Services API will look for a classname - * in the file META-INF/services/javax.xml.stream.XMLInputFactory in jars - * available to the runtime. - * Platform default XMLInputFactory instance. - * + *
    • + *
    • + * Use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. + *
    • + *
    • + * Otherwise, the system-default implementation is returned. + *
    • + *
    + *

    * Once an application has obtained a reference to a XMLInputFactory it * can use the factory to configure and obtain stream instances. - * + *

    + *

    * Note that this is a new method that replaces the deprecated newInstance() method. * No changes in behavior are defined by this replacement method relative to * the deprecated method. - * - * @throws FactoryConfigurationError if an instance of this factory cannot be loaded + *

    + * @throws FactoryConfigurationError in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. */ public static XMLInputFactory newFactory() throws FactoryConfigurationError { - return (XMLInputFactory) FactoryFinder.find( - "javax.xml.stream.XMLInputFactory", - DEFAULIMPL); + return FactoryFinder.find(XMLInputFactory.class, DEFAULIMPL); } /** @@ -206,40 +217,60 @@ public static XMLInputFactory newInstance(String factoryId, ClassLoader classLoader) throws FactoryConfigurationError { - try { - //do not fallback if given classloader can't find the class, throw exception - return (XMLInputFactory) FactoryFinder.find(factoryId, classLoader, null); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } + //do not fallback if given classloader can't find the class, throw exception + return FactoryFinder.find(XMLInputFactory.class, factoryId, classLoader, null); } /** * Create a new instance of the factory. * If the classLoader argument is null, then the ContextClassLoader is used. + *

    + * This method uses the following ordered lookup procedure to determine + * the XMLInputFactory implementation class to load: + *

    + *
      + *
    • + * Use the value of the system property identified by {@code factoryId}. + *
    • + *
    • + * Use the properties file "lib/stax.properties" in the JRE directory. + * This configuration file is in standard java.util.Properties format + * and contains the fully qualified name of the implementation class + * with the key being the given {@code factoryId}. + *
    • + *
    • + * If {@code factoryId} is "javax.xml.stream.XMLInputFactory", + * use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. + *
    • + *
    • + * Otherwise, throws a {@link FactoryConfigurationError}. + *
    • + *
    * + *

    * Note that this is a new method that replaces the deprecated - * newInstance(String factoryId, ClassLoader classLoader) method. + * {@link #newInstance(java.lang.String, java.lang.ClassLoader) + * newInstance(String factoryId, ClassLoader classLoader)} method. * No changes in behavior are defined by this replacement method relative * to the deprecated method. + *

    * * @param factoryId Name of the factory to find, same as * a property name * @param classLoader classLoader to use * @return the factory implementation + * @throws FactoryConfigurationError in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. * @throws FactoryConfigurationError if an instance of this factory cannot be loaded */ public static XMLInputFactory newFactory(String factoryId, ClassLoader classLoader) throws FactoryConfigurationError { - try { - //do not fallback if given classloader can't find the class, throw exception - return (XMLInputFactory) FactoryFinder.find(factoryId, classLoader, null); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } + //do not fallback if given classloader can't find the class, throw exception + return FactoryFinder.find(XMLInputFactory.class, factoryId, classLoader, null); } /** diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/stream/XMLOutputFactory.java --- a/jaxp/src/javax/xml/stream/XMLOutputFactory.java Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/src/javax/xml/stream/XMLOutputFactory.java Wed May 08 23:54:45 2013 -0700 @@ -23,7 +23,7 @@ */ /* - * Copyright (c) 2009 by Oracle Corporation. All Rights Reserved. + * Copyright (c) 2009, 2013, by Oracle Corporation. All Rights Reserved. */ package javax.xml.stream; @@ -120,46 +120,58 @@ protected XMLOutputFactory(){} /** - * Create a new instance of the factory. + * Creates a new instance of the factory in exactly the same manner as the + * {@link #newFactory()} method. * @throws FactoryConfigurationError if an instance of this factory cannot be loaded */ public static XMLOutputFactory newInstance() throws FactoryConfigurationError { - return (XMLOutputFactory) FactoryFinder.find("javax.xml.stream.XMLOutputFactory", - DEFAULIMPL); + return FactoryFinder.find(XMLOutputFactory.class, DEFAULIMPL); } /** * Create a new instance of the factory. + *

    * This static method creates a new factory instance. This method uses the * following ordered lookup procedure to determine the XMLOutputFactory * implementation class to load: + *

    + *
      + *
    • * Use the javax.xml.stream.XMLOutputFactory system property. + *
    • + *
    • * Use the properties file "lib/stax.properties" in the JRE directory. * This configuration file is in standard java.util.Properties format * and contains the fully qualified name of the implementation class * with the key being the system property defined above. - * Use the Services API (as detailed in the JAR specification), if available, - * to determine the classname. The Services API will look for a classname - * in the file META-INF/services/javax.xml.stream.XMLOutputFactory in jars - * available to the runtime. - * Platform default XMLOutputFactory instance. - * + *
    • + *
    • + * Use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. + *
    • + *
    • + * Otherwise, the system-default implementation is returned. + *
    • + *

      * Once an application has obtained a reference to a XMLOutputFactory it * can use the factory to configure and obtain stream instances. - * + *

      + *

      * Note that this is a new method that replaces the deprecated newInstance() method. * No changes in behavior are defined by this replacement method relative to the * deprecated method. - * - * @throws FactoryConfigurationError if an instance of this factory cannot be loaded + *

      + * @throws FactoryConfigurationError in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. */ public static XMLOutputFactory newFactory() throws FactoryConfigurationError { - return (XMLOutputFactory) FactoryFinder.find("javax.xml.stream.XMLOutputFactory", - DEFAULIMPL); + return FactoryFinder.find(XMLOutputFactory.class, DEFAULIMPL); } /** @@ -179,42 +191,59 @@ public static XMLInputFactory newInstance(String factoryId, ClassLoader classLoader) throws FactoryConfigurationError { - try { - //do not fallback if given classloader can't find the class, throw exception - return (XMLInputFactory) FactoryFinder.find(factoryId, classLoader, null); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } + //do not fallback if given classloader can't find the class, throw exception + return FactoryFinder.find(XMLInputFactory.class, factoryId, classLoader, null); } /** * Create a new instance of the factory. * If the classLoader argument is null, then the ContextClassLoader is used. - * - * Note that this is a new method that replaces the deprecated - * newInstance(String factoryId, ClassLoader classLoader) method. + *

      + * This method uses the following ordered lookup procedure to determine + * the XMLOutputFactory implementation class to load: + *

      + *
        + *
      • + * Use the value of the system property identified by {@code factoryId}. + *
      • + *
      • + * Use the properties file "lib/stax.properties" in the JRE directory. + * This configuration file is in standard java.util.Properties format + * and contains the fully qualified name of the implementation class + * with the key being the given {@code factoryId}. + *
      • + *
      • + * If {@code factoryId} is "javax.xml.stream.XMLOutputFactory", + * use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. + *
      • + *
      • + * Otherwise, throws a {@link FactoryConfigurationError}. + *
      • + *
      * - * No changes in behavior are defined by this replacement method relative - * to the deprecated method. - * + *

      + * Note that this is a new method that replaces the deprecated + * {@link #newInstance(java.lang.String, java.lang.ClassLoader) + * newInstance(String factoryId, ClassLoader classLoader)} method. + * No changes in behavior are defined by this replacement method relative + * to the deprecated method. + *

      * * @param factoryId Name of the factory to find, same as * a property name * @param classLoader classLoader to use * @return the factory implementation - * @throws FactoryConfigurationError if an instance of this factory cannot be loaded + * @throws FactoryConfigurationError in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. */ public static XMLOutputFactory newFactory(String factoryId, ClassLoader classLoader) throws FactoryConfigurationError { - try { - //do not fallback if given classloader can't find the class, throw exception - return (XMLOutputFactory) FactoryFinder.find(factoryId, classLoader, null); - } catch (FactoryFinder.ConfigurationError e) { - throw new FactoryConfigurationError(e.getException(), - e.getMessage()); - } + //do not fallback if given classloader can't find the class, throw exception + return FactoryFinder.find(XMLOutputFactory.class, factoryId, classLoader, null); } /** diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/transform/FactoryFinder.java --- a/jaxp/src/javax/xml/transform/FactoryFinder.java Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/src/javax/xml/transform/FactoryFinder.java Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -25,13 +25,15 @@ package javax.xml.transform; -import java.io.BufferedReader; import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Iterator; import java.util.Properties; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; /** *

      Implements pluggable Datatypes.

      @@ -53,7 +55,7 @@ /** * Cache for properties in java.home/lib/jaxp.properties */ - static Properties cacheProps = new Properties(); + private final static Properties cacheProps = new Properties(); /** * Flag indicating if properties from java.home/lib/jaxp.properties @@ -65,7 +67,7 @@ * Security support class use to check access control before * getting certain system resources. */ - static SecuritySupport ss = new SecuritySupport(); + private final static SecuritySupport ss = new SecuritySupport(); // Define system property "jaxp.debug" to get output static { @@ -98,31 +100,31 @@ * * Use bootstrap classLoader if cl = null and useBSClsLoader is true */ - static private Class getProviderClass(String className, ClassLoader cl, + static private Class getProviderClass(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException { try { if (cl == null) { if (useBSClsLoader) { - return Class.forName(className, true, FactoryFinder.class.getClassLoader()); + return Class.forName(className, false, FactoryFinder.class.getClassLoader()); } else { cl = ss.getContextClassLoader(); if (cl == null) { throw new ClassNotFoundException(); } else { - return cl.loadClass(className); + return Class.forName(className, false, cl); } } } else { - return cl.loadClass(className); + return Class.forName(className, false, cl); } } catch (ClassNotFoundException e1) { if (doFallback) { // Use current class loader - should always be bootstrap CL - return Class.forName(className, true, FactoryFinder.class.getClassLoader()); + return Class.forName(className, false, FactoryFinder.class.getClassLoader()); } else { throw e1; @@ -134,24 +136,8 @@ * Create an instance of a class. Delegates to method * getProviderClass() in order to load the class. * - * @param className Name of the concrete class corresponding to the - * service provider - * - * @param cl ClassLoader used to load the factory class. If null - * current Thread's context classLoader is used to load the factory class. - * - * @param doFallback True if the current ClassLoader should be tried as - * a fallback if the class is not found using cl - */ - static Object newInstance(String className, ClassLoader cl, boolean doFallback) - throws ConfigurationError - { - return newInstance(className, cl, doFallback, false, false); - } - - /** - * Create an instance of a class. Delegates to method - * getProviderClass() in order to load the class. + * @param type Base class / Service interface of the factory to + * instantiate. * * @param className Name of the concrete class corresponding to the * service provider @@ -162,14 +148,15 @@ * @param doFallback True if the current ClassLoader should be tried as * a fallback if the class is not found using cl * - * @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter - * is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader. - * * @param useServicesMechanism True use services mechanism */ - static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader, boolean useServicesMechanism) - throws ConfigurationError + static T newInstance(Class type, String className, ClassLoader cl, + boolean doFallback, boolean useServicesMechanism) + throws TransformerFactoryConfigurationError { + assert type != null; + + boolean useBSClsLoader = false; // make sure we have access to restricted packages if (System.getSecurityManager() != null) { if (className != null && className.startsWith(DEFAULT_PACKAGE)) { @@ -179,10 +166,13 @@ } try { - Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); + Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); + if (!type.isAssignableFrom(providerClass)) { + throw new ClassCastException(className + " cannot be cast to " + type.getName()); + } Object instance = null; if (!useServicesMechanism) { - instance = newInstanceNoServiceLoader(providerClass); + instance = newInstanceNoServiceLoader(type, providerClass); } if (instance == null) { instance = providerClass.newInstance(); @@ -191,63 +181,87 @@ dPrint("created new instance of " + providerClass + " using ClassLoader: " + cl); } - return instance; + return type.cast(instance); } catch (ClassNotFoundException x) { - throw new ConfigurationError( - "Provider " + className + " not found", x); + throw new TransformerFactoryConfigurationError(x, + "Provider " + className + " not found"); } catch (Exception x) { - throw new ConfigurationError( - "Provider " + className + " could not be instantiated: " + x, - x); + throw new TransformerFactoryConfigurationError(x, + "Provider " + className + " could not be instantiated: " + x); } } + /** * Try to construct using newTransformerFactoryNoServiceLoader * method if available. */ - private static Object newInstanceNoServiceLoader( - Class providerClass - ) { + private static T newInstanceNoServiceLoader(Class type, Class providerClass) { // Retain maximum compatibility if no security manager. if (System.getSecurityManager() == null) { return null; } try { - Method creationMethod = - providerClass.getDeclaredMethod( - "newTransformerFactoryNoServiceLoader" - ); - return creationMethod.invoke(null, (Object[])null); - } catch (NoSuchMethodException exc) { + final Method creationMethod = + providerClass.getDeclaredMethod( + "newTransformerFactoryNoServiceLoader" + ); + final int modifiers = creationMethod.getModifiers(); + + // Do not call the method if it's not public static. + if (!Modifier.isPublic(modifiers) || !Modifier.isStatic(modifiers)) { return null; - } catch (Exception exc) { - return null; + } + + // Only call the method if it's declared to return an instance of + // TransformerFactory + final Class returnType = creationMethod.getReturnType(); + if (type.isAssignableFrom(returnType)) { + final Object result = creationMethod.invoke(null, (Object[])null); + return type.cast(result); + } else { + // This should not happen, as + // TransformerFactoryImpl.newTransformerFactoryNoServiceLoader is + // declared to return TransformerFactory. + throw new ClassCastException(returnType + " cannot be cast to " + type); + } + } catch (ClassCastException e) { + throw new TransformerFactoryConfigurationError(e, e.getMessage()); + } catch (NoSuchMethodException exc) { + return null; + } catch (Exception exc) { + return null; } } + /** * Finds the implementation Class object in the specified order. Main * entry point. * @return Class object of factory, never null * - * @param factoryId Name of the factory to find, same as - * a property name + * @param type Base class / Service interface of the + * factory to find. + * * @param fallbackClassName Implementation class name, if nothing else * is found. Use null to mean no fallback. * * Package private so this code can be shared. */ - static Object find(String factoryId, String fallbackClassName) - throws ConfigurationError + static T find(Class type, String fallbackClassName) + throws TransformerFactoryConfigurationError { + assert type != null; + + final String factoryId = type.getName(); + dPrint("find factoryId =" + factoryId); // Use the system property first try { String systemProp = ss.getSystemProperty(factoryId); if (systemProp != null) { dPrint("found system property, value=" + systemProp); - return newInstance(systemProp, null, true, false, true); + return newInstance(type, systemProp, null, true, true); } } catch (SecurityException se) { @@ -256,7 +270,6 @@ // try to read from $java.home/lib/jaxp.properties try { - String factoryClassName = null; if (firstTime) { synchronized (cacheProps) { if (firstTime) { @@ -271,11 +284,11 @@ } } } - factoryClassName = cacheProps.getProperty(factoryId); + final String factoryClassName = cacheProps.getProperty(factoryId); if (factoryClassName != null) { dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName); - return newInstance(factoryClassName, null, true, false, true); + return newInstance(type, factoryClassName, null, true, true); } } catch (Exception ex) { @@ -283,113 +296,54 @@ } // Try Jar Service Provider Mechanism - Object provider = findJarServiceProvider(factoryId); + T provider = findServiceProvider(type); if (provider != null) { return provider; } if (fallbackClassName == null) { - throw new ConfigurationError( - "Provider for " + factoryId + " cannot be found", null); + throw new TransformerFactoryConfigurationError(null, + "Provider for " + factoryId + " cannot be found"); } dPrint("loaded from fallback value: " + fallbackClassName); - return newInstance(fallbackClassName, null, true, false, true); + return newInstance(type, fallbackClassName, null, true, true); } /* - * Try to find provider using Jar Service Provider Mechanism + * Try to find provider using the ServiceLoader. + * + * @param type Base class / Service interface of the factory to find. * * @return instance of provider class if found or null */ - private static Object findJarServiceProvider(String factoryId) - throws ConfigurationError + private static T findServiceProvider(final Class type) + throws TransformerFactoryConfigurationError { - String serviceId = "META-INF/services/" + factoryId; - InputStream is = null; - - // First try the Context ClassLoader - ClassLoader cl = ss.getContextClassLoader(); - boolean useBSClsLoader = false; - if (cl != null) { - is = ss.getResourceAsStream(cl, serviceId); - - // If no provider found then try the current ClassLoader - if (is == null) { - cl = FactoryFinder.class.getClassLoader(); - is = ss.getResourceAsStream(cl, serviceId); - useBSClsLoader = true; - } - } else { - // No Context ClassLoader, try the current ClassLoader - cl = FactoryFinder.class.getClassLoader(); - is = ss.getResourceAsStream(cl, serviceId); - useBSClsLoader = true; - } - - if (is == null) { - // No provider found - return null; - } - - if (debug) { // Extra check to avoid computing cl strings - dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl); - } - - BufferedReader rd; - try { - rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); - } - catch (java.io.UnsupportedEncodingException e) { - rd = new BufferedReader(new InputStreamReader(is)); - } - - String factoryClassName = null; - try { - // XXX Does not handle all possible input as specified by the - // Jar Service Provider specification - factoryClassName = rd.readLine(); - rd.close(); - } catch (IOException x) { - // No provider found - return null; - } - - if (factoryClassName != null && !"".equals(factoryClassName)) { - dPrint("found in resource, value=" + factoryClassName); - - // Note: here we do not want to fall back to the current - // ClassLoader because we want to avoid the case where the - // resource file was found using one ClassLoader and the - // provider class was instantiated using a different one. - return newInstance(factoryClassName, cl, false, useBSClsLoader, true); - } - - // No provider found - return null; - } - - static class ConfigurationError extends Error { - private Exception exception; - - /** - * Construct a new instance with the specified detail string and - * exception. - */ - ConfigurationError(String msg, Exception x) { - super(msg); - this.exception = x; - } - - Exception getException() { - return exception; - } - /** - * use the exception chaining mechanism of JDK1.4 - */ - @Override - public Throwable getCause() { - return exception; + try { + return AccessController.doPrivileged(new PrivilegedAction() { + public T run() { + final ServiceLoader serviceLoader = ServiceLoader.load(type); + final Iterator iterator = serviceLoader.iterator(); + if (iterator.hasNext()) { + return iterator.next(); + } else { + return null; + } + } + }); + } catch(ServiceConfigurationError e) { + // It is not possible to wrap an error directly in + // FactoryConfigurationError - so we need to wrap the + // ServiceConfigurationError in a RuntimeException. + // The alternative would be to modify the logic in + // FactoryConfigurationError to allow setting a + // Throwable as the cause, but that could cause + // compatibility issues down the road. + final RuntimeException x = new RuntimeException( + "Provider for " + type + " cannot be created", e); + final TransformerFactoryConfigurationError error = + new TransformerFactoryConfigurationError(x, x.getMessage()); + throw error; } } - } diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/transform/TransformerFactory.java --- a/jaxp/src/javax/xml/transform/TransformerFactory.java Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/src/javax/xml/transform/TransformerFactory.java Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -52,8 +52,8 @@ /** *

      Obtain a new instance of a TransformerFactory. - * This static method creates a new factory instance - * This method uses the following ordered lookup procedure to determine + * This static method creates a new factory instance.

      + *

      This method uses the following ordered lookup procedure to determine * the TransformerFactory implementation class to * load:

      *
        @@ -67,7 +67,7 @@ * format and contains the fully qualified name of the * implementation class with the key being the system property defined * above. - * + *
        * The jaxp.properties file is read only once by the JAXP implementation * and it's values are then cached for future use. If the file does not exist * when the first attempt is made to read from it, no further attempts are @@ -75,14 +75,12 @@ * of any property in jaxp.properties after it has been read for the first time. * *
      • - * Use the Services API (as detailed in the JAR specification), if - * available, to determine the classname. The Services API will look - * for a classname in the file - * META-INF/services/javax.xml.transform.TransformerFactory - * in jars available to the runtime. + * Use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. *
      • *
      • - * Platform default TransformerFactory instance. + * Otherwise, the system-default implementation is returned. *
      • *
      * @@ -92,22 +90,18 @@ * * @return new TransformerFactory instance, never null. * - * @throws TransformerFactoryConfigurationError Thrown if the implementation - * is not available or cannot be instantiated. + * @throws TransformerFactoryConfigurationError Thrown in case of {@linkplain + * java.util.ServiceConfigurationError service configuration error} or if + * the implementation is not available or cannot be instantiated. */ public static TransformerFactory newInstance() throws TransformerFactoryConfigurationError { - try { - return (TransformerFactory) FactoryFinder.find( + + return FactoryFinder.find( /* The default property name according to the JAXP spec */ - "javax.xml.transform.TransformerFactory", + TransformerFactory.class, /* The fallback implementation class name, XSLTC */ "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); - } catch (FactoryFinder.ConfigurationError e) { - throw new TransformerFactoryConfigurationError( - e.getException(), - e.getMessage()); - } } /** @@ -147,14 +141,10 @@ */ public static TransformerFactory newInstance(String factoryClassName, ClassLoader classLoader) throws TransformerFactoryConfigurationError{ - try { - //do not fallback if given classloader can't find the class, throw exception - return (TransformerFactory) FactoryFinder.newInstance(factoryClassName, classLoader, false); - } catch (FactoryFinder.ConfigurationError e) { - throw new TransformerFactoryConfigurationError( - e.getException(), - e.getMessage()); - } + + //do not fallback if given classloader can't find the class, throw exception + return FactoryFinder.newInstance(TransformerFactory.class, + factoryClassName, classLoader, false, false); } /** *

      Process the Source into a Transformer diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/validation/SchemaFactory.java --- a/jaxp/src/javax/xml/validation/SchemaFactory.java Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/src/javax/xml/validation/SchemaFactory.java Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -27,15 +27,14 @@ import java.io.File; import java.net.URL; - import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; - import org.w3c.dom.ls.LSResourceResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.SAXParseException; /** * Factory that creates {@link Schema} objects. Entry-point to @@ -79,7 +78,7 @@ * and has a significant effect on the parsing process, it is impossible * to define the DTD validation as a process independent from parsing. * For this reason, this specification does not define the semantics for - * the XML DTD. This doesn't prohibit implentors from implementing it + * the XML DTD. This doesn't prohibit implementors from implementing it * in a way they see fit, but users are warned that any DTD * validation implemented on this interface necessarily deviate from * the XML DTD semantics as defined in the XML 1.0. @@ -147,14 +146,17 @@ * is looked for. If present, the value is processed just like above. * *

    • - *

      The class loader is asked for service provider provider-configuration files matching - * javax.xml.validation.SchemaFactory in the resource directory META-INF/services. - * See the JAR File Specification for file format and parsing rules. - * Each potential service provider is required to implement the method:

      - *
      -     *        {@link #isSchemaLanguageSupported(String schemaLanguage)}
      -     *     
      - * The first service provider found in class loader order that supports the specified schema language is returned. + * Use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service.
      + * Each potential service provider is required to implement the method + * {@link #isSchemaLanguageSupported(String schemaLanguage)}. + *
      + * The first service provider found that supports the specified schema + * language is returned. + *
      + * In case of {@link java.util.ServiceConfigurationError} a + * {@link SchemaFactoryConfigurationError} will be thrown. *
    • *
    • * Platform default SchemaFactory is located @@ -186,10 +188,12 @@ * If no implementation of the schema language is available. * @throws NullPointerException * If the schemaLanguage parameter is null. + * @throws SchemaFactoryConfigurationError + * If a configuration error is encountered. * * @see #newInstance(String schemaLanguage, String factoryClassName, ClassLoader classLoader) */ - public static final SchemaFactory newInstance(String schemaLanguage) { + public static SchemaFactory newInstance(String schemaLanguage) { ClassLoader cl; cl = ss.getContextClassLoader(); @@ -275,19 +279,19 @@ } - /** - *

      Is specified schema supported by this SchemaFactory?

      - * - * @param schemaLanguage Specifies the schema language which the returned SchemaFactory will understand. + /** + *

      Is specified schema supported by this SchemaFactory?

      + * + * @param schemaLanguage Specifies the schema language which the returned SchemaFactory will understand. * schemaLanguage must specify a valid schema language. - * - * @return true if SchemaFactory supports schemaLanguage, else false. - * - * @throws NullPointerException If schemaLanguage is null. - * @throws IllegalArgumentException If schemaLanguage.length() == 0 - * or schemaLanguage does not specify a valid schema language. - */ - public abstract boolean isSchemaLanguageSupported(String schemaLanguage); + * + * @return true if SchemaFactory supports schemaLanguage, else false. + * + * @throws NullPointerException If schemaLanguage is null. + * @throws IllegalArgumentException If schemaLanguage.length() == 0 + * or schemaLanguage does not specify a valid schema language. + */ + public abstract boolean isSchemaLanguageSupported(String schemaLanguage); /** * Look up the value of a feature flag. diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/validation/SchemaFactoryConfigurationError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/src/javax/xml/validation/SchemaFactoryConfigurationError.java Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 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 javax.xml.validation; + +/** + * Thrown when a problem with configuration with the Schema Factories + * exists. This error will typically be thrown when the class of a + * schema factory specified in the system properties cannot be found + * or instantiated. + * @since 1.8 + */ +public final class SchemaFactoryConfigurationError extends Error { + + static final long serialVersionUID = 3531438703147750126L; + + /** + * Create a new SchemaFactoryConfigurationError with no + * detail message. + */ + public SchemaFactoryConfigurationError() { + } + + + /** + * Create a new SchemaFactoryConfigurationError with + * the String specified as an error message. + * + * @param message The error message for the exception. + */ + public SchemaFactoryConfigurationError(String message) { + super(message); + } + + /** + * Create a new SchemaFactoryConfigurationError with the + * given Throwable base cause. + * + * @param cause The exception or error to be encapsulated in a + * SchemaFactoryConfigurationError. + */ + public SchemaFactoryConfigurationError(Throwable cause) { + super(cause); + } + + /** + * Create a new SchemaFactoryConfigurationError with the + * given Throwable base cause and detail message. + * + * @param cause The exception or error to be encapsulated in a + * SchemaFactoryConfigurationError. + * @param message The detail message. + */ + public SchemaFactoryConfigurationError(String message, Throwable cause) { + super(message, cause); + } + +} diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/validation/SchemaFactoryFinder.java --- a/jaxp/src/javax/xml/validation/SchemaFactoryFinder.java Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/src/javax/xml/validation/SchemaFactoryFinder.java Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -25,19 +25,16 @@ package javax.xml.validation; -import java.io.BufferedReader; import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.NoSuchElementException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Properties; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; /** * Implementation of {@link SchemaFactory#newInstance(String)}. @@ -53,17 +50,17 @@ /** *

      Take care of restrictions imposed by java security model

      */ - private static SecuritySupport ss = new SecuritySupport(); + private static final SecuritySupport ss = new SecuritySupport(); private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xerces.internal"; /** *

      Cache properties for performance.

      */ - private static Properties cacheProps = new Properties(); + private static final Properties cacheProps = new Properties(); - /** - *

      First time requires initialization overhead.

      - */ - private static volatile boolean firstTime = true; + /** + *

      First time requires initialization overhead.

      + */ + private static volatile boolean firstTime = true; static { // Use try/catch block to support applets @@ -115,7 +112,7 @@ return; } } catch( Throwable unused ) { - ; // getContextClassLoader() undefined in JDK1.1 + // getContextClassLoader() undefined in JDK1.1 } if( classLoader==ClassLoader.getSystemClassLoader() ) { @@ -138,9 +135,13 @@ * * @throws NullPointerException * If the schemaLanguage parameter is null. + * @throws SchemaFactoryConfigurationError + * If a configuration error is encountered. */ public SchemaFactory newFactory(String schemaLanguage) { - if(schemaLanguage==null) throw new NullPointerException(); + if(schemaLanguage==null) { + throw new NullPointerException(); + } SchemaFactory f = _newFactory(schemaLanguage); if (f != null) { debugPrintln("factory '" + f.getClass().getName() + "' was found for " + schemaLanguage); @@ -183,7 +184,6 @@ String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties"; - String factoryClassName = null ; // try to read from $java.home/lib/jaxp.properties try { @@ -199,7 +199,7 @@ } } } - factoryClassName = cacheProps.getProperty(propertyName); + final String factoryClassName = cacheProps.getProperty(propertyName); debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties"); if (factoryClassName != null) { @@ -214,21 +214,15 @@ } } - // try META-INF/services files - Iterator sitr = createServiceFileIterator(); - while(sitr.hasNext()) { - URL resource = (URL)sitr.next(); - debugPrintln("looking into " + resource); - try { - sf = loadFromService(schemaLanguage,resource.toExternalForm(), - ss.getURLInputStream(resource)); - if(sf!=null) return sf; - } catch(IOException e) { - if( debug ) { - debugPrintln("failed to read "+resource); - e.printStackTrace(); - } - } + // Try with ServiceLoader + final SchemaFactory factoryImpl = findServiceProvider(schemaLanguage); + + // The following assertion should always be true. + // Uncomment it, recompile, and run with -ea in case of doubts: + // assert factoryImpl == null || factoryImpl.isSchemaLanguageSupported(schemaLanguage); + + if (factoryImpl != null) { + return factoryImpl; } // platform default @@ -246,8 +240,8 @@ * @param className Name of class to create. * @return Created class or null. */ - private Class createClass(String className) { - Class clazz; + private Class createClass(String className) { + Class clazz; // make sure we have access to restricted packages boolean internal = false; if (System.getSecurityManager() != null) { @@ -256,25 +250,27 @@ } } - try { - if (classLoader != null && !internal) { - clazz = classLoader.loadClass(className); - } else { - clazz = Class.forName(className); - } - } catch (Throwable t) { - if(debug) t.printStackTrace(); - return null; + try { + if (classLoader != null && !internal) { + clazz = Class.forName(className, false, classLoader); + } else { + clazz = Class.forName(className); } + } catch (Throwable t) { + if(debug) { + t.printStackTrace(); + } + return null; + } - return clazz; + return clazz; } /** *

      Creates an instance of the specified and returns it.

      * * @param className - * fully qualified class name to be instanciated. + * fully qualified class name to be instantiated. * * @return null * if it fails. Error messages will be printed by this method. @@ -289,7 +285,7 @@ debugPrintln("createInstance(" + className + ")"); // get Class from className - Class clazz = createClass(className); + Class clazz = createClass(className); if (clazz == null) { debugPrintln("failed to getClass(" + className + ")"); return null; @@ -298,9 +294,13 @@ // instantiate Class as a SchemaFactory try { - if (!useServicesMechanism) { - schemaFactory = (SchemaFactory) newInstanceNoServiceLoader(clazz); - } + if (!SchemaFactory.class.isAssignableFrom(clazz)) { + throw new ClassCastException(clazz.getName() + + " cannot be cast to " + SchemaFactory.class); + } + if (!useServicesMechanism) { + schemaFactory = newInstanceNoServiceLoader(clazz); + } if (schemaFactory == null) { schemaFactory = (SchemaFactory) clazz.newInstance(); } @@ -326,11 +326,12 @@ return schemaFactory; } + /** - * Try to construct using newTransformerFactoryNoServiceLoader + * Try to construct using newXMLSchemaFactoryNoServiceLoader * method if available. */ - private static Object newInstanceNoServiceLoader( + private static SchemaFactory newInstanceNoServiceLoader( Class providerClass ) { // Retain maximum compatibility if no security manager. @@ -338,196 +339,87 @@ return null; } try { - Method creationMethod = + final Method creationMethod = providerClass.getDeclaredMethod( "newXMLSchemaFactoryNoServiceLoader" ); - return creationMethod.invoke(null, (Object[])null); - } catch (NoSuchMethodException exc) { - return null; - } catch (Exception exc) { - return null; - } - } + final int modifiers = creationMethod.getModifiers(); - /** Iterator that lazily computes one value and returns it. */ - private static abstract class SingleIterator implements Iterator { - private boolean seen = false; - - public final void remove() { throw new UnsupportedOperationException(); } - public final boolean hasNext() { return !seen; } - public final Object next() { - if(seen) throw new NoSuchElementException(); - seen = true; - return value(); - } + // Do not call the method if it's not public static. + if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) { + return null; + } - protected abstract Object value(); - } - - /** - * Looks up a value in a property file - * while producing all sorts of debug messages. - * - * @return null - * if there was an error. - */ - private SchemaFactory loadFromProperty( String keyName, String resourceName, InputStream in ) - throws IOException { - debugPrintln("Reading "+resourceName ); - - Properties props=new Properties(); - props.load(in); - in.close(); - String factoryClassName = props.getProperty(keyName); - if(factoryClassName != null){ - debugPrintln("found "+keyName+" = " + factoryClassName); - return createInstance(factoryClassName); - } else { - debugPrintln(keyName+" is not in the property file"); + // Only calls "newXMLSchemaFactoryNoServiceLoader" if it's + // declared to return an instance of SchemaFactory. + final Class returnType = creationMethod.getReturnType(); + if (SERVICE_CLASS.isAssignableFrom(returnType)) { + return SERVICE_CLASS.cast(creationMethod.invoke(null, (Object[])null)); + } else { + // Should not happen since + // XMLSchemaFactory.newXMLSchemaFactoryNoServiceLoader is + // declared to return XMLSchemaFactory. + throw new ClassCastException(returnType + + " cannot be cast to " + SERVICE_CLASS); + } + } catch(ClassCastException e) { + throw new SchemaFactoryConfigurationError(e.getMessage(), e); + } catch (NoSuchMethodException exc) { + return null; + } catch (Exception exc) { return null; } } - /** - *

      Look up a value in a property file.

      - * - *

      Set debug to true to trace property evaluation.

      - * - * @param schemaLanguage Schema Language to support. - * @param inputName Name of InputStream. - * @param in InputStream of properties. - * - * @return SchemaFactory as determined by keyName value or null if there was an error. - * - * @throws IOException If IO error reading from in. - */ - private SchemaFactory loadFromService( - String schemaLanguage, - String inputName, - InputStream in) - throws IOException { - - SchemaFactory schemaFactory = null; - final Class[] stringClassArray = {"".getClass()}; - final Object[] schemaLanguageObjectArray = {schemaLanguage}; - final String isSchemaLanguageSupportedMethod = "isSchemaLanguageSupported"; - - debugPrintln("Reading " + inputName); - - // read from InputStream until a match is found - BufferedReader configFile = new BufferedReader(new InputStreamReader(in)); - String line = null; - while ((line = configFile.readLine()) != null) { - // '#' is comment char - int comment = line.indexOf("#"); - switch (comment) { - case -1: break; // no comment - case 0: line = ""; break; // entire line is a comment - default: line = line.substring(0, comment); break; // trim comment - } - - // trim whitespace - line = line.trim(); - - // any content left on line? - if (line.length() == 0) { - continue; - } - - // line content is now the name of the class - Class clazz = createClass(line); - if (clazz == null) { - continue; - } - - // create an instance of the Class - try { - schemaFactory = (SchemaFactory) clazz.newInstance(); - } catch (ClassCastException classCastExcpetion) { - schemaFactory = null; - continue; - } catch (InstantiationException instantiationException) { - schemaFactory = null; - continue; - } catch (IllegalAccessException illegalAccessException) { - schemaFactory = null; - continue; - } - - // does this Class support desired Schema? - try { - Method isSchemaLanguageSupported = clazz.getMethod(isSchemaLanguageSupportedMethod, stringClassArray); - Boolean supported = (Boolean) isSchemaLanguageSupported.invoke(schemaFactory, schemaLanguageObjectArray); - if (supported.booleanValue()) { - break; - } - } catch (NoSuchMethodException noSuchMethodException) { - - } catch (IllegalAccessException illegalAccessException) { - - } catch (InvocationTargetException invocationTargetException) { - - } - schemaFactory = null; + // Call isSchemaLanguageSupported with initial context. + private boolean isSchemaLanguageSupportedBy(final SchemaFactory factory, + final String schemaLanguage, + AccessControlContext acc) { + return AccessController.doPrivileged(new PrivilegedAction() { + public Boolean run() { + return factory.isSchemaLanguageSupported(schemaLanguage); } - - // clean up - configFile.close(); - - // return new instance of SchemaFactory or null - return schemaFactory; + }, acc); } /** - * Returns an {@link Iterator} that enumerates all - * the META-INF/services files that we care. + * Finds a service provider subclass of SchemaFactory that supports the + * given schema language using the ServiceLoader. + * + * @param schemaLanguage The schema language for which we seek a factory. + * @return A SchemaFactory supporting the specified schema language, or null + * if none is found. + * @throws SchemaFactoryConfigurationError if a configuration error is found. */ - private Iterator createServiceFileIterator() { - if (classLoader == null) { - return new SingleIterator() { - protected Object value() { - ClassLoader classLoader = SchemaFactoryFinder.class.getClassLoader(); - //return (ClassLoader.getSystemResource( SERVICE_ID )); - return ss.getResourceAsURL(classLoader, SERVICE_ID); - } - }; - } else { - try { - //final Enumeration e = classLoader.getResources(SERVICE_ID); - final Enumeration e = ss.getResources(classLoader, SERVICE_ID); - if(!e.hasMoreElements()) { - debugPrintln("no "+SERVICE_ID+" file was found"); + private SchemaFactory findServiceProvider(final String schemaLanguage) { + assert schemaLanguage != null; + // store current context. + final AccessControlContext acc = AccessController.getContext(); + try { + return AccessController.doPrivileged(new PrivilegedAction() { + public SchemaFactory run() { + final ServiceLoader loader = + ServiceLoader.load(SERVICE_CLASS); + for (SchemaFactory factory : loader) { + // restore initial context to call + // factory.isSchemaLanguageSupported + if (isSchemaLanguageSupportedBy(factory, schemaLanguage, acc)) { + return factory; + } + } + return null; // no factory found. } - - // wrap it into an Iterator. - return new Iterator() { - public void remove() { - throw new UnsupportedOperationException(); - } - - public boolean hasNext() { - return e.hasMoreElements(); - } - - public Object next() { - return e.nextElement(); - } - }; - } catch (IOException e) { - debugPrintln("failed to enumerate resources "+SERVICE_ID); - if(debug) e.printStackTrace(); - return new ArrayList().iterator(); // empty iterator - } + }); + } catch (ServiceConfigurationError error) { + throw new SchemaFactoryConfigurationError( + "Provider for " + SERVICE_CLASS + " cannot be created", error); } } - private static final Class SERVICE_CLASS = SchemaFactory.class; - private static final String SERVICE_ID = "META-INF/services/" + SERVICE_CLASS.getName(); + private static final Class SERVICE_CLASS = SchemaFactory.class; - - private static String which( Class clazz ) { + private static String which( Class clazz ) { return which( clazz.getName(), clazz.getClassLoader() ); } diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/xpath/XPathFactory.java --- a/jaxp/src/javax/xml/xpath/XPathFactory.java Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/src/javax/xml/xpath/XPathFactory.java Wed May 08 23:54:45 2013 -0700 @@ -90,7 +90,7 @@ * @throws RuntimeException When there is a failure in creating an * XPathFactory for the default object model. */ - public static final XPathFactory newInstance() { + public static XPathFactory newInstance() { try { return newInstance(DEFAULT_OBJECT_MODEL_URI); @@ -121,14 +121,17 @@ * If present, the value is processed just like above. *
    • *
    • - * The class loader is asked for service provider provider-configuration files matching javax.xml.xpath.XPathFactory - * in the resource directory META-INF/services. - * See the JAR File Specification for file format and parsing rules. - * Each potential service provider is required to implement the method: - *
      -    *       {@link #isObjectModelSupported(String objectModel)}
      -    *     
      - * The first service provider found in class loader order that supports the specified object model is returned. + * Use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service. + *
      + * Each potential service provider is required to implement the method + * {@link #isObjectModelSupported(String objectModel)}. + * The first service provider found that supports the specified object + * model is returned. + *
      + * In case of {@link java.util.ServiceConfigurationError} an + * {@link XPathFactoryConfigurationException} will be thrown. *
    • *
    • * Platform default XPathFactory is located in a platform specific way. @@ -152,43 +155,41 @@ * * @return Instance of an XPathFactory. * - * @throws XPathFactoryConfigurationException If the specified object model is unavailable. + * @throws XPathFactoryConfigurationException If the specified object model + * is unavailable, or if there is a configuration error. * @throws NullPointerException If uri is null. - * @throws IllegalArgumentException If uri is null + * @throws IllegalArgumentException If uri is null * or uri.length() == 0. */ - public static final XPathFactory newInstance(final String uri) + public static XPathFactory newInstance(final String uri) throws XPathFactoryConfigurationException { if (uri == null) { - throw new NullPointerException( - "XPathFactory#newInstance(String uri) cannot be called with uri == null" - ); + throw new NullPointerException( + "XPathFactory#newInstance(String uri) cannot be called with uri == null"); } - if (uri.length() == 0) { - throw new IllegalArgumentException( - "XPathFactory#newInstance(String uri) cannot be called with uri == \"\"" - ); - } + if (uri.length() == 0) { + throw new IllegalArgumentException( + "XPathFactory#newInstance(String uri) cannot be called with uri == \"\""); + } - ClassLoader classLoader = ss.getContextClassLoader(); + ClassLoader classLoader = ss.getContextClassLoader(); if (classLoader == null) { //use the current class loader classLoader = XPathFactory.class.getClassLoader(); } - XPathFactory xpathFactory = new XPathFactoryFinder(classLoader).newFactory(uri); + XPathFactory xpathFactory = new XPathFactoryFinder(classLoader).newFactory(uri); - if (xpathFactory == null) { - throw new XPathFactoryConfigurationException( - "No XPathFactory implementation found for the object model: " - + uri - ); - } + if (xpathFactory == null) { + throw new XPathFactoryConfigurationException( + "No XPathFactory implementation found for the object model: " + + uri); + } - return xpathFactory; + return xpathFactory; } /** @@ -242,16 +243,14 @@ ClassLoader cl = classLoader; if (uri == null) { - throw new NullPointerException( - "XPathFactory#newInstance(String uri) cannot be called with uri == null" - ); + throw new NullPointerException( + "XPathFactory#newInstance(String uri) cannot be called with uri == null"); } - if (uri.length() == 0) { - throw new IllegalArgumentException( - "XPathFactory#newInstance(String uri) cannot be called with uri == \"\"" - ); - } + if (uri.length() == 0) { + throw new IllegalArgumentException( + "XPathFactory#newInstance(String uri) cannot be called with uri == \"\""); + } if (cl == null) { cl = ss.getContextClassLoader(); @@ -260,31 +259,32 @@ XPathFactory f = new XPathFactoryFinder(cl).createInstance(factoryClassName); if (f == null) { - throw new XPathFactoryConfigurationException( - "No XPathFactory implementation found for the object model: " - + uri - ); + throw new XPathFactoryConfigurationException( + "No XPathFactory implementation found for the object model: " + + uri); } //if this factory supports the given schemalanguage return this factory else thrown exception - if(f.isObjectModelSupported(uri)){ + if (f.isObjectModelSupported(uri)) { return f; - }else{ - throw new XPathFactoryConfigurationException("Factory " + factoryClassName + " doesn't support given " + uri + " object model"); + } else { + throw new XPathFactoryConfigurationException("Factory " + + factoryClassName + " doesn't support given " + uri + + " object model"); } } - /** - *

      Is specified object model supported by this XPathFactory?

      - * - * @param objectModel Specifies the object model which the returned XPathFactory will understand. - * - * @return true if XPathFactory supports objectModel, else false. - * - * @throws NullPointerException If objectModel is null. - * @throws IllegalArgumentException If objectModel.length() == 0. - */ - public abstract boolean isObjectModelSupported(String objectModel); + /** + *

      Is specified object model supported by this XPathFactory?

      + * + * @param objectModel Specifies the object model which the returned XPathFactory will understand. + * + * @return true if XPathFactory supports objectModel, else false. + * + * @throws NullPointerException If objectModel is null. + * @throws IllegalArgumentException If objectModel.length() == 0. + */ + public abstract boolean isObjectModelSupported(String objectModel); /** *

      Set a feature for this XPathFactory and @@ -314,8 +314,8 @@ * it creates cannot support this feature. * @throws NullPointerException if name is null. */ - public abstract void setFeature(String name, boolean value) - throws XPathFactoryConfigurationException; + public abstract void setFeature(String name, boolean value) + throws XPathFactoryConfigurationException; /** *

      Get the state of the named feature.

      @@ -339,8 +339,8 @@ * it creates cannot support this feature. * @throws NullPointerException if name is null. */ - public abstract boolean getFeature(String name) - throws XPathFactoryConfigurationException; + public abstract boolean getFeature(String name) + throws XPathFactoryConfigurationException; /** *

      Establish a default variable resolver.

      @@ -359,19 +359,19 @@ public abstract void setXPathVariableResolver(XPathVariableResolver resolver); /** - *

      Establish a default function resolver.

      - * - *

      Any XPath objects constructed from this factory will - * use the specified resolver by default.

      - * - *

      A NullPointerException is thrown if - * resolver is null.

      - * - * @param resolver XPath function resolver. - * - * @throws NullPointerException If resolver is - * null. - */ + *

      Establish a default function resolver.

      + * + *

      Any XPath objects constructed from this factory will + * use the specified resolver by default.

      + * + *

      A NullPointerException is thrown if + * resolver is null.

      + * + * @param resolver XPath function resolver. + * + * @throws NullPointerException If resolver is + * null. + */ public abstract void setXPathFunctionResolver(XPathFunctionResolver resolver); /** diff -r 241d13ccb08b -r 4ea383c26563 jaxp/src/javax/xml/xpath/XPathFactoryFinder.java --- a/jaxp/src/javax/xml/xpath/XPathFactoryFinder.java Wed May 08 14:10:53 2013 +0200 +++ b/jaxp/src/javax/xml/xpath/XPathFactoryFinder.java Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -25,20 +25,16 @@ package javax.xml.xpath; -import java.io.BufferedReader; import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.NoSuchElementException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Properties; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; /** * Implementation of {@link XPathFactory#newInstance(String)}. @@ -50,7 +46,7 @@ class XPathFactoryFinder { private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xpath.internal"; - private static SecuritySupport ss = new SecuritySupport() ; + private static final SecuritySupport ss = new SecuritySupport() ; /** debug support code. */ private static boolean debug = false; static { @@ -65,12 +61,12 @@ /** *

      Cache properties for performance.

      */ - private static Properties cacheProps = new Properties(); + private static final Properties cacheProps = new Properties(); - /** - *

      First time requires initialization overhead.

      - */ - private volatile static boolean firstTime = true; + /** + *

      First time requires initialization overhead.

      + */ + private volatile static boolean firstTime = true; /** *

      Conditional debug printing.

      @@ -93,9 +89,8 @@ * to find XPathFactory.

      * * @param loader - * to be used to load resource, {@link XPathFactory}, and - * {@link SchemaFactoryLoader} implementations during - * the resolution process. + * to be used to load resource and {@link XPathFactory} + * implementations during the resolution process. * If this parameter is null, the default system class loader * will be used. */ @@ -113,7 +108,7 @@ return; } } catch( Throwable unused ) { - ; // getContextClassLoader() undefined in JDK1.1 + // getContextClassLoader() undefined in JDK1.1 } if( classLoader==ClassLoader.getSystemClassLoader() ) { @@ -126,7 +121,7 @@ /** *

      Creates a new {@link XPathFactory} object for the specified - * schema language.

      + * object model.

      * * @param uri * Identifies the underlying object model. @@ -136,8 +131,10 @@ * @throws NullPointerException * If the parameter is null. */ - public XPathFactory newFactory(String uri) { - if(uri==null) throw new NullPointerException(); + public XPathFactory newFactory(String uri) throws XPathFactoryConfigurationException { + if (uri == null) { + throw new NullPointerException(); + } XPathFactory f = _newFactory(uri); if (f != null) { debugPrintln("factory '" + f.getClass().getName() + "' was found for " + uri); @@ -154,8 +151,8 @@ * * @return {@link XPathFactory} for the given object model. */ - private XPathFactory _newFactory(String uri) { - XPathFactory xpathFactory; + private XPathFactory _newFactory(String uri) throws XPathFactoryConfigurationException { + XPathFactory xpathFactory = null; String propertyName = SERVICE_CLASS.getName() + ":" + uri; @@ -166,7 +163,9 @@ if(r!=null) { debugPrintln("The value is '"+r+"'"); xpathFactory = createInstance(r, true); - if(xpathFactory != null) return xpathFactory; + if (xpathFactory != null) { + return xpathFactory; + } } else debugPrintln("The property is undefined."); } catch( Throwable t ) { @@ -180,8 +179,6 @@ String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties"; - String factoryClassName = null ; - // try to read from $java.home/lib/jaxp.properties try { if(firstTime){ @@ -196,7 +193,7 @@ } } } - factoryClassName = cacheProps.getProperty(propertyName); + final String factoryClassName = cacheProps.getProperty(propertyName); debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties"); if (factoryClassName != null) { @@ -211,23 +208,16 @@ } } - // try META-INF/services files - Iterator sitr = createServiceFileIterator(); - while(sitr.hasNext()) { - URL resource = (URL)sitr.next(); - debugPrintln("looking into " + resource); - try { - xpathFactory = loadFromService(uri, resource.toExternalForm(), - ss.getURLInputStream(resource)); - if (xpathFactory != null) { - return xpathFactory; - } - } catch(IOException e) { - if( debug ) { - debugPrintln("failed to read "+resource); - e.printStackTrace(); - } - } + // Try with ServiceLoader + assert xpathFactory == null; + xpathFactory = findServiceProvider(uri); + + // The following assertion should always be true. + // Uncomment it, recompile, and run with -ea in case of doubts: + // assert xpathFactory == null || xpathFactory.isObjectModelSupported(uri); + + if (xpathFactory != null) { + return xpathFactory; } // platform default @@ -245,8 +235,8 @@ * @param className Name of class to create. * @return Created class or null. */ - private Class createClass(String className) { - Class clazz; + private Class createClass(String className) { + Class clazz; // make sure we have access to restricted packages boolean internal = false; if (System.getSecurityManager() != null) { @@ -258,47 +248,54 @@ // use approprite ClassLoader try { if (classLoader != null && !internal) { - clazz = classLoader.loadClass(className); + clazz = Class.forName(className, false, classLoader); } else { clazz = Class.forName(className); } } catch (Throwable t) { - if(debug) t.printStackTrace(); - return null; + if(debug) { + t.printStackTrace(); + } + return null; } - return clazz; + return clazz; } /** *

      Creates an instance of the specified and returns it.

      * * @param className - * fully qualified class name to be instanciated. + * fully qualified class name to be instantiated. * * @return null * if it fails. Error messages will be printed by this method. */ - XPathFactory createInstance( String className ) { + XPathFactory createInstance( String className ) + throws XPathFactoryConfigurationException + { return createInstance( className, false ); } - XPathFactory createInstance( String className, boolean useServicesMechanism ) { + + XPathFactory createInstance( String className, boolean useServicesMechanism ) + throws XPathFactoryConfigurationException + { XPathFactory xPathFactory = null; debugPrintln("createInstance(" + className + ")"); // get Class from className - Class clazz = createClass(className); + Class clazz = createClass(className); if (clazz == null) { - debugPrintln("failed to getClass(" + className + ")"); - return null; + debugPrintln("failed to getClass(" + className + ")"); + return null; } debugPrintln("loaded " + className + " from " + which(clazz)); // instantiate Class as a XPathFactory try { if (!useServicesMechanism) { - xPathFactory = (XPathFactory) newInstanceNoServiceLoader(clazz); + xPathFactory = newInstanceNoServiceLoader(clazz); } if (xPathFactory == null) { xPathFactory = (XPathFactory) clazz.newInstance(); @@ -329,203 +326,94 @@ * Try to construct using newXPathFactoryNoServiceLoader * method if available. */ - private static Object newInstanceNoServiceLoader( + private static XPathFactory newInstanceNoServiceLoader( Class providerClass - ) { + ) throws XPathFactoryConfigurationException { // Retain maximum compatibility if no security manager. if (System.getSecurityManager() == null) { return null; } try { Method creationMethod = - providerClass.getDeclaredMethod( - "newXPathFactoryNoServiceLoader" - ); - return creationMethod.invoke(null, (Object[])null); - } catch (NoSuchMethodException exc) { - return null; - } catch (Exception exc) { - return null; - } - } - - /** - *

      Look up a value in a property file.

      - * - *

      Set debug to true to trace property evaluation.

      - * - * @param objectModel URI of object model to support. - * @param inputName Name of InputStream. - * @param in InputStream of properties. - * - * @return XPathFactory as determined by keyName value or null if there was an error. - * - * @throws IOException If IO error reading from in. - */ - private XPathFactory loadFromService( - String objectModel, - String inputName, - InputStream in) - throws IOException { - - XPathFactory xPathFactory = null; - final Class[] stringClassArray = {"".getClass()}; - final Object[] objectModelObjectArray = {objectModel}; - final String isObjectModelSupportedMethod = "isObjectModelSupported"; - - debugPrintln("Reading " + inputName); + providerClass.getDeclaredMethod( + "newXPathFactoryNoServiceLoader" + ); + final int modifiers = creationMethod.getModifiers(); - // read from InputStream until a match is found - BufferedReader configFile = new BufferedReader(new InputStreamReader(in)); - String line = null; - while ((line = configFile.readLine()) != null) { - // '#' is comment char - int comment = line.indexOf("#"); - switch (comment) { - case -1: break; // no comment - case 0: line = ""; break; // entire line is a comment - default: line = line.substring(0, comment); break; // trim comment - } - - // trim whitespace - line = line.trim(); - - // any content left on line? - if (line.length() == 0) { - continue; - } - - // line content is now the name of the class - Class clazz = createClass(line); - if (clazz == null) { - continue; - } - - // create an instance of the Class - try { - xPathFactory = (XPathFactory) clazz.newInstance(); - } catch (ClassCastException classCastExcpetion) { - xPathFactory = null; - continue; - } catch (InstantiationException instantiationException) { - xPathFactory = null; - continue; - } catch (IllegalAccessException illegalAccessException) { - xPathFactory = null; - continue; - } - - // does this Class support desired object model? - try { - Method isObjectModelSupported = clazz.getMethod(isObjectModelSupportedMethod, stringClassArray); - Boolean supported = (Boolean) isObjectModelSupported.invoke(xPathFactory, objectModelObjectArray); - if (supported.booleanValue()) { - break; - } - - } catch (NoSuchMethodException noSuchMethodException) { - - } catch (IllegalAccessException illegalAccessException) { - - } catch (InvocationTargetException invocationTargetException) { - - } - xPathFactory = null; + // Do not call "newXPathFactoryNoServiceLoader" if it's + // not public static. + if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) { + return null; } - // clean up - configFile.close(); - - // return new instance of XPathFactory or null - return xPathFactory; - } - - /** Iterator that lazily computes one value and returns it. */ - private static abstract class SingleIterator implements Iterator { - private boolean seen = false; - - public final void remove() { throw new UnsupportedOperationException(); } - public final boolean hasNext() { return !seen; } - public final Object next() { - if(seen) throw new NoSuchElementException(); - seen = true; - return value(); - } - - protected abstract Object value(); - } - - /** - * Looks up a value in a property file - * while producing all sorts of debug messages. - * - * @return null - * if there was an error. - */ - private XPathFactory loadFromProperty( String keyName, String resourceName, InputStream in ) - throws IOException { - debugPrintln("Reading "+resourceName ); - - Properties props = new Properties(); - props.load(in); - in.close(); - String factoryClassName = props.getProperty(keyName); - if(factoryClassName != null){ - debugPrintln("found "+keyName+" = " + factoryClassName); - return createInstance(factoryClassName, true); - } else { - debugPrintln(keyName+" is not in the property file"); + // Only calls "newXPathFactoryNoServiceLoader" if it's + // declared to return an instance of XPathFactory. + final Class returnType = creationMethod.getReturnType(); + if (SERVICE_CLASS.isAssignableFrom(returnType)) { + return SERVICE_CLASS.cast(creationMethod.invoke(null, (Object[])null)); + } else { + // Should not happen since + // XPathFactoryImpl.newXPathFactoryNoServiceLoader is + // declared to return XPathFactory. + throw new ClassCastException(returnType + + " cannot be cast to " + SERVICE_CLASS); + } + } catch (ClassCastException e) { + throw new XPathFactoryConfigurationException(e); + } catch (NoSuchMethodException exc) { + return null; + } catch (Exception exc) { return null; } } + // Call isObjectModelSupportedBy with initial context. + private boolean isObjectModelSupportedBy(final XPathFactory factory, + final String objectModel, + AccessControlContext acc) { + return AccessController.doPrivileged(new PrivilegedAction() { + public Boolean run() { + return factory.isObjectModelSupported(objectModel); + } + }, acc); + } + /** - * Returns an {@link Iterator} that enumerates all - * the META-INF/services files that we care. + * Finds a service provider subclass of XPathFactory that supports the + * given object model using the ServiceLoader. + * + * @param objectModel URI of object model to support. + * @return An XPathFactory supporting the specified object model, or null + * if none is found. + * @throws XPathFactoryConfigurationException if a configuration error is found. */ - private Iterator createServiceFileIterator() { - if (classLoader == null) { - return new SingleIterator() { - protected Object value() { - ClassLoader classLoader = XPathFactoryFinder.class.getClassLoader(); - return ss.getResourceAsURL(classLoader, SERVICE_ID); - //return (ClassLoader.getSystemResource( SERVICE_ID )); - } - }; - } else { - try { - //final Enumeration e = classLoader.getResources(SERVICE_ID); - final Enumeration e = ss.getResources(classLoader, SERVICE_ID); - if(!e.hasMoreElements()) { - debugPrintln("no "+SERVICE_ID+" file was found"); + private XPathFactory findServiceProvider(final String objectModel) + throws XPathFactoryConfigurationException { + + assert objectModel != null; + // store current context. + final AccessControlContext acc = AccessController.getContext(); + try { + return AccessController.doPrivileged(new PrivilegedAction() { + public XPathFactory run() { + final ServiceLoader loader = + ServiceLoader.load(SERVICE_CLASS); + for (XPathFactory factory : loader) { + // restore initial context to call + // factory.isObjectModelSupportedBy + if (isObjectModelSupportedBy(factory, objectModel, acc)) { + return factory; + } + } + return null; // no factory found. } - - // wrap it into an Iterator. - return new Iterator() { - public void remove() { - throw new UnsupportedOperationException(); - } - - public boolean hasNext() { - return e.hasMoreElements(); - } - - public Object next() { - return e.nextElement(); - } - }; - } catch (IOException e) { - debugPrintln("failed to enumerate resources "+SERVICE_ID); - if(debug) e.printStackTrace(); - return new ArrayList().iterator(); // empty iterator - } + }); + } catch (ServiceConfigurationError error) { + throw new XPathFactoryConfigurationException(error); } } - private static final Class SERVICE_CLASS = XPathFactory.class; - private static final String SERVICE_ID = "META-INF/services/" + SERVICE_CLASS.getName(); - - + private static final Class SERVICE_CLASS = XPathFactory.class; private static String which( Class clazz ) { return which( clazz.getName(), clazz.getClassLoader() ); diff -r 241d13ccb08b -r 4ea383c26563 jaxws/.hgtags --- a/jaxws/.hgtags Wed May 08 14:10:53 2013 +0200 +++ b/jaxws/.hgtags Wed May 08 23:54:45 2013 -0700 @@ -209,3 +209,4 @@ 8c0b6bccfe474576d6b30d1582c4329029330150 jdk8-b85 a5e7c2f093c9996ab3419db1565094a07b059e9c jdk8-b86 72e03566f0a61282cc48ebc869803b256cccd66c jdk8-b87 +24fa5452e5d4e9df8b85196283275a6ca4b4adb4 jdk8-b88 diff -r 241d13ccb08b -r 4ea383c26563 jdk/.hgtags --- a/jdk/.hgtags Wed May 08 14:10:53 2013 +0200 +++ b/jdk/.hgtags Wed May 08 23:54:45 2013 -0700 @@ -209,3 +209,4 @@ 296676d534c52888c36e305a2bf7f345c4ca70f8 jdk8-b85 7989cd0cc3a9149864589438ee2c949015d8aa9a jdk8-b86 d5228e624826a10ccc5b05f30ad8d839b58fe48d jdk8-b87 +8dbb4b159e04de3c447c9242c70505e71f8624c7 jdk8-b88 diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/docs/CORE_PKGS.gmk --- a/jdk/make/docs/CORE_PKGS.gmk Wed May 08 14:10:53 2013 +0200 +++ b/jdk/make/docs/CORE_PKGS.gmk Wed May 08 23:54:45 2013 -0700 @@ -142,6 +142,7 @@ java.util.prefs \ java.util.regex \ java.util.spi \ + java.util.stream \ java.util.zip \ javax.accessibility \ javax.activation \ diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/java/java/FILES_java.gmk --- a/jdk/make/java/java/FILES_java.gmk Wed May 08 14:10:53 2013 +0200 +++ b/jdk/make/java/java/FILES_java.gmk Wed May 08 23:54:45 2013 -0700 @@ -252,6 +252,7 @@ java/util/Scanner.java \ java/util/InputMismatchException.java \ java/util/Stack.java \ + java/util/StringJoiner.java \ java/util/StringTokenizer.java \ java/util/TimeZone.java \ java/util/SimpleTimeZone.java \ diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/netbeans/common/closed-share-sources.ent --- a/jdk/make/netbeans/common/closed-share-sources.ent Wed May 08 14:10:53 2013 +0200 +++ b/jdk/make/netbeans/common/closed-share-sources.ent Wed May 08 23:54:45 2013 -0700 @@ -37,6 +37,7 @@ ${root}/src/closed/share/classes ${includes} ${excludes} + US-ASCII diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/netbeans/common/demo-view.ent --- a/jdk/make/netbeans/common/demo-view.ent Wed May 08 14:10:53 2013 +0200 +++ b/jdk/make/netbeans/common/demo-view.ent Wed May 08 23:54:45 2013 -0700 @@ -31,7 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --> - + ${root}/src/share/demo ${demos} diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/netbeans/common/java-data-native.ent --- a/jdk/make/netbeans/common/java-data-native.ent Wed May 08 14:10:53 2013 +0200 +++ b/jdk/make/netbeans/common/java-data-native.ent Wed May 08 23:54:45 2013 -0700 @@ -1,7 +1,7 @@ - + ${root}/test ${jtreg.tests} diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/netbeans/common/macosx-sources.ent --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/netbeans/common/macosx-sources.ent Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,49 @@ + + + + + + + + java + ${root}/src/macosx/classes + ${includes} + ${excludes} + US-ASCII + + + + ${root}/src/macosx/classes + diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/netbeans/common/macosx-view.ent --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/netbeans/common/macosx-view.ent Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,43 @@ + + + + + + + + ${root}/src/macosx/classes + ${includes} + ${excludes} + diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/netbeans/common/properties.ent --- a/jdk/make/netbeans/common/properties.ent Wed May 08 14:10:53 2013 +0200 +++ b/jdk/make/netbeans/common/properties.ent Wed May 08 23:54:45 2013 -0700 @@ -41,3 +41,5 @@ ${user.home}/.openjdk/build.properties build.properties ${java.home}/.. +${env.JT_HOME} +** diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/netbeans/common/sample-view.ent --- a/jdk/make/netbeans/common/sample-view.ent Wed May 08 14:10:53 2013 +0200 +++ b/jdk/make/netbeans/common/sample-view.ent Wed May 08 23:54:45 2013 -0700 @@ -1,7 +1,7 @@ - + ${root}/src/share/sample ${samples} diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/netbeans/common/share-sources.ent --- a/jdk/make/netbeans/common/share-sources.ent Wed May 08 14:10:53 2013 +0200 +++ b/jdk/make/netbeans/common/share-sources.ent Wed May 08 23:54:45 2013 -0700 @@ -37,6 +37,7 @@ ${root}/src/share/classes ${includes} ${excludes} + US-ASCII diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/netbeans/common/shared.xml --- a/jdk/make/netbeans/common/shared.xml Wed May 08 14:10:53 2013 +0200 +++ b/jdk/make/netbeans/common/shared.xml Wed May 08 23:54:45 2013 -0700 @@ -276,7 +276,7 @@ - + @@ -338,7 +338,7 @@ - diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/netbeans/common/unix-sources.ent --- a/jdk/make/netbeans/common/unix-sources.ent Wed May 08 14:10:53 2013 +0200 +++ b/jdk/make/netbeans/common/unix-sources.ent Wed May 08 23:54:45 2013 -0700 @@ -41,6 +41,7 @@ ${root}/src/solaris/classes ${includes} ${excludes} + US-ASCII diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/netbeans/common/windows-sources.ent --- a/jdk/make/netbeans/common/windows-sources.ent Wed May 08 14:10:53 2013 +0200 +++ b/jdk/make/netbeans/common/windows-sources.ent Wed May 08 23:54:45 2013 -0700 @@ -37,6 +37,7 @@ ${root}/src/windows/classes ${includes} ${excludes} + US-ASCII diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/netbeans/j2se/nbproject/project.xml --- a/jdk/make/netbeans/j2se/nbproject/project.xml Wed May 08 14:10:53 2013 +0200 +++ b/jdk/make/netbeans/j2se/nbproject/project.xml Wed May 08 23:54:45 2013 -0700 @@ -1,7 +1,7 @@ + + + + + + + + + + + + + + + + + + + + + + diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/netbeans/jdbc/nbproject/project.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/netbeans/jdbc/nbproject/project.xml Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + +]> + + org.netbeans.modules.ant.freeform + + + JDBC + + jdbc + &properties; + + + &share-sources; + &jtreg-sources; + &build-folder; + + + &standard-bindings; + + run + + + debug + + + debug + + + + + &share-view; + &jtreg-view; + &file-view; + + + &standard-actions; + + + + + + + &java-data-no-native; + + diff -r 241d13ccb08b -r 4ea383c26563 jdk/make/netbeans/world/nbproject/project.xml --- a/jdk/make/netbeans/world/nbproject/project.xml Wed May 08 14:10:53 2013 +0200 +++ b/jdk/make/netbeans/world/nbproject/project.xml Wed May 08 23:54:45 2013 -0700 @@ -1,7 +1,7 @@ "); + assertEquals(sj.length(), 4); + assertEquals(sj.toString().length(), sj.length()); + sj.add("abcdef"); + assertEquals(sj.length(), 10); + assertEquals(sj.toString().length(), sj.length()); + sj.add("xyz"); + assertEquals(sj.length(), 15); + assertEquals(sj.toString().length(), sj.length()); + } + + public void noAddAndEmptyValue() { + StringJoiner sj = new StringJoiner(DASH, "", "").setEmptyValue(EMPTY); + assertEquals(sj.toString(), EMPTY); + + sj = new StringJoiner(DASH, "<..", ""); + assertEquals(sj.toString(), "<.."); + + sj = new StringJoiner(DASH, "<..", ""); + assertEquals(sj.toString(), "<.."); + + sj = new StringJoiner(DASH, "", "==>"); + assertEquals(sj.toString(), "==>"); + + sj = new StringJoiner(DASH, "{", "}"); + assertEquals(sj.toString(), "{}"); + } + + @Test(expectedExceptions = {NullPointerException.class}) + public void setEmptyValueNull() { + new StringJoiner(DASH, "{", "}").setEmptyValue(null); + } + + @Test(expectedExceptions = {NullPointerException.class}) + public void setDelimiterNull() { + new StringJoiner(null); + } + + @Test(expectedExceptions = {NullPointerException.class}) + public void setPrefixNull() { + new StringJoiner(DASH, null, "}"); + } + + @Test(expectedExceptions = {NullPointerException.class}) + public void setSuffixNull() { + new StringJoiner(DASH, "{", null); + } + + public void stringFromtoString() { + StringJoiner sj = new StringJoiner(", "); + assertEquals(sj.toString(), ""); + sj = new StringJoiner(",", "{", "}"); + assertEquals(sj.toString(), "{}"); + + sj = new StringJoiner(","); + sj.add(ONE); + assertEquals(sj.toString(), ONE); + + sj.add(TWO); + assertEquals(sj.toString(), ONE + "," + TWO); + + sj = new StringJoiner(",", "{--", "--}"); + sj.add(ONE); + sj.add(TWO); + assertEquals(sj.toString(), "{--" + ONE + "," + TWO + "--}"); + + } + + public void stringFromtoStringWithEmptyValue() { + StringJoiner sj = new StringJoiner(" ", "", ""); + assertEquals(sj.toString(), ""); + sj = new StringJoiner(", "); + assertEquals(sj.toString(), ""); + sj = new StringJoiner(",", "{", "}"); + assertEquals(sj.toString(), "{}"); + + sj = new StringJoiner(",", "{", "}").setEmptyValue(""); + assertEquals(sj.toString(), ""); + + sj = new StringJoiner(","); + sj.add(ONE); + assertEquals(sj.toString(), ONE); + + sj.add(TWO); + assertEquals(sj.toString(), ONE + "," + TWO); + + sj = new StringJoiner(",", "{--", "--}"); + sj.add(ONE); + assertEquals(sj.toString(), "{--" + ONE + "--}" ); + + sj.add(TWO); + assertEquals(sj.toString(), "{--" + ONE + "," + TWO + "--}"); + + } + + public void toStringWithCustomEmptyValue() { + StringJoiner sj = new StringJoiner(DASH, "<", ">").setEmptyValue(EMPTY); + assertEquals(sj.toString(), EMPTY); + sj.add(""); + assertEquals(sj.toString(), "<>"); + sj.add(""); + assertEquals(sj.toString(), "<->"); + } + + private void testCombos(String infix, String prefix, String suffix) { + StringJoiner sj = new StringJoiner(infix, prefix, suffix); + assertEquals(sj.toString(), prefix + suffix); + assertEquals(sj.toString().length(), sj.length()); + // EmptyValue + sj = new StringJoiner(infix, prefix, suffix).setEmptyValue(""); + assertEquals(sj.toString(), ""); + assertEquals(sj.toString().length(), sj.length()); + + // empty in front + sj.add(""); + assertEquals(sj.toString(), prefix + suffix); + // empty in middle + sj.add(""); + assertEquals(sj.toString(), prefix + infix + suffix); + sj.add("1"); + assertEquals(sj.toString(), prefix + infix + infix + "1" + suffix); + // empty at end + sj.add(""); + assertEquals(sj.toString(), prefix + infix + infix + "1" + infix + suffix); + + sj = new StringJoiner(infix, prefix, suffix).setEmptyValue(""); + sj.add("1"); + assertEquals(sj.toString(), prefix + "1" + suffix); + sj.add("2"); + assertEquals(sj.toString(), prefix + "1" + infix + "2" + suffix); + sj.add(""); + assertEquals(sj.toString(), prefix + "1" + infix + "2" +infix + suffix); + sj.add("3"); + assertEquals(sj.toString(), prefix + "1" + infix + "2" +infix + infix + "3" + suffix); + } + + public void testDelimiterCombinations() { + testCombos("", "", ""); + testCombos("", "<", ""); + testCombos("", "", ">"); + testCombos("", "<", ">"); + testCombos(",", "", ""); + testCombos(",", "<", ""); + testCombos(",", "", ">"); + testCombos(",", "<", ">"); + } +} + diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/java/util/logging/DrainFindDeadlockTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/DrainFindDeadlockTest.java Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,196 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.lang.Thread.State; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import java.util.Map; + +/** + * @test + * @bug 8010939 + * @summary check for deadlock between findLogger() and drainLoggerRefQueueBounded() + * @author jim.gish@oracle.com + * @build DrainFindDeadlockTest + * @run main/othervm/timeout=10 DrainFindDeadlockTest + */ + +/** + * This test is checking for a deadlock between + * LogManager$LoggerContext.findLogger() and + * LogManager.drainLoggerRefQueueBounded() (which could happen by calling + * Logger.getLogger() and LogManager.readConfiguration() in different threads) + */ +public class DrainFindDeadlockTest { + private LogManager mgr = LogManager.getLogManager(); + private final static int MAX_ITERATIONS = 100; + + // Get a ThreadMXBean so we can check for deadlock. N.B. this may + // not be supported on all platforms, which means we will have to + // resort to the traditional test timeout method. However, if + // we have the support we'll get the deadlock details if one + // is detected. + private final static ThreadMXBean threadMXBean = + ManagementFactory.getThreadMXBean(); + private final boolean threadMXBeanDeadlockSupported = + threadMXBean.isSynchronizerUsageSupported(); + + public static void main(String... args) throws IOException, Exception { + new DrainFindDeadlockTest().testForDeadlock(); + } + + public static void randomDelay() { + int runs = (int) Math.random() * 1000000; + int c = 0; + + for (int i=0; i threadMap = + Thread.getAllStackTraces(); + dumpStack(threadMap.get(x), x); + dumpStack(threadMap.get(y), y); + } + } + + private void dumpStack(StackTraceElement[] aStackElt, Thread aThread) { + if (aStackElt != null) { + System.out.println("Thread:" + aThread.getName() + ": " + + aThread.getState()); + for (StackTraceElement element: aStackElt) { + System.out.println(" " + element); + } + } + } + + @Override + public void run() { + System.out.println("Running " + Thread.currentThread().getName()); + for (int i=0; i < MAX_ITERATIONS*2; i++) { + checkState(t1, t2); + try { + Thread.sleep(10); + } catch (InterruptedException ex) { + }; + } + } + } +} diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/java/util/logging/bundlesearch/ClassPathTestBundle_en.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/bundlesearch/ClassPathTestBundle_en.properties Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,25 @@ +# +# Copyright (c) 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. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +sample1=translation #2 for sample1 +sample2=translation #2 for sample2 +supports-test=ResourceBundleSearchTest diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/java/util/logging/bundlesearch/IndirectlyLoadABundle.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/bundlesearch/IndirectlyLoadABundle.java Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Paths; + +/** + * This class is used to ensure that a resource bundle loadable by a classloader + * is on the caller's stack, but not on the classpath or TCCL to ensure that + * Logger.getLogger() can't load the bundle via a stack search + * + * @author Jim Gish + */ +public class IndirectlyLoadABundle { + + private final static String rbName = "StackSearchableResource"; + + public boolean loadAndTest() throws Throwable { + // Find out where we are running from so we can setup the URLClassLoader URLs + // test.src and test.classes will be set if running in jtreg, but probably + // not otherwise + String testDir = System.getProperty("test.src", System.getProperty("user.dir")); + String testClassesDir = System.getProperty("test.classes", + System.getProperty("user.dir")); + String sep = System.getProperty("file.separator"); + + URL[] urls = new URL[2]; + + // Allow for both jtreg and standalone cases here + urls[0] = Paths.get(testDir, "resources").toUri().toURL(); + urls[1] = Paths.get(testClassesDir).toUri().toURL(); + + System.out.println("INFO: urls[0] = " + urls[0]); + System.out.println("INFO: urls[1] = " + urls[1]); + + // Make sure we can find it via the URLClassLoader + URLClassLoader yetAnotherResourceCL = new URLClassLoader(urls, null); + if (!testForValidResourceSetup(yetAnotherResourceCL)) { + throw new Exception("Couldn't directly load bundle " + rbName + + " as expected. Test config problem"); + } + // But it shouldn't be available via the system classloader + ClassLoader myCL = this.getClass().getClassLoader(); + if (testForValidResourceSetup(myCL)) { + throw new Exception("Was able to directly load bundle " + rbName + + " from " + myCL + " but shouldn't have been" + + " able to. Test config problem"); + } + + Class loadItUpClazz = Class.forName("LoadItUp", true, yetAnotherResourceCL); + ClassLoader actual = loadItUpClazz.getClassLoader(); + if (actual != yetAnotherResourceCL) { + throw new Exception("LoadItUp was loaded by an unexpected CL: " + actual); + } + Object loadItUp = loadItUpClazz.newInstance(); + Method testMethod = loadItUpClazz.getMethod("test", String.class); + try { + return (Boolean) testMethod.invoke(loadItUp, rbName); + } catch (InvocationTargetException ex) { + throw ex.getTargetException(); + } + } + + private boolean testForValidResourceSetup(ClassLoader cl) { + // First make sure the test environment is setup properly and the bundle actually + // exists + return ResourceBundleSearchTest.isOnClassPath(rbName, cl); + } +} diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/java/util/logging/bundlesearch/LoadItUp.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/bundlesearch/LoadItUp.java Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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.MissingResourceException; +import java.util.logging.Logger; + +/* + * This class is loaded onto the call stack when the test method is called + * and then its classloader can be used to find a property bundle in the same + * directory as the class. However, Logger is not allowed + * to find the bundle by looking up the stack for this classloader. + * We verify that this cannot happen. + * + * @author Jim Gish + */ +public class LoadItUp { + + private final static boolean DEBUG = false; + + public Boolean test(String rbName) throws Exception { + // we should not be able to find the resource in this directory via + // getLogger calls. The only way that would be possible given this setup + // is that if Logger.getLogger searched up the call stack + return lookupBundle(rbName); + } + + private boolean lookupBundle(String rbName) { + // See if Logger.getLogger can find the resource in this directory + try { + Logger aLogger = Logger.getLogger("NestedLogger", rbName); + } catch (MissingResourceException re) { + if (DEBUG) { + System.out.println( + "As expected, LoadItUp.lookupBundle() did not find the bundle " + + rbName); + } + return false; + } + System.out.println("FAILED: LoadItUp.lookupBundle() found the bundle " + + rbName + " using a stack search."); + return true; + } +} diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/java/util/logging/bundlesearch/ResourceBundleSearchTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/bundlesearch/ResourceBundleSearchTest.java Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,251 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 8002070 + * @summary Remove the stack search for a resource bundle Logger to use + * @author Jim Gish + * @build ResourceBundleSearchTest IndirectlyLoadABundle LoadItUp + * @run main ResourceBundleSearchTest + */ +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.logging.Logger; + +public class ResourceBundleSearchTest { + + private final static boolean DEBUG = false; + private final static String LOGGER_PREFIX = "myLogger."; + private static int loggerNum = 0; + private final static String PROP_RB_NAME = "ClassPathTestBundle"; + private final static String TCCL_TEST_BUNDLE = "ContextClassLoaderTestBundle"; + + private static int numPass = 0; + private static int numFail = 0; + private static List msgs = new ArrayList<>(); + + public static void main(String[] args) throws Throwable { + ResourceBundleSearchTest test = new ResourceBundleSearchTest(); + test.runTests(); + } + + private void runTests() throws Throwable { + // ensure we are using en as the default Locale so we can find the resource + Locale.setDefault(Locale.ENGLISH); + + String testClasses = System.getProperty("test.classes"); + System.out.println( "test.classes = " + testClasses ); + + ClassLoader myClassLoader = ClassLoader.getSystemClassLoader(); + + // Find out where we are running from so we can setup the URLClassLoader URL + String userDir = System.getProperty("user.dir"); + String testDir = System.getProperty("test.src", userDir); + String sep = System.getProperty("file.separator"); + + URL[] urls = new URL[1]; + + urls[0] = Paths.get(testDir, "resources").toUri().toURL(); + URLClassLoader rbClassLoader = new URLClassLoader(urls); + + // Test 1 - can we find a Logger bundle from doing a stack search? + // We shouldn't be able to + assertFalse(testGetBundleFromStackSearch(), "testGetBundleFromStackSearch"); + + // Test 2 - can we find a Logger bundle off of the Thread context class + // loader? We should be able to. + assertTrue( + testGetBundleFromTCCL(TCCL_TEST_BUNDLE, rbClassLoader), + "testGetBundleFromTCCL"); + + // Test 3 - Can we find a Logger bundle from the classpath? We should be + // able to, but .... + // We check to see if the bundle is on the classpath or not so that this + // will work standalone. In the case of jtreg/samevm, + // the resource bundles are not on the classpath. Running standalone + // (or othervm), they are + if (isOnClassPath(PROP_RB_NAME, myClassLoader)) { + debug("We should be able to see " + PROP_RB_NAME + " on the classpath"); + assertTrue(testGetBundleFromSystemClassLoader(PROP_RB_NAME), + "testGetBundleFromSystemClassLoader"); + } else { + debug("We should not be able to see " + PROP_RB_NAME + " on the classpath"); + assertFalse(testGetBundleFromSystemClassLoader(PROP_RB_NAME), + "testGetBundleFromSystemClassLoader"); + } + + report(); + } + + private void report() throws Exception { + System.out.println("Num passed = " + numPass + " Num failed = " + numFail); + if (numFail > 0) { + // We only care about the messages if they were errors + for (String msg : msgs) { + System.out.println(msg); + } + throw new Exception(numFail + " out of " + (numPass + numFail) + + " tests failed."); + } + } + + public void assertTrue(boolean testResult, String testName) { + if (testResult) { + numPass++; + } else { + numFail++; + System.out.println("FAILED: " + testName + + " was supposed to return true but did NOT!"); + } + } + + public void assertFalse(boolean testResult, String testName) { + if (!testResult) { + numPass++; + } else { + numFail++; + System.out.println("FAILED: " + testName + + " was supposed to return false but did NOT!"); + } + } + + public boolean testGetBundleFromStackSearch() throws Throwable { + // This should fail. This was the old functionality to search up the + // caller's call stack + IndirectlyLoadABundle indirectLoader = new IndirectlyLoadABundle(); + return indirectLoader.loadAndTest(); + } + + public boolean testGetBundleFromTCCL(String bundleName, + ClassLoader setOnTCCL) throws InterruptedException { + // This should succeed. We should be able to get the bundle from the + // thread context class loader + debug("Looking for " + bundleName + " using TCCL"); + LoggingThread lr = new LoggingThread(bundleName, setOnTCCL); + lr.start(); + synchronized (lr) { + try { + lr.wait(); + } catch (InterruptedException ex) { + throw ex; + } + } + msgs.add(lr.msg); + return lr.foundBundle; + } + + /* + * @param String bundleClass + * @param ClassLoader to use for search + * @return true iff bundleClass is on system classpath + */ + public static boolean isOnClassPath(String baseName, ClassLoader cl) { + ResourceBundle rb = null; + try { + rb = ResourceBundle.getBundle(baseName, Locale.getDefault(), cl); + System.out.println("INFO: Found bundle " + baseName + " on " + cl); + } catch (MissingResourceException e) { + System.out.println("INFO: Could not find bundle " + baseName + " on " + cl); + return false; + } + return (rb != null); + } + + private static String newLoggerName() { + // we need a new logger name every time we attempt to find a bundle via + // the Logger.getLogger call, so we'll simply tack on an integer which + // we increment each time this is called + loggerNum++; + return LOGGER_PREFIX + loggerNum; + } + + public boolean testGetBundleFromSystemClassLoader(String bundleName) { + // this should succeed if the bundle is on the system classpath. + try { + Logger aLogger = Logger.getLogger(ResourceBundleSearchTest.newLoggerName(), + bundleName); + } catch (MissingResourceException re) { + msgs.add("INFO: testGetBundleFromSystemClassLoader() did not find bundle " + + bundleName); + return false; + } + msgs.add("INFO: testGetBundleFromSystemClassLoader() found the bundle " + + bundleName); + return true; + } + + public static class LoggingThread extends Thread { + + boolean foundBundle = false; + String msg = null; + ClassLoader clToSetOnTCCL = null; + String bundleName = null; + + public LoggingThread(String bundleName) { + this.bundleName = bundleName; + } + + public LoggingThread(String bundleName, ClassLoader setOnTCCL) { + this.clToSetOnTCCL = setOnTCCL; + this.bundleName = bundleName; + } + + public void run() { + boolean setTCCL = false; + try { + if (clToSetOnTCCL != null) { + Thread.currentThread().setContextClassLoader(clToSetOnTCCL); + setTCCL = true; + } + // this should succeed if the bundle is on the system classpath. + try { + Logger aLogger = Logger.getLogger(ResourceBundleSearchTest.newLoggerName(), + bundleName); + msg = "INFO: LoggingRunnable() found the bundle " + bundleName + + (setTCCL ? " with " : " without ") + "setting the TCCL"; + foundBundle = true; + } catch (MissingResourceException re) { + msg = "INFO: LoggingRunnable() did not find the bundle " + bundleName + + (setTCCL ? " with " : " without ") + "setting the TCCL"; + foundBundle = false; + } + } catch (Throwable e) { + e.printStackTrace(); + System.exit(1); + } + } + } + + private void debug(String msg) { + if (DEBUG) { + System.out.println(msg); + } + } +} diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/java/util/logging/bundlesearch/resources/ContextClassLoaderTestBundle_en.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/bundlesearch/resources/ContextClassLoaderTestBundle_en.properties Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,25 @@ +# +# Copyright (c) 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. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +sample1=translation #3 for sample1 +sample2=translation #3 for sample2 +supports-test=ResourceBundleSearchTest diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/java/util/logging/bundlesearch/resources/StackSearchableResource_en.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/bundlesearch/resources/StackSearchableResource_en.properties Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,25 @@ +# +# Copyright (c) 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. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# +sample1=translation #4 for sample1 +sample2=translation #4 for sample2 +supports-test=ResourceBundleSearchTest diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/java/util/regex/RegExTest.java --- a/jdk/test/java/util/regex/RegExTest.java Wed May 08 14:10:53 2013 +0200 +++ b/jdk/test/java/util/regex/RegExTest.java Wed May 08 23:54:45 2013 -0700 @@ -33,7 +33,7 @@ * 5013885 5003322 4988891 5098443 5110268 6173522 4829857 5027748 6376940 * 6358731 6178785 6284152 6231989 6497148 6486934 6233084 6504326 6635133 * 6350801 6676425 6878475 6919132 6931676 6948903 6990617 7014645 7039066 - * 7067045 7014640 7189363 + * 7067045 7014640 7189363 8007395 */ import java.util.regex.*; @@ -144,6 +144,7 @@ horizontalAndVerticalWSTest(); linebreakTest(); branchTest(); + groupCurlyNotFoundSuppTest(); if (failure) { throw new RuntimeException("RegExTest failed, 1st failure: " + @@ -3947,4 +3948,27 @@ report("branchTest"); } + // This test is for 8007395 + private static void groupCurlyNotFoundSuppTest() throws Exception { + String input = "test this as \ud83d\ude0d"; + for (String pStr : new String[] { "test(.)+(@[a-zA-Z.]+)", + "test(.)*(@[a-zA-Z.]+)", + "test([^B])+(@[a-zA-Z.]+)", + "test([^B])*(@[a-zA-Z.]+)", + "test(\\P{IsControl})+(@[a-zA-Z.]+)", + "test(\\P{IsControl})*(@[a-zA-Z.]+)", + }) { + Matcher m = Pattern.compile(pStr, Pattern.CASE_INSENSITIVE) + .matcher(input); + try { + if (m.find()) { + failCount++; + } + } catch (Exception x) { + failCount++; + } + } + report("GroupCurly NotFoundSupp"); + } + } diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/javax/swing/JInternalFrame/InternalFrameIsNotCollectedTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/swing/JInternalFrame/InternalFrameIsNotCollectedTest.java Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,138 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 8012004 + @summary JINTERNALFRAME NOT BEING FINALIZED AFTER CLOSING + @author mcherkas + @run main InternalFrameIsNotCollectedTest + */ + +import sun.awt.SunToolkit; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.beans.PropertyVetoException; +import java.util.Date; + +public class InternalFrameIsNotCollectedTest { + + public static final int waitTime = 10000; + private static Robot robot; + + public static void sync() { + + SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); + toolkit.realSync(); + } + + public static void main(String[] args) throws Exception { + initRobot(); + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + initUI(); + try { + closeInternalFrame(); + } catch (PropertyVetoException e) { + throw new RuntimeException(e); + } + } + }); + sync(); + invokeGC(); + Thread.sleep(1000); // it's better to wait 1 sec now then 10 sec later + Date startWaiting = new Date(); + synchronized (CustomInternalFrame.waiter) { + // Sync with finalization thread. + Date now = new Date(); + while (now.getTime() - startWaiting.getTime() < waitTime && !CustomInternalFrame.finalized) { + CustomInternalFrame.waiter.wait(waitTime); + now = new Date(); + } + } + if (!CustomInternalFrame.finalized) { + throw new RuntimeException("Closed internal frame wasn't collected"); + } + } + + private static void initRobot() throws AWTException { + robot = new Robot(); + robot.setAutoDelay(100); + } + + private static void closeInternalFrame() throws PropertyVetoException { + robot.keyPress(KeyEvent.VK_CONTROL); + robot.keyPress(KeyEvent.VK_F4); + robot.keyRelease(KeyEvent.VK_F4); + robot.keyRelease(KeyEvent.VK_CONTROL); + } + + private static void initUI() { + JFrame frame = new JFrame("Internal Frame Test"); + frame.getContentPane().setLayout(new BorderLayout()); + JDesktopPane desktopPane = new JDesktopPane(); + desktopPane.setDesktopManager(new DefaultDesktopManager()); + frame.getContentPane().add(desktopPane, BorderLayout.CENTER); + + CustomInternalFrame iFrame = new CustomInternalFrame("Dummy Frame"); + + iFrame.setSize(200, 200); + iFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + desktopPane.add(iFrame); + + frame.setSize(800, 600); + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + frame.setVisible(true); + iFrame.setVisible(true); + } + + private static void invokeGC() { + System.out.println("Firing garbage collection!"); + try { + StringBuilder sb = new StringBuilder(); + while (true) { + sb.append("any string. some test. a little bit more text." + sb.toString()); + } + } catch (Throwable e) { + // do nothing + } + } + + + public static class CustomInternalFrame extends JInternalFrame { + public static volatile boolean finalized = false; + public static Object waiter = new Object(); + + public CustomInternalFrame(String title) { + super(title, true, true, true, true); + } + + protected void finalize() { + System.out.println("Finalized!"); + finalized = true; + waiter.notifyAll(); + } + } +} \ No newline at end of file diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/javax/swing/JMenuItem/ActionListenerCalledTwice/ActionListenerCalledTwiceTest.java --- a/jdk/test/javax/swing/JMenuItem/ActionListenerCalledTwice/ActionListenerCalledTwiceTest.java Wed May 08 14:10:53 2013 +0200 +++ b/jdk/test/javax/swing/JMenuItem/ActionListenerCalledTwice/ActionListenerCalledTwiceTest.java Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -35,43 +35,72 @@ import javax.swing.*; public class ActionListenerCalledTwiceTest { + static String menuItems[] = { "Item1", "Item2" }; + static KeyStroke keyStrokes[] = { + KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.META_MASK), + KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0) + }; + static volatile int listenerCallCounter = 0; public static void main(String[] args) throws Exception { if (sun.awt.OSInfo.getOSType() != sun.awt.OSInfo.OSType.MACOSX) { System.out.println("This test is for MacOS only. Automatically passed on other platforms."); return; } + System.setProperty("apple.laf.useScreenMenuBar", "true"); SwingUtilities.invokeAndWait(new Runnable() { public void run() { createAndShowGUI(); } }); + SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); Robot robot = new Robot(); robot.setAutoDelay(100); - robot.keyPress(KeyEvent.VK_META); - robot.keyPress(KeyEvent.VK_E); - robot.keyRelease(KeyEvent.VK_E); - robot.keyRelease(KeyEvent.VK_META); - toolkit.realSync(); - if (listenerCallCounter != 1) { - throw new Exception("Test failed: ActionListener called " + listenerCallCounter + " times instead of 1!"); + + for (int i = 0; i < menuItems.length; ++i) { + KeyStroke ks = keyStrokes[i]; + int modKeyCode = getModKeyCode(ks.getModifiers()); + + if (modKeyCode != 0) { + robot.keyPress(modKeyCode); + } + + robot.keyPress(ks.getKeyCode()); + robot.keyRelease(ks.getKeyCode()); + + if (modKeyCode != 0) { + robot.keyRelease(modKeyCode); + } + + toolkit.realSync(); + + if (listenerCallCounter != 1) { + throw new Exception("Test failed: ActionListener for " + menuItems[i] + + " called " + listenerCallCounter + " times instead of 1!"); + } + + listenerCallCounter = 0; } } private static void createAndShowGUI() { - JMenuItem newItem = new JMenuItem("Exit"); - newItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.META_MASK)); - newItem.addActionListener( - new ActionListener(){ - public void actionPerformed(ActionEvent e) { - listenerCallCounter++; + JMenu menu = new JMenu("Menu"); + + for (int i = 0; i < menuItems.length; ++i) { + JMenuItem newItem = new JMenuItem(menuItems[i]); + newItem.setAccelerator(keyStrokes[i]); + newItem.addActionListener( + new ActionListener(){ + public void actionPerformed(ActionEvent e) { + listenerCallCounter++; + } } - } - ); - JMenu menu = new JMenu("Menu"); - menu.add(newItem); + ); + menu.add(newItem); + } + JMenuBar bar = new JMenuBar(); bar.add(menu); JFrame frame = new JFrame("Test"); @@ -80,4 +109,24 @@ frame.pack(); frame.setVisible(true); } + + private static int getModKeyCode(int mod) { + if ((mod & (InputEvent.SHIFT_DOWN_MASK | InputEvent.SHIFT_MASK)) != 0) { + return KeyEvent.VK_SHIFT; + } + + if ((mod & (InputEvent.CTRL_DOWN_MASK | InputEvent.CTRL_MASK)) != 0) { + return KeyEvent.VK_CONTROL; + } + + if ((mod & (InputEvent.ALT_DOWN_MASK | InputEvent.ALT_MASK)) != 0) { + return KeyEvent.VK_ALT; + } + + if ((mod & (InputEvent.META_DOWN_MASK | InputEvent.META_MASK)) != 0) { + return KeyEvent.VK_META; + } + + return 0; + } } diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/sun/reflect/CallerSensitive/CallerSensitiveFinder.java --- a/jdk/test/sun/reflect/CallerSensitive/CallerSensitiveFinder.java Wed May 08 14:10:53 2013 +0200 +++ b/jdk/test/sun/reflect/CallerSensitive/CallerSensitiveFinder.java Wed May 08 23:54:45 2013 -0700 @@ -46,10 +46,10 @@ * @bug 8010117 * @summary Verify if CallerSensitive methods are annotated with * sun.reflect.CallerSensitive annotation - * @build CallerSensitiveFinder MethodFinder + * @build CallerSensitiveFinder * @run main/othervm/timeout=900 -mx600m CallerSensitiveFinder */ -public class CallerSensitiveFinder extends MethodFinder { +public class CallerSensitiveFinder { private static int numThreads = 3; private static boolean verbose = false; public static void main(String[] args) throws Exception { @@ -71,8 +71,7 @@ if (classes.isEmpty()) { classes.addAll(PlatformClassPath.getJREClasses()); } - final String method = "sun/reflect/Reflection.getCallerClass"; - CallerSensitiveFinder csfinder = new CallerSensitiveFinder(method); + CallerSensitiveFinder csfinder = new CallerSensitiveFinder(); List errors = csfinder.run(classes); if (!errors.isEmpty()) { @@ -82,8 +81,46 @@ } private final List csMethodsMissingAnnotation = new ArrayList<>(); - public CallerSensitiveFinder(String... methods) { - super(methods); + private final ReferenceFinder finder; + public CallerSensitiveFinder() { + this.finder = new ReferenceFinder(getFilter(), getVisitor()); + } + + private ReferenceFinder.Filter getFilter() { + final String classname = "sun/reflect/Reflection"; + final String method = "getCallerClass"; + return new ReferenceFinder.Filter() { + public boolean accept(ConstantPool cpool, CPRefInfo cpref) { + try { + CONSTANT_NameAndType_info nat = cpref.getNameAndTypeInfo(); + return cpref.getClassName().equals(classname) && nat.getName().equals(method); + } catch (ConstantPoolException ex) { + throw new RuntimeException(ex); + } + } + }; + } + + private ReferenceFinder.Visitor getVisitor() { + return new ReferenceFinder.Visitor() { + public void visit(ClassFile cf, Method m, List refs) { + try { + String name = String.format("%s#%s %s", cf.getName(), + m.getName(cf.constant_pool), + m.descriptor.getValue(cf.constant_pool)); + if (!CallerSensitiveFinder.isCallerSensitive(m, cf.constant_pool)) { + csMethodsMissingAnnotation.add(name); + System.err.println("Missing @CallerSensitive: " + name); + } else { + if (verbose) { + System.out.format("@CS %s%n", name); + } + } + } catch (ConstantPoolException ex) { + throw new RuntimeException(ex); + } + } + }; } public List run(List classes) throws IOException, InterruptedException, @@ -125,27 +162,12 @@ return false; } - public void referenceFound(ClassFile cf, Method m, Set refs) - throws ConstantPoolException - { - String name = String.format("%s#%s %s", cf.getName(), - m.getName(cf.constant_pool), - m.descriptor.getValue(cf.constant_pool)); - if (!CallerSensitiveFinder.isCallerSensitive(m, cf.constant_pool)) { - csMethodsMissingAnnotation.add(name); - System.err.println("Missing @CallerSensitive: " + name); - } else { - if (verbose) { - System.out.format("@CS %s%n", name); - } - } - } - - private final List> tasks = new ArrayList>(); - private FutureTask getTask(final ClassFile cf) { - FutureTask task = new FutureTask(new Callable() { - public String call() throws Exception { - return parse(cf); + private final List> tasks = new ArrayList>(); + private FutureTask getTask(final ClassFile cf) { + FutureTask task = new FutureTask(new Callable() { + public Void call() throws Exception { + finder.parse(cf); + return null; } }); tasks.add(task); @@ -153,8 +175,8 @@ } private void waitForCompletion() throws InterruptedException, ExecutionException { - for (FutureTask t : tasks) { - String s = t.get(); + for (FutureTask t : tasks) { + t.get(); } System.out.println("Parsed " + tasks.size() + " classfiles"); } diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/sun/reflect/CallerSensitive/MethodFinder.java --- a/jdk/test/sun/reflect/CallerSensitive/MethodFinder.java Wed May 08 14:10:53 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,201 +0,0 @@ -/* - * Copyright (c) 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. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please 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.*; -import com.sun.tools.classfile.*; -import static com.sun.tools.classfile.ConstantPool.*; -import com.sun.tools.classfile.Instruction.TypeKind; - -/** - * MethodFinder utility class to find references to the given methods. - */ -public abstract class MethodFinder { - final List methods; - public MethodFinder(String... methods) { - this.methods = Arrays.asList(methods); - } - - /** - * A callback method will be invoked when a method referencing - * any of the lookup methods. - * - * @param cf ClassFile - * @param m Method - * @param refs Set of constant pool indices that reference the methods - * matching the given lookup method names - */ - public abstract void referenceFound(ClassFile cf, Method m, Set refs) - throws ConstantPoolException; - - public String parse(ClassFile cf) throws ConstantPoolException { - List cprefs = new ArrayList(); - int index = 1; - for (ConstantPool.CPInfo cpInfo : cf.constant_pool.entries()) { - if (cpInfo.accept(cpVisitor, null)) { - cprefs.add(index); - } - index += cpInfo.size(); - } - - if (!cprefs.isEmpty()) { - for (Method m : cf.methods) { - Set refs = new HashSet(); - Code_attribute c_attr = (Code_attribute) m.attributes.get(Attribute.Code); - if (c_attr != null) { - for (Instruction instr : c_attr.getInstructions()) { - int idx = instr.accept(codeVisitor, cprefs); - if (idx > 0) { - refs.add(idx); - } - } - } - if (refs.size() > 0) { - referenceFound(cf, m, refs); - } - } - } - return cprefs.isEmpty() ? "" : cf.getName(); - } - - private ConstantPool.Visitor cpVisitor = - new ConstantPool.Visitor() - { - private boolean matches(CPRefInfo info) { - try { - CONSTANT_NameAndType_info nat = info.getNameAndTypeInfo(); - return matches(info.getClassName(), nat.getName(), nat.getType()); - } catch (ConstantPoolException ex) { - return false; - } - } - - private boolean matches(String cn, String name, String type) { - return methods.contains(cn + "." + name); - } - - public Boolean visitClass(CONSTANT_Class_info info, Void p) { - return false; - } - - public Boolean visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, Void p) { - return matches(info); - } - - public Boolean visitMethodref(CONSTANT_Methodref_info info, Void p) { - return matches(info); - } - - public Boolean visitDouble(CONSTANT_Double_info info, Void p) { - return false; - } - - public Boolean visitFieldref(CONSTANT_Fieldref_info info, Void p) { - return false; - } - - public Boolean visitFloat(CONSTANT_Float_info info, Void p) { - return false; - } - - public Boolean visitInteger(CONSTANT_Integer_info info, Void p) { - return false; - } - - public Boolean visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, Void p) { - return false; - } - - public Boolean visitLong(CONSTANT_Long_info info, Void p) { - return false; - } - - public Boolean visitNameAndType(CONSTANT_NameAndType_info info, Void p) { - return false; - } - - public Boolean visitMethodHandle(CONSTANT_MethodHandle_info info, Void p) { - return false; - } - - public Boolean visitMethodType(CONSTANT_MethodType_info info, Void p) { - return false; - } - - public Boolean visitString(CONSTANT_String_info info, Void p) { - return false; - } - - public Boolean visitUtf8(CONSTANT_Utf8_info info, Void p) { - return false; - } - }; - - private Instruction.KindVisitor> codeVisitor = - new Instruction.KindVisitor>() - { - public Integer visitNoOperands(Instruction instr, List p) { - return 0; - } - - public Integer visitArrayType(Instruction instr, TypeKind kind, List p) { - return 0; - } - - public Integer visitBranch(Instruction instr, int offset, List p) { - return 0; - } - - public Integer visitConstantPoolRef(Instruction instr, int index, List p) { - return p.contains(index) ? index : 0; - } - - public Integer visitConstantPoolRefAndValue(Instruction instr, int index, int value, List p) { - return p.contains(index) ? index : 0; - } - - public Integer visitLocal(Instruction instr, int index, List p) { - return 0; - } - - public Integer visitLocalAndValue(Instruction instr, int index, int value, List p) { - return 0; - } - - public Integer visitLookupSwitch(Instruction instr, int default_, int npairs, int[] matches, int[] offsets, List p) { - return 0; - } - - public Integer visitTableSwitch(Instruction instr, int default_, int low, int high, int[] offsets, List p) { - return 0; - } - - public Integer visitValue(Instruction instr, int value, List p) { - return 0; - } - - public Integer visitUnknown(Instruction instr, List p) { - return 0; - } - }; -} - diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/sun/reflect/CallerSensitive/MissingCallerSensitive.java --- a/jdk/test/sun/reflect/CallerSensitive/MissingCallerSensitive.java Wed May 08 14:10:53 2013 +0200 +++ b/jdk/test/sun/reflect/CallerSensitive/MissingCallerSensitive.java Wed May 08 23:54:45 2013 -0700 @@ -27,7 +27,7 @@ * @bug 8010117 * @summary Test CallerSensitiveFinder to find missing annotation * @compile -XDignore.symbol.file MissingCallerSensitive.java - * @build CallerSensitiveFinder MethodFinder + * @build CallerSensitiveFinder * @run main MissingCallerSensitive */ @@ -40,8 +40,7 @@ List classes = new ArrayList<>(); classes.add(Paths.get(testclasses, "MissingCallerSensitive.class")); - final String method = "sun/reflect/Reflection.getCallerClass"; - CallerSensitiveFinder csfinder = new CallerSensitiveFinder(method); + CallerSensitiveFinder csfinder = new CallerSensitiveFinder(); List errors = csfinder.run(classes); if (errors.size() != 1) { throw new RuntimeException("Unexpected number of methods found: " + errors.size()); diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/sun/security/krb5/auto/SSL.java --- a/jdk/test/sun/security/krb5/auto/SSL.java Wed May 08 14:10:53 2013 +0200 +++ b/jdk/test/sun/security/krb5/auto/SSL.java Wed May 08 23:54:45 2013 -0700 @@ -23,10 +23,11 @@ /* * @test - * @bug 6894643 6913636 + * @bug 6894643 6913636 8005523 * @summary Test JSSE Kerberos ciphersuite + * @run main/othervm SSL TLS_KRB5_WITH_RC4_128_SHA - * @run main/othervm SSL TLS_KRB5_WITH_RC4_128_MD5 + * @run main/othervm SSL TLS_KRB5_WITH_RC4_128_SHA unbound * @run main/othervm SSL TLS_KRB5_WITH_3DES_EDE_CBC_SHA * @run main/othervm SSL TLS_KRB5_WITH_3DES_EDE_CBC_MD5 * @run main/othervm SSL TLS_KRB5_WITH_DES_CBC_SHA @@ -38,14 +39,17 @@ */ import java.io.*; import java.net.InetAddress; +import java.security.AccessControlException; +import java.security.Permission; import javax.net.ssl.*; import java.security.Principal; import java.util.Date; +import javax.security.auth.kerberos.ServicePermission; import sun.security.jgss.GSSUtil; import sun.security.krb5.PrincipalName; import sun.security.krb5.internal.ktab.KeyTab; -public class SSL { +public class SSL extends SecurityManager { private static String krb5Cipher; private static final int LOOP_LIMIT = 3; @@ -53,13 +57,32 @@ private static volatile String server; private static volatile int port; + private static String permChecks = ""; + // 0-Not started, 1-Start OK, 2-Failure private static volatile int serverState = 0; + @Override + public void checkPermission(Permission perm, Object context) { + checkPermission(perm); + } + + public void checkPermission(Permission perm) { + if (!(perm instanceof ServicePermission)) { + return; + } + ServicePermission p = (ServicePermission)perm; + permChecks = permChecks + p.getActions().toUpperCase().charAt(0); + } + public static void main(String[] args) throws Exception { krb5Cipher = args[0]; + boolean unbound = args.length > 1; + + System.setSecurityManager(new SSL()); + KDC kdc = KDC.create(OneKDC.REALM); // Run this after KDC, so our own DNS service can be started try { @@ -85,6 +108,7 @@ // and use the middle one as the real key kdc.addPrincipal("host/" + server, "pass2".toCharArray()); + // JAAS config entry name ssl System.setProperty("java.security.auth.login.config", OneKDC.JAAS_CONF); File f = new File(OneKDC.JAAS_CONF); @@ -92,7 +116,9 @@ fos.write(( "ssl {\n" + " com.sun.security.auth.module.Krb5LoginModule required\n" + - " principal=\"host/" + server + "\"\n" + + (unbound ? + " principal=*\n" : + " principal=\"host/" + server + "\"\n") + " useKeyTab=true\n" + " keyTab=" + OneKDC.KTAB + "\n" + " isInitiator=false\n" + @@ -103,7 +129,6 @@ Context c; final Context s = Context.fromJAAS("ssl"); - // There's no keytab file when server starts. s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); Thread server = new Thread(new Runnable() { @@ -127,21 +152,6 @@ throw new Exception("Server already failed"); } - // Now create the keytab - - /* - // Add 3 versions of keys into keytab - KeyTab ktab = KeyTab.create(OneKDC.KTAB); - PrincipalName service = new PrincipalName( - "host/" + server, PrincipalName.KRB_NT_SRV_HST); - ktab.addEntry(service, "pass1".toCharArray(), 1); - ktab.addEntry(service, "pass2".toCharArray(), 2); - ktab.addEntry(service, "pass3".toCharArray(), 3); - ktab.save(); - - // and use the middle one as the real key - kdc.addPrincipal("host/" + server, "pass2".toCharArray()); - */ c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); c.startAsClient("host/" + server, GSSUtil.GSS_KRB5_MECH_OID); c.doAs(new JsseClientAction(), null); @@ -157,20 +167,22 @@ c.startAsClient("host/" + server, GSSUtil.GSS_KRB5_MECH_OID); c.doAs(new JsseClientAction(), null); - // Revoke the old key - /*Thread.sleep(2000); - ktab = KeyTab.create(OneKDC.KTAB); - ktab.addEntry(service, "pass5".toCharArray(), 5, false); - ktab.save(); - - c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); - c.startAsClient("host/" + server, GSSUtil.GSS_KRB5_MECH_OID); - try { - c.doAs(new JsseClientAction(), null); - throw new Exception("Should fail this time."); - } catch (SSLException e) { - // Correct behavior. - }*/ + // Permission checking check. Please note this is highly + // implementation related. + if (unbound) { + // For unbound, server does not know what name to check. + // Client checks "initiate", then server gets the name + // and checks "accept". Second connection resume. + if (!permChecks.equals("IA")) { + throw new Exception(); + } + } else { + // For bound, JAAS checks "accept" once. Server checks again, + // client then checks "initiate". Second connection resume. + if (!permChecks.equals("AAI")) { + throw new Exception(); + } + } } // Following codes copied from diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/sun/security/krb5/auto/SaslGSS.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/krb5/auto/SaslGSS.java Wed May 08 23:54:45 2013 -0700 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 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. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 8012082 + * @summary SASL: auth-conf negotiated, but unencrypted data is accepted, + * reset to unencrypt + * @compile -XDignore.symbol.file SaslGSS.java + * @run main/othervm SaslGSS + */ + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.AuthorizeCallback; +import javax.security.sasl.RealmCallback; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslServer; +import java.io.IOException; +import java.util.HashMap; +import java.util.Locale; +import org.ietf.jgss.*; +import sun.security.jgss.GSSUtil; + +public class SaslGSS { + + public static void main(String[] args) throws Exception { + + String name = "host." + OneKDC.REALM.toLowerCase(Locale.US); + + new OneKDC(null).writeJAASConf(); + System.setProperty("javax.security.auth.useSubjectCredsOnly", "false"); + + // Client in JGSS so that it can control wrap privacy mode + GSSManager m = GSSManager.getInstance(); + GSSContext sc = m.createContext( + m.createName(OneKDC.SERVER, GSSUtil.NT_GSS_KRB5_PRINCIPAL), + GSSUtil.GSS_KRB5_MECH_OID, + null, + GSSContext.DEFAULT_LIFETIME); + sc.requestMutualAuth(false); + + // Server in SASL + final HashMap props = new HashMap(); + props.put(Sasl.QOP, "auth-conf"); + SaslServer ss = Sasl.createSaslServer("GSSAPI", "server", + name, props, + new CallbackHandler() { + public void handle(Callback[] callbacks) + throws IOException, UnsupportedCallbackException { + for (Callback cb : callbacks) { + if (cb instanceof RealmCallback) { + ((RealmCallback) cb).setText(OneKDC.REALM); + } else if (cb instanceof AuthorizeCallback) { + ((AuthorizeCallback) cb).setAuthorized(true); + } + } + } + }); + + // Handshake + byte[] token = new byte[0]; + token = sc.initSecContext(token, 0, token.length); + token = ss.evaluateResponse(token); + token = sc.unwrap(token, 0, token.length, new MessageProp(0, false)); + token[0] = (byte)(((token[0] & 4) != 0) ? 4 : 2); + token = sc.wrap(token, 0, token.length, new MessageProp(0, false)); + ss.evaluateResponse(token); + + // Talk + // 1. Client sends a auth-int message + byte[] hello = "hello".getBytes(); + MessageProp qop = new MessageProp(0, false); + token = sc.wrap(hello, 0, hello.length, qop); + // 2. Server accepts it anyway + ss.unwrap(token, 0, token.length); + // 3. Server sends a message + token = ss.wrap(hello, 0, hello.length); + // 4. Client accepts, should be auth-conf + sc.unwrap(token, 0, token.length, qop); + if (!qop.getPrivacy()) { + throw new Exception(); + } + } +} diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/sun/security/provider/SecureRandom/StrongSeedReader.java --- a/jdk/test/sun/security/provider/SecureRandom/StrongSeedReader.java Wed May 08 14:10:53 2013 +0200 +++ b/jdk/test/sun/security/provider/SecureRandom/StrongSeedReader.java Wed May 08 23:54:45 2013 -0700 @@ -49,7 +49,7 @@ File file = null; try { - file = new File(System.getProperty("java.io.tmpdir") + + file = new File(System.getProperty("java.io.tmpdir"), "StrongSeedReader.tmpdata"); // write a bunch of 0's to the file. diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/sun/security/ssl/javax/net/ssl/TLSv12/ShortRSAKeyGCM.java diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/sun/security/tools/jarsigner/TimestampCheck.java --- a/jdk/test/sun/security/tools/jarsigner/TimestampCheck.java Wed May 08 14:10:53 2013 +0200 +++ b/jdk/test/sun/security/tools/jarsigner/TimestampCheck.java Wed May 08 23:54:45 2013 -0700 @@ -260,6 +260,8 @@ jarsigner(cmd, 7, false); // tsbad2 jarsigner(cmd, 8, false); // tsbad3 jarsigner(cmd, 9, false); // no cert in timestamp + jarsigner(cmd + " -tsapolicyid 1.2.3.4", 0, true); + jarsigner(cmd + " -tsapolicyid 1.2.3.5", 0, false); } else { // Run as a standalone server System.err.println("Press Enter to quit server"); System.in.read(); diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/sun/security/tools/jarsigner/ts.sh --- a/jdk/test/sun/security/tools/jarsigner/ts.sh Wed May 08 14:10:53 2013 +0200 +++ b/jdk/test/sun/security/tools/jarsigner/ts.sh Wed May 08 23:54:45 2013 -0700 @@ -22,7 +22,7 @@ # # @test -# @bug 6543842 6543440 6939248 +# @bug 6543842 6543440 6939248 8009636 # @summary checking response of timestamp # # @run shell/timeout=600 ts.sh diff -r 241d13ccb08b -r 4ea383c26563 jdk/test/sun/security/tools/keytool/console.sh --- a/jdk/test/sun/security/tools/keytool/console.sh Wed May 08 14:10:53 2013 +0200 +++ b/jdk/test/sun/security/tools/keytool/console.sh Wed May 08 23:54:45 2013 -0700 @@ -1,5 +1,3 @@ -#! /bin/sh - # # Copyright (c) 2006, 2008, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -24,10 +22,11 @@ # # @test -# @bug 6418647 +# @bug 6418647 8005527 # @summary Doc bug 5035358 shows sun.security.util.Password.readPassword() is buggy. # @author Weijun Wang -# +# @ignore unable to test manual tools that have input from stdin, +# and output to stderr and stdout # @run shell/manual console.sh if [ "$ALT_PASS" = "" ]; then diff -r 241d13ccb08b -r 4ea383c26563 langtools/.hgtags --- a/langtools/.hgtags Wed May 08 14:10:53 2013 +0200 +++ b/langtools/.hgtags Wed May 08 23:54:45 2013 -0700 @@ -209,3 +209,4 @@ 4a48f31735349782ad13980267358c97076adc66 jdk8-b85 6ab578e141dfd17c4dc03869bb204aafa490c9f4 jdk8-b86 1329f9c38d93c8caf339d7687df8371d06fe9e56 jdk8-b87 +a1e10f3adc47c8602a72e43a41403a642e73e0b1 jdk8-b88 diff -r 241d13ccb08b -r 4ea383c26563 nashorn/.hgtags --- a/nashorn/.hgtags Wed May 08 14:10:53 2013 +0200 +++ b/nashorn/.hgtags Wed May 08 23:54:45 2013 -0700 @@ -197,3 +197,4 @@ e0378f0a50dafdcfb7b04f6401d320f89884baa1 jdk8-b85 002ad9d6735f36d1204e133324c73058c8abb1b0 jdk8-b86 774aeaa89bc15f4365e3c2fc36f6a3a0da70ba28 jdk8-b87 +40c107d1ae6f81a62e35dfe618b827897405e9b2 jdk8-b88 diff -r 241d13ccb08b -r 4ea383c26563 nashorn/bin/verbose_octane.sh --- a/nashorn/bin/verbose_octane.sh Wed May 08 14:10:53 2013 +0200 +++ b/nashorn/bin/verbose_octane.sh Wed May 08 23:54:45 2013 -0700 @@ -26,7 +26,7 @@ ITERS=7 fi NASHORN_JAR=dist/nashorn.jar -JVM_FLAGS="-XX:+UnlockDiagnosticVMOptions -Dnashorn.unstable.relink.threshold=8 -Xms2G -Xmx2G -XX:+TieredCompilation -server -jar ${NASHORN_JAR}" +JVM_FLAGS="-Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+UnlockDiagnosticVMOptions -Dnashorn.unstable.relink.threshold=8 -Xms2G -Xmx2G -XX:+TieredCompilation -server -jar ${NASHORN_JAR}" JVM_FLAGS7="-Xbootclasspath/p:${NASHORN_JAR} ${JVM_FLAGS}" OCTANE_ARGS="--verbose --iterations ${ITERS}" diff -r 241d13ccb08b -r 4ea383c26563 nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java --- a/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java Wed May 08 14:10:53 2013 +0200 +++ b/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java Wed May 08 23:54:45 2013 -0700 @@ -38,7 +38,6 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_FIELD_NAME; import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_TYPE; import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC; -import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPE; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_TYPE; @@ -47,6 +46,8 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC; +import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETPROTOTYPE; +import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETPROTOTYPE_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_INIT_DESC; import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_TYPE; @@ -238,7 +239,7 @@ mi.loadThis(); mi.invokeStatic(PROTOTYPEOBJECT_TYPE, PROTOTYPEOBJECT_SETCONSTRUCTOR, PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC); - mi.putField(SCRIPTFUNCTION_TYPE, PROTOTYPE, OBJECT_DESC); + mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETPROTOTYPE, SCRIPTFUNCTION_SETPROTOTYPE_DESC); } } diff -r 241d13ccb08b -r 4ea383c26563 nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java --- a/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java Wed May 08 14:10:53 2013 +0200 +++ b/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java Wed May 08 23:54:45 2013 -0700 @@ -55,7 +55,6 @@ static final Type TYPE_SCRIPTFUNCTIONIMPL = Type.getType(ScriptFunctionImpl.class); static final Type TYPE_SCRIPTOBJECT = Type.getType(ScriptObject.class); - static final String PROTOTYPE = "prototype"; static final String PROTOTYPE_SUFFIX = "$Prototype"; static final String CONSTRUCTOR_SUFFIX = "$Constructor"; // This field name is known to Nashorn runtime (Context). @@ -88,6 +87,8 @@ Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_PROPERTYMAP, TYPE_METHODHANDLE_ARRAY); static final String SCRIPTFUNCTION_SETARITY = "setArity"; static final String SCRIPTFUNCTION_SETARITY_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE); + static final String SCRIPTFUNCTION_SETPROTOTYPE = "setPrototype"; + static final String SCRIPTFUNCTION_SETPROTOTYPE_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_OBJECT); static final String PROTOTYPEOBJECT_TYPE = TYPE_PROTOTYPEOBJECT.getInternalName(); static final String PROTOTYPEOBJECT_SETCONSTRUCTOR = "setConstructor"; static final String PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_OBJECT, TYPE_OBJECT); diff -r 241d13ccb08b -r 4ea383c26563 nashorn/make/build-nasgen.xml --- a/nashorn/make/build-nasgen.xml Wed May 08 14:10:53 2013 +0200 +++ b/nashorn/make/build-nasgen.xml Wed May 08 23:54:45 2013 -0700 @@ -37,6 +37,7 @@ + diff -r 241d13ccb08b -r 4ea383c26563 nashorn/make/project.properties --- a/nashorn/make/project.properties Wed May 08 14:10:53 2013 +0200 +++ b/nashorn/make/project.properties Wed May 08 23:54:45 2013 -0700 @@ -214,7 +214,7 @@ # -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMethods # add '-Dtest.js.outofprocess' to run each test in a new sub-process -run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:-TieredCompilation -esa -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8 +run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:-TieredCompilation -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8 #-XX:+HeapDumpOnOutOfMemoryError -XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M run.test.jvmargs.octane.main=-Xms${run.test.xms} ${run.test.jvmargs.main} diff -r 241d13ccb08b -r 4ea383c26563 nashorn/src/jdk/internal/dynalink/beans/StaticClassIntrospector.java --- a/nashorn/src/jdk/internal/dynalink/beans/StaticClassIntrospector.java Wed May 08 14:10:53 2013 +0200 +++ b/nashorn/src/jdk/internal/dynalink/beans/StaticClassIntrospector.java Wed May 08 23:54:45 2013 -0700 @@ -106,7 +106,11 @@ @Override MethodHandle editMethodHandle(MethodHandle mh) { - MethodHandle newHandle = MethodHandles.dropArguments(mh, 0, Object.class); + return dropReceiver(mh, Object.class); + } + + static MethodHandle dropReceiver(final MethodHandle mh, final Class receiverClass) { + MethodHandle newHandle = MethodHandles.dropArguments(mh, 0, receiverClass); // NOTE: this is a workaround for the fact that dropArguments doesn't preserve vararg collector state. if(mh.isVarargsCollector() && !newHandle.isVarargsCollector()) { final MethodType type = mh.type(); diff -r 241d13ccb08b -r 4ea383c26563 nashorn/src/jdk/internal/dynalink/beans/StaticClassLinker.java --- a/nashorn/src/jdk/internal/dynalink/beans/StaticClassLinker.java Wed May 08 14:10:53 2013 +0200 +++ b/nashorn/src/jdk/internal/dynalink/beans/StaticClassLinker.java Wed May 08 23:54:45 2013 -0700 @@ -144,7 +144,7 @@ } private static MethodHandle drop(MethodHandle mh) { - return MethodHandles.dropArguments(mh, 0, StaticClass.class); + return StaticClassIntrospector.dropReceiver(mh, StaticClass.class); } @Override diff -r 241d13ccb08b -r 4ea383c26563 nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java --- a/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Wed May 08 14:10:53 2013 +0200 +++ b/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Wed May 08 23:54:45 2013 -0700 @@ -397,10 +397,7 @@ } setContextVariables(ctxt); - final Object val = ctxt.getAttribute(ScriptEngine.FILENAME); - final String fileName = (val != null) ? val.toString() : ""; - Object res = ScriptRuntime.apply(script, ctxtGlobal); - return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(res, ctxtGlobal)); + return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal)); } catch (final Exception e) { throwAsScriptException(e); throw new AssertionError("should not reach here"); diff -r 241d13ccb08b -r 4ea383c26563 nashorn/src/jdk/nashorn/internal/codegen/Attr.java --- a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java Wed May 08 14:10:53 2013 +0200 +++ b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java Wed May 08 23:54:45 2013 -0700 @@ -25,14 +25,16 @@ package jdk.nashorn.internal.codegen; +import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS; import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE; import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX; +import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN; import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; -import static jdk.nashorn.internal.codegen.CompilerConstants.SCRIPT_RETURN; import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; +import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF; import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL; import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; import static jdk.nashorn.internal.ir.Symbol.IS_LET; @@ -42,18 +44,18 @@ import static jdk.nashorn.internal.ir.Symbol.IS_VAR; import static jdk.nashorn.internal.ir.Symbol.KINDMASK; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Deque; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.ListIterator; import java.util.Set; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.AccessNode; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.CallNode; -import jdk.nashorn.internal.ir.CallNode.EvalArgs; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.ForNode; @@ -62,6 +64,7 @@ import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.LexicalContext; +import jdk.nashorn.internal.ir.LexicalContextNode; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.Node; @@ -76,6 +79,7 @@ import jdk.nashorn.internal.ir.TryNode; import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; +import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.TokenType; @@ -105,21 +109,22 @@ */ final class Attr extends NodeOperatorVisitor { + /** * Local definitions in current block (to discriminate from function * declarations always defined in the function scope. This is for * "can be undefined" analysis. */ - private Set localDefs; + private final Deque> localDefs; /** * Local definitions in current block to guard against cases like * NASHORN-467 when things can be undefined as they are used before * their local var definition. *sigh* JavaScript... */ - private Set localUses; + private final Deque> localUses; - private final LexicalContext lexicalContext = new LexicalContext(); + private final Deque returnTypes; private static final DebugLogger LOG = new DebugLogger("attr"); private static final boolean DEBUG = LOG.isEnabled(); @@ -128,10 +133,13 @@ * Constructor. */ Attr() { + localDefs = new ArrayDeque<>(); + localUses = new ArrayDeque<>(); + returnTypes = new ArrayDeque<>(); } @Override - protected Node enterDefault(final Node node) { + protected boolean enterDefault(final Node node) { return start(node); } @@ -142,217 +150,47 @@ @Override public Node leaveAccessNode(final AccessNode accessNode) { - newTemporary(Type.OBJECT, accessNode); //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this + ensureSymbol(Type.OBJECT, accessNode); //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this end(accessNode); return accessNode; } - @Override - public Node enterBlock(final Block block) { - lexicalContext.push(block); - start(block); - - final Set savedLocalDefs = localDefs; - final Set savedLocalUses = localUses; - - block.setFrame(getCurrentFunctionNode().pushFrame()); - - try { - // a block starts out by copying the local defs and local uses - // from the outer level. But we need the copies, as when we - // leave the block the def and use sets given upon entry must - // be restored - localDefs = new HashSet<>(savedLocalDefs); - localUses = new HashSet<>(savedLocalUses); - - block.visitStatements(this); - } finally { - localDefs = savedLocalDefs; - localUses = savedLocalUses; - - getCurrentFunctionNode().popFrame(); - } - - end(block); - - lexicalContext.pop(block); - return null; - } - - @Override - public Node enterCallNode(final CallNode callNode) { - start(callNode); - - callNode.getFunction().accept(this); - - final List acceptedArgs = new ArrayList<>(callNode.getArgs().size()); - for (final Node arg : callNode.getArgs()) { - LOG.info("Doing call arg " + arg); - acceptedArgs.add(arg.accept(this)); - } - callNode.setArgs(acceptedArgs); + private void enterFunctionBody() { - final EvalArgs evalArgs = callNode.getEvalArgs(); - if (evalArgs != null) { - evalArgs.setCode(evalArgs.getCode().accept(this)); - - final IdentNode thisNode = new IdentNode(getCurrentFunctionNode().getThisNode()); - assert thisNode.getSymbol() != null; //should copy attributed symbol and that's it - evalArgs.setThis(thisNode); - } - - newTemporary(callNode.getType(), callNode); // access specialization in FinalizeTypes may narrow it further later - - end(callNode); - - return null; - } - - @Override - public Node enterCatchNode(final CatchNode catchNode) { - final IdentNode exception = catchNode.getException(); - final Block block = getCurrentBlock(); - - start(catchNode); - - // define block-local exception variable - final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET, exception); - newType(def, Type.OBJECT); - addLocalDef(exception.getName()); - - return catchNode; - } - - /** - * Declare the definition of a new symbol. - * - * @param name Name of symbol. - * @param symbolFlags Symbol flags. - * @param node Defining Node. - * - * @return Symbol for given name or null for redefinition. - */ - private Symbol defineSymbol(final Block block, final String name, final int symbolFlags, final Node node) { - int flags = symbolFlags; - Symbol symbol = findSymbol(block, name); // Locate symbol. - - if ((flags & KINDMASK) == IS_GLOBAL) { - flags |= IS_SCOPE; + final FunctionNode functionNode = getLexicalContext().getCurrentFunction(); + final Block body = getLexicalContext().getCurrentBlock(); + initCallee(body); + initThis(body); + if (functionNode.isVarArg()) { + initVarArg(body, functionNode.needsArguments()); } - final FunctionNode function = lexicalContext.getFunction(block); - if (symbol != null) { - // Symbol was already defined. Check if it needs to be redefined. - if ((flags & KINDMASK) == IS_PARAM) { - if (!isLocal(function, symbol)) { - // Not defined in this function. Create a new definition. - symbol = null; - } else if (symbol.isParam()) { - // Duplicate parameter. Null return will force an error. - assert false : "duplicate parameter"; - return null; - } - } else if ((flags & KINDMASK) == IS_VAR) { - if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) { - assert !((flags & IS_LET) == IS_LET && symbol.getBlock() == block) : "duplicate let variable in block"; - // Always create a new definition. - symbol = null; - } else { - // Not defined in this function. Create a new definition. - if (!isLocal(function, symbol) || symbol.less(IS_VAR)) { - symbol = null; - } - } - } - } - - if (symbol == null) { - // If not found, then create a new one. - Block symbolBlock; - - // Determine where to create it. - if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) { - symbolBlock = block; - } else { - symbolBlock = function; - } - - // Create and add to appropriate block. - symbol = new Symbol(name, flags, node, symbolBlock); - symbolBlock.putSymbol(name, symbol); - - if ((flags & Symbol.KINDMASK) != IS_GLOBAL) { - symbolBlock.getFrame().addSymbol(symbol); - symbol.setNeedsSlot(true); - } - } else if (symbol.less(flags)) { - symbol.setFlags(flags); - } - - if (node != null) { - node.setSymbol(symbol); - } - - return symbol; - } - - @Override - public Node enterFunctionNode(final FunctionNode functionNode) { - start(functionNode, false); - if (functionNode.isLazy()) { - LOG.info("LAZY: " + functionNode.getName() + " => Promoting to OBJECT"); - newTemporary(lexicalContext.getCurrentFunction(), Type.OBJECT, functionNode); - functionNode.setReturnType(Type.OBJECT); - end(functionNode); - return null; - } - - lexicalContext.push(functionNode); - - clearLocalDefs(); - clearLocalUses(); - - functionNode.setFrame(functionNode.pushFrame()); - - initCallee(functionNode); - initThis(functionNode); - if (functionNode.isVarArg()) { - initVarArg(functionNode); - } - - initParameters(functionNode); - initScope(functionNode); - initReturn(functionNode); - - // Add all nested declared functions as symbols in this function - for (final FunctionNode nestedFunction : functionNode.getDeclaredFunctions()) { - final IdentNode ident = nestedFunction.getIdent(); - if (ident != null) { - assert nestedFunction.isDeclared(); - final Symbol functionSymbol = defineSymbol(functionNode, ident.getName(), IS_VAR, nestedFunction); - newType(functionSymbol, Type.typeFor(ScriptFunction.class)); - } - } + initParameters(functionNode, body); + initScope(body); + initReturn(body); if (functionNode.isProgram()) { - initFromPropertyMap(functionNode); - } + initFromPropertyMap(body); + } else if(!functionNode.isDeclared()) { + // It's neither declared nor program - it's a function expression then; assign it a self-symbol. - // Add function name as local symbol - if (!functionNode.isDeclared() && !functionNode.isProgram()) { - if(functionNode.getSymbol() != null) { + if (functionNode.getSymbol() != null) { // a temporary left over from an earlier pass when the function was lazy assert functionNode.getSymbol().isTemp(); // remove it functionNode.setSymbol(null); } - final Symbol selfSymbol; - if(functionNode.isAnonymous()) { - selfSymbol = newTemporary(functionNode, Type.OBJECT, functionNode); + final boolean anonymous = functionNode.isAnonymous(); + final String name = anonymous ? null : functionNode.getIdent().getName(); + if (anonymous || body.getExistingSymbol(name) != null) { + // The function is either anonymous, or another local identifier already trumps its name on entry: + // either it has the same name as one of its parameters, or is named "arguments" and also references the + // "arguments" identifier in its body. + ensureSymbol(functionNode, Type.typeFor(ScriptFunction.class), functionNode); } else { - selfSymbol = defineSymbol(functionNode, functionNode.getIdent().getName(), IS_VAR, functionNode); + final Symbol selfSymbol = defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF, functionNode); + assert selfSymbol.isFunctionSelf(); newType(selfSymbol, Type.OBJECT); - selfSymbol.setNode(functionNode); } } @@ -373,73 +211,243 @@ * @see NASHORN-73 */ - final List declaredSymbols = new ArrayList<>(); // This visitor will assign symbol to all declared variables, except function declarations (which are taken care // in a separate step above) and "var" declarations in for loop initializers. - functionNode.accept(new NodeOperatorVisitor() { + body.accept(new NodeOperatorVisitor() { @Override - public Node enterFunctionNode(FunctionNode nestedFn) { - // Don't descend into nested functions - return nestedFn == functionNode ? nestedFn : null; + public boolean enterFunctionNode(final FunctionNode nestedFn) { + return false; } + @Override - public Node enterVarNode(VarNode varNode) { - if(varNode.isStatement() && !varNode.isFunctionDeclaration()) { + public boolean enterVarNode(final VarNode varNode) { + + // any declared symbols that aren't visited need to be typed as well, hence the list + + if (varNode.isStatement()) { + final IdentNode ident = varNode.getName(); - // any declared symbols that aren't visited need to be typed as well, hence the list - declaredSymbols.add(defineSymbol(functionNode, ident.getName(), IS_VAR, new IdentNode(ident))); + final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR, new IdentNode(ident)); + functionNode.addDeclaredSymbol(symbol); + if (varNode.isFunctionDeclaration()) { + newType(symbol, FunctionNode.FUNCTION_TYPE); + } } - return null; + return false; } }); + } - visitFunctionStatements(functionNode); + @Override + public boolean enterBlock(final Block block) { + start(block); + + if (getLexicalContext().isFunctionBody()) { + enterFunctionBody(); + } + pushLocalsBlock(); + + return true; + } + + @Override + public Node leaveBlock(final Block block) { + popLocals(); + return end(block); + } + + @Override + public Node leaveCallNode(final CallNode callNode) { + ensureSymbol(callNode.getType(), callNode); + return end(callNode); + } + + @Override + public boolean enterCallNode(final CallNode callNode) { + return start(callNode); + } + + @Override + public boolean enterCatchNode(final CatchNode catchNode) { + final IdentNode exception = catchNode.getException(); + final Block block = getLexicalContext().getCurrentBlock(); + + start(catchNode); + + // define block-local exception variable + final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET, exception); + newType(def, Type.OBJECT); + addLocalDef(exception.getName()); + + return true; + } + + /** + * Declare the definition of a new symbol. + * + * @param name Name of symbol. + * @param symbolFlags Symbol flags. + * @param node Defining Node. + * + * @return Symbol for given name or null for redefinition. + */ + private Symbol defineSymbol(final Block block, final String name, final int symbolFlags, final Node node) { + int flags = symbolFlags; + Symbol symbol = findSymbol(block, name); // Locate symbol. + + if ((flags & KINDMASK) == IS_GLOBAL) { + flags |= IS_SCOPE; + } + + final FunctionNode function = getLexicalContext().getFunction(block); + if (symbol != null) { + // Symbol was already defined. Check if it needs to be redefined. + if ((flags & KINDMASK) == IS_PARAM) { + if (!isLocal(function, symbol)) { + // Not defined in this function. Create a new definition. + symbol = null; + } else if (symbol.isParam()) { + // Duplicate parameter. Null return will force an error. + assert false : "duplicate parameter"; + return null; + } + } else if ((flags & KINDMASK) == IS_VAR) { + if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) { + // Always create a new definition. + symbol = null; + } else { + // Not defined in this function. Create a new definition. + if (!isLocal(function, symbol) || symbol.less(IS_VAR)) { + symbol = null; + } + } + } + } + + if (symbol == null) { + // If not found, then create a new one. + Block symbolBlock; + + // Determine where to create it. + if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) { + symbolBlock = block; //internal vars are always defined in the block closest to them + } else { + symbolBlock = getLexicalContext().getFunctionBody(function); + } + + // Create and add to appropriate block. + symbol = new Symbol(name, flags); + symbolBlock.putSymbol(name, symbol); + + if ((flags & Symbol.KINDMASK) != IS_GLOBAL) { + symbol.setNeedsSlot(true); + } + } else if (symbol.less(flags)) { + symbol.setFlags(flags); + } + + if (node != null) { + node.setSymbol(symbol); + } + + return symbol; + } + + @Override + public boolean enterFunctionNode(final FunctionNode functionNode) { + start(functionNode, false); + + if (functionNode.isDeclared()) { + final Iterator blocks = getLexicalContext().getBlocks(); + if (blocks.hasNext()) { + defineSymbol( + blocks.next(), + functionNode.getIdent().getName(), + IS_VAR, + functionNode); + } else { + // Q: What's an outermost function in a lexical context that is not a program? + // A: It's a function being compiled lazily! + assert getLexicalContext().getOutermostFunction() == functionNode && !functionNode.isProgram(); + } + } + + if (functionNode.isLazy()) { + LOG.info("LAZY: ", functionNode.getName(), " => Promoting to OBJECT"); + ensureSymbol(getLexicalContext().getCurrentFunction(), Type.OBJECT, functionNode); + end(functionNode); + return false; + } + + returnTypes.push(functionNode.getReturnType()); + pushLocalsFunction(); + return true; + } + + @Override + public Node leaveFunctionNode(final FunctionNode functionNode) { + FunctionNode newFunctionNode = functionNode; + + final LexicalContext lc = getLexicalContext(); //unknown parameters are promoted to object type. - finalizeParameters(functionNode); - finalizeTypes(functionNode); - for (final Symbol symbol : declaredSymbols) { + finalizeParameters(newFunctionNode); + finalizeTypes(newFunctionNode); + for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) { if (symbol.getSymbolType().isUnknown()) { symbol.setType(Type.OBJECT); symbol.setCanBeUndefined(); } } - if (functionNode.getReturnType().isUnknown()) { - LOG.info("Unknown return type promoted to object"); - functionNode.setReturnType(Type.OBJECT); - } + final Block body = newFunctionNode.getBody(); - if (functionNode.getSelfSymbolInit() != null) { - LOG.info("Accepting self symbol init " + functionNode.getSelfSymbolInit() + " for " + functionNode.getName()); - final Node init = functionNode.getSelfSymbolInit(); - final List newStatements = new ArrayList<>(); - newStatements.add(init); - newStatements.addAll(functionNode.getStatements()); - functionNode.setStatements(newStatements); - functionNode.setNeedsSelfSymbol(functionNode.getSelfSymbolInit().accept(this)); + if (newFunctionNode.hasLazyChildren()) { + //the final body has already been assigned as we have left the function node block body by now + objectifySymbols(body); } - if (functionNode.hasLazyChildren()) { - objectifySymbols(functionNode); + if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) { + final IdentNode callee = compilerConstant(CALLEE); + final VarNode selfInit = + new VarNode( + newFunctionNode.getSource(), + newFunctionNode.getToken(), + newFunctionNode.getFinish(), + newFunctionNode.getIdent(), + callee); + + LOG.info("Accepting self symbol init ", selfInit, " for ", newFunctionNode.getName()); + + final List newStatements = new ArrayList<>(); + newStatements.add(selfInit); + assert callee.getSymbol() != null && callee.getSymbol().hasSlot(); + + final IdentNode name = selfInit.getName(); + final Symbol nameSymbol = body.getExistingSymbol(name.getName()); + + assert nameSymbol != null; + + name.setSymbol(nameSymbol); + selfInit.setSymbol(nameSymbol); + + newStatements.addAll(body.getStatements()); + newFunctionNode = newFunctionNode.setBody(lc, body.setStatements(lc, newStatements)); } - functionNode.popFrame(); - - functionNode.setState(CompilationState.ATTR); - - end(functionNode, false); - lexicalContext.pop(functionNode); + if (returnTypes.peek().isUnknown()) { + LOG.info("Unknown return type promoted to object"); + newFunctionNode = newFunctionNode.setReturnType(lc, Type.OBJECT); + } + final Type returnType = returnTypes.pop(); + newFunctionNode = newFunctionNode.setReturnType(lc, returnType.isUnknown() ? Type.OBJECT : returnType); + newFunctionNode = newFunctionNode.setState(lc, CompilationState.ATTR); - return null; - } + popLocals(); - private void visitFunctionStatements(final FunctionNode functionNode) { - final List newStatements = new ArrayList<>(functionNode.getStatements()); - for(ListIterator stmts = newStatements.listIterator(); stmts.hasNext();) { - stmts.set(stmts.next().accept(this)); - } - functionNode.setStatements(newStatements); + end(newFunctionNode, false); + + return newFunctionNode; //.setFlag(lc, lc.getFlags(functionNode)); } @Override @@ -450,7 +458,7 @@ } @Override - public Node enterIdentNode(final IdentNode identNode) { + public boolean enterIdentNode(final IdentNode identNode) { final String name = identNode.getName(); start(identNode); @@ -458,31 +466,28 @@ if (identNode.isPropertyName()) { // assign a pseudo symbol to property name final Symbol pseudoSymbol = pseudoSymbol(name); - LOG.info("IdentNode is property name -> assigning pseudo symbol " + pseudoSymbol); + LOG.info("IdentNode is property name -> assigning pseudo symbol ", pseudoSymbol); LOG.unindent(); identNode.setSymbol(pseudoSymbol); - return null; + return false; } - final Block block = getCurrentBlock(); - final Symbol oldSymbol = identNode.getSymbol(); + final LexicalContext lc = getLexicalContext(); + final Block block = lc.getCurrentBlock(); + final Symbol oldSymbol = identNode.getSymbol(); Symbol symbol = findSymbol(block, name); //If an existing symbol with the name is found, use that otherwise, declare a new one if (symbol != null) { - LOG.info("Existing symbol = " + symbol); - if (isFunctionExpressionSelfReference(symbol)) { - final FunctionNode functionNode = (FunctionNode)symbol.getNode(); - assert functionNode.getCalleeNode() != null; - - final VarNode var = new VarNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), functionNode.getIdent(), functionNode.getCalleeNode()); - //newTemporary(Type.OBJECT, var); //ScriptFunction? TODO - - functionNode.setNeedsSelfSymbol(var); - } - - if (!identNode.isInitializedHere()) { // NASHORN-448 + LOG.info("Existing symbol = ", symbol); + if (symbol.isFunctionSelf()) { + final FunctionNode functionNode = lc.getDefiningFunction(symbol); + assert functionNode != null; + assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null; + lc.setFlag(functionNode.getBody(), Block.NEEDS_SELF_SYMBOL); + newType(symbol, FunctionNode.FUNCTION_TYPE); + } else if (!identNode.isInitializedHere()) { // NASHORN-448 // here is a use outside the local def scope if (!isLocalDef(name)) { newType(symbol, Type.OBJECT); @@ -491,26 +496,18 @@ } identNode.setSymbol(symbol); - // non-local: we need to put symbol in scope (if it isn't already) - if (!isLocal(getCurrentFunctionNode(), symbol) && !symbol.isScope()) { - symbol.setIsScope(); - } + // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already) + maybeForceScope(symbol); } else { - LOG.info("No symbol exists. Declare undefined: " + symbol); - symbol = useSymbol(block, name, identNode); + LOG.info("No symbol exists. Declare undefined: ", symbol); + symbol = defineSymbol(block, name, IS_GLOBAL, identNode); // we have never seen this before, it can be undefined newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway? symbol.setCanBeUndefined(); - symbol.setIsScope(); + Symbol.setSymbolIsScope(lc, symbol); } - assert symbol != null; - if(symbol.isGlobal()) { - setUsesGlobalSymbol(); - } else if(symbol.isScope()) { - final Iterator blocks = lexicalContext.getBlocks(); - blocks.next().setUsesScopeSymbol(symbol, blocks); - } + setBlockScope(name, symbol); if (symbol != oldSymbol && !identNode.isInitializedHere()) { symbol.increaseUseCount(); @@ -519,7 +516,81 @@ end(identNode); - return null; + return false; + } + + /** + * If the symbol isn't already a scope symbol, and it is either not local to the current function, or it is being + * referenced from within a with block, we force it to be a scope symbol. + * @param symbol the symbol that might be scoped + */ + private void maybeForceScope(final Symbol symbol) { + if(!symbol.isScope() && symbolNeedsToBeScope(symbol)) { + Symbol.setSymbolIsScope(getLexicalContext(), symbol); + } + } + + private boolean symbolNeedsToBeScope(Symbol symbol) { + if(symbol.isThis() || symbol.isInternal()) { + return false; + } + boolean previousWasBlock = false; + for(final Iterator it = getLexicalContext().getAllNodes(); it.hasNext();) { + final LexicalContextNode node = it.next(); + if(node instanceof FunctionNode) { + // We reached the function boundary without seeing a definition for the symbol - it needs to be in + // scope. + return true; + } else if(node instanceof WithNode) { + if(previousWasBlock) { + // We reached a WithNode; the symbol must be scoped. Note that if the WithNode was not immediately + // preceded by a block, this means we're currently processing its expression, not its body, + // therefore it doesn't count. + return true; + } + previousWasBlock = false; + } else if(node instanceof Block) { + if(((Block)node).getExistingSymbol(symbol.getName()) == symbol) { + // We reached the block that defines the symbol without reaching either the function boundary, or a + // WithNode. The symbol need not be scoped. + return false; + } + previousWasBlock = true; + } else { + previousWasBlock = false; + } + } + throw new AssertionError(); + } + + private void setBlockScope(final String name, final Symbol symbol) { + assert symbol != null; + if (symbol.isGlobal()) { + setUsesGlobalSymbol(); + return; + } + + if (symbol.isScope()) { + final LexicalContext lc = getLexicalContext(); + + Block scopeBlock = null; + for (final Iterator contextNodeIter = getLexicalContext().getAllNodes(); contextNodeIter.hasNext(); ) { + final LexicalContextNode node = contextNodeIter.next(); + if (node instanceof Block) { + if (((Block)node).getExistingSymbol(name) != null) { + scopeBlock = (Block)node; + break; + } + } else if (node instanceof FunctionNode) { + lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE); + } + } + + if (scopeBlock != null) { + assert getLexicalContext().contains(scopeBlock); + lc.setFlag(scopeBlock, Block.NEEDS_SCOPE); + } + } } /** @@ -528,35 +599,12 @@ * @see #needsParentScope() */ private void setUsesGlobalSymbol() { - for(final Iterator fns = lexicalContext.getFunctions(); fns.hasNext();) { - fns.next().setUsesAncestorScope(); + for (final Iterator fns = getLexicalContext().getFunctions(); fns.hasNext();) { + getLexicalContext().setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE); } } /** - * Declare the use of a symbol in a block. - * - * @param block block in which the symbol is used - * @param name Name of symbol. - * @param node Using node - * - * @return Symbol for given name. - */ - private Symbol useSymbol(final Block block, final String name, final Node node) { - Symbol symbol = findSymbol(block, name); - - if (symbol == null) { - // If not found, declare as a free var. - symbol = defineSymbol(block, name, IS_GLOBAL, node); - } else { - node.setSymbol(symbol); - } - - return symbol; - } - - - /** * Search for symbol in the lexical context starting from the given block. * @param name Symbol name. * @return Found symbol or null if not found. @@ -564,7 +612,7 @@ private Symbol findSymbol(final Block block, final String name) { // Search up block chain to locate symbol. - for(final Iterator blocks = lexicalContext.getBlocks(block); blocks.hasNext();) { + for(final Iterator blocks = getLexicalContext().getBlocks(block); blocks.hasNext();) { // Find name. final Symbol symbol = blocks.next().getExistingSymbol(name); // If found then we are good. @@ -577,13 +625,13 @@ @Override public Node leaveIndexNode(final IndexNode indexNode) { - newTemporary(Type.OBJECT, indexNode); //TODO + ensureSymbol(Type.OBJECT, indexNode); //TODO return indexNode; } @SuppressWarnings("rawtypes") @Override - public Node enterLiteralNode(final LiteralNode literalNode) { + public boolean enterLiteralNode(final LiteralNode literalNode) { try { start(literalNode); assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens @@ -604,26 +652,33 @@ assert !(literalNode.getValue() instanceof Node) : "literals with Node values not supported"; } - getCurrentFunctionNode().newLiteral(literalNode); + getLexicalContext().getCurrentFunction().newLiteral(literalNode); } finally { end(literalNode); } - return null; + + return false; + } + + @Override + public boolean enterObjectNode(final ObjectNode objectNode) { + return start(objectNode); } @Override public Node leaveObjectNode(final ObjectNode objectNode) { - newTemporary(Type.OBJECT, objectNode); - end(objectNode); - return objectNode; + ensureSymbol(Type.OBJECT, objectNode); + return end(objectNode); } + //TODO is this correct why not leave? @Override - public Node enterPropertyNode(final PropertyNode propertyNode) { + public boolean enterPropertyNode(final PropertyNode propertyNode) { // assign a pseudo symbol to property name, see NASHORN-710 + start(propertyNode); propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT)); end(propertyNode); - return propertyNode; + return true; } @Override @@ -636,8 +691,10 @@ if (expr.getType().isUnknown() && symbol.isParam()) { symbol.setType(Type.OBJECT); } - getCurrentFunctionNode().setReturnType(Type.widest(getCurrentFunctionNode().getReturnType(), symbol.getSymbolType())); - LOG.info("Returntype is now " + getCurrentFunctionNode().getReturnType()); + + final Type returnType = Type.widest(returnTypes.pop(), symbol.getSymbolType()); + returnTypes.push(returnType); + LOG.info("Returntype is now ", returnType); } end(returnNode); @@ -649,25 +706,29 @@ public Node leaveSwitchNode(final SwitchNode switchNode) { Type type = Type.UNKNOWN; + final List newCases = new ArrayList<>(); for (final CaseNode caseNode : switchNode.getCases()) { final Node test = caseNode.getTest(); + + CaseNode newCaseNode = caseNode; if (test != null) { if (test instanceof LiteralNode) { //go down to integers if we can final LiteralNode lit = (LiteralNode)test; if (lit.isNumeric() && !(lit.getValue() instanceof Integer)) { if (JSType.isRepresentableAsInt(lit.getNumber())) { - caseNode.setTest(LiteralNode.newInstance(lit, lit.getInt32()).accept(this)); + newCaseNode = caseNode.setTest(LiteralNode.newInstance(lit, lit.getInt32()).accept(this)); } } } else { // the "all integer" case that CodeGenerator optimizes for currently assumes literals only type = Type.OBJECT; - break; } - type = Type.widest(type, caseNode.getTest().getType()); + type = Type.widest(type, newCaseNode.getTest().getType()); } + + newCases.add(newCaseNode); } //only optimize for all integers @@ -675,11 +736,11 @@ type = Type.OBJECT; } - switchNode.setTag(newInternal(getCurrentFunctionNode().uniqueName(SWITCH_TAG_PREFIX.tag()), type)); + switchNode.setTag(newInternal(getLexicalContext().getCurrentFunction().uniqueName(SWITCH_TAG_PREFIX.symbolName()), type)); end(switchNode); - return switchNode; + return switchNode.setCases(getLexicalContext(), newCases); } @Override @@ -696,25 +757,25 @@ } @Override - public Node enterVarNode(final VarNode varNode) { + public boolean enterVarNode(final VarNode varNode) { start(varNode); final IdentNode ident = varNode.getName(); final String name = ident.getName(); - final Symbol symbol = defineSymbol(getCurrentBlock(), name, IS_VAR, ident); + final Symbol symbol = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR, ident); assert symbol != null; - LOG.info("VarNode " + varNode + " set symbol " + symbol); + LOG.info("VarNode ", varNode, " set symbol ", symbol); varNode.setSymbol(symbol); // NASHORN-467 - use before definition of vars - conservative - if (localUses.contains(ident.getName())) { + if (isLocalUse(ident.getName())) { newType(symbol, Type.OBJECT); symbol.setCanBeUndefined(); } - return varNode; + return true; } @Override @@ -734,7 +795,7 @@ addLocalDef(name); final Symbol symbol = varNode.getSymbol(); - final boolean isScript = lexicalContext.getFunction(symbol.getBlock()).isProgram(); //see NASHORN-56 + final boolean isScript = getLexicalContext().getDefiningFunction(symbol).isProgram(); //see NASHORN-56 if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) { // Forbid integers as local vars for now as we have no way to treat them as undefined newType(symbol, init.getType()); @@ -751,14 +812,14 @@ @Override public Node leaveADD(final UnaryNode unaryNode) { - newTemporary(arithType(), unaryNode); + ensureSymbol(arithType(), unaryNode); end(unaryNode); return unaryNode; } @Override public Node leaveBIT_NOT(final UnaryNode unaryNode) { - newTemporary(Type.INT, unaryNode); + ensureSymbol(Type.INT, unaryNode); end(unaryNode); return unaryNode; } @@ -766,30 +827,29 @@ @Override public Node leaveDECINC(final UnaryNode unaryNode) { // @see assignOffset - ensureAssignmentSlots(getCurrentFunctionNode(), unaryNode.rhs()); + ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), unaryNode.rhs()); final Type type = arithType(); newType(unaryNode.rhs().getSymbol(), type); - newTemporary(type, unaryNode); + ensureSymbol(type, unaryNode); end(unaryNode); return unaryNode; } @Override public Node leaveDELETE(final UnaryNode unaryNode) { - final FunctionNode currentFunctionNode = getCurrentFunctionNode(); - final boolean strictMode = currentFunctionNode.isStrictMode(); + final FunctionNode currentFunctionNode = getLexicalContext().getCurrentFunction(); + final boolean strictMode = currentFunctionNode.isStrict(); final Node rhs = unaryNode.rhs(); final Node strictFlagNode = LiteralNode.newInstance(unaryNode, strictMode).accept(this); Request request = Request.DELETE; - final RuntimeNode runtimeNode; final List args = new ArrayList<>(); if (rhs instanceof IdentNode) { // If this is a declared variable or a function parameter, delete always fails (except for globals). final String name = ((IdentNode)rhs).getName(); - final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !rhs.getSymbol().isTopLevel()); + final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !isProgramLevelSymbol(name)); if (failDelete && rhs.getSymbol().isThis()) { return LiteralNode.newInstance(unaryNode, true).accept(this); @@ -797,7 +857,7 @@ final Node literalNode = LiteralNode.newInstance(unaryNode, name).accept(this); if (!failDelete) { - args.add(currentFunctionNode.getScopeNode()); + args.add(compilerConstant(SCOPE)); } args.add(literalNode); args.add(strictFlagNode); @@ -825,42 +885,62 @@ return LiteralNode.newInstance(unaryNode, true).accept(this); } - runtimeNode = new RuntimeNode(unaryNode, request, args); - assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //clone constructor should do this + final RuntimeNode runtimeNode = new RuntimeNode(unaryNode, request, args); + assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //unary parent constructor should do this return leaveRuntimeNode(runtimeNode); } + /** + * Is the symbol denoted by the specified name in the current lexical context defined in the program level + * @param name the name of the symbol + * @return true if the symbol denoted by the specified name in the current lexical context defined in the program level. + */ + private boolean isProgramLevelSymbol(final String name) { + for(final Iterator it = getLexicalContext().getBlocks(); it.hasNext();) { + final Block next = it.next(); + if(next.getExistingSymbol(name) != null) { + return next == getLexicalContext().getFunctionBody(getLexicalContext().getOutermostFunction()); + } + } + throw new AssertionError("Couldn't find symbol " + name + " in the context"); + } + @Override public Node leaveNEW(final UnaryNode unaryNode) { - newTemporary(Type.OBJECT, unaryNode); + ensureSymbol(Type.OBJECT, unaryNode); end(unaryNode); return unaryNode; } @Override public Node leaveNOT(final UnaryNode unaryNode) { - newTemporary(Type.BOOLEAN, unaryNode); + ensureSymbol(Type.BOOLEAN, unaryNode); end(unaryNode); return unaryNode; } + private IdentNode compilerConstant(CompilerConstants cc) { + final FunctionNode functionNode = getLexicalContext().getCurrentFunction(); + final IdentNode node = new IdentNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), cc.symbolName()); + node.setSymbol(functionNode.compilerConstant(cc)); + return node; + } + @Override public Node leaveTYPEOF(final UnaryNode unaryNode) { - final Node rhs = unaryNode.rhs(); - - RuntimeNode runtimeNode; + final Node rhs = unaryNode.rhs(); List args = new ArrayList<>(); if (rhs instanceof IdentNode && !rhs.getSymbol().isParam() && !rhs.getSymbol().isVar()) { - args.add(getCurrentFunctionNode().getScopeNode()); + args.add(compilerConstant(SCOPE)); args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null } else { args.add(rhs); args.add(LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' } - runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args); + RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args); assert runtimeNode.getSymbol() == unaryNode.getSymbol(); runtimeNode = (RuntimeNode)leaveRuntimeNode(runtimeNode); @@ -872,21 +952,20 @@ @Override public Node leaveRuntimeNode(final RuntimeNode runtimeNode) { - newTemporary(runtimeNode.getRequest().getReturnType(), runtimeNode); + ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode); return runtimeNode; } @Override public Node leaveSUB(final UnaryNode unaryNode) { - newTemporary(arithType(), unaryNode); + ensureSymbol(arithType(), unaryNode); end(unaryNode); return unaryNode; } @Override public Node leaveVOID(final UnaryNode unaryNode) { - final RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.VOID); - runtimeNode.accept(this); + final RuntimeNode runtimeNode = (RuntimeNode)new RuntimeNode(unaryNode, Request.VOID).accept(this); assert runtimeNode.getSymbol().getSymbolType().isObject(); end(unaryNode); return runtimeNode; @@ -903,7 +982,7 @@ ensureTypeNotUnknown(lhs); ensureTypeNotUnknown(rhs); - newTemporary(Type.widest(lhs.getType(), rhs.getType()), binaryNode); + ensureSymbol(Type.widest(lhs.getType(), rhs.getType()), binaryNode); end(binaryNode); @@ -912,7 +991,7 @@ @Override public Node leaveAND(final BinaryNode binaryNode) { - newTemporary(Type.OBJECT, binaryNode); + ensureSymbol(Type.OBJECT, binaryNode); end(binaryNode); return binaryNode; } @@ -921,39 +1000,39 @@ * This is a helper called before an assignment. * @param binaryNode assignment node */ - private Node enterAssignmentNode(final BinaryNode binaryNode) { + private boolean enterAssignmentNode(final BinaryNode binaryNode) { start(binaryNode); final Node lhs = binaryNode.lhs(); if (lhs instanceof IdentNode) { - final Block block = getCurrentBlock(); + final Block block = getLexicalContext().getCurrentBlock(); final IdentNode ident = (IdentNode)lhs; final String name = ident.getName(); - Symbol symbol = findSymbol(getCurrentBlock(), name); + Symbol symbol = findSymbol(block, name); if (symbol == null) { symbol = defineSymbol(block, name, IS_GLOBAL, ident); binaryNode.setSymbol(symbol); - } else if (!isLocal(getCurrentFunctionNode(), symbol)) { - symbol.setIsScope(); + } else { + maybeForceScope(symbol); } addLocalDef(name); } - return binaryNode; + return true; } private boolean isLocal(FunctionNode function, Symbol symbol) { - final Block block = symbol.getBlock(); - // some temp symbols have no block, so can be assumed local - return block == null || lexicalContext.getFunction(block) == function; + final FunctionNode definingFn = getLexicalContext().getDefiningFunction(symbol); + // Temp symbols are not assigned to a block, so their defining fn is null; those can be assumed local + return definingFn == null || definingFn == function; } @Override - public Node enterASSIGN(final BinaryNode binaryNode) { + public boolean enterASSIGN(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -963,7 +1042,7 @@ } @Override - public Node enterASSIGN_ADD(final BinaryNode binaryNode) { + public boolean enterASSIGN_ADD(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -978,7 +1057,7 @@ } @Override - public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) { + public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -988,7 +1067,7 @@ } @Override - public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) { + public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -998,7 +1077,7 @@ } @Override - public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) { + public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1008,7 +1087,7 @@ } @Override - public Node enterASSIGN_DIV(final BinaryNode binaryNode) { + public boolean enterASSIGN_DIV(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1018,7 +1097,7 @@ } @Override - public Node enterASSIGN_MOD(final BinaryNode binaryNode) { + public boolean enterASSIGN_MOD(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1028,7 +1107,7 @@ } @Override - public Node enterASSIGN_MUL(final BinaryNode binaryNode) { + public boolean enterASSIGN_MUL(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1038,7 +1117,7 @@ } @Override - public Node enterASSIGN_SAR(final BinaryNode binaryNode) { + public boolean enterASSIGN_SAR(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1048,7 +1127,7 @@ } @Override - public Node enterASSIGN_SHL(final BinaryNode binaryNode) { + public boolean enterASSIGN_SHL(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1058,7 +1137,7 @@ } @Override - public Node enterASSIGN_SHR(final BinaryNode binaryNode) { + public boolean enterASSIGN_SHR(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1068,7 +1147,7 @@ } @Override - public Node enterASSIGN_SUB(final BinaryNode binaryNode) { + public boolean enterASSIGN_SUB(final BinaryNode binaryNode) { return enterAssignmentNode(binaryNode); } @@ -1094,13 +1173,13 @@ @Override public Node leaveCOMMARIGHT(final BinaryNode binaryNode) { - newTemporary(binaryNode.rhs().getType(), binaryNode); + ensureSymbol(binaryNode.rhs().getType(), binaryNode); return binaryNode; } @Override public Node leaveCOMMALEFT(final BinaryNode binaryNode) { - newTemporary(binaryNode.lhs().getType(), binaryNode); + ensureSymbol(binaryNode.lhs().getType(), binaryNode); return binaryNode; } @@ -1113,7 +1192,7 @@ final Node lhs = binaryNode.lhs(); final Node rhs = binaryNode.rhs(); - newTemporary(Type.BOOLEAN, binaryNode); + ensureSymbol(Type.BOOLEAN, binaryNode); ensureTypeNotUnknown(lhs); ensureTypeNotUnknown(rhs); @@ -1131,7 +1210,7 @@ //newType(binaryNode.lhs().getSymbol(), operandType); //newType(binaryNode.rhs().getSymbol(), operandType); - newTemporary(destType, binaryNode); + ensureSymbol(destType, binaryNode); return binaryNode; } @@ -1216,7 +1295,7 @@ @Override public Node leaveOR(final BinaryNode binaryNode) { - newTemporary(Type.OBJECT, binaryNode); + ensureSymbol(Type.OBJECT, binaryNode); end(binaryNode); return binaryNode; } @@ -1244,7 +1323,7 @@ @Override public Node leaveForNode(final ForNode forNode) { if (forNode.isForIn()) { - forNode.setIterator(newInternal(getCurrentFunctionNode().uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73 + forNode.setIterator(newInternal(getLexicalContext().getCurrentFunction().uniqueName(ITERATOR_PREFIX.symbolName()), Type.OBJECT)); //NASHORN-73 /* * Iterators return objects, so we need to widen the scope of the * init variable if it, for example, has been assigned double type @@ -1267,65 +1346,50 @@ ensureTypeNotUnknown(rhs); final Type type = Type.widest(lhs.getType(), rhs.getType()); - newTemporary(type, ternaryNode); + ensureSymbol(type, ternaryNode); end(ternaryNode); + assert ternaryNode.getSymbol() != null; return ternaryNode; } - private void initThis(final FunctionNode functionNode) { - final Symbol thisSymbol = defineSymbol(functionNode, THIS.tag(), IS_PARAM | IS_THIS, null); + private void initThis(final Block block) { + final Symbol thisSymbol = defineSymbol(block, THIS.symbolName(), IS_PARAM | IS_THIS, null); newType(thisSymbol, Type.OBJECT); thisSymbol.setNeedsSlot(true); - functionNode.getThisNode().setSymbol(thisSymbol); - LOG.info("Initialized scope symbol: " + thisSymbol); } - private void initScope(final FunctionNode functionNode) { - final Symbol scopeSymbol = defineSymbol(functionNode, SCOPE.tag(), IS_VAR | IS_INTERNAL, null); + private void initScope(final Block block) { + final Symbol scopeSymbol = defineSymbol(block, SCOPE.symbolName(), IS_VAR | IS_INTERNAL, null); newType(scopeSymbol, Type.typeFor(ScriptObject.class)); scopeSymbol.setNeedsSlot(true); - functionNode.getScopeNode().setSymbol(scopeSymbol); - LOG.info("Initialized scope symbol: " + scopeSymbol); } - private void initReturn(final FunctionNode functionNode) { - final Symbol returnSymbol = defineSymbol(functionNode, SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null); + private void initReturn(final Block block) { + final Symbol returnSymbol = defineSymbol(block, RETURN.symbolName(), IS_VAR | IS_INTERNAL, null); newType(returnSymbol, Type.OBJECT); returnSymbol.setNeedsSlot(true); - functionNode.getResultNode().setSymbol(returnSymbol); - LOG.info("Initialized return symbol: " + returnSymbol); //return symbol is always object as it's the __return__ thing. What returnType is is another matter though } - private void initVarArg(final FunctionNode functionNode) { - if (functionNode.isVarArg()) { - final Symbol varArgsSymbol = defineSymbol(functionNode, VARARGS.tag(), IS_PARAM | IS_INTERNAL, null); - varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY); - varArgsSymbol.setNeedsSlot(true); - functionNode.getVarArgsNode().setSymbol(varArgsSymbol); - LOG.info("Initialized varargs symbol: " + varArgsSymbol); + private void initVarArg(final Block block, final boolean needsArguments) { + final Symbol varArgsSymbol = defineSymbol(block, VARARGS.symbolName(), IS_PARAM | IS_INTERNAL, null); + varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY); + varArgsSymbol.setNeedsSlot(true); - if (functionNode.needsArguments()) { - final String argumentsName = functionNode.getArgumentsNode().getName(); - final Symbol argumentsSymbol = defineSymbol(functionNode, argumentsName, IS_VAR | IS_INTERNAL, null); - newType(argumentsSymbol, Type.typeFor(ScriptObject.class)); - argumentsSymbol.setNeedsSlot(true); - functionNode.getArgumentsNode().setSymbol(argumentsSymbol); - addLocalDef(argumentsName); - LOG.info("Initialized vararg varArgsSymbol=" + varArgsSymbol + " argumentsSymbol=" + argumentsSymbol); - } + if (needsArguments) { + final Symbol argumentsSymbol = defineSymbol(block, ARGUMENTS.symbolName(), IS_VAR | IS_INTERNAL, null); + newType(argumentsSymbol, Type.typeFor(ScriptObject.class)); + argumentsSymbol.setNeedsSlot(true); + addLocalDef(ARGUMENTS.symbolName()); } } - private void initCallee(final FunctionNode functionNode) { - assert functionNode.getCalleeNode() != null : functionNode + " has no callee"; - final Symbol calleeSymbol = defineSymbol(functionNode, CALLEE.tag(), IS_PARAM | IS_INTERNAL, null); - newType(calleeSymbol, Type.typeFor(ScriptFunction.class)); + private void initCallee(final Block block) { + final Symbol calleeSymbol = defineSymbol(block, CALLEE.symbolName(), IS_PARAM | IS_INTERNAL, null); + newType(calleeSymbol, FunctionNode.FUNCTION_TYPE); calleeSymbol.setNeedsSlot(true); - functionNode.getCalleeNode().setSymbol(calleeSymbol); - LOG.info("Initialized callee symbol " + calleeSymbol); } /** @@ -1334,25 +1398,19 @@ * * @param functionNode the function node */ - private void initParameters(final FunctionNode functionNode) { - //If a function is specialized, we don't need to tag either it return - // type or its parameters with the widest (OBJECT) type for safety. - functionNode.setReturnType(Type.UNKNOWN); - + private void initParameters(final FunctionNode functionNode, final Block body) { for (final IdentNode param : functionNode.getParameters()) { addLocalDef(param.getName()); - final Symbol paramSymbol = defineSymbol(functionNode, param.getName(), IS_PARAM, param); + final Symbol paramSymbol = defineSymbol(body, param.getName(), IS_PARAM, param); if (paramSymbol != null) { final Type callSiteParamType = functionNode.getSpecializedType(param); if (callSiteParamType != null) { - LOG.info("Param " + paramSymbol + " has a callsite type " + callSiteParamType + ". Using that."); - - System.err.println("Param " + param + " has a callsite type " + callSiteParamType + ". Using that."); + LOG.info("Param ", paramSymbol, " has a callsite type ", callSiteParamType, ". Using that."); } newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType); } - LOG.info("Initialized param " + paramSymbol); + LOG.info("Initialized param ", paramSymbol); } } @@ -1378,7 +1436,7 @@ // this function, we can tell the runtime system that no matter what the // call site is, use this information. TODO if (!paramSymbol.getSymbolType().isObject()) { - LOG.finest("Parameter " + ident + " could profit from specialization to " + paramSymbol.getSymbolType()); + LOG.finest("Parameter ", ident, " could profit from specialization to ", paramSymbol.getSymbolType()); } newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType())); @@ -1392,19 +1450,18 @@ /** * Move any properties from a global map into the scope of this method - * @param functionNode the function node for which to init scope vars + * @param block the function node body for which to init scope vars */ - private void initFromPropertyMap(final FunctionNode functionNode) { + private void initFromPropertyMap(final Block block) { // For a script, add scope symbols as defined in the property map - assert functionNode.isProgram(); final PropertyMap map = Context.getGlobalMap(); for (final Property property : map.getProperties()) { final String key = property.getKey(); - final Symbol symbol = defineSymbol(functionNode, key, IS_GLOBAL, null); + final Symbol symbol = defineSymbol(block, key, IS_GLOBAL, null); newType(symbol, Type.OBJECT); - LOG.info("Added global symbol from property map " + symbol); + LOG.info("Added global symbol from property map ", symbol); } } @@ -1412,7 +1469,7 @@ final Symbol symbol = node.getSymbol(); - LOG.info("Ensure type not unknown for: " + symbol); + LOG.info("Ensure type not unknown for: ", symbol); /* * Note that not just unknowns, but params need to be blown @@ -1452,7 +1509,7 @@ } private Symbol exceptionSymbol() { - return newInternal(getCurrentFunctionNode().uniqueName(EXCEPTION_PREFIX.tag()), Type.typeFor(ECMAException.class)); + return newInternal(getLexicalContext().getCurrentFunction().uniqueName(EXCEPTION_PREFIX.symbolName()), Type.typeFor(ECMAException.class)); } /** @@ -1512,15 +1569,15 @@ } Type from = node.getType(); if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) { - LOG.fine("Had to post pass widen '" + node + "' " + Debug.id(node) + " from " + node.getType() + " to " + to); + LOG.fine("Had to post pass widen '", node, "' " + Debug.id(node), " from ", node.getType(), " to ", to); newType(node.getSymbol(), to); changed.add(node); } } @Override - public Node enterFunctionNode(final FunctionNode node) { - return node.isLazy() ? null : node; + public boolean enterFunctionNode(final FunctionNode node) { + return !node.isLazy(); } /** @@ -1574,7 +1631,7 @@ } else { type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too. } - newTemporary(type, binaryNode); + ensureSymbol(type, binaryNode); newType(lhs.getSymbol(), type); end(binaryNode); return binaryNode; @@ -1589,32 +1646,25 @@ final Node lhs = binaryNode.lhs(); newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType - newTemporary(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine + ensureSymbol(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine - ensureAssignmentSlots(getCurrentFunctionNode(), binaryNode); + ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), binaryNode); end(binaryNode); return binaryNode; } - private static boolean isFunctionExpressionSelfReference(final Symbol symbol) { - if (symbol.isVar() && symbol.getNode() == symbol.getBlock() && symbol.getNode() instanceof FunctionNode) { - return ((FunctionNode)symbol.getNode()).getIdent().getName().equals(symbol.getName()); - } - return false; + private Symbol ensureSymbol(final FunctionNode functionNode, final Type type, final Node node) { + LOG.info("New TEMPORARY added to ", functionNode.getName(), " type=", type); + return functionNode.ensureSymbol(getLexicalContext().getCurrentBlock(), type, node); } - private static Symbol newTemporary(final FunctionNode functionNode, final Type type, final Node node) { - LOG.info("New TEMPORARY added to " + functionNode.getName() + " type=" + type); - return functionNode.newTemporary(type, node); - } - - private Symbol newTemporary(final Type type, final Node node) { - return newTemporary(getCurrentFunctionNode(), type, node); + private Symbol ensureSymbol(final Type type, final Node node) { + return ensureSymbol(getLexicalContext().getCurrentFunction(), type, node); } private Symbol newInternal(final String name, final Type type) { - final Symbol iter = defineSymbol(getCurrentFunctionNode(), name, IS_VAR | IS_INTERNAL, null); + final Symbol iter = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR | IS_INTERNAL, null); iter.setType(type); // NASHORN-73 return iter; } @@ -1624,40 +1674,51 @@ symbol.setType(type); if (symbol.getSymbolType() != oldType) { - LOG.info("New TYPE " + type + " for " + symbol + " (was " + oldType + ")"); + LOG.info("New TYPE ", type, " for ", symbol," (was ", oldType, ")"); } if (symbol.isParam()) { symbol.setType(type); - LOG.info("Param type change " + symbol); + LOG.info("Param type change ", symbol); } } - private void clearLocalDefs() { - localDefs = new HashSet<>(); + private void pushLocalsFunction() { + localDefs.push(new HashSet()); + localUses.push(new HashSet()); + } + + private void pushLocalsBlock() { + localDefs.push(localDefs.isEmpty() ? new HashSet() : new HashSet<>(localDefs.peek())); + localUses.push(localUses.isEmpty() ? new HashSet() : new HashSet<>(localUses.peek())); + } + + private void popLocals() { + localDefs.pop(); + localUses.pop(); } private boolean isLocalDef(final String name) { - return localDefs.contains(name); + return localDefs.peek().contains(name); } private void addLocalDef(final String name) { - LOG.info("Adding local def of symbol: '" + name + "'"); - localDefs.add(name); + LOG.info("Adding local def of symbol: '", name, "'"); + localDefs.peek().add(name); } private void removeLocalDef(final String name) { - LOG.info("Removing local def of symbol: '" + name + "'"); - localDefs.remove(name); + LOG.info("Removing local def of symbol: '", name, "'"); + localDefs.peek().remove(name); } - private void clearLocalUses() { - localUses = new HashSet<>(); + private boolean isLocalUse(final String name) { + return localUses.peek().contains(name); } private void addLocalUse(final String name) { - LOG.info("Adding local use of symbol: '" + name + "'"); - localUses.add(name); + LOG.info("Adding local use of symbol: '", name, "'"); + localUses.peek().add(name); } /** @@ -1665,30 +1726,28 @@ * This is done when the function contains unevaluated black boxes such as * lazy sub-function nodes that have not been compiled. * - * @param functionNode function node in whose scope symbols should conservatively be made objects + * @param body body for the function node we are leaving */ - private static void objectifySymbols(final FunctionNode functionNode) { - functionNode.accept(new NodeVisitor() { + private static void objectifySymbols(final Block body) { + body.accept(new NodeVisitor() { private void toObject(final Block block) { for (final Iterator iter = block.symbolIterator(); iter.hasNext();) { final Symbol symbol = iter.next(); - newType(symbol, Type.OBJECT); + if (!symbol.isTemp()) { + newType(symbol, Type.OBJECT); + } } } @Override - public Node enterBlock(final Block block) { + public boolean enterBlock(final Block block) { toObject(block); - return block; + return true; } @Override - public Node enterFunctionNode(final FunctionNode node) { - toObject(node); - if (node.isLazy()) { - return null; - } - return node; + public boolean enterFunctionNode(final FunctionNode node) { + return false; } }); } @@ -1702,11 +1761,11 @@ return cn.substring(lastDot + 1); } - private Node start(final Node node) { + private boolean start(final Node node) { return start(node, true); } - private Node start(final Node node, final boolean printNode) { + private boolean start(final Node node, final boolean printNode) { if (DEBUG) { final StringBuilder sb = new StringBuilder(); @@ -1715,13 +1774,13 @@ append("] "). append(printNode ? node.toString() : ""). append(" in '"). - append(getCurrentFunctionNode().getName()). + append(getLexicalContext().getCurrentFunction().getName()). append("'"); - LOG.info(sb.toString()); + LOG.info(sb); LOG.indent(); } - return node; + return true; } private Node end(final Node node) { @@ -1737,7 +1796,7 @@ append("] "). append(printNode ? node.toString() : ""). append(" in '"). - append(getCurrentFunctionNode().getName()); + append(getLexicalContext().getCurrentFunction().getName()); if (node.getSymbol() == null) { sb.append(" "); @@ -1746,7 +1805,7 @@ } LOG.unindent(); - LOG.info(sb.toString()); + LOG.info(sb); } return node; diff -r 241d13ccb08b -r 4ea383c26563 nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java --- a/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java Wed May 08 14:10:53 2013 +0200 +++ b/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java Wed May 08 23:54:45 2013 -0700 @@ -58,12 +58,14 @@ import java.util.EnumSet; import java.util.HashSet; import java.util.Set; + import jdk.internal.org.objectweb.asm.ClassReader; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.org.objectweb.asm.util.TraceClassVisitor; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.SplitNode; import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptObject; @@ -219,14 +221,14 @@ private void defineCommonStatics(final boolean strictMode) { // source - used to store the source data (text) for this script. Shared across // compile units. Set externally by the compiler. - field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.tag(), Source.class); + field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.symbolName(), Source.class); // constants - used to the constants array for this script. Shared across // compile units. Set externally by the compiler. - field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.tag(), Object[].class); + field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.symbolName(), Object[].class); // strictMode - was this script compiled in strict mode. Set externally by the compiler. - field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.tag(), boolean.class, strictMode); + field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.symbolName(), boolean.class, strictMode); } /** @@ -238,9 +240,9 @@ if (constantMethodNeeded.contains(String.class)) { // $getString - get the ith entry from the constants table and cast to String. - final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.tag(), String.class, int.class); + final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.symbolName(), String.class, int.class); getStringMethod.begin(); - getStringMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor()) + getStringMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor()) .load(Type.INT, 0) .arrayload() .checkcast(String.class) @@ -250,7 +252,7 @@ if (constantMethodNeeded.contains(PropertyMap.class)) { // $getMap - get the ith entry from the constants table and cast to PropertyMap. - final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.tag(), PropertyMap.class, int.class); + final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.symbolName(), PropertyMap.class, int.class); getMapMethod.begin(); getMapMethod.loadConstants() .load(Type.INT, 0) @@ -260,7 +262,7 @@ getMapMethod.end(); // $setMap - overwrite an existing map. - final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.tag(), void.class, int.class, PropertyMap.class); + final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.symbolName(), void.class, int.class, PropertyMap.class); setMapMethod.begin(); setMapMethod.loadConstants() .load(Type.INT, 0) @@ -289,7 +291,7 @@ final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, cls, int.class); getArrayMethod.begin(); - getArrayMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor()) + getArrayMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor()) .load(Type.INT, 0) .arrayload() .checkcast(cls) @@ -307,7 +309,7 @@ */ static String getArrayMethodName(final Class cls) { assert cls.isArray(); - return GET_ARRAY_PREFIX.tag() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.tag(); + return GET_ARRAY_PREFIX.symbolName() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName(); } /** @@ -409,6 +411,10 @@ methodsStarted.remove(method); } + SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class rtype, final Class... ptypes) { + return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode); + } + /** * Add a new method to the class - defaults to public method * @@ -433,7 +439,7 @@ * @return method emitter to use for weaving this method */ MethodEmitter method(final EnumSet methodFlags, final String methodName, final Class rtype, final Class... ptypes) { - return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, methodDescriptor(rtype, ptypes), null, null)); + return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes)); } /** @@ -484,7 +490,7 @@ * @return method emitter to use for weaving */ MethodEmitter clinit() { - return method(EnumSet.of(Flag.STATIC), CLINIT.tag(), void.class); + return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class); } /** @@ -493,7 +499,7 @@ * @return method emitter to use for weaving ()V */ MethodEmitter init() { - return method(INIT.tag(), void.class); + return method(INIT.symbolName(), void.class); } /** @@ -503,7 +509,7 @@ * @return method emitter to use for weaving ()V */ MethodEmitter init(final Class... ptypes) { - return method(INIT.tag(), void.class, ptypes); + return method(INIT.symbolName(), void.class, ptypes); } /** @@ -515,7 +521,7 @@ * @return method emitter to use for weaving (...)V */ MethodEmitter init(final EnumSet flags, final Class... ptypes) { - return method(flags, INIT.tag(), void.class, ptypes); + return method(flags, INIT.symbolName(), void.class, ptypes); } /** @@ -628,4 +634,9 @@ return v; } } + + private MethodVisitor methodVisitor(EnumSet flags, final String methodName, final Class rtype, final Class... ptypes) { + return cw.visitMethod(Flag.getValue(flags), methodName, methodDescriptor(rtype, ptypes), null, null); + } + } diff -r 241d13ccb08b -r 4ea383c26563 nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java --- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed May 08 14:10:53 2013 +0200 +++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed May 08 23:54:45 2013 -0700 @@ -27,14 +27,18 @@ import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.PRIVATE; import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC; +import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS; +import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE; import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP; import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING; -import static jdk.nashorn.internal.codegen.CompilerConstants.LEAF; import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.REGEX_PREFIX; +import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN; import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_ARRAY_ARG; import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX; +import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; +import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor; @@ -48,8 +52,10 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT; import java.io.PrintWriter; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; +import java.util.Deque; import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; @@ -67,11 +73,11 @@ import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.BreakNode; +import jdk.nashorn.internal.ir.BreakableNode; import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.ContinueNode; -import jdk.nashorn.internal.ir.DoWhileNode; import jdk.nashorn.internal.ir.EmptyNode; import jdk.nashorn.internal.ir.ExecuteNode; import jdk.nashorn.internal.ir.ForNode; @@ -81,10 +87,12 @@ import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.LexicalContext; +import jdk.nashorn.internal.ir.LexicalContextNode; import jdk.nashorn.internal.ir.LineNumberNode; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; +import jdk.nashorn.internal.ir.LoopNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyNode; @@ -107,8 +115,10 @@ import jdk.nashorn.internal.parser.Lexer.RegexToken; import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.runtime.Context; +import jdk.nashorn.internal.runtime.Debug; import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.ECMAException; +import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.Property; import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; @@ -157,14 +167,29 @@ /** How many regexp fields have been emitted */ private int regexFieldCount; - /** Used for temporary signaling between enterCallNode and enterFunctionNode to handle the special case of calling - * a just-defined anonymous function expression. */ - private boolean functionNodeIsCallee; - /** Map of shared scope call sites */ private final Map scopeCalls = new HashMap<>(); - private final LexicalContext lexicalContext = new LexicalContext(); + /** Compile unit stack - every time we start a sub method (e.g. a split) we push one */ + private final Deque compileUnits = new ArrayDeque<>(); + + /** Method emitter stack - every time we start a sub method (e.g. a split) we push one */ + private final Deque methodEmitters = new ArrayDeque<>(); + + /** The discard stack - whenever we enter a discard node we keep track of its return value status - + * i.e. should we keep it or throw it away */ + private final Deque discard = new ArrayDeque<>(); + + // A stack tracking the next free local variable slot in the blocks. There's one entry for every block + // currently on the lexical context stack. + private int[] nextFreeSlots = new int[16]; + private int nextFreeSlotsSize = 0; + + /** Current method emitter */ + private MethodEmitter method; + + /** Current compile unit */ + private CompileUnit unit; /** When should we stop caching regexp expressions in fields to limit bytecode size? */ private static final int MAX_REGEX_FIELDS = 2 * 1024; @@ -177,6 +202,7 @@ * @param compiler */ CodeGenerator(final Compiler compiler) { + super(new DynamicScopeTrackingLexicalContext()); this.compiler = compiler; this.callSiteFlags = compiler.getEnv()._callsite_flags; } @@ -188,7 +214,37 @@ * @return the correct flags for a call site in the current function */ int getCallSiteFlags() { - return getCurrentFunctionNode().isStrictMode() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags; + return getLexicalContext().getCurrentFunction().isStrict() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags; + } + + private void pushMethodEmitter(final MethodEmitter newMethod) { + methodEmitters.push(newMethod); + this.method = newMethod; + } + + private void popMethodEmitter(final MethodEmitter oldMethod) { + assert methodEmitters.peek() == oldMethod; + methodEmitters.pop(); + if (!methodEmitters.isEmpty()) { + this.method = methodEmitters.peek(); + } else { + this.method = null; + } + } + + private void push(final CompileUnit newUnit) { + compileUnits.push(newUnit); + this.unit = newUnit; + } + + private void pop(final CompileUnit oldUnit) { + assert compileUnits.peek() == oldUnit; + compileUnits.pop(); + if (!compileUnits.isEmpty()) { + this.unit = compileUnits.peek(); + } else { + this.unit = null; + } } /** @@ -217,7 +273,7 @@ assert identNode.getSymbol().isScope() : identNode + " is not in scope!"; final int flags = CALLSITE_SCOPE | getCallSiteFlags(); - method.loadScope(); + method.loadCompilerConstant(SCOPE); if (isFastScope(symbol)) { // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope. @@ -231,27 +287,103 @@ } /** + * A lexical context that also tracks if we have any dynamic scopes in the context. Such scopes can have new + * variables introduced into them at run time - a with block or a function directly containing an eval call. + */ + private static class DynamicScopeTrackingLexicalContext extends LexicalContext { + int dynamicScopeCount = 0; + + @Override + public T push(T node) { + if(isDynamicScopeBoundary(node)) { + ++dynamicScopeCount; + } + return super.push(node); + } + + @Override + public T pop(T node) { + final T popped = super.pop(node); + if(isDynamicScopeBoundary(popped)) { + --dynamicScopeCount; + } + return popped; + } + + private boolean isDynamicScopeBoundary(LexicalContextNode node) { + if(node instanceof Block) { + // Block's immediate parent is a with node. Note we aren't testing for a WithNode, as that'd capture + // processing of WithNode.expression too, but it should be unaffected. + return !isEmpty() && peek() instanceof WithNode; + } else if(node instanceof FunctionNode) { + // Function has a direct eval in it (so a top-level "var ..." in the eval code can introduce a new + // variable into the function's scope), and it isn't strict (as evals in strict functions get an + // isolated scope). + return isFunctionDynamicScope((FunctionNode)node); + } + return false; + } + } + + boolean inDynamicScope() { + return ((DynamicScopeTrackingLexicalContext)getLexicalContext()).dynamicScopeCount > 0; + } + + static boolean isFunctionDynamicScope(FunctionNode fn) { + return fn.hasEval() && !fn.isStrict(); + } + + /** * Check if this symbol can be accessed directly with a putfield or getfield or dynamic load * * @param function function to check for fast scope * @return true if fast scope */ private boolean isFastScope(final Symbol symbol) { - if (!symbol.isScope() || !symbol.getBlock().needsScope()) { + if(!symbol.isScope()) { + return false; + } + final LexicalContext lc = getLexicalContext(); + if(!inDynamicScope()) { + // If there's no with or eval in context, and the symbol is marked as scoped, it is fast scoped. Such a + // symbol must either be global, or its defining block must need scope. + assert symbol.isGlobal() || lc.getDefiningBlock(symbol).needsScope() : symbol.getName(); + return true; + } + if(symbol.isGlobal()) { + // Shortcut: if there's a with or eval in context, globals can't be fast scoped return false; } - // Allow fast scope access if no function contains with or eval - for(final Iterator it = lexicalContext.getFunctions(getCurrentFunctionNode()); it.hasNext();) { - final FunctionNode func = it.next(); - if (func.hasWith() || func.hasEval()) { - return false; + // Otherwise, check if there's a dynamic scope between use of the symbol and its definition + final String name = symbol.getName(); + boolean previousWasBlock = false; + for (final Iterator it = lc.getAllNodes(); it.hasNext();) { + final LexicalContextNode node = it.next(); + if(node instanceof Block) { + // If this block defines the symbol, then we can fast scope the symbol. + final Block block = (Block)node; + if(block.getExistingSymbol(name) == symbol) { + assert block.needsScope(); + return true; + } + previousWasBlock = true; + } else { + if((node instanceof WithNode && previousWasBlock) || (node instanceof FunctionNode && isFunctionDynamicScope((FunctionNode)node))) { + // If we hit a scope that can have symbols introduced into it at run time before finding the defining + // block, the symbol can't be fast scoped. A WithNode only counts if we've immediately seen a block + // before - its block. Otherwise, we are currently processing the WithNode's expression, and that's + // obviously not subjected to introducing new symbols. + return false; + } + previousWasBlock = false; } } - return true; + // Should've found the symbol defined in a block + throw new AssertionError(); } private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) { - method.load(isFastScope(symbol) ? getScopeProtoDepth(getCurrentBlock(), symbol) : -1); + method.load(isFastScope(symbol) ? getScopeProtoDepth(getLexicalContext().getCurrentBlock(), symbol) : -1); final SharedScopeCall scopeCall = getScopeGet(valueType, symbol, flags | CALLSITE_FAST_SCOPE); scopeCall.generateInvoke(method); return method; @@ -271,10 +403,10 @@ private int getScopeProtoDepth(final Block startingBlock, final Symbol symbol) { int depth = 0; - final Block definingBlock = symbol.getBlock(); - for(final Iterator blocks = lexicalContext.getBlocks(startingBlock); blocks.hasNext();) { + final String name = symbol.getName(); + for(final Iterator blocks = getLexicalContext().getBlocks(startingBlock); blocks.hasNext();) { final Block currentBlock = blocks.next(); - if (currentBlock == definingBlock) { + if (currentBlock.getExistingSymbol(name) == symbol) { return depth; } if (currentBlock.needsScope()) { @@ -285,9 +417,9 @@ } private void loadFastScopeProto(final Symbol symbol, final boolean swap) { - final int depth = getScopeProtoDepth(getCurrentBlock(), symbol); + final int depth = getScopeProtoDepth(getLexicalContext().getCurrentBlock(), symbol); assert depth != -1; - if(depth > 0) { + if (depth > 0) { if (swap) { method.swap(); } @@ -328,46 +460,46 @@ */ final CodeGenerator codegen = this; - node.accept(new NodeVisitor(getCurrentCompileUnit(), method) { + node.accept(new NodeVisitor() { @Override - public Node enterIdentNode(final IdentNode identNode) { + public boolean enterIdentNode(final IdentNode identNode) { loadIdent(identNode); - return null; + return false; } @Override - public Node enterAccessNode(final AccessNode accessNode) { + public boolean enterAccessNode(final AccessNode accessNode) { if (!baseAlreadyOnStack) { load(accessNode.getBase()).convert(Type.OBJECT); } assert method.peekType().isObject(); method.dynamicGet(node.getType(), accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction()); - return null; + return false; } @Override - public Node enterIndexNode(final IndexNode indexNode) { + public boolean enterIndexNode(final IndexNode indexNode) { if (!baseAlreadyOnStack) { load(indexNode.getBase()).convert(Type.OBJECT); load(indexNode.getIndex()); } method.dynamicGetIndex(node.getType(), getCallSiteFlags(), indexNode.isFunction()); - return null; + return false; } @Override - public Node enterFunctionNode(FunctionNode functionNode) { + public boolean enterFunctionNode(FunctionNode functionNode) { // function nodes will always leave a constructed function object on stack, no need to load the symbol // separately as in enterDefault() functionNode.accept(codegen); - return null; + return false; } @Override - public Node enterDefault(final Node otherNode) { + public boolean enterDefault(final Node otherNode) { otherNode.accept(codegen); // generate code for whatever we are looking at. method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there) - return null; + return false; } }); @@ -375,14 +507,9 @@ } @Override - public Node enterAccessNode(final AccessNode accessNode) { - if (accessNode.testResolved()) { - return null; - } - + public boolean enterAccessNode(final AccessNode accessNode) { load(accessNode); - - return null; + return false; } /** @@ -407,7 +534,7 @@ final boolean isInternal = symbol.isParam() || symbol.isInternal() || symbol.isThis() || !symbol.canBeUndefined(); if (symbol.hasSlot() && !isInternal) { - assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + getCurrentFunctionNode(); + assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + getLexicalContext().getCurrentFunction(); if (symbol.getSymbolType().isNumber()) { numbers.add(symbol); } else if (symbol.getSymbolType().isObject()) { @@ -441,22 +568,20 @@ * @param block block containing symbols. */ private void symbolInfo(final Block block) { - for (final Symbol symbol : block.getFrame().getSymbols()) { - method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel()); + for (final Iterator iter = block.symbolIterator(); iter.hasNext(); ) { + final Symbol symbol = iter.next(); + if (symbol.hasSlot()) { + method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel()); + } } } @Override - public Node enterBlock(final Block block) { - if (block.testResolved()) { - return null; - } - lexicalContext.push(block); - + public boolean enterBlock(final Block block) { method.label(block.getEntryLabel()); initLocals(block); - return block; + return true; } @Override @@ -464,10 +589,10 @@ method.label(block.getBreakLabel()); symbolInfo(block); - if (block.needsScope()) { + if (block.needsScope() && !block.isTerminal()) { popBlockScope(block); } - lexicalContext.pop(block); + --nextFreeSlotsSize; return block; } @@ -477,34 +602,30 @@ final Label skipLabel = new Label("skip_catch"); /* pop scope a la try-finally */ - method.loadScope(); + method.loadCompilerConstant(SCOPE); method.invoke(ScriptObject.GET_PROTO); - method.storeScope(); + method.storeCompilerConstant(SCOPE); method._goto(skipLabel); method.label(exitLabel); method._catch(recoveryLabel); - method.loadScope(); + method.loadCompilerConstant(SCOPE); method.invoke(ScriptObject.GET_PROTO); - method.storeScope(); + method.storeCompilerConstant(SCOPE); method.athrow(); method.label(skipLabel); method._try(block.getEntryLabel(), exitLabel, recoveryLabel, Throwable.class); } @Override - public Node enterBreakNode(final BreakNode breakNode) { - if (breakNode.testResolved()) { - return null; - } - - for (int i = 0; i < breakNode.getScopeNestingLevel(); i++) { + public boolean enterBreakNode(final BreakNode breakNode) { + final BreakableNode breakFrom = getLexicalContext().getBreakable(breakNode.getLabel()); + for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(breakFrom); i++) { closeWith(); } - - method.splitAwareGoto(breakNode.getTargetLabel()); - - return null; + method.splitAwareGoto(getLexicalContext(), breakFrom.getBreakLabel()); + + return false; } private int loadArgs(final List args) { @@ -541,21 +662,17 @@ } @Override - public Node enterCallNode(final CallNode callNode) { - if (callNode.testResolved()) { - return null; - } - + public boolean enterCallNode(final CallNode callNode) { final List args = callNode.getArgs(); final Node function = callNode.getFunction(); - final Block currentBlock = getCurrentBlock(); - - function.accept(new NodeVisitor(getCurrentCompileUnit(), method) { + final Block currentBlock = getLexicalContext().getCurrentBlock(); + + function.accept(new NodeVisitor() { private void sharedScopeCall(final IdentNode identNode, final int flags) { final Symbol symbol = identNode.getSymbol(); int scopeCallFlags = flags; - method.loadScope(); + method.loadCompilerConstant(SCOPE); if (isFastScope(symbol)) { method.load(getScopeProtoDepth(currentBlock, symbol)); scopeCallFlags |= CALLSITE_FAST_SCOPE; @@ -591,7 +708,7 @@ // We don't need ScriptFunction object for 'eval' method.pop(); - method.loadScope(); // Load up self (scope). + method.loadCompilerConstant(SCOPE); // Load up self (scope). final CallNode.EvalArgs evalArgs = callNode.getEvalArgs(); // load evaluated code @@ -618,7 +735,7 @@ } @Override - public Node enterIdentNode(final IdentNode node) { + public boolean enterIdentNode(final IdentNode node) { final Symbol symbol = node.getSymbol(); if (symbol.isScope()) { @@ -626,27 +743,27 @@ final int useCount = symbol.getUseCount(); // Threshold for generating shared scope callsite is lower for fast scope symbols because we know - // we can dial in the correct scope. However, we als need to enable it for non-fast scopes to + // we can dial in the correct scope. However, we also need to enable it for non-fast scopes to // support huge scripts like mandreel.js. if (callNode.isEval()) { evalCall(node, flags); } else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD || (!isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD) - || callNode.inWithBlock()) { + || CodeGenerator.this.inDynamicScope()) { scopeCall(node, flags); } else { sharedScopeCall(node, flags); } - assert method.peekType().equals(callNode.getType()); + assert method.peekType().equals(callNode.getType()) : method.peekType() + "!=" + callNode.getType(); } else { enterDefault(node); } - return null; + return false; } @Override - public Node enterAccessNode(final AccessNode node) { + public boolean enterAccessNode(final AccessNode node) { load(node.getBase()); method.convert(Type.OBJECT); method.dup(); @@ -655,35 +772,34 @@ method.dynamicCall(callNode.getType(), 2 + loadArgs(args), getCallSiteFlags()); assert method.peekType().equals(callNode.getType()); - return null; + return false; } @Override - public Node enterFunctionNode(final FunctionNode callee) { + public boolean enterFunctionNode(final FunctionNode origCallee) { + // NOTE: visiting the callee will leave a constructed ScriptFunction object on the stack if + // callee.needsCallee() == true + final FunctionNode callee = (FunctionNode)origCallee.accept(CodeGenerator.this); + final boolean isVarArg = callee.isVarArg(); final int argCount = isVarArg ? -1 : callee.getParameters().size(); final String signature = new FunctionSignature(true, callee.needsCallee(), callee.getReturnType(), isVarArg ? null : callee.getParameters()).toString(); - if (callee.needsCallee()) { - newFunctionObject(callee); - } - - if (callee.isStrictMode()) { // self is undefined + if (callee.isStrict()) { // self is undefined method.loadUndefined(Type.OBJECT); } else { // get global from scope (which is the self) globalInstance(); } loadArgs(args, signature, isVarArg, argCount); + assert callee.getCompileUnit() != null : "no compile unit for " + callee.getName() + " " + Debug.id(callee) + " " + callNode; method.invokestatic(callee.getCompileUnit().getUnitClassName(), callee.getName(), signature); assert method.peekType().equals(callee.getReturnType()) : method.peekType() + " != " + callee.getReturnType(); - functionNodeIsCallee = true; - callee.accept(CodeGenerator.this); - return null; + return false; } @Override - public Node enterIndexNode(final IndexNode node) { + public boolean enterIndexNode(final IndexNode node) { load(node.getBase()); method.convert(Type.OBJECT); method.dup(); @@ -697,11 +813,11 @@ method.dynamicCall(callNode.getType(), 2 + loadArgs(args), getCallSiteFlags()); assert method.peekType().equals(callNode.getType()); - return null; + return false; } @Override - protected Node enterDefault(final Node node) { + protected boolean enterDefault(final Node node) { // Load up function. load(function); method.convert(Type.OBJECT); //TODO, e.g. booleans can be used as functions @@ -709,58 +825,41 @@ method.dynamicCall(callNode.getType(), 2 + loadArgs(args), getCallSiteFlags() | CALLSITE_SCOPE); assert method.peekType().equals(callNode.getType()); - return null; + return false; } }); method.store(callNode.getSymbol()); - return null; + return false; } @Override - public Node enterContinueNode(final ContinueNode continueNode) { - if (continueNode.testResolved()) { - return null; - } - - for (int i = 0; i < continueNode.getScopeNestingLevel(); i++) { + public boolean enterContinueNode(final ContinueNode continueNode) { + final LoopNode continueTo = getLexicalContext().getContinueTo(continueNode.getLabel()); + for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(continueTo); i++) { closeWith(); } - - method.splitAwareGoto(continueNode.getTargetLabel()); - - return null; + method.splitAwareGoto(getLexicalContext(), continueTo.getContinueLabel()); + + return false; } @Override - public Node enterDoWhileNode(final DoWhileNode doWhileNode) { - return enterWhileNode(doWhileNode); - } - - @Override - public Node enterEmptyNode(final EmptyNode emptyNode) { - return null; + public boolean enterEmptyNode(final EmptyNode emptyNode) { + return false; } @Override - public Node enterExecuteNode(final ExecuteNode executeNode) { - if (executeNode.testResolved()) { - return null; - } - + public boolean enterExecuteNode(final ExecuteNode executeNode) { final Node expression = executeNode.getExpression(); expression.accept(this); - return null; + return false; } @Override - public Node enterForNode(final ForNode forNode) { - if (forNode.testResolved()) { - return null; - } - + public boolean enterForNode(final ForNode forNode) { final Node test = forNode.getTest(); final Block body = forNode.getBody(); final Node modify = forNode.getModify(); @@ -790,6 +889,10 @@ new Store(init) { @Override + protected void storeNonDiscard() { + return; + } + @Override protected void evaluate() { method.load(iter); method.invoke(interfaceCallNoLookup(Iterator.class, "next", Object.class)); @@ -829,7 +932,19 @@ method.label(breakLabel); } - return null; + return false; + } + + private static int assignSlots(final Block block, final int firstSlot) { + int nextSlot = firstSlot; + for (final Iterator iter = block.symbolIterator(); iter.hasNext(); ) { + final Symbol next = iter.next(); + if (next.hasSlot()) { + next.setSlot(nextSlot); + nextSlot += next.slotCount(); + } + } + return nextSlot; } /** @@ -838,21 +953,26 @@ * @param block block with local vars. */ private void initLocals(final Block block) { - final FunctionNode function = lexicalContext.getFunction(block); - final boolean isFunctionNode = block == function; - - /* - * Get the symbols from the frame and realign the frame so that all - * slots get correct numbers. The slot numbering is not fixed until - * after initLocals has been run - */ - final Frame frame = block.getFrame(); - final List symbols = frame.getSymbols(); - - /* Fix the predefined slots so they have numbers >= 0, like varargs. */ - frame.realign(); - - if (isFunctionNode) { + final boolean isFunctionBody = getLexicalContext().isFunctionBody(); + + final int nextFreeSlot; + if (isFunctionBody) { + // On entry to function, start with slot 0 + nextFreeSlot = 0; + } else { + // Otherwise, continue from previous block's first free slot + nextFreeSlot = nextFreeSlots[nextFreeSlotsSize - 1]; + } + if(nextFreeSlotsSize == nextFreeSlots.length) { + final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2]; + System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize); + nextFreeSlots = newNextFreeSlots; + } + nextFreeSlots[nextFreeSlotsSize++] = assignSlots(block, nextFreeSlot); + + final FunctionNode function = getLexicalContext().getCurrentFunction(); + if (isFunctionBody) { + /* Fix the predefined slots so they have numbers >= 0, like varargs. */ if (function.needsParentScope()) { initParentScope(); } @@ -876,14 +996,18 @@ final List nameList = new ArrayList<>(); final List locals = new ArrayList<>(); - // Initalize symbols and values final List newSymbols = new ArrayList<>(); final List values = new ArrayList<>(); final boolean hasArguments = function.needsArguments(); - for (final Symbol symbol : symbols) { - if (symbol.isInternal() || symbol.isThis()) { + + final Iterator symbols = block.symbolIterator(); + + while (symbols.hasNext()) { + final Symbol symbol = symbols.next(); + + if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) { continue; } @@ -907,9 +1031,6 @@ } } - /* Correct slot numbering again */ - frame.realign(); - // we may have locals that need to be initialized initSymbols(locals); @@ -931,7 +1052,7 @@ @Override protected void loadScope(MethodEmitter m) { if(function.needsParentScope()) { - m.loadScope(); + m.loadCompilerConstant(SCOPE); } else { m.loadNull(); } @@ -940,118 +1061,102 @@ foc.makeObject(method); // runScript(): merge scope into global - if (isFunctionNode && function.isProgram()) { + if (isFunctionBody && function.isProgram()) { method.invoke(ScriptRuntime.MERGE_SCOPE); } - method.storeScope(); + method.storeCompilerConstant(SCOPE); } else { // Since we don't have a scope, parameters didn't get assigned array indices by the FieldObjectCreator, so // we need to assign them separately here. int nextParam = 0; - if (isFunctionNode && function.isVarArg()) { + if (isFunctionBody && function.isVarArg()) { for (final IdentNode param : function.getParameters()) { param.getSymbol().setFieldIndex(nextParam++); } } + + final Iterator iter = block.symbolIterator(); + final List symbols = new ArrayList<>(); + while (iter.hasNext()) { + symbols.add(iter.next()); + } initSymbols(symbols); } // Debugging: print symbols? @see --print-symbols flag - printSymbols(block, (isFunctionNode ? "Function " : "Block in ") + (function.getIdent() == null ? "" : function.getIdent().getName())); + printSymbols(block, (isFunctionBody ? "Function " : "Block in ") + (function.getIdent() == null ? "" : function.getIdent().getName())); } private void initArguments(final FunctionNode function) { - method.loadVarArgs(); + method.loadCompilerConstant(VARARGS); if(function.needsCallee()) { - method.loadCallee(); + method.loadCompilerConstant(CALLEE); } else { // If function is strict mode, "arguments.callee" is not populated, so we don't necessarily need the // caller. - assert function.isStrictMode(); + assert function.isStrict(); method.loadNull(); } method.load(function.getParameters().size()); globalAllocateArguments(); - method.storeArguments(); + method.storeCompilerConstant(ARGUMENTS); } private void initParentScope() { - method.loadCallee(); + method.loadCompilerConstant(CALLEE); method.invoke(ScriptFunction.GET_SCOPE); - method.storeScope(); + method.storeCompilerConstant(SCOPE); } @Override - public Node enterFunctionNode(final FunctionNode functionNode) { - final boolean isCallee = functionNodeIsCallee; - functionNodeIsCallee = false; - - if (functionNode.testResolved()) { - return null; - } - - if(!(isCallee || functionNode == compiler.getFunctionNode())) { - newFunctionObject(functionNode); + public boolean enterFunctionNode(final FunctionNode functionNode) { + if (functionNode.isLazy()) { + // Must do it now; can't postpone it until leaveFunctionNode() + newFunctionObject(functionNode, functionNode); + return false; } - if (functionNode.isLazy()) { - return null; - } - - LOG.info("=== BEGIN " + functionNode.getName()); - lexicalContext.push(functionNode); - - setCurrentCompileUnit(functionNode.getCompileUnit()); - assert getCurrentCompileUnit() != null; - - setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(functionNode)); - functionNode.setMethodEmitter(method); + LOG.info("=== BEGIN ", functionNode.getName()); + + assert functionNode.getCompileUnit() != null : "no compile unit for " + functionNode.getName() + " " + Debug.id(functionNode); + push(functionNode.getCompileUnit()); + assert !compileUnits.isEmpty(); + + pushMethodEmitter(unit.getClassEmitter().method(functionNode)); // Mark end for variable tables. method.begin(); - method.label(functionNode.getEntryLabel()); - - initLocals(functionNode); - functionNode.setState(CompilationState.EMITTED); - - return functionNode; + + return true; } @Override public Node leaveFunctionNode(final FunctionNode functionNode) { - // Mark end for variable tables. - method.label(functionNode.getBreakLabel()); - - if (!functionNode.needsScope()) { - method.markerVariable(LEAF.tag(), functionNode.getEntryLabel(), functionNode.getBreakLabel()); - } - - symbolInfo(functionNode); try { method.end(); // wrap up this method + pop(functionNode.getCompileUnit()); + popMethodEmitter(method); + LOG.info("=== END ", functionNode.getName()); + + final FunctionNode newFunctionNode = functionNode.setState(getLexicalContext(), CompilationState.EMITTED); + + newFunctionObject(newFunctionNode, functionNode); + return newFunctionNode; } catch (final Throwable t) { Context.printStackTrace(t); final VerifyError e = new VerifyError("Code generation bug in \"" + functionNode.getName() + "\": likely stack misaligned: " + t + " " + functionNode.getSource().getName()); e.initCause(t); throw e; } - - lexicalContext.pop(functionNode); - LOG.info("=== END " + functionNode.getName()); - return functionNode; } @Override - public Node enterIdentNode(final IdentNode identNode) { - return null; + public boolean enterIdentNode(final IdentNode identNode) { + return false; } @Override - public Node enterIfNode(final IfNode ifNode) { - if (ifNode.testResolved()) { - return null; - } - + public boolean enterIfNode(final IfNode ifNode) { final Node test = ifNode.getTest(); final Block pass = ifNode.getPass(); final Block fail = ifNode.getFail(); @@ -1082,30 +1187,21 @@ method.label(afterLabel); } - return null; + return false; } @Override - public Node enterIndexNode(final IndexNode indexNode) { - if (indexNode.testResolved()) { - return null; - } - + public boolean enterIndexNode(final IndexNode indexNode) { load(indexNode); - - return null; + return false; } @Override - public Node enterLineNumberNode(final LineNumberNode lineNumberNode) { - if (lineNumberNode.testResolved()) { - return null; - } - - final Label label = new Label("line:" + lineNumberNode.getLineNumber() + " (" + getCurrentFunctionNode().getName() + ")"); + public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) { + final Label label = new Label((String)null); method.label(label); method.lineNumber(lineNumberNode.getLineNumber(), label); - return null; + return false; } /** @@ -1131,43 +1227,43 @@ final Type elementType = arrayType.getElementType(); if (units != null) { - final CompileUnit savedCompileUnit = getCurrentCompileUnit(); - final MethodEmitter savedMethod = getCurrentMethodEmitter(); - - try { - for (final ArrayUnit unit : units) { - setCurrentCompileUnit(unit.getCompileUnit()); - - final String className = getCurrentCompileUnit().getUnitClassName(); - final String name = getCurrentFunctionNode().uniqueName(SPLIT_PREFIX.tag()); - final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type); - - setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature)); - method.setFunctionNode(getCurrentFunctionNode()); - method.begin(); - - fixScopeSlot(); - - method.load(arrayType, SPLIT_ARRAY_ARG.slot()); - - for (int i = unit.getLo(); i < unit.getHi(); i++) { - storeElement(nodes, elementType, postsets[i]); - } - - method._return(); - method.end(); - - savedMethod.loadThis(); - savedMethod.swap(); - savedMethod.loadCallee(); - savedMethod.swap(); - savedMethod.loadScope(); - savedMethod.swap(); - savedMethod.invokestatic(className, name, signature); + final MethodEmitter savedMethod = method; + + for (final ArrayUnit arrayUnit : units) { + push(arrayUnit.getCompileUnit()); + + final String className = unit.getUnitClassName(); + final String name = getLexicalContext().getCurrentFunction().uniqueName(SPLIT_PREFIX.symbolName()); + final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type); + + final MethodEmitter me = unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature); + pushMethodEmitter(me); + + method.setFunctionNode(getLexicalContext().getCurrentFunction()); + method.begin(); + + fixScopeSlot(); + + method.load(arrayType, SPLIT_ARRAY_ARG.slot()); + + for (int i = arrayUnit.getLo(); i < arrayUnit.getHi(); i++) { + storeElement(nodes, elementType, postsets[i]); } - } finally { - setCurrentCompileUnit(savedCompileUnit); - setCurrentMethodEmitter(savedMethod); + + method._return(); + method.end(); + popMethodEmitter(me); + + assert method == savedMethod; + method.loadCompilerConstant(THIS); + method.swap(); + method.loadCompilerConstant(CALLEE); + method.swap(); + method.loadCompilerConstant(SCOPE); + method.swap(); + method.invokestatic(className, name, signature); + + pop(unit); } return method; @@ -1217,12 +1313,12 @@ * @param string string to load */ void loadConstant(final String string) { - final String unitClassName = getCurrentCompileUnit().getUnitClassName(); - final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter(); + final String unitClassName = unit.getUnitClassName(); + final ClassEmitter classEmitter = unit.getClassEmitter(); final int index = compiler.getConstantData().add(string); method.load(index); - method.invokestatic(unitClassName, GET_STRING.tag(), methodDescriptor(String.class, int.class)); + method.invokestatic(unitClassName, GET_STRING.symbolName(), methodDescriptor(String.class, int.class)); classEmitter.needGetConstantMethod(String.class); } @@ -1233,14 +1329,14 @@ * @param object object to load */ void loadConstant(final Object object) { - final String unitClassName = getCurrentCompileUnit().getUnitClassName(); - final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter(); + final String unitClassName = unit.getUnitClassName(); + final ClassEmitter classEmitter = unit.getClassEmitter(); final int index = compiler.getConstantData().add(object); final Class cls = object.getClass(); if (cls == PropertyMap.class) { method.load(index); - method.invokestatic(unitClassName, GET_MAP.tag(), methodDescriptor(PropertyMap.class, int.class)); + method.invokestatic(unitClassName, GET_MAP.symbolName(), methodDescriptor(PropertyMap.class, int.class)); classEmitter.needGetConstantMethod(PropertyMap.class); } else if (cls.isArray()) { method.load(index); @@ -1303,14 +1399,14 @@ return loadRegexToken(regexToken); } // emit field - final String regexName = getCurrentFunctionNode().uniqueName(REGEX_PREFIX.tag()); - final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter(); + final String regexName = getLexicalContext().getCurrentFunction().uniqueName(REGEX_PREFIX.symbolName()); + final ClassEmitter classEmitter = unit.getClassEmitter(); classEmitter.field(EnumSet.of(PRIVATE, STATIC), regexName, Object.class); regexFieldCount++; // get field, if null create new regex, finally clone regex object - method.getStatic(getCurrentCompileUnit().getUnitClassName(), regexName, typeDescriptor(Object.class)); + method.getStatic(unit.getUnitClassName(), regexName, typeDescriptor(Object.class)); method.dup(); final Label cachedLabel = new Label("cached"); method.ifnonnull(cachedLabel); @@ -1318,7 +1414,7 @@ method.pop(); loadRegexToken(regexToken); method.dup(); - method.putStatic(getCurrentCompileUnit().getUnitClassName(), regexName, typeDescriptor(Object.class)); + method.putStatic(unit.getUnitClassName(), regexName, typeDescriptor(Object.class)); method.label(cachedLabel); globalRegExpCopy(); @@ -1328,18 +1424,14 @@ @SuppressWarnings("rawtypes") @Override - public Node enterLiteralNode(final LiteralNode literalNode) { + public boolean enterLiteralNode(final LiteralNode literalNode) { assert literalNode.getSymbol() != null : literalNode + " has no symbol"; load(literalNode).store(literalNode.getSymbol()); - return null; + return false; } @Override - public Node enterObjectNode(final ObjectNode objectNode) { - if (objectNode.testResolved()) { - return null; - } - + public boolean enterObjectNode(final ObjectNode objectNode) { final List elements = objectNode.getElements(); final int size = elements.size(); @@ -1404,14 +1496,14 @@ if (!hasGettersSetters) { method.store(objectNode.getSymbol()); - return null; + return false; } for (final Node element : elements) { final PropertyNode propertyNode = (PropertyNode)element; final Object key = propertyNode.getKey(); - final FunctionNode getter = (FunctionNode)propertyNode.getGetter(); - final FunctionNode setter = (FunctionNode)propertyNode.getSetter(); + final FunctionNode getter = propertyNode.getGetter(); + final FunctionNode setter = propertyNode.getSetter(); if (getter == null && setter == null) { continue; @@ -1436,35 +1528,25 @@ method.store(objectNode.getSymbol()); - return null; + return false; } @Override - public Node enterReturnNode(final ReturnNode returnNode) { - if (returnNode.testResolved()) { - return null; - } - - // Set the split return flag in the scope if this is a split method fragment. - if (method.getSplitNode() != null) { - assert method.getSplitNode().hasReturn() : "unexpected return in split node"; - - method.loadScope(); - method.checkcast(Scope.class); - method.load(0); - method.invoke(Scope.SET_SPLIT_STATE); - } + public boolean enterReturnNode(final ReturnNode returnNode) { + method.registerReturn(); + + final Type returnType = getLexicalContext().getCurrentFunction().getReturnType(); final Node expression = returnNode.getExpression(); if (expression != null) { load(expression); } else { - method.loadUndefined(getCurrentFunctionNode().getReturnType()); + method.loadUndefined(returnType); } - method._return(getCurrentFunctionNode().getReturnType()); - - return null; + method._return(returnType); + + return false; } private static boolean isNullLiteral(final Node node) { @@ -1542,19 +1624,20 @@ } assert args.size() == 2; - final Node lhs = args.get(0); - final Node rhs = args.get(1); - final Type returnType = node.getType(); - load(lhs); - load(rhs); + + load(args.get(0)); + load(args.get(1)); Request finalRequest = request; + //if the request is a comparison, i.e. one that can be reversed + //it keeps its semantic, but make sure that the object comes in + //last final Request reverse = Request.reverse(request); - if (method.peekType().isObject() && reverse != null) { - if (!method.peekType(1).isObject()) { - method.swap(); + if (method.peekType().isObject() && reverse != null) { //rhs is object + if (!method.peekType(1).isObject()) { //lhs is not object + method.swap(); //prefer object as lhs finalRequest = reverse; } } @@ -1581,11 +1664,7 @@ } @Override - public Node enterRuntimeNode(final RuntimeNode runtimeNode) { - if (runtimeNode.testResolved()) { - return null; - } - + public boolean enterRuntimeNode(final RuntimeNode runtimeNode) { /* * First check if this should be something other than a runtime node * AccessSpecializer might have changed the type @@ -1624,7 +1703,7 @@ method.add(); method.convert(type); method.store(symbol); - return null; + return false; default: // it's ok to send this one on with only primitive arguments, maybe INSTANCEOF(true, true) or similar // assert false : runtimeNode + " has all primitive arguments. This is an inconsistent state"; @@ -1636,11 +1715,11 @@ final List args = runtimeNode.getArgs(); if (nullCheck(runtimeNode, args, new FunctionSignature(false, false, runtimeNode.getType(), args).toString())) { - return null; + return false; } if (!runtimeNode.isFinal() && specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) { - return null; + return false; } for (final Node arg : runtimeNode.getArgs()) { @@ -1658,129 +1737,146 @@ method.convert(runtimeNode.getType()); method.store(runtimeNode.getSymbol()); - return null; + return false; } @Override - public Node enterSplitNode(final SplitNode splitNode) { - if (splitNode.testResolved()) { - return null; - } - + public boolean enterSplitNode(final SplitNode splitNode) { final CompileUnit splitCompileUnit = splitNode.getCompileUnit(); - final FunctionNode fn = getCurrentFunctionNode(); + final FunctionNode fn = getLexicalContext().getCurrentFunction(); final String className = splitCompileUnit.getUnitClassName(); final String name = splitNode.getName(); - final Class rtype = fn.getReturnType().getTypeClass(); - final boolean needsArguments = fn.needsArguments(); - final Class[] ptypes = needsArguments ? + final Class rtype = fn.getReturnType().getTypeClass(); + final boolean needsArguments = fn.needsArguments(); + final Class[] ptypes = needsArguments ? new Class[] {ScriptFunction.class, Object.class, ScriptObject.class, Object.class} : new Class[] {ScriptFunction.class, Object.class, ScriptObject.class}; - setCurrentCompileUnit(splitCompileUnit); - splitNode.setCompileUnit(splitCompileUnit); + final MethodEmitter caller = method; + push(splitCompileUnit); final Call splitCall = staticCallNoLookup( className, name, methodDescriptor(rtype, ptypes)); - setCurrentMethodEmitter( - splitCompileUnit.getClassEmitter().method( - EnumSet.of(Flag.PUBLIC, Flag.STATIC), - name, - rtype, - ptypes)); + final MethodEmitter splitEmitter = + splitCompileUnit.getClassEmitter().method( + splitNode, + name, + rtype, + ptypes); + + pushMethodEmitter(splitEmitter); method.setFunctionNode(fn); - method.setSplitNode(splitNode); - splitNode.setMethodEmitter(method); - - final MethodEmitter caller = splitNode.getCaller(); - if(fn.needsCallee()) { - caller.loadCallee(); + + if (fn.needsCallee()) { + caller.loadCompilerConstant(CALLEE); } else { caller.loadNull(); } - caller.loadThis(); - caller.loadScope(); + caller.loadCompilerConstant(THIS); + caller.loadCompilerConstant(SCOPE); if (needsArguments) { - caller.loadArguments(); + caller.loadCompilerConstant(ARGUMENTS); } caller.invoke(splitCall); - caller.storeResult(); + caller.storeCompilerConstant(RETURN); method.begin(); method.loadUndefined(fn.getReturnType()); - method.storeResult(); + method.storeCompilerConstant(RETURN); fixScopeSlot(); - return splitNode; + return true; } private void fixScopeSlot() { - if (getCurrentFunctionNode().getScopeNode().getSymbol().getSlot() != SCOPE.slot()) { + if (getLexicalContext().getCurrentFunction().compilerConstant(SCOPE).getSlot() != SCOPE.slot()) { // TODO hack to move the scope to the expected slot (that's needed because split methods reuse the same slots as the root method) method.load(Type.typeFor(ScriptObject.class), SCOPE.slot()); - method.storeScope(); + method.storeCompilerConstant(SCOPE); } } @Override public Node leaveSplitNode(final SplitNode splitNode) { + assert method instanceof SplitMethodEmitter; + final boolean hasReturn = method.hasReturn(); + final List