--- a/.hgtags Wed May 08 11:22:25 2013 +0100
+++ b/.hgtags Thu May 16 11:47:51 2013 +0100
@@ -209,3 +209,5 @@
1872c12529090e1c1dbf567f02ad7ae6231b8f0c jdk8-b85
da9a4c9312816451884aa6db6f18be51a07bff13 jdk8-b86
5ebf6c63714de2c9dcf831074086d31daec819df jdk8-b87
+e517701a4d0e25ae9c7945bca6e1762a8c5d8aa6 jdk8-b88
+4dec41b3c5e3bb616f0c6f15830d940905aa5d16 jdk8-b89
--- a/.hgtags-top-repo Wed May 08 11:22:25 2013 +0100
+++ b/.hgtags-top-repo Thu May 16 11:47:51 2013 +0100
@@ -209,3 +209,5 @@
7fc358f5943676b82f1dccd3152b1ac07d92e38b jdk8-b85
df9b5240f0a76c91cfe1a5b39da4d08df56e05be jdk8-b86
b9415faa7066a4d3b16d466556d5428446918d95 jdk8-b87
+e1a929afcfc492470d50be0b6b0e8dc77d3760b9 jdk8-b88
+892a0196d10c67f3a12f0eefb0bb536e423d8868 jdk8-b89
--- a/common/makefiles/NativeCompilation.gmk Wed May 08 11:22:25 2013 +0100
+++ b/common/makefiles/NativeCompilation.gmk Thu May 16 11:47:51 2013 +0100
@@ -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) \
--- a/common/makefiles/javadoc/CORE_PKGS.gmk Wed May 08 11:22:25 2013 +0100
+++ b/common/makefiles/javadoc/CORE_PKGS.gmk Thu May 16 11:47:51 2013 +0100
@@ -142,6 +142,7 @@
java.util.prefs \
java.util.regex \
java.util.spi \
+ java.util.stream \
java.util.zip \
javax.accessibility \
javax.activation \
--- a/common/makefiles/javadoc/Javadoc.gmk Wed May 08 11:22:25 2013 +0100
+++ b/common/makefiles/javadoc/Javadoc.gmk Thu May 16 11:47:51 2013 +0100
@@ -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)) ; \
--- a/corba/.hgtags Wed May 08 11:22:25 2013 +0100
+++ b/corba/.hgtags Thu May 16 11:47:51 2013 +0100
@@ -209,3 +209,5 @@
9583a6431596bac1959d2d8828f5ea217843dd12 jdk8-b85
44a8ce4a759f2668ff434661a93ff462ea472478 jdk8-b86
f1709874d55a06bc3d5dfa02dbcdfbc59f4cba34 jdk8-b87
+4e3a881ebb1ee96ce0872508b0066d74f310dbfa jdk8-b88
+fe4150590ee597f4e125fea950aa3b352622cc2d jdk8-b89
--- a/corba/src/share/classes/com/sun/tools/corba/se/idl/toJavaPortable/UnionGen.java Wed May 08 11:22:25 2013 +0100
+++ b/corba/src/share/classes/com/sun/tools/corba/se/idl/toJavaPortable/UnionGen.java Thu May 16 11:47:51 2013 +0100
@@ -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;
--- a/hotspot/.hgtags Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/.hgtags Thu May 16 11:47:51 2013 +0100
@@ -337,3 +337,7 @@
d4c2667846607042370760e23f64c3ab9350e60d jdk8-b87
01d5f04e64dc2d64625b2db2056f5ed4de918a45 hs25-b29
c4af77d2045476c56fbf3f914b336bb1b7cd18af hs25-b30
+8482058e74bc8c1a890e6f3be3eff192dba6ce67 jdk8-b88
+4ec91349972255650f97bedfd07e6423e02428cf hs25-b31
+9c1fe0b419b40a9ecdd1653cc9af1b6d67a12c46 jdk8-b89
+69494caf57908ba2c8efa9eaaa472b4d1875588a hs25-b32
--- a/hotspot/agent/doc/c2replay.html Wed May 08 11:22:25 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-<html>
-<head>
-<title>
-C2 Replay
-</title>
-</head>
-<body>
-
-<h1>C2 compiler replay</h1>
-<p>
-The C2 compiler replay is a function to repeat the compiling process from a crashed java process in compiled method<br>
-This function only exists in debug version of VM
-</p>
-<h2>Usage</h2>
-<pre>
-First, use SA to attach to the core file, if suceeded, do
- clhsdb>dumpreplaydata <address> | -a | <thread_id> [> 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=<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)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/agent/doc/cireplay.html Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,41 @@
+<html>
+<head>
+<title>
+Replay
+</title>
+</head>
+<body>
+
+<h1>Compiler replay</h1>
+<p>
+The compiler replay is a function to repeat the compiling process from a crashed java process in compiled method<br>
+This function only exists in debug version of VM
+</p>
+<h2>Usage</h2>
+<pre>
+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)
--- a/hotspot/agent/doc/clhsdb.html Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/agent/doc/clhsdb.html Thu May 16 11:47:51 2013 +0100
@@ -15,7 +15,7 @@
<p>
There is also JavaScript based SA command line interface called <a href="jsdb.html">jsdb</a>.
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 <b>source</b> command.
<b>help</b> command prints usage message for all supported commands (or a specific command)
@@ -49,7 +49,7 @@
dumpheap [ file ] <font color="red">dump heap in hprof binary format</font>
dumpideal -a | id <font color="red">dump ideal graph like debug flag -XX:+PrintIdeal</font>
dumpilt -a | id <font color="red">dump inline tree for C2 compilation</font>
- dumpreplaydata <address> | -a | <thread_id> [>replay.txt] <font color="red">dump replay data into a file</font>
+ dumpreplaydata <address> | -a | <thread_id> [>replay.txt] <font color="red">dump replay data into a file</font>
echo [ true | false ] <font color="red">turn on/off command echo mode</font>
examine [ address/count ] | [ address,address] <font color="red">show contents of memory from given address</font>
field [ type [ name fieldtype isStatic offset address ] ] <font color="red">print info about a field of HotSpot type</font>
@@ -96,11 +96,11 @@
<h3>JavaScript integration</h3>
-<p>Few CLHSDB commands are already implemented in JavaScript. It is possible to extend CLHSDB command set
+<p>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 <b>jsload</b> command. <b>jseval</b>
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 <b><code>registerCommand</code></b>
-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.
</p>
@@ -127,11 +127,11 @@
</code>
</pre>
-<h3>C2 Compilation Replay</h3>
+<h3>Compilation Replay</h3>
<p>
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.
-<a href="c2replay.html">c2replay.html</a>
+The replay function can reproduce the compiling process in the core.
+<a href="cireplay.html">cireplay.html</a>
</body>
</html>
--- a/hotspot/agent/src/os/bsd/MacosxDebuggerLocal.m Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/agent/src/os/bsd/MacosxDebuggerLocal.m Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/agent/src/os/bsd/ps_core.c Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/agent/src/os/bsd/ps_core.c Thu May 16 11:47:51 2013 +0100
@@ -199,10 +199,10 @@
//---------------------------------------------------------------
// Part of the class sharing workaround:
//
-// With class sharing, pages are mapped from classes[_g].jsa file.
+// With class sharing, pages are mapped from classes.jsa file.
// The read-only class sharing pages are mapped as MAP_SHARED,
// PROT_READ pages. These pages are not dumped into core dump.
-// With this workaround, these pages are read from classes[_g].jsa.
+// With this workaround, these pages are read from classes.jsa.
// FIXME: !HACK ALERT!
// The format of sharing achive file header is needed to read shared heap
@@ -298,14 +298,12 @@
lib_info* lib = ph->libs;
while (lib != NULL) {
// we are iterating over shared objects from the core dump. look for
- // libjvm[_g].so.
+ // libjvm.so.
const char *jvm_name = 0;
#ifdef __APPLE__
- if ((jvm_name = strstr(lib->name, "/libjvm.dylib")) != 0 ||
- (jvm_name = strstr(lib->name, "/libjvm_g.dylib")) != 0)
+ if ((jvm_name = strstr(lib->name, "/libjvm.dylib")) != 0)
#else
- if ((jvm_name = strstr(lib->name, "/libjvm.so")) != 0 ||
- (jvm_name = strstr(lib->name, "/libjvm_g.so")) != 0)
+ if ((jvm_name = strstr(lib->name, "/libjvm.so")) != 0)
#endif // __APPLE__
{
char classes_jsa[PATH_MAX];
@@ -389,7 +387,7 @@
}
ph->core->classes_jsa_fd = fd;
- // add read-only maps from classes[_g].jsa to the list of maps
+ // add read-only maps from classes.jsa to the list of maps
for (m = 0; m < NUM_SHARED_MAPS; m++) {
if (header._space[m]._read_only) {
base = (uintptr_t) header._space[m]._base;
--- a/hotspot/agent/src/os/linux/ps_core.c Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/agent/src/os/linux/ps_core.c Thu May 16 11:47:51 2013 +0100
@@ -195,10 +195,10 @@
//---------------------------------------------------------------
// Part of the class sharing workaround:
//
-// With class sharing, pages are mapped from classes[_g].jsa file.
+// With class sharing, pages are mapped from classes.jsa file.
// The read-only class sharing pages are mapped as MAP_SHARED,
// PROT_READ pages. These pages are not dumped into core dump.
-// With this workaround, these pages are read from classes[_g].jsa.
+// With this workaround, these pages are read from classes.jsa.
// FIXME: !HACK ALERT!
// The format of sharing achive file header is needed to read shared heap
@@ -284,10 +284,9 @@
lib_info* lib = ph->libs;
while (lib != NULL) {
// we are iterating over shared objects from the core dump. look for
- // libjvm[_g].so.
+ // libjvm.so.
const char *jvm_name = 0;
- if ((jvm_name = strstr(lib->name, "/libjvm.so")) != 0 ||
- (jvm_name = strstr(lib->name, "/libjvm_g.so")) != 0) {
+ if ((jvm_name = strstr(lib->name, "/libjvm.so")) != 0) {
char classes_jsa[PATH_MAX];
struct FileMapHeader header;
size_t n = 0;
@@ -371,7 +370,7 @@
}
ph->core->classes_jsa_fd = fd;
- // add read-only maps from classes[_g].jsa to the list of maps
+ // add read-only maps from classes.jsa to the list of maps
for (m = 0; m < NUM_SHARED_MAPS; m++) {
if (header._space[m]._read_only) {
base = (uintptr_t) header._space[m]._base;
--- a/hotspot/agent/src/os/solaris/proc/saproc.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/agent/src/os/solaris/proc/saproc.cpp Thu May 16 11:47:51 2013 +0100
@@ -589,8 +589,7 @@
JNIEnv* env = dbg->env;
jobject this_obj = dbg->this_obj;
const char* jvm_name = 0;
- if ((jvm_name = strstr(obj_name, "libjvm.so")) != NULL ||
- (jvm_name = strstr(obj_name, "libjvm_g.so")) != NULL) {
+ if ((jvm_name = strstr(obj_name, "libjvm.so")) != NULL) {
jvm_name = obj_name;
} else {
return 0;
@@ -598,7 +597,7 @@
struct ps_prochandle* ph = (struct ps_prochandle*) env->GetLongField(this_obj, p_ps_prochandle_ID);
- // initialize classes[_g].jsa file descriptor field.
+ // initialize classes.jsa file descriptor field.
dbg->env->SetIntField(this_obj, classes_jsa_fd_ID, -1);
// check whether class sharing is on by reading variable "UseSharedSpaces"
@@ -641,7 +640,7 @@
print_debug("looking for %s\n", classes_jsa);
- // open the classes[_g].jsa
+ // open the classes.jsa
int fd = libsaproc_open(classes_jsa, O_RDONLY);
if (fd < 0) {
char errMsg[ERR_MSG_SIZE];
@@ -651,7 +650,7 @@
print_debug("opened shared archive file %s\n", classes_jsa);
}
- // parse classes[_g].jsa
+ // parse classes.jsa
struct FileMapHeader* pheader = (struct FileMapHeader*) malloc(sizeof(struct FileMapHeader));
if (pheader == NULL) {
close(fd);
@@ -798,8 +797,8 @@
if (! isProcess) {
/*
* With class sharing, shared perm. gen heap is allocated in with MAP_SHARED|PROT_READ.
- * These pages are mapped from the file "classes[_g].jsa". MAP_SHARED pages are not dumped
- * in Solaris core.To read shared heap pages, we have to read classes[_g].jsa file.
+ * These pages are mapped from the file "classes.jsa". MAP_SHARED pages are not dumped
+ * in Solaris core.To read shared heap pages, we have to read classes.jsa file.
*/
Pobject_iter(ph, init_classsharing_workaround, &dbg);
exception = env->ExceptionOccurred();
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/HotSpotAgent.java Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/HotSpotAgent.java Thu May 16 11:47:51 2013 +0100
@@ -24,20 +24,29 @@
package sun.jvm.hotspot;
-import java.io.PrintStream;
-import java.net.*;
-import java.rmi.*;
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.debugger.bsd.*;
-import sun.jvm.hotspot.debugger.proc.*;
-import sun.jvm.hotspot.debugger.remote.*;
-import sun.jvm.hotspot.debugger.windbg.*;
-import sun.jvm.hotspot.debugger.linux.*;
-import sun.jvm.hotspot.memory.*;
-import sun.jvm.hotspot.oops.*;
-import sun.jvm.hotspot.runtime.*;
-import sun.jvm.hotspot.types.*;
-import sun.jvm.hotspot.utilities.*;
+import java.rmi.RemoteException;
+
+import sun.jvm.hotspot.debugger.Debugger;
+import sun.jvm.hotspot.debugger.DebuggerException;
+import sun.jvm.hotspot.debugger.JVMDebugger;
+import sun.jvm.hotspot.debugger.MachineDescription;
+import sun.jvm.hotspot.debugger.MachineDescriptionAMD64;
+import sun.jvm.hotspot.debugger.MachineDescriptionIA64;
+import sun.jvm.hotspot.debugger.MachineDescriptionIntelX86;
+import sun.jvm.hotspot.debugger.MachineDescriptionSPARC32Bit;
+import sun.jvm.hotspot.debugger.MachineDescriptionSPARC64Bit;
+import sun.jvm.hotspot.debugger.NoSuchSymbolException;
+import sun.jvm.hotspot.debugger.bsd.BsdDebuggerLocal;
+import sun.jvm.hotspot.debugger.linux.LinuxDebuggerLocal;
+import sun.jvm.hotspot.debugger.proc.ProcDebuggerLocal;
+import sun.jvm.hotspot.debugger.remote.RemoteDebugger;
+import sun.jvm.hotspot.debugger.remote.RemoteDebuggerClient;
+import sun.jvm.hotspot.debugger.remote.RemoteDebuggerServer;
+import sun.jvm.hotspot.debugger.windbg.WindbgDebuggerLocal;
+import sun.jvm.hotspot.runtime.VM;
+import sun.jvm.hotspot.types.TypeDataBase;
+import sun.jvm.hotspot.utilities.PlatformInfo;
+import sun.jvm.hotspot.utilities.UnsupportedPlatformException;
/** <P> This class wraps much of the basic functionality and is the
* highest-level factory for VM data structures. It makes it simple
@@ -475,7 +484,7 @@
}
private void setupJVMLibNamesSolaris() {
- jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so", "gamma_g" };
+ jvmLibNames = new String[] { "libjvm.so" };
}
//
@@ -507,7 +516,7 @@
}
private void setupJVMLibNamesWin32() {
- jvmLibNames = new String[] { "jvm.dll", "jvm_g.dll" };
+ jvmLibNames = new String[] { "jvm.dll" };
}
//
@@ -547,7 +556,7 @@
}
private void setupJVMLibNamesLinux() {
- jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so" };
+ jvmLibNames = new String[] { "libjvm.so" };
}
//
@@ -572,7 +581,7 @@
}
private void setupJVMLibNamesBsd() {
- jvmLibNames = new String[] { "libjvm.so", "libjvm_g.so" };
+ jvmLibNames = new String[] { "libjvm.so" };
}
//
@@ -595,7 +604,7 @@
}
private void setupJVMLibNamesDarwin() {
- jvmLibNames = new String[] { "libjvm.dylib", "libjvm_g.dylib" };
+ jvmLibNames = new String[] { "libjvm.dylib" };
}
/** Convenience routine which should be called by per-platform
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/LinuxVtblAccess.java Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/LinuxVtblAccess.java Thu May 16 11:47:51 2013 +0100
@@ -24,9 +24,9 @@
package sun.jvm.hotspot;
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.types.*;
-import sun.jvm.hotspot.types.basic.*;
+import sun.jvm.hotspot.debugger.SymbolLookup;
+import sun.jvm.hotspot.types.Type;
+import sun.jvm.hotspot.types.basic.BasicVtblAccess;
public class LinuxVtblAccess extends BasicVtblAccess {
private String vt;
@@ -35,8 +35,7 @@
String[] dllNames) {
super(symbolLookup, dllNames);
- if (symbolLookup.lookup("libjvm.so", "__vt_10JavaThread") != null ||
- symbolLookup.lookup("libjvm_g.so", "__vt_10JavaThread") != null) {
+ if (symbolLookup.lookup("libjvm.so", "__vt_10JavaThread") != null) {
// old C++ ABI
vt = "__vt_";
} else {
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ci/ciEnv.java Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ci/ciEnv.java Thu May 16 11:47:51 2013 +0100
@@ -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);
}
}
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java Thu May 16 11:47:51 2013 +0100
@@ -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); }
}
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/CompileTask.java Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/compiler/CompileTask.java Thu May 16 11:47:51 2013 +0100
@@ -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());
+ }
}
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java Thu May 16 11:47:51 2013 +0100
@@ -24,17 +24,28 @@
package sun.jvm.hotspot.debugger.bsd;
-import java.io.*;
-import java.net.*;
-import java.util.*;
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.debugger.x86.*;
-import sun.jvm.hotspot.debugger.cdbg.*;
-import sun.jvm.hotspot.utilities.*;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import sun.jvm.hotspot.debugger.Address;
+import sun.jvm.hotspot.debugger.DebuggerBase;
+import sun.jvm.hotspot.debugger.DebuggerException;
+import sun.jvm.hotspot.debugger.DebuggerUtilities;
+import sun.jvm.hotspot.debugger.MachineDescription;
+import sun.jvm.hotspot.debugger.NotInHeapException;
+import sun.jvm.hotspot.debugger.OopHandle;
+import sun.jvm.hotspot.debugger.ReadResult;
+import sun.jvm.hotspot.debugger.ThreadProxy;
+import sun.jvm.hotspot.debugger.UnalignedAddressException;
+import sun.jvm.hotspot.debugger.UnmappedAddressException;
+import sun.jvm.hotspot.debugger.cdbg.CDebugger;
+import sun.jvm.hotspot.debugger.cdbg.ClosestSymbol;
+import sun.jvm.hotspot.debugger.cdbg.LoadObject;
+import sun.jvm.hotspot.runtime.JavaThread;
+import sun.jvm.hotspot.runtime.Threads;
import sun.jvm.hotspot.runtime.VM;
-import sun.jvm.hotspot.runtime.Threads;
-import sun.jvm.hotspot.runtime.JavaThread;
-import java.lang.reflect.*;
+import sun.jvm.hotspot.utilities.PlatformInfo;
/** <P> An implementation of the JVMDebugger interface. The basic debug
facilities are implemented through ptrace interface in the JNI code
@@ -246,10 +257,8 @@
/* called from attach methods */
private void findABIVersion() throws DebuggerException {
String libjvmName = isDarwin ? "libjvm.dylib" : "libjvm.so";
- String libjvm_gName = isDarwin? "libjvm_g.dylib" : "libjvm_g.so";
String javaThreadVt = isDarwin ? "_vt_10JavaThread" : "__vt_10JavaThread";
- if (lookupByName0(libjvmName, javaThreadVt) != 0 ||
- lookupByName0(libjvm_gName, javaThreadVt) != 0) {
+ if (lookupByName0(libjvmName, javaThreadVt) != 0) {
// old C++ ABI
useGCC32ABI = false;
} else {
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java Thu May 16 11:47:51 2013 +0100
@@ -24,14 +24,25 @@
package sun.jvm.hotspot.debugger.linux;
-import java.io.*;
-import java.net.*;
-import java.util.*;
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.debugger.x86.*;
-import sun.jvm.hotspot.debugger.cdbg.*;
-import sun.jvm.hotspot.utilities.*;
-import java.lang.reflect.*;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import sun.jvm.hotspot.debugger.Address;
+import sun.jvm.hotspot.debugger.DebuggerBase;
+import sun.jvm.hotspot.debugger.DebuggerException;
+import sun.jvm.hotspot.debugger.DebuggerUtilities;
+import sun.jvm.hotspot.debugger.MachineDescription;
+import sun.jvm.hotspot.debugger.NotInHeapException;
+import sun.jvm.hotspot.debugger.OopHandle;
+import sun.jvm.hotspot.debugger.ReadResult;
+import sun.jvm.hotspot.debugger.ThreadProxy;
+import sun.jvm.hotspot.debugger.UnalignedAddressException;
+import sun.jvm.hotspot.debugger.UnmappedAddressException;
+import sun.jvm.hotspot.debugger.cdbg.CDebugger;
+import sun.jvm.hotspot.debugger.cdbg.ClosestSymbol;
+import sun.jvm.hotspot.debugger.cdbg.LoadObject;
+import sun.jvm.hotspot.utilities.PlatformInfo;
/** <P> An implementation of the JVMDebugger interface. The basic debug
facilities are implemented through ptrace interface in the JNI code
@@ -238,8 +249,7 @@
/* called from attach methods */
private void findABIVersion() throws DebuggerException {
- if (lookupByName0("libjvm.so", "__vt_10JavaThread") != 0 ||
- lookupByName0("libjvm_g.so", "__vt_10JavaThread") != 0) {
+ if (lookupByName0("libjvm.so", "__vt_10JavaThread") != 0) {
// old C++ ABI
useGCC32ABI = false;
} else {
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JMap.java Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/JMap.java Thu May 16 11:47:51 2013 +0100
@@ -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")) {
--- a/hotspot/make/excludeSrc.make Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/make/excludeSrc.make Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+# 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
@@ -81,22 +81,25 @@
cmsAdaptiveSizePolicy.cpp cmsCollectorPolicy.cpp \
cmsGCAdaptivePolicyCounters.cpp cmsLockVerifier.cpp compactibleFreeListSpace.cpp \
concurrentMarkSweepGeneration.cpp concurrentMarkSweepThread.cpp \
- freeChunk.cpp adaptiveFreeList.cpp promotionInfo.cpp vmCMSOperations.cpp collectionSetChooser.cpp \
- concurrentG1Refine.cpp concurrentG1RefineThread.cpp concurrentMark.cpp concurrentMarkThread.cpp \
- dirtyCardQueue.cpp g1AllocRegion.cpp g1BlockOffsetTable.cpp g1CollectedHeap.cpp g1GCPhaseTimes.cpp \
- g1CollectorPolicy.cpp g1ErgoVerbose.cpp g1_globals.cpp g1HRPrinter.cpp g1MarkSweep.cpp \
- g1MMUTracker.cpp g1MonitoringSupport.cpp g1RemSet.cpp g1SATBCardTableModRefBS.cpp heapRegion.cpp \
- heapRegionRemSet.cpp heapRegionSeq.cpp heapRegionSet.cpp heapRegionSets.cpp ptrQueue.cpp \
- satbQueue.cpp sparsePRT.cpp survRateGroup.cpp vm_operations_g1.cpp adjoiningGenerations.cpp \
- adjoiningVirtualSpaces.cpp asPSOldGen.cpp asPSYoungGen.cpp cardTableExtension.cpp \
- gcTaskManager.cpp gcTaskThread.cpp objectStartArray.cpp parallelScavengeHeap.cpp parMarkBitMap.cpp \
- pcTasks.cpp psAdaptiveSizePolicy.cpp psCompactionManager.cpp psGCAdaptivePolicyCounters.cpp \
- psGenerationCounters.cpp psMarkSweep.cpp psMarkSweepDecorator.cpp psOldGen.cpp psParallelCompact.cpp \
- psPromotionLAB.cpp psPromotionManager.cpp psScavenge.cpp psTasks.cpp psVirtualspace.cpp \
- psYoungGen.cpp vmPSOperations.cpp asParNewGeneration.cpp parCardTableModRefBS.cpp \
- parGCAllocBuffer.cpp parNewGeneration.cpp mutableSpace.cpp gSpaceCounters.cpp allocationStats.cpp \
- spaceCounters.cpp gcAdaptivePolicyCounters.cpp mutableNUMASpace.cpp immutableSpace.cpp \
- immutableSpace.cpp g1MemoryPool.cpp psMemoryPool.cpp yieldingWorkGroup.cpp g1Log.cpp
+ freeChunk.cpp adaptiveFreeList.cpp promotionInfo.cpp vmCMSOperations.cpp \
+ collectionSetChooser.cpp concurrentG1Refine.cpp concurrentG1RefineThread.cpp \
+ concurrentMark.cpp concurrentMarkThread.cpp dirtyCardQueue.cpp g1AllocRegion.cpp \
+ g1BlockOffsetTable.cpp g1CardCounts.cpp g1CollectedHeap.cpp g1CollectorPolicy.cpp \
+ g1ErgoVerbose.cpp g1GCPhaseTimes.cpp g1HRPrinter.cpp g1HotCardCache.cpp g1Log.cpp \
+ g1MMUTracker.cpp g1MarkSweep.cpp g1MemoryPool.cpp g1MonitoringSupport.cpp \
+ g1RemSet.cpp g1SATBCardTableModRefBS.cpp g1_globals.cpp heapRegion.cpp \
+ heapRegionRemSet.cpp heapRegionSeq.cpp heapRegionSet.cpp heapRegionSets.cpp \
+ ptrQueue.cpp satbQueue.cpp sparsePRT.cpp survRateGroup.cpp vm_operations_g1.cpp \
+ adjoiningGenerations.cpp adjoiningVirtualSpaces.cpp asPSOldGen.cpp asPSYoungGen.cpp \
+ cardTableExtension.cpp gcTaskManager.cpp gcTaskThread.cpp objectStartArray.cpp \
+ parallelScavengeHeap.cpp parMarkBitMap.cpp pcTasks.cpp psAdaptiveSizePolicy.cpp \
+ psCompactionManager.cpp psGCAdaptivePolicyCounters.cpp psGenerationCounters.cpp \
+ psMarkSweep.cpp psMarkSweepDecorator.cpp psMemoryPool.cpp psOldGen.cpp \
+ psParallelCompact.cpp psPromotionLAB.cpp psPromotionManager.cpp psScavenge.cpp \
+ psTasks.cpp psVirtualspace.cpp psYoungGen.cpp vmPSOperations.cpp asParNewGeneration.cpp \
+ parCardTableModRefBS.cpp parGCAllocBuffer.cpp parNewGeneration.cpp mutableSpace.cpp \
+ gSpaceCounters.cpp allocationStats.cpp spaceCounters.cpp gcAdaptivePolicyCounters.cpp \
+ mutableNUMASpace.cpp immutableSpace.cpp yieldingWorkGroup.cpp
endif
ifeq ($(INCLUDE_NMT), false)
--- a/hotspot/make/hotspot_version Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/make/hotspot_version Thu May 16 11:47:51 2013 +0100
@@ -35,7 +35,7 @@
HS_MAJOR_VER=25
HS_MINOR_VER=0
-HS_BUILD_NUMBER=30
+HS_BUILD_NUMBER=32
JDK_MAJOR_VER=1
JDK_MINOR_VER=8
--- a/hotspot/make/windows/makefiles/compile.make Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/make/windows/makefiles/compile.make Thu May 16 11:47:51 2013 +0100
@@ -52,7 +52,7 @@
# improving the quality of crash log stack traces involving jvm.dll.
# These are always used in all compiles
-CXX_FLAGS=/nologo /W3 /WX
+CXX_FLAGS=$(EXTRA_CFLAGS) /nologo /W3 /WX
# Let's add debug information when Full Debug Symbols is enabled
!if "$(ENABLE_FULL_DEBUG_SYMBOLS)" == "1"
--- a/hotspot/make/windows/makefiles/defs.make Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/make/windows/makefiles/defs.make Thu May 16 11:47:51 2013 +0100
@@ -193,7 +193,7 @@
MAKE_ARGS += JDK_BUILD_NUMBER=$(COOKED_BUILD_NUMBER)
endif
-NMAKE= MAKEFLAGS= MFLAGS= nmake -NOLOGO
+NMAKE= MAKEFLAGS= MFLAGS= EXTRA_CFLAGS="$(EXTRA_CFLAGS)" nmake -NOLOGO
ifndef SYSTEM_UNAME
SYSTEM_UNAME := $(shell uname)
export SYSTEM_UNAME
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/cpu/sparc/vm/compiledIC_sparc.cpp Thu May 16 11:47:51 2013 +0100
@@ -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
--- a/hotspot/src/cpu/sparc/vm/globalDefinitions_sparc.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/cpu/sparc/vm/globalDefinitions_sparc.hpp Thu May 16 11:47:51 2013 +0100
@@ -30,4 +30,6 @@
const int StackAlignmentInBytes = (2*wordSize);
+#define SUPPORTS_NATIVE_CX8
+
#endif // CPU_SPARC_VM_GLOBALDEFINITIONS_SPARC_HPP
--- a/hotspot/src/cpu/sparc/vm/jni_sparc.h Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/cpu/sparc/vm/jni_sparc.h Thu May 16 11:47:51 2013 +0100
@@ -23,7 +23,12 @@
* questions.
*/
-#if defined(__GNUC__) && (__GNUC__ >= 4)
+// Note: please do not change these without also changing jni_md.h in the JDK
+// repository
+#ifndef __has_attribute
+ #define __has_attribute(x) 0
+#endif
+#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility)
#define JNIEXPORT __attribute__((visibility("default")))
#define JNIIMPORT __attribute__((visibility("default")))
#else
--- a/hotspot/src/cpu/sparc/vm/sparc.ad Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/cpu/sparc/vm/sparc.ad Thu May 16 11:47:51 2013 +0100
@@ -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);
}
%}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/cpu/x86/vm/compiledIC_x86.cpp Thu May 16 11:47:51 2013 +0100
@@ -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
--- a/hotspot/src/cpu/x86/vm/globalDefinitions_x86.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/cpu/x86/vm/globalDefinitions_x86.hpp Thu May 16 11:47:51 2013 +0100
@@ -27,4 +27,6 @@
const int StackAlignmentInBytes = 16;
+#define SUPPORTS_NATIVE_CX8
+
#endif // CPU_X86_VM_GLOBALDEFINITIONS_X86_HPP
--- a/hotspot/src/cpu/x86/vm/jni_x86.h Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/cpu/x86/vm/jni_x86.h Thu May 16 11:47:51 2013 +0100
@@ -28,7 +28,13 @@
#if defined(SOLARIS) || defined(LINUX) || defined(_ALLBSD_SOURCE)
-#if defined(__GNUC__) && (__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2)
+
+// Note: please do not change these without also changing jni_md.h in the JDK
+// repository
+#ifndef __has_attribute
+ #define __has_attribute(x) 0
+#endif
+#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility)
#define JNIEXPORT __attribute__((visibility("default")))
#define JNIIMPORT __attribute__((visibility("default")))
#else
--- a/hotspot/src/cpu/x86/vm/x86_32.ad Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/cpu/x86/vm/x86_32.ad Thu May 16 11:47:51 2013 +0100
@@ -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);
}
%}
--- a/hotspot/src/cpu/x86/vm/x86_64.ad Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/cpu/x86/vm/x86_64.ad Thu May 16 11:47:51 2013 +0100
@@ -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);
}
%}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/cpu/zero/vm/compiledIC_zero.cpp Thu May 16 11:47:51 2013 +0100
@@ -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
--- a/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp Thu May 16 11:47:51 2013 +0100
@@ -212,7 +212,13 @@
// Update the invocation counter
if ((UseCompiler || CountCompiledCalls) && !method->is_synchronized()) {
- InvocationCounter *counter = method->invocation_counter();
+ MethodCounters* mcs = method->method_counters();
+ if (mcs == NULL) {
+ CALL_VM_NOCHECK(mcs = InterpreterRuntime::build_method_counters(thread, method));
+ if (HAS_PENDING_EXCEPTION)
+ goto unwind_and_return;
+ }
+ InvocationCounter *counter = mcs->invocation_counter();
counter->increment();
if (counter->reached_InvocationLimit()) {
CALL_VM_NOCHECK(
--- a/hotspot/src/cpu/zero/vm/jni_zero.h Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/cpu/zero/vm/jni_zero.h Thu May 16 11:47:51 2013 +0100
@@ -25,7 +25,13 @@
*/
-#if defined(__GNUC__) && (__GNUC__ >= 4)
+
+// Note: please do not change these without also changing jni_md.h in the JDK
+// repository
+#ifndef __has_attribute
+ #define __has_attribute(x) 0
+#endif
+#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility)
#define JNIEXPORT __attribute__((visibility("default")))
#define JNIIMPORT __attribute__((visibility("default")))
#else
--- a/hotspot/src/os/bsd/vm/os_bsd.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/os/bsd/vm/os_bsd.cpp Thu May 16 11:47:51 2013 +0100
@@ -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) {
--- a/hotspot/src/os/linux/vm/os_linux.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/os/linux/vm/os_linux.cpp Thu May 16 11:47:51 2013 +0100
@@ -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 &&
--- a/hotspot/src/os/linux/vm/os_linux.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/os/linux/vm/os_linux.hpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/os/posix/vm/os_posix.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/os/posix/vm/os_posix.cpp Thu May 16 11:47:51 2013 +0100
@@ -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);
+}
--- a/hotspot/src/os/solaris/vm/os_solaris.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/os/solaris/vm/os_solaris.cpp Thu May 16 11:47:51 2013 +0100
@@ -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;
--- a/hotspot/src/os/windows/vm/os_windows.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/os/windows/vm/os_windows.cpp Thu May 16 11:47:51 2013 +0100
@@ -1221,8 +1221,10 @@
// Needs to be in os specific directory because windows requires another
// header file <direct.h>
-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<int>(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;
--- a/hotspot/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp Thu May 16 11:47:51 2013 +0100
@@ -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));
}
--- a/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp Thu May 16 11:47:51 2013 +0100
@@ -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));
}
--- a/hotspot/src/os_cpu/linux_zero/vm/os_linux_zero.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/os_cpu/linux_zero/vm/os_linux_zero.cpp Thu May 16 11:47:51 2013 +0100
@@ -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));
--- a/hotspot/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/share/vm/adlc/main.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/adlc/main.cpp Thu May 16 11:47:51 2013 +0100
@@ -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");
--- a/hotspot/src/share/vm/asm/assembler.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/asm/assembler.cpp Thu May 16 11:47:51 2013 +0100
@@ -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;
--- a/hotspot/src/share/vm/ci/ciEnv.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/ci/ciEnv.cpp Thu May 16 11:47:51 2013 +0100
@@ -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();
}
--- a/hotspot/src/share/vm/ci/ciEnv.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/ci/ciEnv.hpp Thu May 16 11:47:51 2013 +0100
@@ -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);
};
--- a/hotspot/src/share/vm/ci/ciMethod.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/ci/ciMethod.hpp Thu May 16 11:47:51 2013 +0100
@@ -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();
--- a/hotspot/src/share/vm/ci/ciReplay.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/ci/ciReplay.cpp Thu May 16 11:47:51 2013 +0100
@@ -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 <klass> <name> <signature> <entry_bci>
+ // 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 <klass> <name> <signature> <entry_bci> <comp_level>
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;
--- a/hotspot/src/share/vm/classfile/bytecodeAssembler.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/classfile/bytecodeAssembler.cpp Thu May 16 11:47:51 2013 +0100
@@ -75,8 +75,8 @@
int idx = i + _orig->length();
switch (entry._tag) {
case BytecodeCPEntry::UTF8:
+ entry._u.utf8->increment_refcount();
cp->symbol_at_put(idx, entry._u.utf8);
- entry._u.utf8->increment_refcount();
break;
case BytecodeCPEntry::KLASS:
cp->unresolved_klass_at_put(
--- a/hotspot/src/share/vm/classfile/classFileParser.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/classfile/classFileParser.cpp Thu May 16 11:47:51 2013 +0100
@@ -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;
}
}
--- a/hotspot/src/share/vm/classfile/classFileParser.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/classfile/classFileParser.hpp Thu May 16 11:47:51 2013 +0100
@@ -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);
}
}
--- a/hotspot/src/share/vm/classfile/classLoader.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/classfile/classLoader.cpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/share/vm/classfile/classLoaderData.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp Thu May 16 11:47:51 2013 +0100
@@ -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
--- a/hotspot/src/share/vm/classfile/classLoaderData.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/classfile/classLoaderData.hpp Thu May 16 11:47:51 2013 +0100
@@ -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) {
--- a/hotspot/src/share/vm/classfile/classLoaderData.inline.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/classfile/classLoaderData.inline.hpp Thu May 16 11:47:51 2013 +0100
@@ -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);
}
--- a/hotspot/src/share/vm/classfile/dictionary.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/classfile/dictionary.cpp Thu May 16 11:47:51 2013 +0100
@@ -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;
--- a/hotspot/src/share/vm/classfile/javaClasses.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/classfile/javaClasses.cpp Thu May 16 11:47:51 2013 +0100
@@ -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;
}
--- a/hotspot/src/share/vm/classfile/javaClasses.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/classfile/javaClasses.hpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/share/vm/classfile/symbolTable.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/classfile/symbolTable.cpp Thu May 16 11:47:51 2013 +0100
@@ -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;
}
--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp Thu May 16 11:47:51 2013 +0100
@@ -830,7 +830,7 @@
Klass *kk;
{
MutexLocker mu(SystemDictionary_lock, THREAD);
- kk = find_class(name, ik->class_loader_data());
+ kk = find_class(d_index, d_hash, name, ik->class_loader_data());
}
if (kk != NULL) {
// No clean up is needed if the shared class has been entered
--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp Thu May 16 11:47:51 2013 +0100
@@ -517,13 +517,18 @@
template(sun_management_ManagementFactory, "sun/management/ManagementFactory") \
template(sun_management_Sensor, "sun/management/Sensor") \
template(sun_management_Agent, "sun/management/Agent") \
+ template(sun_management_DiagnosticCommandImpl, "sun/management/DiagnosticCommandImpl") \
template(sun_management_GarbageCollectorImpl, "sun/management/GarbageCollectorImpl") \
+ template(sun_management_ManagementFactoryHelper, "sun/management/ManagementFactoryHelper") \
+ template(getDiagnosticCommandMBean_name, "getDiagnosticCommandMBean") \
+ template(getDiagnosticCommandMBean_signature, "()Lcom/sun/management/DiagnosticCommandMBean;") \
template(getGcInfoBuilder_name, "getGcInfoBuilder") \
template(getGcInfoBuilder_signature, "()Lsun/management/GcInfoBuilder;") \
template(com_sun_management_GcInfo, "com/sun/management/GcInfo") \
template(com_sun_management_GcInfo_constructor_signature, "(Lsun/management/GcInfoBuilder;JJJ[Ljava/lang/management/MemoryUsage;[Ljava/lang/management/MemoryUsage;[Ljava/lang/Object;)V") \
template(createGCNotification_name, "createGCNotification") \
template(createGCNotification_signature, "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/sun/management/GcInfo;)V") \
+ template(createDiagnosticFrameworkNotification_name, "createDiagnosticFrameworkNotification") \
template(createMemoryPoolMBean_name, "createMemoryPoolMBean") \
template(createMemoryManagerMBean_name, "createMemoryManagerMBean") \
template(createGarbageCollectorMBean_name, "createGarbageCollectorMBean") \
--- a/hotspot/src/share/vm/code/codeCache.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/code/codeCache.cpp Thu May 16 11:47:51 2013 +0100
@@ -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;
--- a/hotspot/src/share/vm/code/codeCache.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/code/codeCache.hpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/share/vm/code/compiledIC.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/code/compiledIC.cpp Thu May 16 11:47:51 2013 +0100
@@ -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
--- a/hotspot/src/share/vm/code/compiledIC.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/code/compiledIC.hpp Thu May 16 11:47:51 2013 +0100
@@ -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;
--- a/hotspot/src/share/vm/code/stubs.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/code/stubs.cpp Thu May 16 11:47:51 2013 +0100
@@ -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();
--- a/hotspot/src/share/vm/code/vtableStubs.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/code/vtableStubs.cpp Thu May 16 11:47:51 2013 +0100
@@ -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;
--- a/hotspot/src/share/vm/compiler/compileBroker.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/compiler/compileBroker.cpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Thu May 16 11:47:51 2013 +0100
@@ -193,7 +193,8 @@
FreeBlockDictionary<FreeChunk>::DictionaryChoice dictionaryChoice) :
CardGeneration(rs, initial_byte_size, level, ct),
_dilatation_factor(((double)MinChunkSize)/((double)(CollectedHeap::min_fill_size()))),
- _debug_collection_type(Concurrent_collection_type)
+ _debug_collection_type(Concurrent_collection_type),
+ _did_compact(false)
{
HeapWord* bottom = (HeapWord*) _virtual_space.low();
HeapWord* end = (HeapWord*) _virtual_space.high();
@@ -917,18 +918,15 @@
return;
}
- // Compute some numbers about the state of the heap.
- const size_t used_after_gc = used();
- const size_t capacity_after_gc = capacity();
+ // The heap has been compacted but not reset yet.
+ // Any metric such as free() or used() will be incorrect.
CardGeneration::compute_new_size();
// Reset again after a possible resizing
- cmsSpace()->reset_after_compaction();
-
- assert(used() == used_after_gc && used_after_gc <= capacity(),
- err_msg("used: " SIZE_FORMAT " used_after_gc: " SIZE_FORMAT
- " capacity: " SIZE_FORMAT, used(), used_after_gc, capacity()));
+ if (did_compact()) {
+ cmsSpace()->reset_after_compaction();
+ }
}
void ConcurrentMarkSweepGeneration::compute_new_size_free_list() {
@@ -1578,6 +1576,8 @@
return false;
}
+void CMSCollector::set_did_compact(bool v) { _cmsGen->set_did_compact(v); }
+
// Clear _expansion_cause fields of constituent generations
void CMSCollector::clear_expansion_cause() {
_cmsGen->clear_expansion_cause();
@@ -1675,7 +1675,6 @@
}
acquire_control_and_collect(full, clear_all_soft_refs);
_full_gcs_since_conc_gc++;
-
}
void CMSCollector::request_full_gc(unsigned int full_gc_count) {
@@ -1857,6 +1856,7 @@
}
}
+ set_did_compact(should_compact);
if (should_compact) {
// If the collection is being acquired from the background
// collector, there may be references on the discovered
@@ -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 "
@@ -2722,6 +2718,7 @@
Chunk::clean_chunk_pool();
}
+ set_did_compact(false);
_between_prologue_and_epilogue = false; // ready for next cycle
}
@@ -2853,8 +2850,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 +2912,7 @@
warning("Unrecognized value %d for CMSRemarkVerifyVariant",
CMSRemarkVerifyVariant);
}
- gclog_or_tty->print(" done] ");
+ if (!silent) gclog_or_tty->print(" done] ");
return true;
}
@@ -3426,8 +3423,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 +6008,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 +6038,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
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp Thu May 16 11:47:51 2013 +0100
@@ -604,6 +604,8 @@
ConcurrentMarkSweepPolicy* _collector_policy;
ConcurrentMarkSweepPolicy* collector_policy() { return _collector_policy; }
+ void set_did_compact(bool v);
+
// XXX Move these to CMSStats ??? FIX ME !!!
elapsedTimer _inter_sweep_timer; // time between sweeps
elapsedTimer _intra_sweep_timer; // time _in_ sweeps
@@ -990,7 +992,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;
@@ -1081,6 +1083,10 @@
CollectionTypes _debug_collection_type;
+ // True if a compactiing collection was done.
+ bool _did_compact;
+ bool did_compact() { return _did_compact; }
+
// Fraction of current occupancy at which to start a CMS collection which
// will collect this generation (at least).
double _initiating_occupancy;
@@ -1121,6 +1127,8 @@
// Adaptive size policy
CMSAdaptiveSizePolicy* size_policy();
+ void set_did_compact(bool v) { _did_compact = v; }
+
bool refs_discovery_is_atomic() const { return false; }
bool refs_discovery_is_mt() const {
// Note: CMS does MT-discovery during the parallel-remark
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 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,40 +26,12 @@
#include "gc_implementation/g1/concurrentG1Refine.hpp"
#include "gc_implementation/g1/concurrentG1RefineThread.hpp"
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
-#include "gc_implementation/g1/g1CollectorPolicy.hpp"
-#include "gc_implementation/g1/g1GCPhaseTimes.hpp"
-#include "gc_implementation/g1/g1RemSet.hpp"
-#include "gc_implementation/g1/heapRegionSeq.inline.hpp"
-#include "memory/space.inline.hpp"
-#include "runtime/atomic.hpp"
-#include "runtime/java.hpp"
-#include "utilities/copy.hpp"
-
-// Possible sizes for the card counts cache: odd primes that roughly double in size.
-// (See jvmtiTagMap.cpp).
-
-#define MAX_SIZE ((size_t) -1)
+#include "gc_implementation/g1/g1HotCardCache.hpp"
-size_t ConcurrentG1Refine::_cc_cache_sizes[] = {
- 16381, 32771, 76831, 150001, 307261,
- 614563, 1228891, 2457733, 4915219, 9830479,
- 19660831, 39321619, 78643219, 157286461, MAX_SIZE
- };
-
-ConcurrentG1Refine::ConcurrentG1Refine() :
- _card_counts(NULL), _card_epochs(NULL),
- _n_card_counts(0), _max_cards(0), _max_n_card_counts(0),
- _cache_size_index(0), _expand_card_counts(false),
- _hot_cache(NULL),
- _def_use_cache(false), _use_cache(false),
- // We initialize the epochs of the array to 0. By initializing
- // _n_periods to 1 and not 0 we automatically invalidate all the
- // entries on the array. Otherwise we might accidentally think that
- // we claimed a card that was in fact never set (see CR7033292).
- _n_periods(1),
- _threads(NULL), _n_threads(0)
+ConcurrentG1Refine::ConcurrentG1Refine(G1CollectedHeap* g1h) :
+ _threads(NULL), _n_threads(0),
+ _hot_card_cache(g1h)
{
-
// Ergomonically select initial concurrent refinement parameters
if (FLAG_IS_DEFAULT(G1ConcRefinementGreenZone)) {
FLAG_SET_DEFAULT(G1ConcRefinementGreenZone, MAX2<int>(ParallelGCThreads, 1));
@@ -75,13 +47,17 @@
FLAG_SET_DEFAULT(G1ConcRefinementRedZone, yellow_zone() * 2);
}
set_red_zone(MAX2<int>(G1ConcRefinementRedZone, yellow_zone()));
+
_n_worker_threads = thread_num();
// We need one extra thread to do the young gen rset size sampling.
_n_threads = _n_worker_threads + 1;
+
reset_threshold_step();
_threads = NEW_C_HEAP_ARRAY(ConcurrentG1RefineThread*, _n_threads, mtGC);
+
int worker_id_offset = (int)DirtyCardQueueSet::num_par_ids();
+
ConcurrentG1RefineThread *next = NULL;
for (int i = _n_threads - 1; i >= 0; i--) {
ConcurrentG1RefineThread* t = new ConcurrentG1RefineThread(this, next, worker_id_offset, i);
@@ -100,74 +76,8 @@
}
}
-int ConcurrentG1Refine::thread_num() {
- return MAX2<int>((G1ConcRefinementThreads > 0) ? G1ConcRefinementThreads : ParallelGCThreads, 1);
-}
-
void ConcurrentG1Refine::init() {
- if (G1ConcRSLogCacheSize > 0) {
- _g1h = G1CollectedHeap::heap();
-
- _max_cards = _g1h->max_capacity() >> CardTableModRefBS::card_shift;
- _max_n_card_counts = _max_cards * G1MaxHotCardCountSizePercent / 100;
-
- size_t max_card_num = ((size_t)1 << (sizeof(unsigned)*BitsPerByte-1)) - 1;
- guarantee(_max_cards < max_card_num, "card_num representation");
-
- // We need _n_card_counts to be less than _max_n_card_counts here
- // so that the expansion call (below) actually allocates the
- // _counts and _epochs arrays.
- assert(_n_card_counts == 0, "pre-condition");
- assert(_max_n_card_counts > 0, "pre-condition");
-
- // Find the index into cache size array that is of a size that's
- // large enough to hold desired_sz.
- size_t desired_sz = _max_cards / InitialCacheFraction;
- int desired_sz_index = 0;
- while (_cc_cache_sizes[desired_sz_index] < desired_sz) {
- desired_sz_index += 1;
- assert(desired_sz_index < MAX_CC_CACHE_INDEX, "invariant");
- }
- assert(desired_sz_index < MAX_CC_CACHE_INDEX, "invariant");
-
- // If the desired_sz value is between two sizes then
- // _cc_cache_sizes[desired_sz_index-1] < desired_sz <= _cc_cache_sizes[desired_sz_index]
- // we will start with the lower size in the optimistic expectation that
- // we will not need to expand up. Note desired_sz_index could also be 0.
- if (desired_sz_index > 0 &&
- _cc_cache_sizes[desired_sz_index] > desired_sz) {
- desired_sz_index -= 1;
- }
-
- if (!expand_card_count_cache(desired_sz_index)) {
- // Allocation was unsuccessful - exit
- vm_exit_during_initialization("Could not reserve enough space for card count cache");
- }
- assert(_n_card_counts > 0, "post-condition");
- assert(_cache_size_index == desired_sz_index, "post-condition");
-
- Copy::fill_to_bytes(&_card_counts[0],
- _n_card_counts * sizeof(CardCountCacheEntry));
- Copy::fill_to_bytes(&_card_epochs[0], _n_card_counts * sizeof(CardEpochCacheEntry));
-
- ModRefBarrierSet* bs = _g1h->mr_bs();
- guarantee(bs->is_a(BarrierSet::CardTableModRef), "Precondition");
- _ct_bs = (CardTableModRefBS*)bs;
- _ct_bot = _ct_bs->byte_for_const(_g1h->reserved_region().start());
-
- _def_use_cache = true;
- _use_cache = true;
- _hot_cache_size = (1 << G1ConcRSLogCacheSize);
- _hot_cache = NEW_C_HEAP_ARRAY(jbyte*, _hot_cache_size, mtGC);
- _n_hot = 0;
- _hot_cache_idx = 0;
-
- // For refining the cards in the hot cache in parallel
- int n_workers = (ParallelGCThreads > 0 ?
- _g1h->workers()->total_workers() : 1);
- _hot_cache_par_chunk_size = MAX2(1, _hot_cache_size / n_workers);
- _hot_cache_par_claimed_idx = 0;
- }
+ _hot_card_cache.initialize();
}
void ConcurrentG1Refine::stop() {
@@ -188,17 +98,6 @@
}
ConcurrentG1Refine::~ConcurrentG1Refine() {
- if (G1ConcRSLogCacheSize > 0) {
- // Please see the comment in allocate_card_count_cache
- // for why we call os::malloc() and os::free() directly.
- assert(_card_counts != NULL, "Logic");
- os::free(_card_counts, mtGC);
- assert(_card_epochs != NULL, "Logic");
- os::free(_card_epochs, mtGC);
-
- assert(_hot_cache != NULL, "Logic");
- FREE_C_HEAP_ARRAY(jbyte*, _hot_cache, mtGC);
- }
if (_threads != NULL) {
for (int i = 0; i < _n_threads; i++) {
delete _threads[i];
@@ -215,317 +114,10 @@
}
}
-bool ConcurrentG1Refine::is_young_card(jbyte* card_ptr) {
- HeapWord* start = _ct_bs->addr_for(card_ptr);
- HeapRegion* r = _g1h->heap_region_containing(start);
- if (r != NULL && r->is_young()) {
- return true;
- }
- // This card is not associated with a heap region
- // so can't be young.
- return false;
-}
-
-jbyte* ConcurrentG1Refine::add_card_count(jbyte* card_ptr, int* count, bool* defer) {
- unsigned new_card_num = ptr_2_card_num(card_ptr);
- unsigned bucket = hash(new_card_num);
- assert(0 <= bucket && bucket < _n_card_counts, "Bounds");
-
- CardCountCacheEntry* count_ptr = &_card_counts[bucket];
- CardEpochCacheEntry* epoch_ptr = &_card_epochs[bucket];
-
- // We have to construct a new entry if we haven't updated the counts
- // during the current period, or if the count was updated for a
- // different card number.
- unsigned int new_epoch = (unsigned int) _n_periods;
- julong new_epoch_entry = make_epoch_entry(new_card_num, new_epoch);
-
- while (true) {
- // Fetch the previous epoch value
- julong prev_epoch_entry = epoch_ptr->_value;
- julong cas_res;
-
- if (extract_epoch(prev_epoch_entry) != new_epoch) {
- // This entry has not yet been updated during this period.
- // Note: we update the epoch value atomically to ensure
- // that there is only one winner that updates the cached
- // card_ptr value even though all the refine threads share
- // the same epoch value.
-
- cas_res = (julong) Atomic::cmpxchg((jlong) new_epoch_entry,
- (volatile jlong*)&epoch_ptr->_value,
- (jlong) prev_epoch_entry);
-
- if (cas_res == prev_epoch_entry) {
- // We have successfully won the race to update the
- // epoch and card_num value. Make it look like the
- // count and eviction count were previously cleared.
- count_ptr->_count = 1;
- count_ptr->_evict_count = 0;
- *count = 0;
- // We can defer the processing of card_ptr
- *defer = true;
- return card_ptr;
- }
- // We did not win the race to update the epoch field, so some other
- // thread must have done it. The value that gets returned by CAS
- // should be the new epoch value.
- assert(extract_epoch(cas_res) == new_epoch, "unexpected epoch");
- // We could 'continue' here or just re-read the previous epoch value
- prev_epoch_entry = epoch_ptr->_value;
- }
-
- // The epoch entry for card_ptr has been updated during this period.
- unsigned old_card_num = extract_card_num(prev_epoch_entry);
-
- // The card count that will be returned to caller
- *count = count_ptr->_count;
-
- // Are we updating the count for the same card?
- if (new_card_num == old_card_num) {
- // Same card - just update the count. We could have more than one
- // thread racing to update count for the current card. It should be
- // OK not to use a CAS as the only penalty should be some missed
- // increments of the count which delays identifying the card as "hot".
-
- if (*count < max_jubyte) count_ptr->_count++;
- // We can defer the processing of card_ptr
- *defer = true;
- return card_ptr;
- }
-
- // Different card - evict old card info
- if (count_ptr->_evict_count < max_jubyte) count_ptr->_evict_count++;
- if (count_ptr->_evict_count > G1CardCountCacheExpandThreshold) {
- // Trigger a resize the next time we clear
- _expand_card_counts = true;
- }
-
- cas_res = (julong) Atomic::cmpxchg((jlong) new_epoch_entry,
- (volatile jlong*)&epoch_ptr->_value,
- (jlong) prev_epoch_entry);
-
- if (cas_res == prev_epoch_entry) {
- // We successfully updated the card num value in the epoch entry
- count_ptr->_count = 0; // initialize counter for new card num
- jbyte* old_card_ptr = card_num_2_ptr(old_card_num);
-
- // Even though the region containg the card at old_card_num was not
- // in the young list when old_card_num was recorded in the epoch
- // cache it could have been added to the free list and subsequently
- // added to the young list in the intervening time. See CR 6817995.
- // We do not deal with this case here - it will be handled in
- // HeapRegion::oops_on_card_seq_iterate_careful after it has been
- // determined that the region containing the card has been allocated
- // to, and it's safe to check the young type of the region.
-
- // We do not want to defer processing of card_ptr in this case
- // (we need to refine old_card_ptr and card_ptr)
- *defer = false;
- return old_card_ptr;
- }
- // Someone else beat us - try again.
- }
-}
-
-jbyte* ConcurrentG1Refine::cache_insert(jbyte* card_ptr, bool* defer) {
- int count;
- jbyte* cached_ptr = add_card_count(card_ptr, &count, defer);
- assert(cached_ptr != NULL, "bad cached card ptr");
-
- // We've just inserted a card pointer into the card count cache
- // and got back the card that we just inserted or (evicted) the
- // previous contents of that count slot.
-
- // The card we got back could be in a young region. When the
- // returned card (if evicted) was originally inserted, we had
- // determined that its containing region was not young. However
- // it is possible for the region to be freed during a cleanup
- // pause, then reallocated and tagged as young which will result
- // in the returned card residing in a young region.
- //
- // We do not deal with this case here - the change from non-young
- // to young could be observed at any time - it will be handled in
- // HeapRegion::oops_on_card_seq_iterate_careful after it has been
- // determined that the region containing the card has been allocated
- // to.
-
- // The card pointer we obtained from card count cache is not hot
- // so do not store it in the cache; return it for immediate
- // refining.
- if (count < G1ConcRSHotCardLimit) {
- return cached_ptr;
- }
-
- // Otherwise, the pointer we got from the _card_counts cache is hot.
- jbyte* res = NULL;
- MutexLockerEx x(HotCardCache_lock, Mutex::_no_safepoint_check_flag);
- if (_n_hot == _hot_cache_size) {
- res = _hot_cache[_hot_cache_idx];
- _n_hot--;
- }
- // Now _n_hot < _hot_cache_size, and we can insert at _hot_cache_idx.
- _hot_cache[_hot_cache_idx] = cached_ptr;
- _hot_cache_idx++;
- if (_hot_cache_idx == _hot_cache_size) _hot_cache_idx = 0;
- _n_hot++;
-
- // The card obtained from the hot card cache could be in a young
- // region. See above on how this can happen.
-
- return res;
-}
-
-void ConcurrentG1Refine::clean_up_cache(int worker_i,
- G1RemSet* g1rs,
- DirtyCardQueue* into_cset_dcq) {
- assert(!use_cache(), "cache should be disabled");
- int start_idx;
-
- while ((start_idx = _hot_cache_par_claimed_idx) < _n_hot) { // read once
- int end_idx = start_idx + _hot_cache_par_chunk_size;
-
- if (start_idx ==
- Atomic::cmpxchg(end_idx, &_hot_cache_par_claimed_idx, start_idx)) {
- // The current worker has successfully claimed the chunk [start_idx..end_idx)
- end_idx = MIN2(end_idx, _n_hot);
- for (int i = start_idx; i < end_idx; i++) {
- jbyte* entry = _hot_cache[i];
- if (entry != NULL) {
- if (g1rs->concurrentRefineOneCard(entry, worker_i, true)) {
- // 'entry' contains references that point into the current
- // collection set. We need to record 'entry' in the DCQS
- // that's used for that purpose.
- //
- // The only time we care about recording cards that contain
- // references that point into the collection set is during
- // RSet updating while within an evacuation pause.
- // In this case worker_i should be the id of a GC worker thread
- assert(SafepointSynchronize::is_at_safepoint(), "not during an evacuation pause");
- assert(worker_i < (int) (ParallelGCThreads == 0 ? 1 : ParallelGCThreads), "incorrect worker id");
- into_cset_dcq->enqueue(entry);
- }
- }
- }
- }
- }
-}
-
-// The arrays used to hold the card counts and the epochs must have
-// a 1:1 correspondence. Hence they are allocated and freed together
-// Returns true if the allocations of both the counts and epochs
-// were successful; false otherwise.
-bool ConcurrentG1Refine::allocate_card_count_cache(size_t n,
- CardCountCacheEntry** counts,
- CardEpochCacheEntry** epochs) {
- // We call the allocation/free routines directly for the counts
- // and epochs arrays. The NEW_C_HEAP_ARRAY/FREE_C_HEAP_ARRAY
- // macros call AllocateHeap and FreeHeap respectively.
- // AllocateHeap will call vm_exit_out_of_memory in the event
- // of an allocation failure and abort the JVM. With the
- // _counts/epochs arrays we only need to abort the JVM if the
- // initial allocation of these arrays fails.
- //
- // Additionally AllocateHeap/FreeHeap do some tracing of
- // allocate/free calls so calling one without calling the
- // other can cause inconsistencies in the tracing. So we
- // call neither.
-
- assert(*counts == NULL, "out param");
- assert(*epochs == NULL, "out param");
-
- size_t counts_size = n * sizeof(CardCountCacheEntry);
- size_t epochs_size = n * sizeof(CardEpochCacheEntry);
-
- *counts = (CardCountCacheEntry*) os::malloc(counts_size, mtGC);
- if (*counts == NULL) {
- // allocation was unsuccessful
- return false;
- }
-
- *epochs = (CardEpochCacheEntry*) os::malloc(epochs_size, mtGC);
- if (*epochs == NULL) {
- // allocation was unsuccessful - free counts array
- assert(*counts != NULL, "must be");
- os::free(*counts, mtGC);
- *counts = NULL;
- return false;
- }
-
- // We successfully allocated both counts and epochs
- return true;
-}
-
-// Returns true if the card counts/epochs cache was
-// successfully expanded; false otherwise.
-bool ConcurrentG1Refine::expand_card_count_cache(int cache_size_idx) {
- // Can we expand the card count and epoch tables?
- if (_n_card_counts < _max_n_card_counts) {
- assert(cache_size_idx >= 0 && cache_size_idx < MAX_CC_CACHE_INDEX, "oob");
-
- size_t cache_size = _cc_cache_sizes[cache_size_idx];
- // Make sure we don't go bigger than we will ever need
- cache_size = MIN2(cache_size, _max_n_card_counts);
-
- // Should we expand the card count and card epoch tables?
- if (cache_size > _n_card_counts) {
- // We have been asked to allocate new, larger, arrays for
- // the card counts and the epochs. Attempt the allocation
- // of both before we free the existing arrays in case
- // the allocation is unsuccessful...
- CardCountCacheEntry* counts = NULL;
- CardEpochCacheEntry* epochs = NULL;
-
- if (allocate_card_count_cache(cache_size, &counts, &epochs)) {
- // Allocation was successful.
- // We can just free the old arrays; we're
- // not interested in preserving the contents
- if (_card_counts != NULL) os::free(_card_counts, mtGC);
- if (_card_epochs != NULL) os::free(_card_epochs, mtGC);
-
- // Cache the size of the arrays and the index that got us there.
- _n_card_counts = cache_size;
- _cache_size_index = cache_size_idx;
-
- _card_counts = counts;
- _card_epochs = epochs;
-
- // We successfully allocated/expanded the caches.
- return true;
- }
- }
- }
-
- // We did not successfully expand the caches.
- return false;
-}
-
-void ConcurrentG1Refine::clear_and_record_card_counts() {
- if (G1ConcRSLogCacheSize == 0) {
- return;
- }
-
- double start = os::elapsedTime();
-
- if (_expand_card_counts) {
- int new_idx = _cache_size_index + 1;
-
- if (expand_card_count_cache(new_idx)) {
- // Allocation was successful and _n_card_counts has
- // been updated to the new size. We only need to clear
- // the epochs so we don't read a bogus epoch value
- // when inserting a card into the hot card cache.
- Copy::fill_to_bytes(&_card_epochs[0], _n_card_counts * sizeof(CardEpochCacheEntry));
- }
- _expand_card_counts = false;
- }
-
- int this_epoch = (int) _n_periods;
- assert((this_epoch+1) <= max_jint, "to many periods");
- // Update epoch
- _n_periods++;
- double cc_clear_time_ms = (os::elapsedTime() - start) * 1000;
- _g1h->g1_policy()->phase_times()->record_cc_clear_time_ms(cc_clear_time_ms);
+int ConcurrentG1Refine::thread_num() {
+ int n_threads = (G1ConcRefinementThreads > 0) ? G1ConcRefinementThreads
+ : ParallelGCThreads;
+ return MAX2<int>(n_threads, 1);
}
void ConcurrentG1Refine::print_worker_threads_on(outputStream* st) const {
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 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 @@
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_CONCURRENTG1REFINE_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_CONCURRENTG1REFINE_HPP
+#include "gc_implementation/g1/g1HotCardCache.hpp"
#include "memory/allocation.hpp"
-#include "memory/cardTableModRefBS.hpp"
#include "runtime/thread.hpp"
#include "utilities/globalDefinitions.hpp"
// Forward decl
class ConcurrentG1RefineThread;
+class G1CollectedHeap;
+class G1HotCardCache;
class G1RemSet;
class ConcurrentG1Refine: public CHeapObj<mtGC> {
@@ -61,141 +63,14 @@
int _thread_threshold_step;
+ // We delay the refinement of 'hot' cards using the hot card cache.
+ G1HotCardCache _hot_card_cache;
+
// Reset the threshold step value based of the current zone boundaries.
void reset_threshold_step();
- // The cache for card refinement.
- bool _use_cache;
- bool _def_use_cache;
-
- size_t _n_periods; // Used as clearing epoch
-
- // An evicting cache of the number of times each card
- // is accessed. Reduces, but does not eliminate, the amount
- // of duplicated processing of dirty cards.
-
- enum SomePrivateConstants {
- epoch_bits = 32,
- card_num_shift = epoch_bits,
- epoch_mask = AllBits,
- card_num_mask = AllBits,
-
- // The initial cache size is approximately this fraction
- // of a maximal cache (i.e. the size needed for all cards
- // in the heap)
- InitialCacheFraction = 512
- };
-
- const static julong card_num_mask_in_place =
- (julong) card_num_mask << card_num_shift;
-
- typedef struct {
- julong _value; // | card_num | epoch |
- } CardEpochCacheEntry;
-
- julong make_epoch_entry(unsigned int card_num, unsigned int epoch) {
- assert(0 <= card_num && card_num < _max_cards, "Bounds");
- assert(0 <= epoch && epoch <= _n_periods, "must be");
-
- return ((julong) card_num << card_num_shift) | epoch;
- }
-
- unsigned int extract_epoch(julong v) {
- return (v & epoch_mask);
- }
-
- unsigned int extract_card_num(julong v) {
- return (v & card_num_mask_in_place) >> card_num_shift;
- }
-
- typedef struct {
- unsigned char _count;
- unsigned char _evict_count;
- } CardCountCacheEntry;
-
- CardCountCacheEntry* _card_counts;
- CardEpochCacheEntry* _card_epochs;
-
- // The current number of buckets in the card count cache
- size_t _n_card_counts;
-
- // The number of cards for the entire reserved heap
- size_t _max_cards;
-
- // The max number of buckets for the card counts and epochs caches.
- // This is the maximum that the counts and epochs will grow to.
- // It is specified as a fraction or percentage of _max_cards using
- // G1MaxHotCardCountSizePercent.
- size_t _max_n_card_counts;
-
- // Possible sizes of the cache: odd primes that roughly double in size.
- // (See jvmtiTagMap.cpp).
- enum {
- MAX_CC_CACHE_INDEX = 15 // maximum index into the cache size array.
- };
-
- static size_t _cc_cache_sizes[MAX_CC_CACHE_INDEX];
-
- // The index in _cc_cache_sizes corresponding to the size of
- // _card_counts.
- int _cache_size_index;
-
- bool _expand_card_counts;
-
- const jbyte* _ct_bot;
-
- jbyte** _hot_cache;
- int _hot_cache_size;
- int _n_hot;
- int _hot_cache_idx;
-
- int _hot_cache_par_chunk_size;
- volatile int _hot_cache_par_claimed_idx;
-
- // Needed to workaround 6817995
- CardTableModRefBS* _ct_bs;
- G1CollectedHeap* _g1h;
-
- // Helper routine for expand_card_count_cache().
- // The arrays used to hold the card counts and the epochs must have
- // a 1:1 correspondence. Hence they are allocated and freed together.
- // Returns true if the allocations of both the counts and epochs
- // were successful; false otherwise.
- bool allocate_card_count_cache(size_t n,
- CardCountCacheEntry** counts,
- CardEpochCacheEntry** epochs);
-
- // Expands the arrays that hold the card counts and epochs
- // to the cache size at index. Returns true if the expansion/
- // allocation was successful; false otherwise.
- bool expand_card_count_cache(int index);
-
- // hash a given key (index of card_ptr) with the specified size
- static unsigned int hash(size_t key, size_t size) {
- return (unsigned int) (key % size);
- }
-
- // hash a given key (index of card_ptr)
- unsigned int hash(size_t key) {
- return hash(key, _n_card_counts);
- }
-
- unsigned int ptr_2_card_num(jbyte* card_ptr) {
- return (unsigned int) (card_ptr - _ct_bot);
- }
-
- jbyte* card_num_2_ptr(unsigned int card_num) {
- return (jbyte*) (_ct_bot + card_num);
- }
-
- // Returns the count of this card after incrementing it.
- jbyte* add_card_count(jbyte* card_ptr, int* count, bool* defer);
-
- // Returns true if this card is in a young region
- bool is_young_card(jbyte* card_ptr);
-
public:
- ConcurrentG1Refine();
+ ConcurrentG1Refine(G1CollectedHeap* g1h);
~ConcurrentG1Refine();
void init(); // Accomplish some initialization that has to wait.
@@ -206,34 +81,6 @@
// Iterate over the conc refine threads
void threads_do(ThreadClosure *tc);
- // If this is the first entry for the slot, writes into the cache and
- // returns NULL. If it causes an eviction, returns the evicted pointer.
- // Otherwise, its a cache hit, and returns NULL.
- jbyte* cache_insert(jbyte* card_ptr, bool* defer);
-
- // Process the cached entries.
- void clean_up_cache(int worker_i, G1RemSet* g1rs, DirtyCardQueue* into_cset_dcq);
-
- // Set up for parallel processing of the cards in the hot cache
- void clear_hot_cache_claimed_index() {
- _hot_cache_par_claimed_idx = 0;
- }
-
- // Discard entries in the hot cache.
- void clear_hot_cache() {
- _hot_cache_idx = 0; _n_hot = 0;
- }
-
- bool hot_cache_is_empty() { return _n_hot == 0; }
-
- bool use_cache() { return _use_cache; }
- void set_use_cache(bool b) {
- if (b) _use_cache = _def_use_cache;
- else _use_cache = false;
- }
-
- void clear_and_record_card_counts();
-
static int thread_num();
void print_worker_threads_on(outputStream* st) const;
@@ -250,6 +97,8 @@
int worker_thread_num() const { return _n_worker_threads; }
int thread_threshold_step() const { return _thread_threshold_step; }
+
+ G1HotCardCache* hot_card_cache() { return &_hot_card_cache; }
};
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_CONCURRENTG1REFINE_HPP
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp Thu May 16 11:47:51 2013 +0100
@@ -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();
--- a/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.cpp Thu May 16 11:47:51 2013 +0100
@@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CardCounts.cpp Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "gc_implementation/g1/g1CardCounts.hpp"
+#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
+#include "gc_implementation/g1/g1CollectorPolicy.hpp"
+#include "gc_implementation/g1/g1GCPhaseTimes.hpp"
+#include "memory/cardTableModRefBS.hpp"
+#include "services/memTracker.hpp"
+#include "utilities/copy.hpp"
+
+void G1CardCounts::clear_range(size_t from_card_num, size_t to_card_num) {
+ if (has_count_table()) {
+ check_card_num(from_card_num,
+ err_msg("from card num out of range: "SIZE_FORMAT, from_card_num));
+ assert(from_card_num < to_card_num,
+ err_msg("Wrong order? from: " SIZE_FORMAT ", to: "SIZE_FORMAT,
+ from_card_num, to_card_num));
+ assert(to_card_num <= _committed_max_card_num,
+ err_msg("to card num out of range: "
+ "to: "SIZE_FORMAT ", "
+ "max: "SIZE_FORMAT,
+ to_card_num, _committed_max_card_num));
+
+ to_card_num = MIN2(_committed_max_card_num, to_card_num);
+
+ Copy::fill_to_bytes(&_card_counts[from_card_num], (to_card_num - from_card_num));
+ }
+}
+
+G1CardCounts::G1CardCounts(G1CollectedHeap *g1h):
+ _g1h(g1h), _card_counts(NULL),
+ _reserved_max_card_num(0), _committed_max_card_num(0),
+ _committed_size(0) {}
+
+void G1CardCounts::initialize() {
+ assert(_g1h->max_capacity() > 0, "initialization order");
+ assert(_g1h->capacity() == 0, "initialization order");
+
+ if (G1ConcRSHotCardLimit > 0) {
+ // The max value we can store in the counts table is
+ // max_jubyte. Guarantee the value of the hot
+ // threshold limit is no more than this.
+ guarantee(G1ConcRSHotCardLimit <= max_jubyte, "sanity");
+
+ ModRefBarrierSet* bs = _g1h->mr_bs();
+ guarantee(bs->is_a(BarrierSet::CardTableModRef), "Precondition");
+ _ct_bs = (CardTableModRefBS*)bs;
+ _ct_bot = _ct_bs->byte_for_const(_g1h->reserved_region().start());
+
+ // Allocate/Reserve the counts table
+ size_t reserved_bytes = _g1h->max_capacity();
+ _reserved_max_card_num = reserved_bytes >> CardTableModRefBS::card_shift;
+
+ size_t reserved_size = _reserved_max_card_num * sizeof(jbyte);
+ ReservedSpace rs(ReservedSpace::allocation_align_size_up(reserved_size));
+ if (!rs.is_reserved()) {
+ warning("Could not reserve enough space for the card counts table");
+ guarantee(!has_reserved_count_table(), "should be NULL");
+ return;
+ }
+
+ MemTracker::record_virtual_memory_type((address)rs.base(), mtGC);
+
+ _card_counts_storage.initialize(rs, 0);
+ _card_counts = (jubyte*) _card_counts_storage.low();
+ }
+}
+
+void G1CardCounts::resize(size_t heap_capacity) {
+ // Expand the card counts table to handle a heap with the given capacity.
+
+ if (!has_reserved_count_table()) {
+ // Don't expand if we failed to reserve the card counts table.
+ return;
+ }
+
+ assert(_committed_size ==
+ ReservedSpace::allocation_align_size_up(_committed_size),
+ err_msg("Unaligned? committed_size: " SIZE_FORMAT, _committed_size));
+
+ // Verify that the committed space for the card counts
+ // matches our committed max card num.
+ size_t prev_committed_size = _committed_size;
+ size_t prev_committed_card_num = prev_committed_size / sizeof(jbyte);
+ assert(prev_committed_card_num == _committed_max_card_num,
+ err_msg("Card mismatch: "
+ "prev: " SIZE_FORMAT ", "
+ "committed: "SIZE_FORMAT,
+ prev_committed_card_num, _committed_max_card_num));
+
+ size_t new_size = (heap_capacity >> CardTableModRefBS::card_shift) * sizeof(jbyte);
+ size_t new_committed_size = ReservedSpace::allocation_align_size_up(new_size);
+ size_t new_committed_card_num =
+ MIN2(_reserved_max_card_num, new_committed_size / sizeof(jbyte));
+
+ if (_committed_max_card_num < new_committed_card_num) {
+ // we need to expand the backing store for the card counts
+ size_t expand_size = new_committed_size - prev_committed_size;
+
+ if (!_card_counts_storage.expand_by(expand_size)) {
+ warning("Card counts table backing store commit failure");
+ return;
+ }
+ assert(_card_counts_storage.committed_size() == new_committed_size,
+ "expansion commit failure");
+
+ _committed_size = new_committed_size;
+ _committed_max_card_num = new_committed_card_num;
+
+ clear_range(prev_committed_card_num, _committed_max_card_num);
+ }
+}
+
+uint G1CardCounts::add_card_count(jbyte* card_ptr) {
+ // Returns the number of times the card has been refined.
+ // If we failed to reserve/commit the counts table, return 0.
+ // If card_ptr is beyond the committed end of the counts table,
+ // return 0.
+ // Otherwise return the actual count.
+ // Unless G1ConcRSHotCardLimit has been set appropriately,
+ // returning 0 will result in the card being considered
+ // cold and will be refined immediately.
+ uint count = 0;
+ if (has_count_table()) {
+ size_t card_num = ptr_2_card_num(card_ptr);
+ if (card_num < _committed_max_card_num) {
+ count = (uint) _card_counts[card_num];
+ if (count < G1ConcRSHotCardLimit) {
+ _card_counts[card_num] += 1;
+ }
+ assert(_card_counts[card_num] <= G1ConcRSHotCardLimit,
+ err_msg("Refinement count overflow? "
+ "new count: "UINT32_FORMAT,
+ (uint) _card_counts[card_num]));
+ }
+ }
+ return count;
+}
+
+bool G1CardCounts::is_hot(uint count) {
+ return (count >= G1ConcRSHotCardLimit);
+}
+
+void G1CardCounts::clear_region(HeapRegion* hr) {
+ assert(!hr->isHumongous(), "Should have been cleared");
+ if (has_count_table()) {
+ HeapWord* bottom = hr->bottom();
+
+ // We use the last address in hr as hr could be the
+ // last region in the heap. In which case trying to find
+ // the card for hr->end() will be an OOB accesss to the
+ // card table.
+ HeapWord* last = hr->end() - 1;
+ assert(_g1h->g1_committed().contains(last),
+ err_msg("last not in committed: "
+ "last: " PTR_FORMAT ", "
+ "committed: [" PTR_FORMAT ", " PTR_FORMAT ")",
+ last,
+ _g1h->g1_committed().start(),
+ _g1h->g1_committed().end()));
+
+ const jbyte* from_card_ptr = _ct_bs->byte_for_const(bottom);
+ const jbyte* last_card_ptr = _ct_bs->byte_for_const(last);
+
+#ifdef ASSERT
+ HeapWord* start_addr = _ct_bs->addr_for(from_card_ptr);
+ assert(start_addr == hr->bottom(), "alignment");
+ HeapWord* last_addr = _ct_bs->addr_for(last_card_ptr);
+ assert((last_addr + CardTableModRefBS::card_size_in_words) == hr->end(), "alignment");
+#endif // ASSERT
+
+ // Clear the counts for the (exclusive) card range.
+ size_t from_card_num = ptr_2_card_num(from_card_ptr);
+ size_t to_card_num = ptr_2_card_num(last_card_ptr) + 1;
+ clear_range(from_card_num, to_card_num);
+ }
+}
+
+void G1CardCounts::clear_all() {
+ assert(SafepointSynchronize::is_at_safepoint(), "don't call this otherwise");
+ clear_range((size_t)0, _committed_max_card_num);
+}
+
+G1CardCounts::~G1CardCounts() {
+ if (has_reserved_count_table()) {
+ _card_counts_storage.release();
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CardCounts.hpp Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1CARDCOUNTS_HPP
+#define SHARE_VM_GC_IMPLEMENTATION_G1_G1CARDCOUNTS_HPP
+
+#include "memory/allocation.hpp"
+#include "runtime/virtualspace.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+class CardTableModRefBS;
+class G1CollectedHeap;
+class HeapRegion;
+
+// Table to track the number of times a card has been refined. Once
+// a card has been refined a certain number of times, it is
+// considered 'hot' and its refinement is delayed by inserting the
+// card into the hot card cache. The card will then be refined when
+// it is evicted from the hot card cache, or when the hot card cache
+// is 'drained' during the next evacuation pause.
+
+class G1CardCounts: public CHeapObj<mtGC> {
+ G1CollectedHeap* _g1h;
+
+ // The table of counts
+ jubyte* _card_counts;
+
+ // Max capacity of the reserved space for the counts table
+ size_t _reserved_max_card_num;
+
+ // Max capacity of the committed space for the counts table
+ size_t _committed_max_card_num;
+
+ // Size of committed space for the counts table
+ size_t _committed_size;
+
+ // CardTable bottom.
+ const jbyte* _ct_bot;
+
+ // Barrier set
+ CardTableModRefBS* _ct_bs;
+
+ // The virtual memory backing the counts table
+ VirtualSpace _card_counts_storage;
+
+ // Returns true if the card counts table has been reserved.
+ bool has_reserved_count_table() { return _card_counts != NULL; }
+
+ // Returns true if the card counts table has been reserved and committed.
+ bool has_count_table() {
+ return has_reserved_count_table() && _committed_max_card_num > 0;
+ }
+
+ void check_card_num(size_t card_num, const char* msg) {
+ assert(card_num >= 0 && card_num < _committed_max_card_num, msg);
+ }
+
+ size_t ptr_2_card_num(const jbyte* card_ptr) {
+ assert(card_ptr >= _ct_bot,
+ err_msg("Inavalied card pointer: "
+ "card_ptr: " PTR_FORMAT ", "
+ "_ct_bot: " PTR_FORMAT,
+ card_ptr, _ct_bot));
+ size_t card_num = pointer_delta(card_ptr, _ct_bot, sizeof(jbyte));
+ check_card_num(card_num,
+ err_msg("card pointer out of range: " PTR_FORMAT, card_ptr));
+ return card_num;
+ }
+
+ jbyte* card_num_2_ptr(size_t card_num) {
+ check_card_num(card_num,
+ err_msg("card num out of range: "SIZE_FORMAT, card_num));
+ return (jbyte*) (_ct_bot + card_num);
+ }
+
+ // Clear the counts table for the given (exclusive) index range.
+ void clear_range(size_t from_card_num, size_t to_card_num);
+
+ public:
+ G1CardCounts(G1CollectedHeap* g1h);
+ ~G1CardCounts();
+
+ void initialize();
+
+ // Resize the committed space for the card counts table in
+ // response to a resize of the committed space for the heap.
+ void resize(size_t heap_capacity);
+
+ // Increments the refinement count for the given card.
+ // Returns the pre-increment count value.
+ uint add_card_count(jbyte* card_ptr);
+
+ // Returns true if the given count is high enough to be considered
+ // 'hot'; false otherwise.
+ bool is_hot(uint count);
+
+ // Clears the card counts for the cards spanned by the region
+ void clear_region(HeapRegion* hr);
+
+ // Clear the entire card counts table during GC.
+ // Updates the policy stats with the duration.
+ void clear_all();
+};
+
+#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1CARDCOUNTS_HPP
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Thu May 16 11:47:51 2013 +0100
@@ -96,7 +96,7 @@
_sts(sts), _g1rs(g1rs), _cg1r(cg1r), _concurrent(true)
{}
bool do_card_ptr(jbyte* card_ptr, int worker_i) {
- bool oops_into_cset = _g1rs->concurrentRefineOneCard(card_ptr, worker_i, false);
+ bool oops_into_cset = _g1rs->refine_card(card_ptr, worker_i, false);
// This path is executed by the concurrent refine or mutator threads,
// concurrently, and so we do not care if card_ptr contains references
// that point into the collection set.
@@ -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
@@ -1452,9 +1452,10 @@
_hr_printer.end_gc(true /* full */, (size_t) total_collections());
}
- if (_cg1r->use_cache()) {
- _cg1r->clear_and_record_card_counts();
- _cg1r->clear_hot_cache();
+ G1HotCardCache* hot_card_cache = _cg1r->hot_card_cache();
+ if (hot_card_cache->use_cache()) {
+ hot_card_cache->reset_card_counts();
+ hot_card_cache->reset_hot_cache();
}
// Rebuild remembered sets of all regions.
@@ -1767,6 +1768,8 @@
Universe::heap()->barrier_set()->resize_covered_region(_g1_committed);
// Tell the BOT about the update.
_bot_shared->resize(_g1_committed.word_size());
+ // Tell the hot card cache about the update
+ _cg1r->hot_card_cache()->resize_card_counts(capacity());
}
bool G1CollectedHeap::expand(size_t expand_bytes) {
@@ -1831,7 +1834,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;
@@ -1843,33 +1846,32 @@
ReservedSpace::page_align_size_down(shrink_bytes);
aligned_shrink_bytes = align_size_down(aligned_shrink_bytes,
HeapRegion::GrainBytes);
- uint num_regions_deleted = 0;
- MemRegion mr = _hrs.shrink_by(aligned_shrink_bytes, &num_regions_deleted);
+ uint num_regions_to_remove = (uint)(shrink_bytes / HeapRegion::GrainBytes);
+
+ uint num_regions_removed = _hrs.shrink_by(num_regions_to_remove);
HeapWord* old_end = (HeapWord*) _g1_storage.high();
- assert(mr.end() == old_end, "post-condition");
+ size_t shrunk_bytes = num_regions_removed * HeapRegion::GrainBytes;
ergo_verbose3(ErgoHeapSizing,
"shrink the heap",
ergo_format_byte("requested shrinking amount")
ergo_format_byte("aligned shrinking amount")
ergo_format_byte("attempted shrinking amount"),
- shrink_bytes, aligned_shrink_bytes, mr.byte_size());
- if (mr.byte_size() > 0) {
+ shrink_bytes, aligned_shrink_bytes, shrunk_bytes);
+ if (num_regions_removed > 0) {
+ _g1_storage.shrink_by(shrunk_bytes);
+ HeapWord* new_end = (HeapWord*) _g1_storage.high();
+
if (_hr_printer.is_active()) {
- HeapWord* curr = mr.end();
- while (curr > mr.start()) {
+ HeapWord* curr = old_end;
+ while (curr > new_end) {
HeapWord* curr_end = curr;
curr -= HeapRegion::GrainWords;
_hr_printer.uncommit(curr, curr_end);
}
- assert(curr == mr.start(), "post-condition");
}
- _g1_storage.shrink_by(mr.byte_size());
- HeapWord* new_end = (HeapWord*) _g1_storage.high();
- assert(mr.start() == new_end, "post-condition");
-
- _expansion_regions += num_regions_deleted;
+ _expansion_regions += num_regions_removed;
update_committed_space(old_end, new_end);
HeapRegionRemSet::shrink_heap(n_regions());
g1_policy()->record_new_heap_size(n_regions());
@@ -1955,13 +1957,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);
@@ -2007,7 +2002,7 @@
Universe::check_alignment(init_byte_size, HeapRegion::GrainBytes, "g1 heap");
Universe::check_alignment(max_byte_size, HeapRegion::GrainBytes, "g1 heap");
- _cg1r = new ConcurrentG1Refine();
+ _cg1r = new ConcurrentG1Refine(this);
// Reserve the maximum.
@@ -2068,6 +2063,9 @@
(HeapWord*) _g1_reserved.end(),
_expansion_regions);
+ // Do later initialization work for concurrent refinement.
+ _cg1r->init();
+
// 6843694 - ensure that the maximum region index can fit
// in the remembered set structures.
const uint max_region_idx = (1U << (sizeof(RegionIdx_t)*BitsPerByte-1)) - 1;
@@ -2085,20 +2083,20 @@
_g1h = this;
- _in_cset_fast_test_length = max_regions();
- _in_cset_fast_test_base =
+ _in_cset_fast_test_length = max_regions();
+ _in_cset_fast_test_base =
NEW_C_HEAP_ARRAY(bool, (size_t) _in_cset_fast_test_length, mtGC);
- // We're biasing _in_cset_fast_test to avoid subtracting the
- // beginning of the heap every time we want to index; basically
- // it's the same with what we do with the card table.
- _in_cset_fast_test = _in_cset_fast_test_base -
+ // We're biasing _in_cset_fast_test to avoid subtracting the
+ // beginning of the heap every time we want to index; basically
+ // it's the same with what we do with the card table.
+ _in_cset_fast_test = _in_cset_fast_test_base -
((uintx) _g1_reserved.start() >> HeapRegion::LogOfHRGrainBytes);
- // Clear the _cset_fast_test bitmap in anticipation of adding
- // regions to the incremental collection set for the first
- // evacuation pause.
- clear_cset_fast_test();
+ // Clear the _cset_fast_test bitmap in anticipation of adding
+ // regions to the incremental collection set for the first
+ // evacuation pause.
+ clear_cset_fast_test();
// Create the ConcurrentMark data structure and thread.
// (Must do this late, so that "max_regions" is defined.)
@@ -2160,9 +2158,6 @@
// counts and that mechanism.
SpecializationStats::clear();
- // Do later initialization work for concurrent refinement.
- _cg1r->init();
-
// Here we allocate the dummy full region that is required by the
// G1AllocRegion class. If we don't pass an address in the reserved
// space here, lots of asserts fire.
@@ -2321,7 +2316,8 @@
bool concurrent,
int worker_i) {
// Clean cards in the hot card cache
- concurrent_g1_refine()->clean_up_cache(worker_i, g1_rem_set(), into_cset_dcq);
+ G1HotCardCache* hot_card_cache = _cg1r->hot_card_cache();
+ hot_card_cache->drain(worker_i, g1_rem_set(), into_cset_dcq);
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
int n_completed_buffers = 0;
@@ -3614,7 +3610,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 +4393,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 +5075,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
@@ -5612,8 +5607,11 @@
NOT_PRODUCT(set_evacuation_failure_alot_for_current_gc();)
g1_rem_set()->prepare_for_oops_into_collection_set_do();
- concurrent_g1_refine()->set_use_cache(false);
- concurrent_g1_refine()->clear_hot_cache_claimed_index();
+
+ // Disable the hot card cache.
+ G1HotCardCache* hot_card_cache = _cg1r->hot_card_cache();
+ hot_card_cache->reset_hot_cache_claimed_index();
+ hot_card_cache->set_use_cache(false);
uint n_workers;
if (G1CollectedHeap::use_parallel_gc_threads()) {
@@ -5695,8 +5693,11 @@
release_gc_alloc_regions(n_workers);
g1_rem_set()->cleanup_after_oops_into_collection_set_do();
- concurrent_g1_refine()->clear_hot_cache();
- concurrent_g1_refine()->set_use_cache(true);
+ // Reset and re-enable the hot card cache.
+ // Note the counts for the cards in the regions in the
+ // collection set are reset when the collection set is freed.
+ hot_card_cache->reset_hot_cache();
+ hot_card_cache->set_use_cache(true);
finalize_for_evac_failure();
@@ -5758,6 +5759,12 @@
assert(!hr->is_empty(), "the region should not be empty");
assert(free_list != NULL, "pre-condition");
+ // Clear the card counts for this region.
+ // Note: we only need to do this if the region is not young
+ // (since we don't refine cards in young regions).
+ if (!hr->is_young()) {
+ _cg1r->hot_card_cache()->reset_card_counts(hr);
+ }
*pre_used += hr->used();
hr->hr_clear(par, true /* clear_space */);
free_list->add_as_head(hr);
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Thu May 16 11:47:51 2013 +0100
@@ -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;
}
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp Thu May 16 11:47:51 2013 +0100
@@ -309,7 +309,8 @@
void G1CollectorPolicy::initialize_flags() {
set_min_alignment(HeapRegion::GrainBytes);
- set_max_alignment(GenRemSet::max_alignment_constraint(rem_set_name()));
+ size_t card_table_alignment = GenRemSet::max_alignment_constraint(rem_set_name());
+ set_max_alignment(MAX2(card_table_alignment, min_alignment()));
if (SurvivorRatio < 1) {
vm_exit_during_initialization("Invalid survivor ratio specified");
}
--- a/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -155,11 +155,6 @@
G1GCPhaseTimes::G1GCPhaseTimes(uint max_gc_threads) :
_max_gc_threads(max_gc_threads),
- _min_clear_cc_time_ms(-1.0),
- _max_clear_cc_time_ms(-1.0),
- _cur_clear_cc_time_ms(0.0),
- _cum_clear_cc_time_ms(0.0),
- _num_cc_clears(0L),
_last_gc_worker_start_times_ms(_max_gc_threads, "%.1lf", false),
_last_ext_root_scan_times_ms(_max_gc_threads, "%.1lf"),
_last_satb_filtering_times_ms(_max_gc_threads, "%.1lf"),
@@ -212,11 +207,11 @@
_last_gc_worker_times_ms.set(i, worker_time);
double worker_known_time = _last_ext_root_scan_times_ms.get(i) +
- _last_satb_filtering_times_ms.get(i) +
- _last_update_rs_times_ms.get(i) +
- _last_scan_rs_times_ms.get(i) +
- _last_obj_copy_times_ms.get(i) +
- _last_termination_times_ms.get(i);
+ _last_satb_filtering_times_ms.get(i) +
+ _last_update_rs_times_ms.get(i) +
+ _last_scan_rs_times_ms.get(i) +
+ _last_obj_copy_times_ms.get(i) +
+ _last_termination_times_ms.get(i);
double worker_other_time = worker_time - worker_known_time;
_last_gc_worker_other_times_ms.set(i, worker_other_time);
@@ -285,15 +280,6 @@
}
print_stats(1, "Code Root Fixup", _cur_collection_code_root_fixup_time_ms);
print_stats(1, "Clear CT", _cur_clear_ct_time_ms);
- if (Verbose && G1Log::finest()) {
- print_stats(1, "Cur Clear CC", _cur_clear_cc_time_ms);
- print_stats(1, "Cum Clear CC", _cum_clear_cc_time_ms);
- print_stats(1, "Min Clear CC", _min_clear_cc_time_ms);
- print_stats(1, "Max Clear CC", _max_clear_cc_time_ms);
- if (_num_cc_clears > 0) {
- print_stats(1, "Avg Clear CC", _cum_clear_cc_time_ms / ((double)_num_cc_clears));
- }
- }
double misc_time_ms = pause_time_sec * MILLIUNITS - accounted_time_ms();
print_stats(1, "Other", misc_time_ms);
if (_cur_verify_before_time_ms > 0.0) {
@@ -311,19 +297,3 @@
print_stats(2, "Verify After", _cur_verify_after_time_ms);
}
}
-
-void G1GCPhaseTimes::record_cc_clear_time_ms(double ms) {
- if (!(Verbose && G1Log::finest())) {
- return;
- }
-
- if (_min_clear_cc_time_ms < 0.0 || ms <= _min_clear_cc_time_ms) {
- _min_clear_cc_time_ms = ms;
- }
- if (_max_clear_cc_time_ms < 0.0 || ms >= _max_clear_cc_time_ms) {
- _max_clear_cc_time_ms = ms;
- }
- _cur_clear_cc_time_ms = ms;
- _cum_clear_cc_time_ms += ms;
- _num_cc_clears++;
-}
--- a/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -133,13 +133,6 @@
double _cur_ref_proc_time_ms;
double _cur_ref_enq_time_ms;
- // Card Table Count Cache stats
- double _min_clear_cc_time_ms; // min
- double _max_clear_cc_time_ms; // max
- double _cur_clear_cc_time_ms; // clearing time during current pause
- double _cum_clear_cc_time_ms; // cummulative clearing time
- jlong _num_cc_clears; // number of times the card count cache has been cleared
-
double _cur_collection_start_sec;
double _root_region_scan_wait_time_ms;
@@ -227,8 +220,6 @@
_root_region_scan_wait_time_ms = time_ms;
}
- void record_cc_clear_time_ms(double ms);
-
void record_young_free_cset_time_ms(double time_ms) {
_recorded_young_free_cset_time_ms = time_ms;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1HotCardCache.cpp Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "gc_implementation/g1/dirtyCardQueue.hpp"
+#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
+#include "gc_implementation/g1/g1HotCardCache.hpp"
+#include "gc_implementation/g1/g1RemSet.hpp"
+#include "gc_implementation/g1/heapRegion.hpp"
+#include "runtime/atomic.hpp"
+
+G1HotCardCache::G1HotCardCache(G1CollectedHeap *g1h):
+ _g1h(g1h), _hot_cache(NULL), _use_cache(false), _card_counts(g1h) {}
+
+void G1HotCardCache::initialize() {
+ if (default_use_cache()) {
+ _use_cache = true;
+
+ _hot_cache_size = (1 << G1ConcRSLogCacheSize);
+ _hot_cache = NEW_C_HEAP_ARRAY(jbyte*, _hot_cache_size, mtGC);
+
+ _n_hot = 0;
+ _hot_cache_idx = 0;
+
+ // For refining the cards in the hot cache in parallel
+ int n_workers = (ParallelGCThreads > 0 ?
+ _g1h->workers()->total_workers() : 1);
+ _hot_cache_par_chunk_size = MAX2(1, _hot_cache_size / n_workers);
+ _hot_cache_par_claimed_idx = 0;
+
+ _card_counts.initialize();
+ }
+}
+
+G1HotCardCache::~G1HotCardCache() {
+ if (default_use_cache()) {
+ assert(_hot_cache != NULL, "Logic");
+ FREE_C_HEAP_ARRAY(jbyte*, _hot_cache, mtGC);
+ }
+}
+
+jbyte* G1HotCardCache::insert(jbyte* card_ptr) {
+ uint count = _card_counts.add_card_count(card_ptr);
+ if (!_card_counts.is_hot(count)) {
+ // The card is not hot so do not store it in the cache;
+ // return it for immediate refining.
+ return card_ptr;
+ }
+
+ // Otherwise, the card is hot.
+ jbyte* res = NULL;
+ MutexLockerEx x(HotCardCache_lock, Mutex::_no_safepoint_check_flag);
+ if (_n_hot == _hot_cache_size) {
+ res = _hot_cache[_hot_cache_idx];
+ _n_hot--;
+ }
+
+ // Now _n_hot < _hot_cache_size, and we can insert at _hot_cache_idx.
+ _hot_cache[_hot_cache_idx] = card_ptr;
+ _hot_cache_idx++;
+
+ if (_hot_cache_idx == _hot_cache_size) {
+ // Wrap around
+ _hot_cache_idx = 0;
+ }
+ _n_hot++;
+
+ return res;
+}
+
+void G1HotCardCache::drain(int worker_i,
+ G1RemSet* g1rs,
+ DirtyCardQueue* into_cset_dcq) {
+ if (!default_use_cache()) {
+ assert(_hot_cache == NULL, "Logic");
+ return;
+ }
+
+ assert(_hot_cache != NULL, "Logic");
+ assert(!use_cache(), "cache should be disabled");
+ int start_idx;
+
+ while ((start_idx = _hot_cache_par_claimed_idx) < _n_hot) { // read once
+ int end_idx = start_idx + _hot_cache_par_chunk_size;
+
+ if (start_idx ==
+ Atomic::cmpxchg(end_idx, &_hot_cache_par_claimed_idx, start_idx)) {
+ // The current worker has successfully claimed the chunk [start_idx..end_idx)
+ end_idx = MIN2(end_idx, _n_hot);
+ for (int i = start_idx; i < end_idx; i++) {
+ jbyte* card_ptr = _hot_cache[i];
+ if (card_ptr != NULL) {
+ if (g1rs->refine_card(card_ptr, worker_i, true)) {
+ // The part of the heap spanned by the card contains references
+ // that point into the current collection set.
+ // We need to record the card pointer in the DirtyCardQueueSet
+ // that we use for such cards.
+ //
+ // The only time we care about recording cards that contain
+ // references that point into the collection set is during
+ // RSet updating while within an evacuation pause.
+ // In this case worker_i should be the id of a GC worker thread
+ assert(SafepointSynchronize::is_at_safepoint(), "Should be at a safepoint");
+ assert(worker_i < (int) (ParallelGCThreads == 0 ? 1 : ParallelGCThreads),
+ err_msg("incorrect worker id: "INT32_FORMAT, worker_i));
+
+ into_cset_dcq->enqueue(card_ptr);
+ }
+ }
+ }
+ }
+ }
+ // The existing entries in the hot card cache, which were just refined
+ // above, are discarded prior to re-enabling the cache near the end of the GC.
+}
+
+void G1HotCardCache::resize_card_counts(size_t heap_capacity) {
+ _card_counts.resize(heap_capacity);
+}
+
+void G1HotCardCache::reset_card_counts(HeapRegion* hr) {
+ _card_counts.clear_region(hr);
+}
+
+void G1HotCardCache::reset_card_counts() {
+ _card_counts.clear_all();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1HotCardCache.hpp Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1HOTCARDCACHE_HPP
+#define SHARE_VM_GC_IMPLEMENTATION_G1_G1HOTCARDCACHE_HPP
+
+#include "gc_implementation/g1/g1_globals.hpp"
+#include "gc_implementation/g1/g1CardCounts.hpp"
+#include "memory/allocation.hpp"
+#include "runtime/safepoint.hpp"
+#include "runtime/thread.inline.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+class DirtyCardQueue;
+class G1CollectedHeap;
+class G1RemSet;
+class HeapRegion;
+
+// An evicting cache of cards that have been logged by the G1 post
+// write barrier. Placing a card in the cache delays the refinement
+// of the card until the card is evicted, or the cache is drained
+// during the next evacuation pause.
+//
+// The first thing the G1 post write barrier does is to check whether
+// the card containing the updated pointer is already dirty and, if
+// so, skips the remaining code in the barrier.
+//
+// Delaying the refinement of a card will make the card fail the
+// first is_dirty check in the write barrier, skipping the remainder
+// of the write barrier.
+//
+// This can significantly reduce the overhead of the write barrier
+// code, increasing throughput.
+
+class G1HotCardCache: public CHeapObj<mtGC> {
+ G1CollectedHeap* _g1h;
+
+ // The card cache table
+ jbyte** _hot_cache;
+
+ int _hot_cache_size;
+ int _n_hot;
+ int _hot_cache_idx;
+
+ int _hot_cache_par_chunk_size;
+ volatile int _hot_cache_par_claimed_idx;
+
+ bool _use_cache;
+
+ G1CardCounts _card_counts;
+
+ bool default_use_cache() const {
+ return (G1ConcRSLogCacheSize > 0);
+ }
+
+ public:
+ G1HotCardCache(G1CollectedHeap* g1h);
+ ~G1HotCardCache();
+
+ void initialize();
+
+ bool use_cache() { return _use_cache; }
+
+ void set_use_cache(bool b) {
+ _use_cache = (b ? default_use_cache() : false);
+ }
+
+ // Returns the card to be refined or NULL.
+ //
+ // Increments the count for given the card. if the card is not 'hot',
+ // it is returned for immediate refining. Otherwise the card is
+ // added to the hot card cache.
+ // If there is enough room in the hot card cache for the card we're
+ // adding, NULL is returned and no further action in needed.
+ // If we evict a card from the cache to make room for the new card,
+ // the evicted card is then returned for refinement.
+ jbyte* insert(jbyte* card_ptr);
+
+ // Refine the cards that have delayed as a result of
+ // being in the cache.
+ void drain(int worker_i, G1RemSet* g1rs, DirtyCardQueue* into_cset_dcq);
+
+ // Set up for parallel processing of the cards in the hot cache
+ void reset_hot_cache_claimed_index() {
+ _hot_cache_par_claimed_idx = 0;
+ }
+
+ // Resets the hot card cache and discards the entries.
+ void reset_hot_cache() {
+ assert(SafepointSynchronize::is_at_safepoint(), "Should be at a safepoint");
+ assert(Thread::current()->is_VM_thread(), "Current thread should be the VMthread");
+ _hot_cache_idx = 0; _n_hot = 0;
+ }
+
+ bool hot_cache_is_empty() { return _n_hot == 0; }
+
+ // Resizes the card counts table to match the given capacity
+ void resize_card_counts(size_t heap_capacity);
+
+ // Zeros the values in the card counts table for entire committed heap
+ void reset_card_counts();
+
+ // Zeros the values in the card counts table for the given region
+ void reset_card_counts(HeapRegion* hr);
+};
+
+#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1HOTCARDCACHE_HPP
--- a/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp Thu May 16 11:47:51 2013 +0100
@@ -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();
--- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 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
@@ -29,6 +29,7 @@
#include "gc_implementation/g1/g1BlockOffsetTable.inline.hpp"
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
#include "gc_implementation/g1/g1CollectorPolicy.hpp"
+#include "gc_implementation/g1/g1HotCardCache.hpp"
#include "gc_implementation/g1/g1GCPhaseTimes.hpp"
#include "gc_implementation/g1/g1OopClosures.inline.hpp"
#include "gc_implementation/g1/g1RemSet.inline.hpp"
@@ -169,14 +170,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);
}
@@ -248,7 +248,7 @@
assert(SafepointSynchronize::is_at_safepoint(), "not during an evacuation pause");
assert(worker_i < (int) (ParallelGCThreads == 0 ? 1 : ParallelGCThreads), "should be a GC worker");
- if (_g1rs->concurrentRefineOneCard(card_ptr, worker_i, true)) {
+ if (_g1rs->refine_card(card_ptr, worker_i, true)) {
// 'card_ptr' contains references that point into the collection
// set. We need to record the card in the DCQS
// (G1CollectedHeap::into_cset_dirty_card_queue_set())
@@ -289,9 +289,6 @@
#if CARD_REPEAT_HISTO
ct_freq_update_histo_and_reset();
#endif
- if (worker_i == 0) {
- _cg1r->clear_and_record_card_counts();
- }
// We cache the value of 'oc' closure into the appropriate slot in the
// _cset_rs_update_cl for this worker
@@ -397,7 +394,7 @@
// RSet updating,
// * the post-write barrier shouldn't be logging updates to young
// regions (but there is a situation where this can happen - see
- // the comment in G1RemSet::concurrentRefineOneCard below -
+ // the comment in G1RemSet::refine_card() below -
// that should not be applicable here), and
// * during actual RSet updating, the filtering of cards in young
// regions in HeapRegion::oops_on_card_seq_iterate_careful is
@@ -503,8 +500,6 @@
claim_val);
}
-
-
G1TriggerClosure::G1TriggerClosure() :
_triggered(false) { }
@@ -525,13 +520,91 @@
_record_refs_into_cset(record_refs_into_cset),
_push_ref_cl(push_ref_cl), _worker_i(worker_i) { }
-bool G1RemSet::concurrentRefineOneCard_impl(jbyte* card_ptr, int worker_i,
- bool check_for_refs_into_cset) {
+// Returns true if the given card contains references that point
+// into the collection set, if we're checking for such references;
+// false otherwise.
+
+bool G1RemSet::refine_card(jbyte* card_ptr, int worker_i,
+ bool check_for_refs_into_cset) {
+
+ // If the card is no longer dirty, nothing to do.
+ if (*card_ptr != CardTableModRefBS::dirty_card_val()) {
+ // No need to return that this card contains refs that point
+ // into the collection set.
+ return false;
+ }
+
// Construct the region representing the card.
HeapWord* start = _ct_bs->addr_for(card_ptr);
// And find the region containing it.
HeapRegion* r = _g1->heap_region_containing(start);
- assert(r != NULL, "unexpected null");
+ if (r == NULL) {
+ // Again no need to return that this card contains refs that
+ // point into the collection set.
+ return false; // Not in the G1 heap (might be in perm, for example.)
+ }
+
+ // Why do we have to check here whether a card is on a young region,
+ // given that we dirty young regions and, as a result, the
+ // post-barrier is supposed to filter them out and never to enqueue
+ // them? When we allocate a new region as the "allocation region" we
+ // actually dirty its cards after we release the lock, since card
+ // dirtying while holding the lock was a performance bottleneck. So,
+ // as a result, it is possible for other threads to actually
+ // allocate objects in the region (after the acquire the lock)
+ // before all the cards on the region are dirtied. This is unlikely,
+ // and it doesn't happen often, but it can happen. So, the extra
+ // check below filters out those cards.
+ if (r->is_young()) {
+ return false;
+ }
+
+ // While we are processing RSet buffers during the collection, we
+ // actually don't want to scan any cards on the collection set,
+ // since we don't want to update remebered sets with entries that
+ // point into the collection set, given that live objects from the
+ // collection set are about to move and such entries will be stale
+ // very soon. This change also deals with a reliability issue which
+ // involves scanning a card in the collection set and coming across
+ // an array that was being chunked and looking malformed. Note,
+ // however, that if evacuation fails, we have to scan any objects
+ // that were not moved and create any missing entries.
+ if (r->in_collection_set()) {
+ return false;
+ }
+
+ // The result from the hot card cache insert call is either:
+ // * pointer to the current card
+ // (implying that the current card is not 'hot'),
+ // * null
+ // (meaning we had inserted the card ptr into the "hot" card cache,
+ // which had some headroom),
+ // * a pointer to a "hot" card that was evicted from the "hot" cache.
+ //
+
+ G1HotCardCache* hot_card_cache = _cg1r->hot_card_cache();
+ if (hot_card_cache->use_cache()) {
+ assert(!check_for_refs_into_cset, "sanity");
+ assert(!SafepointSynchronize::is_at_safepoint(), "sanity");
+
+ card_ptr = hot_card_cache->insert(card_ptr);
+ if (card_ptr == NULL) {
+ // There was no eviction. Nothing to do.
+ return false;
+ }
+
+ start = _ct_bs->addr_for(card_ptr);
+ r = _g1->heap_region_containing(start);
+ if (r == NULL) {
+ // Not in the G1 heap
+ return false;
+ }
+
+ // Checking whether the region we got back from the cache
+ // is young here is inappropriate. The region could have been
+ // freed, reallocated and tagged as young while in the cache.
+ // Hence we could see its young type change at any time.
+ }
// Don't use addr_for(card_ptr + 1) which can ask for
// a card beyond the heap. This is not safe without a perm
@@ -611,140 +684,17 @@
_conc_refine_cards++;
}
- return trigger_cl.triggered();
-}
-
-bool G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i,
- bool check_for_refs_into_cset) {
- // If the card is no longer dirty, nothing to do.
- if (*card_ptr != CardTableModRefBS::dirty_card_val()) {
- // No need to return that this card contains refs that point
- // into the collection set.
- return false;
- }
-
- // Construct the region representing the card.
- HeapWord* start = _ct_bs->addr_for(card_ptr);
- // And find the region containing it.
- HeapRegion* r = _g1->heap_region_containing(start);
- if (r == NULL) {
- // Again no need to return that this card contains refs that
- // point into the collection set.
- return false; // Not in the G1 heap (might be in perm, for example.)
- }
- // Why do we have to check here whether a card is on a young region,
- // given that we dirty young regions and, as a result, the
- // post-barrier is supposed to filter them out and never to enqueue
- // them? When we allocate a new region as the "allocation region" we
- // actually dirty its cards after we release the lock, since card
- // dirtying while holding the lock was a performance bottleneck. So,
- // as a result, it is possible for other threads to actually
- // allocate objects in the region (after the acquire the lock)
- // before all the cards on the region are dirtied. This is unlikely,
- // and it doesn't happen often, but it can happen. So, the extra
- // check below filters out those cards.
- if (r->is_young()) {
- return false;
- }
- // While we are processing RSet buffers during the collection, we
- // actually don't want to scan any cards on the collection set,
- // since we don't want to update remebered sets with entries that
- // point into the collection set, given that live objects from the
- // collection set are about to move and such entries will be stale
- // very soon. This change also deals with a reliability issue which
- // involves scanning a card in the collection set and coming across
- // an array that was being chunked and looking malformed. Note,
- // however, that if evacuation fails, we have to scan any objects
- // that were not moved and create any missing entries.
- if (r->in_collection_set()) {
- return false;
- }
+ // This gets set to true if the card being refined has
+ // references that point into the collection set.
+ bool has_refs_into_cset = trigger_cl.triggered();
- // Should we defer processing the card?
- //
- // Previously the result from the insert_cache call would be
- // either card_ptr (implying that card_ptr was currently "cold"),
- // null (meaning we had inserted the card ptr into the "hot"
- // cache, which had some headroom), or a "hot" card ptr
- // extracted from the "hot" cache.
- //
- // Now that the _card_counts cache in the ConcurrentG1Refine
- // instance is an evicting hash table, the result we get back
- // could be from evicting the card ptr in an already occupied
- // bucket (in which case we have replaced the card ptr in the
- // bucket with card_ptr and "defer" is set to false). To avoid
- // having a data structure (updates to which would need a lock)
- // to hold these unprocessed dirty cards, we need to immediately
- // process card_ptr. The actions needed to be taken on return
- // from cache_insert are summarized in the following table:
- //
- // res defer action
- // --------------------------------------------------------------
- // null false card evicted from _card_counts & replaced with
- // card_ptr; evicted ptr added to hot cache.
- // No need to process res; immediately process card_ptr
- //
- // null true card not evicted from _card_counts; card_ptr added
- // to hot cache.
- // Nothing to do.
- //
- // non-null false card evicted from _card_counts & replaced with
- // card_ptr; evicted ptr is currently "cold" or
- // caused an eviction from the hot cache.
- // Immediately process res; process card_ptr.
- //
- // non-null true card not evicted from _card_counts; card_ptr is
- // currently cold, or caused an eviction from hot
- // cache.
- // Immediately process res; no need to process card_ptr.
-
-
- jbyte* res = card_ptr;
- bool defer = false;
+ // We should only be detecting that the card contains references
+ // that point into the collection set if the current thread is
+ // a GC worker thread.
+ assert(!has_refs_into_cset || SafepointSynchronize::is_at_safepoint(),
+ "invalid result at non safepoint");
- // This gets set to true if the card being refined has references
- // that point into the collection set.
- bool oops_into_cset = false;
-
- if (_cg1r->use_cache()) {
- jbyte* res = _cg1r->cache_insert(card_ptr, &defer);
- if (res != NULL && (res != card_ptr || defer)) {
- start = _ct_bs->addr_for(res);
- r = _g1->heap_region_containing(start);
- if (r != NULL) {
- // Checking whether the region we got back from the cache
- // is young here is inappropriate. The region could have been
- // freed, reallocated and tagged as young while in the cache.
- // Hence we could see its young type change at any time.
- //
- // Process card pointer we get back from the hot card cache. This
- // will check whether the region containing the card is young
- // _after_ checking that the region has been allocated from.
- oops_into_cset = concurrentRefineOneCard_impl(res, worker_i,
- false /* check_for_refs_into_cset */);
- // The above call to concurrentRefineOneCard_impl is only
- // performed if the hot card cache is enabled. This cache is
- // disabled during an evacuation pause - which is the only
- // time when we need know if the card contains references
- // that point into the collection set. Also when the hot card
- // cache is enabled, this code is executed by the concurrent
- // refine threads - rather than the GC worker threads - and
- // concurrentRefineOneCard_impl will return false.
- assert(!oops_into_cset, "should not see true here");
- }
- }
- }
-
- if (!defer) {
- oops_into_cset =
- concurrentRefineOneCard_impl(card_ptr, worker_i, check_for_refs_into_cset);
- // We should only be detecting that the card contains references
- // that point into the collection set if the current thread is
- // a GC worker thread.
- assert(!oops_into_cset || SafepointSynchronize::is_at_safepoint(),
- "invalid result at non safepoint");
- }
- return oops_into_cset;
+ return has_refs_into_cset;
}
class HRRSStatsIter: public HeapRegionClosure {
@@ -847,13 +797,16 @@
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
dcqs.concatenate_logs();
}
- bool cg1r_use_cache = _cg1r->use_cache();
- _cg1r->set_use_cache(false);
+
+ G1HotCardCache* hot_card_cache = _cg1r->hot_card_cache();
+ bool use_hot_card_cache = hot_card_cache->use_cache();
+ hot_card_cache->set_use_cache(false);
+
DirtyCardQueue into_cset_dcq(&_g1->into_cset_dirty_card_queue_set());
updateRS(&into_cset_dcq, 0);
_g1->into_cset_dirty_card_queue_set().clear();
- _cg1r->set_use_cache(cg1r_use_cache);
+ hot_card_cache->set_use_cache(use_hot_card_cache);
assert(JavaThread::dirty_card_queue_set().completed_buffers_num() == 0, "All should be consumed");
}
}
--- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 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
@@ -53,27 +53,19 @@
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.
OopsInHeapRegionClosure** _cset_rs_update_cl;
- // The routine that performs the actual work of refining a dirty
- // card.
- // If check_for_refs_into_refs is true then a true result is returned
- // if the card contains oops that have references into the current
- // collection set.
- bool concurrentRefineOneCard_impl(jbyte* card_ptr, int worker_i,
- bool check_for_refs_into_cset);
-
public:
// This is called to reset dual hash tables after the gc pause
// is finished and the initial hash table is no longer being
@@ -90,8 +82,7 @@
// function can be helpful in partitioning the work to be done. It
// should be the same as the "i" passed to the calling thread's
// work(i) function. In the sequential case this param will be ingored.
- void oops_into_collection_set_do(OopsInHeapRegionClosure* blk,
- int worker_i);
+ void oops_into_collection_set_do(OopsInHeapRegionClosure* blk, int worker_i);
// Prepare for and cleanup after an oops_into_collection_set_do
// call. Must call each of these once before and after (in sequential
@@ -124,14 +115,13 @@
void scrub_par(BitMap* region_bm, BitMap* card_bm,
uint worker_num, int claim_val);
- // Refine the card corresponding to "card_ptr". If "sts" is non-NULL,
- // join and leave around parts that must be atomic wrt GC. (NULL means
- // being done at a safepoint.)
+ // Refine the card corresponding to "card_ptr".
// If check_for_refs_into_cset is true, a true result is returned
// if the given card contains oops that have references into the
// current collection set.
- virtual bool concurrentRefineOneCard(jbyte* card_ptr, int worker_i,
- bool check_for_refs_into_cset);
+ virtual bool refine_card(jbyte* card_ptr,
+ int worker_i,
+ bool check_for_refs_into_cset);
// Print any relevant summary info.
virtual void print_summary_info();
--- a/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp Thu May 16 11:47:51 2013 +0100
@@ -163,16 +163,12 @@
"Select green, yellow and red zones adaptively to meet the " \
"the pause requirements.") \
\
- develop(intx, G1ConcRSLogCacheSize, 10, \
+ product(uintx, G1ConcRSLogCacheSize, 10, \
"Log base 2 of the length of conc RS hot-card cache.") \
\
- develop(intx, G1ConcRSHotCardLimit, 4, \
+ product(uintx, G1ConcRSHotCardLimit, 4, \
"The threshold that defines (>=) a hot card.") \
\
- develop(intx, G1MaxHotCardCountSizePercent, 25, \
- "The maximum size of the hot card count cache as a " \
- "percentage of the number of cards for the maximum heap.") \
- \
develop(bool, G1PrintOopAppls, false, \
"When true, print applications of closures to external locs.") \
\
@@ -247,10 +243,6 @@
"If non-0 is the number of parallel rem set update threads, " \
"otherwise the value is determined ergonomically.") \
\
- develop(intx, G1CardCountCacheExpandThreshold, 16, \
- "Expand the card count cache if the number of collisions for " \
- "a particular entry exceeds this value.") \
- \
develop(bool, G1VerifyCTCleanup, false, \
"Verify card table cleanup.") \
\
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Thu May 16 11:47:51 2013 +0100
@@ -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)) {
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp Thu May 16 11:47:51 2013 +0100
@@ -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<mtGC> {
+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
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.cpp Thu May 16 11:47:51 2013 +0100
@@ -124,11 +124,11 @@
}
assert(_regions[index] == NULL, "invariant");
_regions[index] = new_hr;
- increment_length(&_allocated_length);
+ increment_allocated_length();
}
// Have to increment the length first, otherwise we will get an
// assert failure at(index) below.
- increment_length(&_length);
+ increment_length();
HeapRegion* hr = at(index);
list->add_as_tail(hr);
@@ -201,45 +201,29 @@
}
}
-MemRegion HeapRegionSeq::shrink_by(size_t shrink_bytes,
- uint* num_regions_deleted) {
+uint HeapRegionSeq::shrink_by(uint num_regions_to_remove) {
// Reset this in case it's currently pointing into the regions that
// we just removed.
_next_search_index = 0;
- assert(shrink_bytes % os::vm_page_size() == 0, "unaligned");
- assert(shrink_bytes % HeapRegion::GrainBytes == 0, "unaligned");
assert(length() > 0, "the region sequence should not be empty");
assert(length() <= _allocated_length, "invariant");
assert(_allocated_length > 0, "we should have at least one region committed");
+ assert(num_regions_to_remove < length(), "We should never remove all regions");
- // around the loop, i will be the next region to be removed
- uint i = length() - 1;
- assert(i > 0, "we should never remove all regions");
- // [last_start, end) is the MemRegion that covers the regions we will remove.
- HeapWord* end = at(i)->end();
- HeapWord* last_start = end;
- *num_regions_deleted = 0;
- while (shrink_bytes > 0) {
- HeapRegion* cur = at(i);
- // We should leave the humongous regions where they are.
- if (cur->isHumongous()) break;
- // We should stop shrinking if we come across a non-empty region.
- if (!cur->is_empty()) break;
+ uint i = 0;
+ for (; i < num_regions_to_remove; i++) {
+ HeapRegion* cur = at(length() - 1);
- i -= 1;
- *num_regions_deleted += 1;
- shrink_bytes -= cur->capacity();
- last_start = cur->bottom();
- decrement_length(&_length);
- // We will reclaim the HeapRegion. _allocated_length should be
- // covering this index. So, even though we removed the region from
- // the active set by decreasing _length, we still have it
- // available in the future if we need to re-use it.
- assert(i > 0, "we should never remove all regions");
- assert(length() > 0, "we should never remove all regions");
+ if (!cur->is_empty()) {
+ // We have to give up if the region can not be moved
+ break;
}
- return MemRegion(last_start, end);
+ assert(!cur->isHumongous(), "Humongous regions should not be empty");
+
+ decrement_length();
+ }
+ return i;
}
#ifndef PRODUCT
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.hpp Thu May 16 11:47:51 2013 +0100
@@ -92,14 +92,19 @@
// address is valid.
inline uintx addr_to_index_biased(HeapWord* addr) const;
- void increment_length(uint* length) {
- assert(*length < _max_length, "pre-condition");
- *length += 1;
+ void increment_allocated_length() {
+ assert(_allocated_length < _max_length, "pre-condition");
+ _allocated_length++;
}
- void decrement_length(uint* length) {
- assert(*length > 0, "pre-condition");
- *length -= 1;
+ void increment_length() {
+ assert(_length < _max_length, "pre-condition");
+ _length++;
+ }
+
+ void decrement_length() {
+ assert(_length > 0, "pre-condition");
+ _length--;
}
public:
@@ -153,11 +158,9 @@
void iterate_from(HeapRegion* hr, HeapRegionClosure* blk) const;
// Tag as uncommitted as many regions that are completely free as
- // possible, up to shrink_bytes, from the suffix of the committed
- // sequence. Return a MemRegion that corresponds to the address
- // range of the uncommitted regions. Assume shrink_bytes is page and
- // heap region aligned.
- MemRegion shrink_by(size_t shrink_bytes, uint* num_regions_deleted);
+ // possible, up to num_regions_to_remove, from the suffix of the committed
+ // sequence. Return the actual number of removed regions.
+ uint shrink_by(uint num_regions_to_remove);
// Do some sanity checking.
void verify_optional() PRODUCT_RETURN;
--- a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp Thu May 16 11:47:51 2013 +0100
@@ -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;
--- a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.hpp Thu May 16 11:47:51 2013 +0100
@@ -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);
}
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp Thu May 16 11:47:51 2013 +0100
@@ -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");
}
}
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskThread.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/gcTaskThread.cpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/objectStartArray.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/objectStartArray.cpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Thu May 16 11:47:51 2013 +0100
@@ -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();
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.hpp Thu May 16 11:47:51 2013 +0100
@@ -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; }
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Thu May 16 11:47:51 2013 +0100
@@ -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,
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp Thu May 16 11:47:51 2013 +0100
@@ -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 <class T> static inline void adjust_pointer(T* p, bool is_root);
- static void adjust_root_pointer(oop* p) { adjust_pointer(p, true); }
-
template <class T>
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 <class T> static inline void mark_and_push(ParCompactionManager* cm,
T* p);
+ template <class T> 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 <class T>
-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);
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp Thu May 16 11:47:51 2013 +0100
@@ -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();
--- a/hotspot/src/share/vm/gc_implementation/shared/markSweep.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/shared/markSweep.cpp Thu May 16 11:47:51 2013 +0100
@@ -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(),
--- a/hotspot/src/share/vm/gc_implementation/shared/markSweep.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/shared/markSweep.hpp Thu May 16 11:47:51 2013 +0100
@@ -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 <class T> 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 <class T> static inline void adjust_pointer(T* p);
};
class PreservedMark VALUE_OBJ_CLASS_SPEC {
--- a/hotspot/src/share/vm/gc_implementation/shared/markSweep.inline.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/shared/markSweep.inline.hpp Thu May 16 11:47:51 2013 +0100
@@ -76,7 +76,7 @@
_objarray_stack.push(task);
}
-template <class T> inline void MarkSweep::adjust_pointer(T* p, bool isroot) {
+template <class T> 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);
--- a/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp Thu May 16 11:47:51 2013 +0100
@@ -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.
--- a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp Thu May 16 11:47:51 2013 +0100
@@ -32,6 +32,7 @@
#include "interpreter/interpreterRuntime.hpp"
#include "memory/cardTableModRefBS.hpp"
#include "memory/resourceArea.hpp"
+#include "oops/methodCounters.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/oop.inline.hpp"
#include "prims/jvmtiExport.hpp"
@@ -304,11 +305,12 @@
#define METHOD istate->method()
-#define INVOCATION_COUNT METHOD->invocation_counter()
-#define BACKEDGE_COUNT METHOD->backedge_counter()
-
-
-#define INCR_INVOCATION_COUNT INVOCATION_COUNT->increment()
+#define GET_METHOD_COUNTERS(res) \
+ res = METHOD->method_counters(); \
+ if (res == NULL) { \
+ CALL_VM(res = InterpreterRuntime::build_method_counters(THREAD, METHOD), handle_exception); \
+ }
+
#define OSR_REQUEST(res, branch_pc) \
CALL_VM(res=InterpreterRuntime::frequency_counter_overflow(THREAD, branch_pc), handle_exception);
/*
@@ -325,10 +327,12 @@
#define DO_BACKEDGE_CHECKS(skip, branch_pc) \
if ((skip) <= 0) { \
+ MethodCounters* mcs; \
+ GET_METHOD_COUNTERS(mcs); \
if (UseLoopCounter) { \
bool do_OSR = UseOnStackReplacement; \
- BACKEDGE_COUNT->increment(); \
- if (do_OSR) do_OSR = BACKEDGE_COUNT->reached_InvocationLimit(); \
+ mcs->backedge_counter()->increment(); \
+ if (do_OSR) do_OSR = mcs->backedge_counter()->reached_InvocationLimit(); \
if (do_OSR) { \
nmethod* osr_nmethod; \
OSR_REQUEST(osr_nmethod, branch_pc); \
@@ -341,7 +345,7 @@
} \
} \
} /* UseCompiler ... */ \
- INCR_INVOCATION_COUNT; \
+ mcs->invocation_counter()->increment(); \
SAFEPOINT; \
}
@@ -618,11 +622,13 @@
// count invocations
assert(initialized, "Interpreter not initialized");
if (_compiling) {
+ MethodCounters* mcs;
+ GET_METHOD_COUNTERS(mcs);
if (ProfileInterpreter) {
- METHOD->increment_interpreter_invocation_count();
+ METHOD->increment_interpreter_invocation_count(THREAD);
}
- INCR_INVOCATION_COUNT;
- if (INVOCATION_COUNT->reached_InvocationLimit()) {
+ mcs->invocation_counter()->increment();
+ if (mcs->invocation_counter()->reached_InvocationLimit()) {
CALL_VM((void)InterpreterRuntime::frequency_counter_overflow(THREAD, NULL), handle_exception);
// We no longer retry on a counter overflow
--- a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp Thu May 16 11:47:51 2013 +0100
@@ -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",
--- a/hotspot/src/share/vm/memory/allocation.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/allocation.cpp Thu May 16 11:47:51 2013 +0100
@@ -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
--- a/hotspot/src/share/vm/memory/allocation.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/allocation.hpp Thu May 16 11:47:51 2013 +0100
@@ -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))
--- a/hotspot/src/share/vm/memory/allocation.inline.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/allocation.inline.hpp Thu May 16 11:47:51 2013 +0100
@@ -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;
}
@@ -128,14 +132,14 @@
int alignment = os::vm_allocation_granularity();
_size = align_size_up(_size, alignment);
- _addr = os::reserve_memory(_size, NULL, alignment);
+ _addr = os::reserve_memory(_size, NULL, alignment, F);
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;
--- a/hotspot/src/share/vm/memory/blockOffsetTable.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/blockOffsetTable.cpp Thu May 16 11:47:51 2013 +0100
@@ -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 {
--- a/hotspot/src/share/vm/memory/cardTableModRefBS.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/cardTableModRefBS.cpp Thu May 16 11:47:51 2013 +0100
@@ -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
--- a/hotspot/src/share/vm/memory/collectorPolicy.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/collectorPolicy.cpp Thu May 16 11:47:51 2013 +0100
@@ -48,6 +48,17 @@
// CollectorPolicy methods.
void CollectorPolicy::initialize_flags() {
+ assert(max_alignment() >= min_alignment(),
+ err_msg("max_alignment: " SIZE_FORMAT " less than min_alignment: " SIZE_FORMAT,
+ max_alignment(), min_alignment()));
+ assert(max_alignment() % min_alignment() == 0,
+ err_msg("max_alignment: " SIZE_FORMAT " not aligned by min_alignment: " SIZE_FORMAT,
+ max_alignment(), min_alignment()));
+
+ if (MaxHeapSize < InitialHeapSize) {
+ vm_exit_during_initialization("Incompatible initial and maximum heap sizes specified");
+ }
+
if (MetaspaceSize > MaxMetaspaceSize) {
MaxMetaspaceSize = MetaspaceSize;
}
@@ -71,21 +82,9 @@
}
void CollectorPolicy::initialize_size_info() {
- // User inputs from -mx and ms are aligned
- set_initial_heap_byte_size(InitialHeapSize);
- if (initial_heap_byte_size() == 0) {
- set_initial_heap_byte_size(NewSize + OldSize);
- }
- set_initial_heap_byte_size(align_size_up(_initial_heap_byte_size,
- min_alignment()));
-
- set_min_heap_byte_size(Arguments::min_heap_size());
- if (min_heap_byte_size() == 0) {
- set_min_heap_byte_size(NewSize + OldSize);
- }
- set_min_heap_byte_size(align_size_up(_min_heap_byte_size,
- min_alignment()));
-
+ // User inputs from -mx and ms must be aligned
+ set_min_heap_byte_size(align_size_up(Arguments::min_heap_size(), min_alignment()));
+ set_initial_heap_byte_size(align_size_up(InitialHeapSize, min_alignment()));
set_max_heap_byte_size(align_size_up(MaxHeapSize, max_alignment()));
// Check heap parameter properties
@@ -201,9 +200,6 @@
// All sizes must be multiples of the generation granularity.
set_min_alignment((uintx) Generation::GenGrain);
set_max_alignment(compute_max_alignment());
- assert(max_alignment() >= min_alignment() &&
- max_alignment() % min_alignment() == 0,
- "invalid alignment constraints");
CollectorPolicy::initialize_flags();
@@ -233,9 +229,6 @@
GenCollectorPolicy::initialize_flags();
OldSize = align_size_down(OldSize, min_alignment());
- if (NewSize + OldSize > MaxHeapSize) {
- MaxHeapSize = NewSize + OldSize;
- }
if (FLAG_IS_CMDLINE(OldSize) && FLAG_IS_DEFAULT(NewSize)) {
// NewRatio will be used later to set the young generation size so we use
@@ -250,6 +243,27 @@
}
MaxHeapSize = align_size_up(MaxHeapSize, max_alignment());
+ // adjust max heap size if necessary
+ if (NewSize + OldSize > MaxHeapSize) {
+ if (FLAG_IS_CMDLINE(MaxHeapSize)) {
+ // somebody set a maximum heap size with the intention that we should not
+ // exceed it. Adjust New/OldSize as necessary.
+ uintx calculated_size = NewSize + OldSize;
+ double shrink_factor = (double) MaxHeapSize / calculated_size;
+ // align
+ NewSize = align_size_down((uintx) (NewSize * shrink_factor), min_alignment());
+ // OldSize is already aligned because above we aligned MaxHeapSize to
+ // max_alignment(), and we just made sure that NewSize is aligned to
+ // min_alignment(). In initialize_flags() we verified that max_alignment()
+ // is a multiple of min_alignment().
+ OldSize = MaxHeapSize - NewSize;
+ } else {
+ MaxHeapSize = NewSize + OldSize;
+ }
+ }
+ // need to do this again
+ MaxHeapSize = align_size_up(MaxHeapSize, max_alignment());
+
always_do_update_barrier = UseConcMarkSweepGC;
// Check validity of heap flags
--- a/hotspot/src/share/vm/memory/filemap.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/filemap.cpp Thu May 16 11:47:51 2013 +0100
@@ -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);
}
--- a/hotspot/src/share/vm/memory/genCollectedHeap.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/genCollectedHeap.cpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/share/vm/memory/genCollectedHeap.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/genCollectedHeap.hpp Thu May 16 11:47:51 2013 +0100
@@ -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
--- a/hotspot/src/share/vm/memory/genMarkSweep.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/genMarkSweep.cpp Thu May 16 11:47:51 2013 +0100
@@ -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;
--- a/hotspot/src/share/vm/memory/metachunk.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/metachunk.cpp Thu May 16 11:47:51 2013 +0100
@@ -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.
--- a/hotspot/src/share/vm/memory/metachunk.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/metachunk.hpp Thu May 16 11:47:51 2013 +0100
@@ -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; }
--- a/hotspot/src/share/vm/memory/metaspace.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/metaspace.cpp Thu May 16 11:47:51 2013 +0100
@@ -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<Metablock, FreeList>::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<Metablock, FreeList>::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());
--- a/hotspot/src/share/vm/memory/metaspace.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/metaspace.hpp Thu May 16 11:47:51 2013 +0100
@@ -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;
--- a/hotspot/src/share/vm/memory/metaspaceCounters.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/metaspaceCounters.cpp Thu May 16 11:47:51 2013 +0100
@@ -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);
}
--- a/hotspot/src/share/vm/memory/metaspaceCounters.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/metaspaceCounters.hpp Thu May 16 11:47:51 2013 +0100
@@ -37,6 +37,7 @@
size_t max_capacity,
size_t curr_capacity,
size_t used);
+ size_t calc_total_capacity();
public:
MetaspaceCounters();
~MetaspaceCounters();
--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/share/vm/memory/sharedHeap.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/sharedHeap.cpp Thu May 16 11:47:51 2013 +0100
@@ -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;
--- a/hotspot/src/share/vm/memory/sharedHeap.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/sharedHeap.hpp Thu May 16 11:47:51 2013 +0100
@@ -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
--- a/hotspot/src/share/vm/memory/universe.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/universe.cpp Thu May 16 11:47:51 2013 +0100
@@ -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 ");
--- a/hotspot/src/share/vm/memory/universe.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/memory/universe.hpp Thu May 16 11:47:51 2013 +0100
@@ -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; }
--- a/hotspot/src/share/vm/oops/constantPool.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/oops/constantPool.cpp Thu May 16 11:47:51 2013 +0100
@@ -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 <clinit> 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<u2>* new_ops = MetadataFactory::new_array<u2>(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<u2>(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<u2>* new_ops = MetadataFactory::new_array<u2>(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) {
--- a/hotspot/src/share/vm/oops/constantPool.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/oops/constantPool.hpp Thu May 16 11:47:51 2013 +0100
@@ -111,7 +111,6 @@
int _version;
} _saved;
- Monitor* _lock;
void set_tags(Array<u1>* 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
--- a/hotspot/src/share/vm/oops/cpCache.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/oops/cpCache.cpp Thu May 16 11:47:51 2013 +0100
@@ -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;
}
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/share/vm/oops/instanceKlass.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/oops/instanceKlass.hpp Thu May 16 11:47:51 2013 +0100
@@ -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();
--- a/hotspot/src/share/vm/oops/klassVtable.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/oops/klassVtable.cpp Thu May 16 11:47:51 2013 +0100
@@ -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<Method*>* 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) {
--- a/hotspot/src/share/vm/oops/method.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/oops/method.cpp Thu May 16 11:47:51 2013 +0100
@@ -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" );
--- a/hotspot/src/share/vm/oops/method.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/oops/method.hpp Thu May 16 11:47:51 2013 +0100
@@ -67,7 +67,7 @@
// | ConstMethod* (oop) |
// |------------------------------------------------------|
// | methodData (oop) |
-// | interp_invocation_count |
+// | methodCounters |
// |------------------------------------------------------|
// | access_flags |
// | vtable_index |
@@ -76,16 +76,6 @@
// |------------------------------------------------------|
// | method_size | intrinsic_id| flags |
// |------------------------------------------------------|
-// | throwout_count | num_breakpoints |
-// |------------------------------------------------------|
-// | invocation_counter |
-// | backedge_counter |
-// |------------------------------------------------------|
-// | prev_time (tiered only, 64 bit wide) |
-// | |
-// |------------------------------------------------------|
-// | rate (tiered) |
-// |------------------------------------------------------|
// | code (pointer) |
// | i2i (pointer) |
// | adapter (pointer) |
--- a/hotspot/src/share/vm/oops/oop.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/oops/oop.cpp Thu May 16 11:47:51 2013 +0100
@@ -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;
--- a/hotspot/src/share/vm/opto/graphKit.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/opto/graphKit.cpp Thu May 16 11:47:51 2013 +0100
@@ -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
--- a/hotspot/src/share/vm/opto/output.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/opto/output.cpp Thu May 16 11:47:51 2013 +0100
@@ -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
--- a/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp Thu May 16 11:47:51 2013 +0100
@@ -341,6 +341,44 @@
memcpy(writeable_address(length), annos->adr_at(0), length);
}
+// BootstrapMethods_attribute {
+// u2 attribute_name_index;
+// u4 attribute_length;
+// u2 num_bootstrap_methods;
+// { u2 bootstrap_method_ref;
+// u2 num_bootstrap_arguments;
+// u2 bootstrap_arguments[num_bootstrap_arguments];
+// } bootstrap_methods[num_bootstrap_methods];
+// }
+void JvmtiClassFileReconstituter::write_bootstrapmethod_attribute() {
+ Array<u2>* operands = cpool()->operands();
+ write_attribute_name_index("BootstrapMethods");
+ int num_bootstrap_methods = ConstantPool::operand_array_length(operands);
+
+ // calculate length of attribute
+ int length = sizeof(u2); // num_bootstrap_methods
+ for (int n = 0; n < num_bootstrap_methods; n++) {
+ u2 num_bootstrap_arguments = cpool()->operand_argument_count_at(n);
+ length += sizeof(u2); // bootstrap_method_ref
+ length += sizeof(u2); // num_bootstrap_arguments
+ length += sizeof(u2) * num_bootstrap_arguments; // bootstrap_arguments[num_bootstrap_arguments]
+ }
+ write_u4(length);
+
+ // write attribute
+ write_u2(num_bootstrap_methods);
+ for (int n = 0; n < num_bootstrap_methods; n++) {
+ u2 bootstrap_method_ref = cpool()->operand_bootstrap_method_ref_index_at(n);
+ u2 num_bootstrap_arguments = cpool()->operand_argument_count_at(n);
+ write_u2(bootstrap_method_ref);
+ write_u2(num_bootstrap_arguments);
+ for (int arg = 0; arg < num_bootstrap_arguments; arg++) {
+ u2 bootstrap_argument = cpool()->operand_argument_index_at(n, arg);
+ write_u2(bootstrap_argument);
+ }
+ }
+}
+
// Write InnerClasses attribute
// JVMSpec| InnerClasses_attribute {
@@ -513,6 +551,11 @@
AnnotationArray* param_anno = method->parameter_annotations();
AnnotationArray* default_anno = method->annotation_default();
+ // skip generated default interface methods
+ if (method->is_overpass()) {
+ return;
+ }
+
write_u2(access_flags.get_flags() & JVM_RECOGNIZED_METHOD_MODIFIERS);
write_u2(const_method->name_index());
write_u2(const_method->signature_index());
@@ -592,6 +635,9 @@
if (anno != NULL) {
++attr_count; // has RuntimeVisibleAnnotations attribute
}
+ if (cpool()->operands() != NULL) {
+ ++attr_count;
+ }
write_u2(attr_count);
@@ -610,6 +656,9 @@
if (anno != NULL) {
write_annotations_attribute("RuntimeVisibleAnnotations", anno);
}
+ if (cpool()->operands() != NULL) {
+ write_bootstrapmethod_attribute();
+ }
}
// Write the method information portion of ClassFile structure
@@ -619,8 +668,19 @@
HandleMark hm(thread());
Array<Method*>* methods = ikh()->methods();
int num_methods = methods->length();
+ int num_overpass = 0;
- write_u2(num_methods);
+ // count the generated default interface methods
+ // these will not be re-created by write_method_info
+ // and should not be included in the total count
+ for (int index = 0; index < num_methods; index++) {
+ Method* method = methods->at(index);
+ if (method->is_overpass()) {
+ num_overpass++;
+ }
+ }
+
+ write_u2(num_methods - num_overpass);
if (JvmtiExport::can_maintain_original_method_order()) {
int index;
int original_index;
--- a/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.hpp Thu May 16 11:47:51 2013 +0100
@@ -127,6 +127,7 @@
void write_signature_attribute(u2 generic_signaure_index);
void write_attribute_name_index(const char* name);
void write_annotations_attribute(const char* attr_name, AnnotationArray* annos);
+ void write_bootstrapmethod_attribute();
address writeable_address(size_t size);
void write_u1(u1 x);
--- a/hotspot/src/share/vm/prims/jvmtiEnv.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/prims/jvmtiEnv.cpp Thu May 16 11:47:51 2013 +0100
@@ -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) {
--- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp Thu May 16 11:47:51 2013 +0100
@@ -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<u2>* 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) {
--- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/share/vm/prims/jvmtiTagMap.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/prims/jvmtiTagMap.cpp Thu May 16 11:47:51 2013 +0100
@@ -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; i<initial_size; i++) {
_table[i] = NULL;
--- a/hotspot/src/share/vm/prims/methodHandles.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/prims/methodHandles.cpp Thu May 16 11:47:51 2013 +0100
@@ -67,7 +67,8 @@
TraceTime timer("MethodHandles adapters generation", TraceStartupTime);
_adapter_code = MethodHandlesAdapterBlob::create(adapter_code_size);
if (_adapter_code == NULL)
- vm_exit_out_of_memory(adapter_code_size, "CodeCache: no room for MethodHandles adapters");
+ vm_exit_out_of_memory(adapter_code_size, OOM_MALLOC_ERROR,
+ "CodeCache: no room for MethodHandles adapters");
{
CodeBuffer code(_adapter_code);
MethodHandlesAdapterGenerator g(&code);
--- a/hotspot/src/share/vm/prims/perf.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/prims/perf.cpp Thu May 16 11:47:51 2013 +0100
@@ -142,20 +142,20 @@
}
switch(variability) {
- case 1: /* V_Constant */
+ case PerfData::V_Constant:
pl = PerfDataManager::create_long_constant(NULL_NS, (char *)name_utf,
(PerfData::Units)units, value,
CHECK_NULL);
break;
- case 2: /* V_Variable */
- pl = PerfDataManager::create_long_variable(NULL_NS, (char *)name_utf,
+ case PerfData::V_Monotonic:
+ pl = PerfDataManager::create_long_counter(NULL_NS, (char *)name_utf,
(PerfData::Units)units, value,
CHECK_NULL);
break;
- case 3: /* V_Monotonic Counter */
- pl = PerfDataManager::create_long_counter(NULL_NS, (char *)name_utf,
+ case PerfData::V_Variable:
+ pl = PerfDataManager::create_long_variable(NULL_NS, (char *)name_utf,
(PerfData::Units)units, value,
CHECK_NULL);
break;
--- a/hotspot/src/share/vm/prims/unsafe.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/prims/unsafe.cpp Thu May 16 11:47:51 2013 +0100
@@ -315,10 +315,7 @@
OrderAccess::fence();
UNSAFE_END
-#if defined(SPARC) || defined(X86)
-// Sparc and X86 have atomic jlong (8 bytes) instructions
-
-#else
+#ifndef SUPPORTS_NATIVE_CX8
// Keep old code for platforms which may not have atomic jlong (8 bytes) instructions
// Volatile long versions must use locks if !VM_Version::supports_cx8().
@@ -356,7 +353,7 @@
}
UNSAFE_END
-#endif // not SPARC and not X86
+#endif // not SUPPORTS_NATIVE_CX8
#define DEFINE_GETSETOOP(jboolean, Boolean) \
\
@@ -420,8 +417,7 @@
DEFINE_GETSETOOP_VOLATILE(jfloat, Float);
DEFINE_GETSETOOP_VOLATILE(jdouble, Double);
-#if defined(SPARC) || defined(X86)
-// Sparc and X86 have atomic jlong (8 bytes) instructions
+#ifdef SUPPORTS_NATIVE_CX8
DEFINE_GETSETOOP_VOLATILE(jlong, Long);
#endif
@@ -450,8 +446,7 @@
UNSAFE_ENTRY(void, Unsafe_SetOrderedLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong x))
UnsafeWrapper("Unsafe_SetOrderedLong");
-#if defined(SPARC) || defined(X86)
- // Sparc and X86 have atomic jlong (8 bytes) instructions
+#ifdef SUPPORTS_NATIVE_CX8
SET_FIELD_VOLATILE(obj, offset, jlong, x);
#else
// Keep old code for platforms which may not have atomic long (8 bytes) instructions
--- a/hotspot/src/share/vm/prims/whitebox.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/prims/whitebox.cpp Thu May 16 11:47:51 2013 +0100
@@ -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
@@ -93,6 +93,15 @@
return closure.found();
WB_END
+WB_ENTRY(void, WB_PrintHeapSizes(JNIEnv* env, jobject o)) {
+ CollectorPolicy * p = Universe::heap()->collector_policy();
+ gclog_or_tty->print_cr("Minimum heap "SIZE_FORMAT" Initial heap "
+ SIZE_FORMAT" Maximum heap "SIZE_FORMAT" Min alignment "SIZE_FORMAT" Max alignment "SIZE_FORMAT,
+ p->min_heap_byte_size(), p->initial_heap_byte_size(), p->max_heap_byte_size(),
+ p->min_alignment(), p->max_alignment());
+}
+WB_END
+
#if INCLUDE_ALL_GCS
WB_ENTRY(jboolean, WB_G1IsHumongous(JNIEnv* env, jobject o, jobject obj))
G1CollectedHeap* g1 = G1CollectedHeap::heap();
@@ -310,12 +319,8 @@
WB_ENTRY(jboolean, WB_IsInStringTable(JNIEnv* env, jobject o, jstring javaString))
ResourceMark rm(THREAD);
int len;
- jchar* name = java_lang_String::as_unicode_string(JNIHandles::resolve(javaString), len);
- oop found_string = StringTable::the_table()->lookup(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 +329,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) {
@@ -385,6 +395,7 @@
CC"(Ljava/lang/String;[Lsun/hotspot/parser/DiagnosticCommand;)[Ljava/lang/Object;",
(void*) &WB_ParseCommandLine
},
+ {CC"printHeapSizes", CC"()V", (void*)&WB_PrintHeapSizes },
#if INCLUDE_ALL_GCS
{CC"g1InConcurrentMark", CC"()Z", (void*)&WB_G1InConcurrentMark},
{CC"g1IsHumongous", CC"(Ljava/lang/Object;)Z", (void*)&WB_G1IsHumongous },
@@ -425,6 +436,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 +449,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();
}
}
--- a/hotspot/src/share/vm/runtime/arguments.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/arguments.cpp Thu May 16 11:47:51 2013 +0100
@@ -747,16 +747,16 @@
return;
}
- int index = *count;
+ int new_count = *count + 1;
// expand the array and add arg to the last element
- (*count)++;
if (*bldarray == NULL) {
- *bldarray = NEW_C_HEAP_ARRAY(char*, *count, mtInternal);
+ *bldarray = NEW_C_HEAP_ARRAY(char*, new_count, mtInternal);
} else {
- *bldarray = REALLOC_C_HEAP_ARRAY(char*, *bldarray, *count, mtInternal);
+ *bldarray = REALLOC_C_HEAP_ARRAY(char*, *bldarray, new_count, mtInternal);
}
- (*bldarray)[index] = strdup(arg);
+ (*bldarray)[*count] = strdup(arg);
+ *count = new_count;
}
void Arguments::build_jvm_args(const char* arg) {
@@ -1617,30 +1617,38 @@
FLAG_SET_ERGO(uintx, MaxHeapSize, (uintx)reasonable_max);
}
- // If the initial_heap_size has not been set with InitialHeapSize
- // or -Xms, then set it as fraction of the size of physical memory,
- // respecting the maximum and minimum sizes of the heap.
- if (FLAG_IS_DEFAULT(InitialHeapSize)) {
+ // If the minimum or initial heap_size have not been set or requested to be set
+ // ergonomically, set them accordingly.
+ if (InitialHeapSize == 0 || min_heap_size() == 0) {
julong reasonable_minimum = (julong)(OldSize + NewSize);
reasonable_minimum = MIN2(reasonable_minimum, (julong)MaxHeapSize);
reasonable_minimum = limit_by_allocatable_memory(reasonable_minimum);
- julong reasonable_initial = phys_mem / InitialRAMFraction;
-
- reasonable_initial = MAX2(reasonable_initial, reasonable_minimum);
- reasonable_initial = MIN2(reasonable_initial, (julong)MaxHeapSize);
-
- reasonable_initial = limit_by_allocatable_memory(reasonable_initial);
-
- if (PrintGCDetails && Verbose) {
- // Cannot use gclog_or_tty yet.
- tty->print_cr(" Initial heap size " SIZE_FORMAT, (uintx)reasonable_initial);
- tty->print_cr(" Minimum heap size " SIZE_FORMAT, (uintx)reasonable_minimum);
+ if (InitialHeapSize == 0) {
+ julong reasonable_initial = phys_mem / InitialRAMFraction;
+
+ reasonable_initial = MAX3(reasonable_initial, reasonable_minimum, (julong)min_heap_size());
+ reasonable_initial = MIN2(reasonable_initial, (julong)MaxHeapSize);
+
+ reasonable_initial = limit_by_allocatable_memory(reasonable_initial);
+
+ if (PrintGCDetails && Verbose) {
+ // Cannot use gclog_or_tty yet.
+ tty->print_cr(" Initial heap size " SIZE_FORMAT, (uintx)reasonable_initial);
+ }
+ FLAG_SET_ERGO(uintx, InitialHeapSize, (uintx)reasonable_initial);
}
- FLAG_SET_ERGO(uintx, InitialHeapSize, (uintx)reasonable_initial);
- set_min_heap_size((uintx)reasonable_minimum);
+ // If the minimum heap size has not been set (via -Xms),
+ // synchronize with InitialHeapSize to avoid errors with the default value.
+ if (min_heap_size() == 0) {
+ set_min_heap_size(MIN2((uintx)reasonable_minimum, InitialHeapSize));
+ if (PrintGCDetails && Verbose) {
+ // Cannot use gclog_or_tty yet.
+ tty->print_cr(" Minimum heap size " SIZE_FORMAT, min_heap_size());
+ }
+ }
}
}
@@ -2043,6 +2051,10 @@
"G1RefProcDrainInterval");
status = status && verify_min_value((intx)G1ConcMarkStepDurationMillis, 1,
"G1ConcMarkStepDurationMillis");
+ status = status && verify_interval(G1ConcRSHotCardLimit, 0, max_jubyte,
+ "G1ConcRSHotCardLimit");
+ status = status && verify_interval(G1ConcRSLogCacheSize, 0, 31,
+ "G1ConcRSLogCacheSize");
}
#endif // INCLUDE_ALL_GCS
@@ -2224,6 +2236,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 +2383,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;
@@ -2377,7 +2438,8 @@
// -Xms
} else if (match_option(option, "-Xms", &tail)) {
julong long_initial_heap_size = 0;
- ArgsRange errcode = parse_memory_size(tail, &long_initial_heap_size, 1);
+ // an initial heap size of 0 means automatically determine
+ ArgsRange errcode = parse_memory_size(tail, &long_initial_heap_size, 0);
if (errcode != arg_in_range) {
jio_fprintf(defaultStream::error_stream(),
"Invalid initial heap size: %s\n", option->optionString);
@@ -2388,7 +2450,7 @@
// Currently the minimum size and the initial heap sizes are the same.
set_min_heap_size(InitialHeapSize);
// -Xmx
- } else if (match_option(option, "-Xmx", &tail)) {
+ } else if (match_option(option, "-Xmx", &tail) || match_option(option, "-XX:MaxHeapSize=", &tail)) {
julong long_max_heap_size = 0;
ArgsRange errcode = parse_memory_size(tail, &long_max_heap_size, 1);
if (errcode != arg_in_range) {
--- a/hotspot/src/share/vm/runtime/compilationPolicy.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/compilationPolicy.cpp Thu May 16 11:47:51 2013 +0100
@@ -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;
}
--- a/hotspot/src/share/vm/runtime/globals.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/globals.hpp Thu May 16 11:47:51 2013 +0100
@@ -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") \
@@ -2965,7 +2968,7 @@
\
/* gc parameters */ \
product(uintx, InitialHeapSize, 0, \
- "Initial heap size (in bytes); zero means OldSize + NewSize") \
+ "Initial heap size (in bytes); zero means use ergonomics") \
\
product(uintx, MaxHeapSize, ScaleForWordSize(96*M), \
"Maximum heap size (in bytes)") \
@@ -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") \
\
--- a/hotspot/src/share/vm/runtime/objectMonitor.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/objectMonitor.cpp Thu May 16 11:47:51 2013 +0100
@@ -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) ;
--- a/hotspot/src/share/vm/runtime/os.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/os.cpp Thu May 16 11:47:51 2013 +0100
@@ -1457,6 +1457,18 @@
return result;
}
+
+char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint,
+ MEMFLAGS flags) {
+ char* result = pd_reserve_memory(bytes, addr, alignment_hint);
+ if (result != NULL) {
+ MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC);
+ MemTracker::record_virtual_memory_type((address)result, flags);
+ }
+
+ return result;
+}
+
char* os::attempt_reserve_memory_at(size_t bytes, char* addr) {
char* result = pd_attempt_reserve_memory_at(bytes, addr);
if (result != NULL) {
--- a/hotspot/src/share/vm/runtime/os.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/os.hpp Thu May 16 11:47:51 2013 +0100
@@ -255,6 +255,8 @@
static int vm_allocation_granularity();
static char* reserve_memory(size_t bytes, char* addr = 0,
size_t alignment_hint = 0);
+ static char* reserve_memory(size_t bytes, char* addr,
+ size_t alignment_hint, MEMFLAGS flags);
static char* reserve_memory_aligned(size_t size, size_t alignment);
static char* attempt_reserve_memory_at(size_t bytes, char* addr);
static void split_reserved_memory(char *base, size_t size,
@@ -454,6 +456,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 +480,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
--- a/hotspot/src/share/vm/runtime/serviceThread.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/serviceThread.cpp Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 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
@@ -29,6 +29,8 @@
#include "runtime/mutexLocker.hpp"
#include "prims/jvmtiImpl.hpp"
#include "services/gcNotifier.hpp"
+#include "services/diagnosticArgument.hpp"
+#include "services/diagnosticFramework.hpp"
ServiceThread* ServiceThread::_instance = NULL;
@@ -83,6 +85,7 @@
bool sensors_changed = false;
bool has_jvmti_events = false;
bool has_gc_notification_event = false;
+ bool has_dcmd_notification_event = false;
JvmtiDeferredEvent jvmti_event;
{
// Need state transition ThreadBlockInVM so that this thread
@@ -98,7 +101,8 @@
MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
while (!(sensors_changed = LowMemoryDetector::has_pending_requests()) &&
!(has_jvmti_events = JvmtiDeferredEventQueue::has_events()) &&
- !(has_gc_notification_event = GCNotifier::has_event())) {
+ !(has_gc_notification_event = GCNotifier::has_event()) &&
+ !(has_dcmd_notification_event = DCmdFactory::has_pending_jmx_notification())) {
// wait until one of the sensors has pending requests, or there is a
// pending JVMTI event or JMX GC notification to post
Service_lock->wait(Mutex::_no_safepoint_check_flag);
@@ -120,6 +124,10 @@
if(has_gc_notification_event) {
GCNotifier::sendNotification(CHECK);
}
+
+ if(has_dcmd_notification_event) {
+ DCmdFactory::send_notification(CHECK);
+ }
}
}
--- a/hotspot/src/share/vm/runtime/sharedRuntime.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp Thu May 16 11:47:51 2013 +0100
@@ -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();
--- a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp Thu May 16 11:47:51 2013 +0100
@@ -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());
--- a/hotspot/src/share/vm/runtime/stubRoutines.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/stubRoutines.cpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/share/vm/runtime/sweeper.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/sweeper.cpp Thu May 16 11:47:51 2013 +0100
@@ -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) {
--- a/hotspot/src/share/vm/runtime/sweeper.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/sweeper.hpp Thu May 16 11:47:51 2013 +0100
@@ -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
--- a/hotspot/src/share/vm/runtime/synchronizer.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/synchronizer.cpp Thu May 16 11:47:51 2013 +0100
@@ -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.
--- a/hotspot/src/share/vm/runtime/thread.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/thread.cpp Thu May 16 11:47:51 2013 +0100
@@ -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);
}
--- a/hotspot/src/share/vm/runtime/virtualspace.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/virtualspace.cpp Thu May 16 11:47:51 2013 +0100
@@ -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,
--- a/hotspot/src/share/vm/runtime/virtualspace.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/virtualspace.hpp Thu May 16 11:47:51 2013 +0100
@@ -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
--- a/hotspot/src/share/vm/runtime/vmStructs.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/vmStructs.cpp Thu May 16 11:47:51 2013 +0100
@@ -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) \
\
--- a/hotspot/src/share/vm/runtime/vmThread.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/vmThread.cpp Thu May 16 11:47:51 2013 +0100
@@ -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();
--- a/hotspot/src/share/vm/runtime/vm_operations.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/runtime/vm_operations.hpp Thu May 16 11:47:51 2013 +0100
@@ -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();
};
--- a/hotspot/src/share/vm/services/attachListener.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/services/attachListener.cpp Thu May 16 11:47:51 2013 +0100
@@ -157,7 +157,7 @@
Thread* THREAD = Thread::current();
// All the supplied jcmd arguments are stored as a single
// string (op->arg(0)). This is parsed by the Dcmd framework.
- DCmd::parse_and_execute(out, op->arg(0), ' ', THREAD);
+ DCmd::parse_and_execute(DCmd_Source_AttachAPI, out, op->arg(0), ' ', THREAD);
if (HAS_PENDING_EXCEPTION) {
java_lang_Throwable::print(PENDING_EXCEPTION, out);
out->cr();
--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp Thu May 16 11:47:51 2013 +0100
@@ -34,26 +34,33 @@
void DCmdRegistrant::register_dcmds(){
// Registration of the diagnostic commands
- // First boolean argument specifies if the command is enabled
- // Second boolean argument specifies if the command is hidden
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<HelpDCmd>(true, false));
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<VersionDCmd>(true, false));
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CommandLineDCmd>(true, false));
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<PrintSystemPropertiesDCmd>(true, false));
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<PrintVMFlagsDCmd>(true, false));
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<VMUptimeDCmd>(true, false));
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<SystemGCDCmd>(true, false));
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<RunFinalizationDCmd>(true, false));
+ // First argument specifies which interfaces will export the command
+ // Second argument specifies if the command is enabled
+ // Third argument specifies if the command is hidden
+ uint32_t full_export = DCmd_Source_Internal | DCmd_Source_AttachAPI
+ | DCmd_Source_MBean;
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<HelpDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<VersionDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<CommandLineDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<PrintSystemPropertiesDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<PrintVMFlagsDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<VMUptimeDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<SystemGCDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<RunFinalizationDCmd>(full_export, true, false));
#if INCLUDE_SERVICES // Heap dumping/inspection supported
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<HeapDumpDCmd>(true, false));
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHistogramDCmd>(true, false));
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassStatsDCmd>(true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<HeapDumpDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHistogramDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassStatsDCmd>(full_export, true, false));
#endif // INCLUDE_SERVICES
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ThreadDumpDCmd>(true, false));
- //Enhanced JMX Agent Support
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JMXStartRemoteDCmd>(true,false));
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JMXStartLocalDCmd>(true,false));
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JMXStopRemoteDCmd>(true,false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ThreadDumpDCmd>(full_export, true, false));
+
+ // Enhanced JMX Agent Support
+ // These commands won't be exported via the DiagnosticCommandMBean until an
+ // appropriate permission is created for them
+ uint32_t jmx_agent_export_flags = DCmd_Source_Internal | DCmd_Source_AttachAPI;
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JMXStartRemoteDCmd>(jmx_agent_export_flags, true,false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JMXStartLocalDCmd>(jmx_agent_export_flags, true,false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JMXStopRemoteDCmd>(jmx_agent_export_flags, true,false));
}
@@ -72,29 +79,37 @@
_dcmdparser.add_dcmd_argument(&_cmd);
};
-void HelpDCmd::execute(TRAPS) {
+void HelpDCmd::execute(DCmdSource source, TRAPS) {
if (_all.value()) {
- GrowableArray<const char*>* cmd_list = DCmdFactory::DCmd_list();
+ GrowableArray<const char*>* cmd_list = DCmdFactory::DCmd_list(source);
for (int i = 0; i < cmd_list->length(); i++) {
- DCmdFactory* factory = DCmdFactory::factory(cmd_list->at(i),
+ DCmdFactory* factory = DCmdFactory::factory(source, cmd_list->at(i),
strlen(cmd_list->at(i)));
- if (!factory->is_hidden()) {
- output()->print_cr("%s%s", factory->name(),
- factory->is_enabled() ? "" : " [disabled]");
- output()->print_cr("\t%s", factory->description());
- output()->cr();
- }
+ output()->print_cr("%s%s", factory->name(),
+ factory->is_enabled() ? "" : " [disabled]");
+ output()->print_cr("\t%s", factory->description());
+ output()->cr();
factory = factory->next();
}
} else if (_cmd.has_value()) {
DCmd* cmd = NULL;
- DCmdFactory* factory = DCmdFactory::factory(_cmd.value(),
+ DCmdFactory* factory = DCmdFactory::factory(source, _cmd.value(),
strlen(_cmd.value()));
if (factory != NULL) {
output()->print_cr("%s%s", factory->name(),
factory->is_enabled() ? "" : " [disabled]");
output()->print_cr(factory->description());
output()->print_cr("\nImpact: %s", factory->impact());
+ JavaPermission p = factory->permission();
+ if(p._class != NULL) {
+ if(p._action != NULL) {
+ output()->print_cr("\nPermission: %s(%s, %s)",
+ p._class, p._name == NULL ? "null" : p._name, p._action);
+ } else {
+ output()->print_cr("\nPermission: %s(%s)",
+ p._class, p._name == NULL ? "null" : p._name);
+ }
+ }
output()->cr();
cmd = factory->create_resource_instance(output());
if (cmd != NULL) {
@@ -106,14 +121,12 @@
}
} else {
output()->print_cr("The following commands are available:");
- GrowableArray<const char *>* cmd_list = DCmdFactory::DCmd_list();
+ GrowableArray<const char *>* cmd_list = DCmdFactory::DCmd_list(source);
for (int i = 0; i < cmd_list->length(); i++) {
- DCmdFactory* factory = DCmdFactory::factory(cmd_list->at(i),
+ DCmdFactory* factory = DCmdFactory::factory(source, cmd_list->at(i),
strlen(cmd_list->at(i)));
- if (!factory->is_hidden()) {
- output()->print_cr("%s%s", factory->name(),
- factory->is_enabled() ? "" : " [disabled]");
- }
+ output()->print_cr("%s%s", factory->name(),
+ factory->is_enabled() ? "" : " [disabled]");
factory = factory->_next;
}
output()->print_cr("\nFor more information about a specific command use 'help <command>'.");
@@ -131,7 +144,7 @@
}
}
-void VersionDCmd::execute(TRAPS) {
+void VersionDCmd::execute(DCmdSource source, TRAPS) {
output()->print_cr("%s version %s", Abstract_VM_Version::vm_name(),
Abstract_VM_Version::vm_release());
JDK_Version jdk_version = JDK_Version::current();
@@ -150,7 +163,7 @@
_dcmdparser.add_dcmd_option(&_all);
}
-void PrintVMFlagsDCmd::execute(TRAPS) {
+void PrintVMFlagsDCmd::execute(DCmdSource source, TRAPS) {
if (_all.value()) {
CommandLineFlags::printFlags(output(), true);
} else {
@@ -169,7 +182,7 @@
}
}
-void PrintSystemPropertiesDCmd::execute(TRAPS) {
+void PrintSystemPropertiesDCmd::execute(DCmdSource source, TRAPS) {
// load sun.misc.VMSupport
Symbol* klass = vmSymbols::sun_misc_VMSupport();
Klass* k = SystemDictionary::resolve_or_fail(klass, true, CHECK);
@@ -219,7 +232,7 @@
_dcmdparser.add_dcmd_option(&_date);
}
-void VMUptimeDCmd::execute(TRAPS) {
+void VMUptimeDCmd::execute(DCmdSource source, TRAPS) {
if (_date.value()) {
output()->date_stamp(true, "", ": ");
}
@@ -239,11 +252,15 @@
}
}
-void SystemGCDCmd::execute(TRAPS) {
- Universe::heap()->collect(GCCause::_java_lang_system_gc);
+void SystemGCDCmd::execute(DCmdSource source, TRAPS) {
+ if (!DisableExplicitGC) {
+ Universe::heap()->collect(GCCause::_java_lang_system_gc);
+ } else {
+ output()->print_cr("Explicit GC is disabled, no GC has been performed.");
+ }
}
-void RunFinalizationDCmd::execute(TRAPS) {
+void RunFinalizationDCmd::execute(DCmdSource source, TRAPS) {
Klass* k = SystemDictionary::resolve_or_fail(vmSymbols::java_lang_System(),
true, CHECK);
instanceKlassHandle klass(THREAD, k);
@@ -263,7 +280,7 @@
_dcmdparser.add_dcmd_argument(&_filename);
}
-void HeapDumpDCmd::execute(TRAPS) {
+void HeapDumpDCmd::execute(DCmdSource source, TRAPS) {
// Request a full GC before heap dump if _all is false
// This helps reduces the amount of unreachable objects in the dump
// and makes it easier to browse.
@@ -301,7 +318,7 @@
_dcmdparser.add_dcmd_option(&_all);
}
-void ClassHistogramDCmd::execute(TRAPS) {
+void ClassHistogramDCmd::execute(DCmdSource source, TRAPS) {
VM_GC_HeapInspection heapop(output(),
!_all.value() /* request full gc if false */,
true /* need_prologue */);
@@ -337,7 +354,7 @@
_dcmdparser.add_dcmd_argument(&_columns);
}
-void ClassStatsDCmd::execute(TRAPS) {
+void ClassStatsDCmd::execute(DCmdSource source, TRAPS) {
if (!UnlockDiagnosticVMOptions) {
output()->print_cr("GC.class_stats command requires -XX:+UnlockDiagnosticVMOptions");
return;
@@ -384,7 +401,7 @@
_dcmdparser.add_dcmd_option(&_locks);
}
-void ThreadDumpDCmd::execute(TRAPS) {
+void ThreadDumpDCmd::execute(DCmdSource source, TRAPS) {
// thread stacks
VM_PrintThreads op1(output(), _locks.value());
VMThread::execute(&op1);
@@ -526,7 +543,8 @@
}
}
-void JMXStartRemoteDCmd::execute(TRAPS) {
+
+void JMXStartRemoteDCmd::execute(DCmdSource source, TRAPS) {
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
@@ -593,7 +611,7 @@
// do nothing
}
-void JMXStartLocalDCmd::execute(TRAPS) {
+void JMXStartLocalDCmd::execute(DCmdSource source, TRAPS) {
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
@@ -611,7 +629,7 @@
}
-void JMXStopRemoteDCmd::execute(TRAPS) {
+void JMXStopRemoteDCmd::execute(DCmdSource source, TRAPS) {
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp Thu May 16 11:47:51 2013 +0100
@@ -51,7 +51,7 @@
}
static const char* impact() { return "Low"; }
static int num_arguments();
- virtual void execute(TRAPS);
+ virtual void execute(DCmdSource source, TRAPS);
};
class VersionDCmd : public DCmd {
@@ -62,8 +62,13 @@
return "Print JVM version information.";
}
static const char* impact() { return "Low"; }
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.util.PropertyPermission",
+ "java.vm.version", "read"};
+ return p;
+ }
static int num_arguments() { return 0; }
- virtual void execute(TRAPS);
+ virtual void execute(DCmdSource source, TRAPS);
};
class CommandLineDCmd : public DCmd {
@@ -74,8 +79,13 @@
return "Print the command line used to start this VM instance.";
}
static const char* impact() { return "Low"; }
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.lang.management.ManagementPermission",
+ "monitor", NULL};
+ return p;
+ }
static int num_arguments() { return 0; }
- virtual void execute(TRAPS) {
+ virtual void execute(DCmdSource source, TRAPS) {
Arguments::print_on(_output);
}
};
@@ -91,8 +101,13 @@
static const char* impact() {
return "Low";
}
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.util.PropertyPermission",
+ "*", "read"};
+ return p;
+ }
static int num_arguments() { return 0; }
- virtual void execute(TRAPS);
+ virtual void execute(DCmdSource source, TRAPS);
};
// See also: print_flag in attachListener.cpp
@@ -108,8 +123,13 @@
static const char* impact() {
return "Low";
}
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.lang.management.ManagementPermission",
+ "monitor", NULL};
+ return p;
+ }
static int num_arguments();
- virtual void execute(TRAPS);
+ virtual void execute(DCmdSource source, TRAPS);
};
class VMUptimeDCmd : public DCmdWithParser {
@@ -125,7 +145,7 @@
return "Low";
}
static int num_arguments();
- virtual void execute(TRAPS);
+ virtual void execute(DCmdSource source, TRAPS);
};
class SystemGCDCmd : public DCmd {
@@ -139,7 +159,7 @@
return "Medium: Depends on Java heap size and content.";
}
static int num_arguments() { return 0; }
- virtual void execute(TRAPS);
+ virtual void execute(DCmdSource source, TRAPS);
};
class RunFinalizationDCmd : public DCmd {
@@ -153,7 +173,7 @@
return "Medium: Depends on Java content.";
}
static int num_arguments() { return 0; }
- virtual void execute(TRAPS);
+ virtual void execute(DCmdSource source, TRAPS);
};
#if INCLUDE_SERVICES // Heap dumping supported
@@ -174,8 +194,13 @@
return "High: Depends on Java heap size and content. "
"Request a full GC unless the '-all' option is specified.";
}
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.lang.management.ManagementPermission",
+ "monitor", NULL};
+ return p;
+ }
static int num_arguments();
- virtual void execute(TRAPS);
+ virtual void execute(DCmdSource source, TRAPS);
};
#endif // INCLUDE_SERVICES
@@ -194,8 +219,13 @@
static const char* impact() {
return "High: Depends on Java heap size and content.";
}
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.lang.management.ManagementPermission",
+ "monitor", NULL};
+ return p;
+ }
static int num_arguments();
- virtual void execute(TRAPS);
+ virtual void execute(DCmdSource source, TRAPS);
};
class ClassStatsDCmd : public DCmdWithParser {
@@ -216,7 +246,7 @@
return "High: Depends on Java heap size and content.";
}
static int num_arguments();
- virtual void execute(TRAPS);
+ virtual void execute(DCmdSource source, TRAPS);
};
// See also: thread_dump in attachListener.cpp
@@ -232,8 +262,13 @@
static const char* impact() {
return "Medium: Depends on the number of threads.";
}
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.lang.management.ManagementPermission",
+ "monitor", NULL};
+ return p;
+ }
static int num_arguments();
- virtual void execute(TRAPS);
+ virtual void execute(DCmdSource source, TRAPS);
};
// Enhanced JMX Agent support
@@ -281,7 +316,7 @@
static int num_arguments();
- virtual void execute(TRAPS);
+ virtual void execute(DCmdSource source, TRAPS);
};
@@ -302,7 +337,7 @@
return "Start local management agent.";
}
- virtual void execute(TRAPS);
+ virtual void execute(DCmdSource source, TRAPS);
};
@@ -321,7 +356,7 @@
return "Stop remote management agent.";
}
- virtual void execute(TRAPS);
+ virtual void execute(DCmdSource source, TRAPS);
};
#endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP
--- a/hotspot/src/share/vm/services/diagnosticFramework.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/services/diagnosticFramework.cpp Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2012, 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
@@ -359,7 +359,7 @@
while (arg != NULL) {
array->append(new DCmdArgumentInfo(arg->name(), arg->description(),
arg->type(), arg->default_string(), arg->is_mandatory(),
- false, idx));
+ false, arg->allow_multiple(), idx));
idx++;
arg = arg->next();
}
@@ -367,32 +367,42 @@
while (arg != NULL) {
array->append(new DCmdArgumentInfo(arg->name(), arg->description(),
arg->type(), arg->default_string(), arg->is_mandatory(),
- true));
+ true, arg->allow_multiple()));
arg = arg->next();
}
return array;
}
DCmdFactory* DCmdFactory::_DCmdFactoryList = NULL;
+bool DCmdFactory::_has_pending_jmx_notification = false;
-void DCmd::parse_and_execute(outputStream* out, const char* cmdline,
- char delim, TRAPS) {
+void DCmd::parse_and_execute(DCmdSource source, outputStream* out,
+ const char* cmdline, char delim, TRAPS) {
if (cmdline == NULL) return; // Nothing to do!
DCmdIter iter(cmdline, '\n');
+ int count = 0;
while (iter.has_next()) {
+ if(source == DCmd_Source_MBean && count > 0) {
+ // When diagnostic commands are invoked via JMX, each command line
+ // must contains one and only one command because of the Permission
+ // checks performed by the DiagnosticCommandMBean
+ THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
+ "Invalid syntax");
+ }
CmdLine line = iter.next();
if (line.is_stop()) {
break;
}
if (line.is_executable()) {
- DCmd* command = DCmdFactory::create_local_DCmd(line, out, CHECK);
+ DCmd* command = DCmdFactory::create_local_DCmd(source, line, out, CHECK);
assert(command != NULL, "command error must be handled before this line");
DCmdMark mark(command);
command->parse(&line, delim, CHECK);
- command->execute(CHECK);
+ command->execute(source, CHECK);
}
+ count++;
}
}
@@ -420,15 +430,78 @@
return _dcmdparser.argument_info_array();
}
-Mutex* DCmdFactory::_dcmdFactory_lock = new Mutex(Mutex::leaf, "DCmdFactory", true);
+void DCmdFactory::push_jmx_notification_request() {
+ MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
+ _has_pending_jmx_notification = true;
+ Service_lock->notify_all();
+}
+
+void DCmdFactory::send_notification(TRAPS) {
+ DCmdFactory::send_notification_internal(THREAD);
+ // Clearing pending exception to avoid premature termination of
+ // the service thread
+ if (HAS_PENDING_EXCEPTION) {
+ CLEAR_PENDING_EXCEPTION;
+ }
+}
+void DCmdFactory::send_notification_internal(TRAPS) {
+ ResourceMark rm(THREAD);
+ HandleMark hm(THREAD);
+ bool notif = false;
+ {
+ MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
+ notif = _has_pending_jmx_notification;
+ _has_pending_jmx_notification = false;
+ }
+ if (notif) {
+
+ Klass* k = Management::sun_management_ManagementFactoryHelper_klass(CHECK);
+ instanceKlassHandle mgmt_factory_helper_klass(THREAD, k);
-DCmdFactory* DCmdFactory::factory(const char* name, size_t len) {
+ JavaValue result(T_OBJECT);
+ JavaCalls::call_static(&result,
+ mgmt_factory_helper_klass,
+ vmSymbols::getDiagnosticCommandMBean_name(),
+ vmSymbols::getDiagnosticCommandMBean_signature(),
+ CHECK);
+
+ instanceOop m = (instanceOop) result.get_jobject();
+ instanceHandle dcmd_mbean_h(THREAD, m);
+
+ Klass* k2 = Management::sun_management_DiagnosticCommandImpl_klass(CHECK);
+ instanceKlassHandle dcmd_mbean_klass(THREAD, k2);
+
+ if (!dcmd_mbean_h->is_a(k2)) {
+ THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
+ "ManagementFactory.getDiagnosticCommandMBean didn't return a DiagnosticCommandMBean instance");
+ }
+
+ JavaValue result2(T_VOID);
+ JavaCallArguments args2(dcmd_mbean_h);
+
+ JavaCalls::call_virtual(&result2,
+ dcmd_mbean_klass,
+ vmSymbols::createDiagnosticFrameworkNotification_name(),
+ vmSymbols::void_method_signature(),
+ &args2,
+ CHECK);
+ }
+}
+
+Mutex* DCmdFactory::_dcmdFactory_lock = new Mutex(Mutex::leaf, "DCmdFactory", true);
+bool DCmdFactory::_send_jmx_notification = false;
+
+DCmdFactory* DCmdFactory::factory(DCmdSource source, const char* name, size_t len) {
MutexLockerEx ml(_dcmdFactory_lock, Mutex::_no_safepoint_check_flag);
DCmdFactory* factory = _DCmdFactoryList;
while (factory != NULL) {
if (strlen(factory->name()) == len &&
strncmp(name, factory->name(), len) == 0) {
- return factory;
+ if(factory->export_flags() & source) {
+ return factory;
+ } else {
+ return NULL;
+ }
}
factory = factory->_next;
}
@@ -439,11 +512,16 @@
MutexLockerEx ml(_dcmdFactory_lock, Mutex::_no_safepoint_check_flag);
factory->_next = _DCmdFactoryList;
_DCmdFactoryList = factory;
+ if (_send_jmx_notification && !factory->_hidden
+ && (factory->_export_flags & DCmd_Source_MBean)) {
+ DCmdFactory::push_jmx_notification_request();
+ }
return 0; // Actually, there's no checks for duplicates
}
-DCmd* DCmdFactory::create_global_DCmd(CmdLine &line, outputStream* out, TRAPS) {
- DCmdFactory* f = factory(line.cmd_addr(), line.cmd_len());
+DCmd* DCmdFactory::create_global_DCmd(DCmdSource source, CmdLine &line,
+ outputStream* out, TRAPS) {
+ DCmdFactory* f = factory(source, line.cmd_addr(), line.cmd_len());
if (f != NULL) {
if (f->is_enabled()) {
THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(),
@@ -455,8 +533,9 @@
"Unknown diagnostic command");
}
-DCmd* DCmdFactory::create_local_DCmd(CmdLine &line, outputStream* out, TRAPS) {
- DCmdFactory* f = factory(line.cmd_addr(), line.cmd_len());
+DCmd* DCmdFactory::create_local_DCmd(DCmdSource source, CmdLine &line,
+ outputStream* out, TRAPS) {
+ DCmdFactory* f = factory(source, line.cmd_addr(), line.cmd_len());
if (f != NULL) {
if (!f->is_enabled()) {
THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(),
@@ -468,12 +547,12 @@
"Unknown diagnostic command");
}
-GrowableArray<const char*>* DCmdFactory::DCmd_list() {
+GrowableArray<const char*>* DCmdFactory::DCmd_list(DCmdSource source) {
MutexLockerEx ml(_dcmdFactory_lock, Mutex::_no_safepoint_check_flag);
GrowableArray<const char*>* array = new GrowableArray<const char*>();
DCmdFactory* factory = _DCmdFactoryList;
while (factory != NULL) {
- if (!factory->is_hidden()) {
+ if (!factory->is_hidden() && (factory->export_flags() & source)) {
array->append(factory->name());
}
factory = factory->next();
@@ -481,15 +560,16 @@
return array;
}
-GrowableArray<DCmdInfo*>* DCmdFactory::DCmdInfo_list() {
+GrowableArray<DCmdInfo*>* DCmdFactory::DCmdInfo_list(DCmdSource source ) {
MutexLockerEx ml(_dcmdFactory_lock, Mutex::_no_safepoint_check_flag);
GrowableArray<DCmdInfo*>* array = new GrowableArray<DCmdInfo*>();
DCmdFactory* factory = _DCmdFactoryList;
while (factory != NULL) {
- if (!factory->is_hidden()) {
+ if (!factory->is_hidden() && (factory->export_flags() & source)) {
array->append(new DCmdInfo(factory->name(),
factory->description(), factory->impact(),
- factory->num_arguments(), factory->is_enabled()));
+ factory->permission(), factory->num_arguments(),
+ factory->is_enabled()));
}
factory = factory->next();
}
--- a/hotspot/src/share/vm/services/diagnosticFramework.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/services/diagnosticFramework.hpp Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 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
@@ -34,6 +34,22 @@
#include "utilities/ostream.hpp"
+enum DCmdSource {
+ DCmd_Source_Internal = 0x01U, // invocation from the JVM
+ DCmd_Source_AttachAPI = 0x02U, // invocation via the attachAPI
+ DCmd_Source_MBean = 0x04U // invocation via a MBean
+};
+
+// Warning: strings referenced by the JavaPermission struct are passed to
+// the native part of the JDK. Avoid use of dynamically allocated strings
+// that could be de-allocated before the JDK native code had time to
+// convert them into Java Strings.
+struct JavaPermission {
+ const char* _class;
+ const char* _name;
+ const char* _action;
+};
+
// CmdLine is the class used to handle a command line containing a single
// diagnostic command and its arguments. It provides methods to access the
// command name and the beginning of the arguments. The class is also
@@ -113,26 +129,30 @@
// used to export the description to the JMX interface of the framework.
class DCmdInfo : public ResourceObj {
protected:
- const char* _name;
- const char* _description;
- const char* _impact;
- int _num_arguments;
- bool _is_enabled;
+ const char* _name; /* Name of the diagnostic command */
+ const char* _description; /* Short description */
+ const char* _impact; /* Impact on the JVM */
+ JavaPermission _permission; /* Java Permission required to execute this command if any */
+ int _num_arguments; /* Number of supported options or arguments */
+ bool _is_enabled; /* True if the diagnostic command can be invoked, false otherwise */
public:
DCmdInfo(const char* name,
const char* description,
const char* impact,
+ JavaPermission permission,
int num_arguments,
bool enabled) {
this->_name = name;
this->_description = description;
this->_impact = impact;
+ this->_permission = permission;
this->_num_arguments = num_arguments;
this->_is_enabled = enabled;
}
const char* name() const { return _name; }
const char* description() const { return _description; }
const char* impact() const { return _impact; }
+ JavaPermission permission() const { return _permission; }
int num_arguments() const { return _num_arguments; }
bool is_enabled() const { return _is_enabled; }
@@ -144,16 +164,20 @@
// framework.
class DCmdArgumentInfo : public ResourceObj {
protected:
- const char* _name;
- const char* _description;
- const char* _type;
- const char* _default_string;
- bool _mandatory;
- bool _option;
- int _position;
+ const char* _name; /* Option/Argument name*/
+ const char* _description; /* Short description */
+ const char* _type; /* Type: STRING, BOOLEAN, etc. */
+ const char* _default_string; /* Default value in a parsable string */
+ bool _mandatory; /* True if the option/argument is mandatory */
+ bool _option; /* True if it is an option, false if it is an argument */
+ /* (see diagnosticFramework.hpp for option/argument definitions) */
+ bool _multiple; /* True is the option can be specified several time */
+ int _position; /* Expected position for this argument (this field is */
+ /* meaningless for options) */
public:
DCmdArgumentInfo(const char* name, const char* description, const char* type,
- const char* default_string, bool mandatory, bool option) {
+ const char* default_string, bool mandatory, bool option,
+ bool multiple) {
this->_name = name;
this->_description = description;
this->_type = type;
@@ -161,11 +185,12 @@
this->_option = option;
this->_mandatory = mandatory;
this->_option = option;
+ this->_multiple = multiple;
this->_position = -1;
}
DCmdArgumentInfo(const char* name, const char* description, const char* type,
const char* default_string, bool mandatory, bool option,
- int position) {
+ bool multiple, int position) {
this->_name = name;
this->_description = description;
this->_type = type;
@@ -173,6 +198,7 @@
this->_option = option;
this->_mandatory = mandatory;
this->_option = option;
+ this->_multiple = multiple;
this->_position = position;
}
const char* name() const { return _name; }
@@ -181,11 +207,29 @@
const char* default_string() const { return _default_string; }
bool is_mandatory() const { return _mandatory; }
bool is_option() const { return _option; }
+ bool is_multiple() const { return _multiple; }
int position() const { return _position; }
};
// The DCmdParser class can be used to create an argument parser for a
// diagnostic command. It is not mandatory to use it to parse arguments.
+// The DCmdParser parses a CmdLine instance according to the parameters that
+// have been declared by its associated diagnostic command. A parameter can
+// either be an option or an argument. Options are identified by the option name
+// while arguments are identified by their position in the command line. The
+// position of an argument is defined relative to all arguments passed on the
+// command line, options are not considered when defining an argument position.
+// The generic syntax of a diagnostic command is:
+//
+// <command name> [<option>=<value>] [<argument_value>]
+//
+// Example:
+//
+// command_name option1=value1 option2=value argumentA argumentB argumentC
+//
+// In this command line, the diagnostic command receives five parameters, two
+// options named option1 and option2, and three arguments. argumentA's position
+// is 0, argumentB's position is 1 and argumentC's position is 2.
class DCmdParser {
private:
GenDCmdArgument* _options;
@@ -249,6 +293,19 @@
// longer description can provide more specific details like the fact that Thread Dump
// impact depends on the heap size.
static const char* impact() { return "Low: No impact"; }
+ // The permission() method returns the description of Java Permission. This
+ // permission is required when the diagnostic command is invoked via the
+ // DiagnosticCommandMBean. The rationale for this permission check is that
+ // the DiagnosticCommandMBean can be used to perform remote invocations of
+ // diagnostic commands through the PlatformMBeanServer. The (optional) Java
+ // Permission associated with each diagnostic command should ease the work
+ // of system administrators to write policy files granting permissions to
+ // execute diagnostic commands to remote users. Any diagnostic command with
+ // a potential impact on security should overwrite this method.
+ static const JavaPermission permission() {
+ JavaPermission p = {NULL, NULL, NULL};
+ return p;
+ }
static int num_arguments() { return 0; }
outputStream* output() { return _output; }
bool is_heap_allocated() { return _is_heap_allocated; }
@@ -263,7 +320,7 @@
"The argument list of this diagnostic command should be empty.");
}
}
- virtual void execute(TRAPS) { }
+ virtual void execute(DCmdSource source, TRAPS) { }
virtual void reset(TRAPS) { }
virtual void cleanup() { }
@@ -278,7 +335,7 @@
}
// main method to invoke the framework
- static void parse_and_execute(outputStream* out, const char* cmdline,
+ static void parse_and_execute(DCmdSource source, outputStream* out, const char* cmdline,
char delim, TRAPS);
};
@@ -291,9 +348,10 @@
static const char* description() { return "No Help";}
static const char* disabled_message() { return "Diagnostic command currently disabled"; }
static const char* impact() { return "Low: No impact"; }
+ static const JavaPermission permission() {JavaPermission p = {NULL, NULL, NULL}; return p; }
static int num_arguments() { return 0; }
virtual void parse(CmdLine *line, char delim, TRAPS);
- virtual void execute(TRAPS) { }
+ virtual void execute(DCmdSource source, TRAPS) { }
virtual void reset(TRAPS);
virtual void cleanup();
virtual void print_help(const char* name);
@@ -323,6 +381,8 @@
class DCmdFactory: public CHeapObj<mtInternal> {
private:
static Mutex* _dcmdFactory_lock;
+ static bool _send_jmx_notification;
+ static bool _has_pending_jmx_notification;
// Pointer to the next factory in the singly-linked list of registered
// diagnostic commands
DCmdFactory* _next;
@@ -333,19 +393,23 @@
// When hidden, a diagnostic command doesn't appear in the list of commands
// provided by the 'help' command.
bool _hidden;
+ uint32_t _export_flags;
int _num_arguments;
static DCmdFactory* _DCmdFactoryList;
public:
- DCmdFactory(int num_arguments, bool enabled, bool hidden) {
+ DCmdFactory(int num_arguments, uint32_t flags, bool enabled, bool hidden) {
_next = NULL;
_enabled = enabled;
_hidden = hidden;
+ _export_flags = flags;
_num_arguments = num_arguments;
}
bool is_enabled() const { return _enabled; }
void set_enabled(bool b) { _enabled = b; }
bool is_hidden() const { return _hidden; }
void set_hidden(bool b) { _hidden = b; }
+ uint32_t export_flags() { return _export_flags; }
+ void set_export_flags(uint32_t f) { _export_flags = f; }
int num_arguments() { return _num_arguments; }
DCmdFactory* next() { return _next; }
virtual DCmd* create_Cheap_instance(outputStream* output) = 0;
@@ -353,19 +417,29 @@
virtual const char* name() const = 0;
virtual const char* description() const = 0;
virtual const char* impact() const = 0;
+ virtual const JavaPermission permission() const = 0;
virtual const char* disabled_message() const = 0;
// Register a DCmdFactory to make a diagnostic command available.
// Once registered, a diagnostic command must not be unregistered.
// To prevent a diagnostic command from being executed, just set the
// enabled flag to false.
static int register_DCmdFactory(DCmdFactory* factory);
- static DCmdFactory* factory(const char* cmd, size_t len);
+ static DCmdFactory* factory(DCmdSource source, const char* cmd, size_t len);
// Returns a C-heap allocated diagnostic command for the given command line
- static DCmd* create_global_DCmd(CmdLine &line, outputStream* out, TRAPS);
+ static DCmd* create_global_DCmd(DCmdSource source, CmdLine &line, outputStream* out, TRAPS);
// Returns a resourceArea allocated diagnostic command for the given command line
- static DCmd* create_local_DCmd(CmdLine &line, outputStream* out, TRAPS);
- static GrowableArray<const char*>* DCmd_list();
- static GrowableArray<DCmdInfo*>* DCmdInfo_list();
+ static DCmd* create_local_DCmd(DCmdSource source, CmdLine &line, outputStream* out, TRAPS);
+ static GrowableArray<const char*>* DCmd_list(DCmdSource source);
+ static GrowableArray<DCmdInfo*>* DCmdInfo_list(DCmdSource source);
+
+ static void set_jmx_notification_enabled(bool enabled) {
+ _send_jmx_notification = enabled;
+ }
+ static void push_jmx_notification_request();
+ static bool has_pending_jmx_notification() { return _has_pending_jmx_notification; }
+ static void send_notification(TRAPS);
+private:
+ static void send_notification_internal(TRAPS);
friend class HelpDCmd;
};
@@ -374,8 +448,8 @@
// where this template is used to create and register factories.
template <class DCmdClass> class DCmdFactoryImpl : public DCmdFactory {
public:
- DCmdFactoryImpl(bool enabled, bool hidden) :
- DCmdFactory(DCmdClass::num_arguments(), enabled, hidden) { }
+ DCmdFactoryImpl(uint32_t flags, bool enabled, bool hidden) :
+ DCmdFactory(DCmdClass::num_arguments(), flags, enabled, hidden) { }
// Returns a C-heap allocated instance
virtual DCmd* create_Cheap_instance(outputStream* output) {
return new (ResourceObj::C_HEAP, mtInternal) DCmdClass(output, true);
@@ -393,6 +467,9 @@
virtual const char* impact() const {
return DCmdClass::impact();
}
+ virtual const JavaPermission permission() const {
+ return DCmdClass::permission();
+ }
virtual const char* disabled_message() const {
return DCmdClass::disabled_message();
}
--- a/hotspot/src/share/vm/services/jmm.h Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/services/jmm.h Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2011, 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
@@ -49,7 +49,8 @@
JMM_VERSION_1_1 = 0x20010100, // JDK 6
JMM_VERSION_1_2 = 0x20010200, // JDK 7
JMM_VERSION_1_2_1 = 0x20010201, // JDK 7 GA
- JMM_VERSION = 0x20010202
+ JMM_VERSION_1_2_2 = 0x20010202,
+ JMM_VERSION = 0x20010203
};
typedef struct {
@@ -62,7 +63,8 @@
unsigned int isObjectMonitorUsageSupported : 1;
unsigned int isSynchronizerUsageSupported : 1;
unsigned int isThreadAllocatedMemorySupported : 1;
- unsigned int : 23;
+ unsigned int isRemoteDiagnosticCommandsSupported : 1;
+ unsigned int : 22;
} jmmOptionalSupport;
typedef enum {
@@ -190,21 +192,27 @@
} jmmGCStat;
typedef struct {
- const char* name;
- const char* description;
- const char* impact;
- int num_arguments;
- jboolean enabled;
+ const char* name; /* Name of the diagnostic command */
+ const char* description; /* Short description */
+ const char* impact; /* Impact on the JVM */
+ const char* permission_class; /* Class name of the required permission if any */
+ const char* permission_name; /* Permission name of the required permission if any */
+ const char* permission_action; /* Action name of the required permission if any*/
+ int num_arguments; /* Number of supported options or arguments */
+ jboolean enabled; /* True if the diagnostic command can be invoked, false otherwise */
} dcmdInfo;
typedef struct {
- const char* name;
- const char* description;
- const char* type;
- const char* default_string;
- jboolean mandatory;
- jboolean option;
- int position;
+ const char* name; /* Option/Argument name*/
+ const char* description; /* Short description */
+ const char* type; /* Type: STRING, BOOLEAN, etc. */
+ const char* default_string; /* Default value in a parsable string */
+ jboolean mandatory; /* True if the option/argument is mandatory */
+ jboolean option; /* True if it is an option, false if it is an argument */
+ /* (see diagnosticFramework.hpp for option/argument definitions) */
+ jboolean multiple; /* True if the option can be specified several time */
+ int position; /* Expected position for this argument (this field is */
+ /* meaningless for options) */
} dcmdArgInfo;
typedef struct jmmInterface_1_ {
@@ -327,6 +335,9 @@
jstring (JNICALL *ExecuteDiagnosticCommand)
(JNIEnv *env,
jstring command);
+ void (JNICALL *SetDiagnosticFrameworkNotificationEnabled)
+ (JNIEnv *env,
+ jboolean enabled);
} JmmInterface;
#ifdef __cplusplus
--- a/hotspot/src/share/vm/services/management.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/services/management.cpp Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2012, 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
@@ -68,6 +68,9 @@
Klass* Management::_managementFactory_klass = NULL;
Klass* Management::_garbageCollectorImpl_klass = NULL;
Klass* Management::_gcInfo_klass = NULL;
+Klass* Management::_diagnosticCommandImpl_klass = NULL;
+Klass* Management::_managementFactoryHelper_klass = NULL;
+
jmmOptionalSupport Management::_optional_support = {0};
TimeStamp Management::_stamp;
@@ -128,11 +131,14 @@
_optional_support.isSynchronizerUsageSupported = 1;
#endif // INCLUDE_SERVICES
_optional_support.isThreadAllocatedMemorySupported = 1;
+ _optional_support.isRemoteDiagnosticCommandsSupported = 1;
// Registration of the diagnostic commands
DCmdRegistrant::register_dcmds();
DCmdRegistrant::register_dcmds_ext();
- DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<NMTDCmd>(true, false));
+ uint32_t full_export = DCmd_Source_Internal | DCmd_Source_AttachAPI
+ | DCmd_Source_MBean;
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<NMTDCmd>(full_export, true, false));
}
void Management::initialize(TRAPS) {
@@ -262,6 +268,20 @@
return _gcInfo_klass;
}
+Klass* Management::sun_management_DiagnosticCommandImpl_klass(TRAPS) {
+ if (_diagnosticCommandImpl_klass == NULL) {
+ _diagnosticCommandImpl_klass = load_and_initialize_klass(vmSymbols::sun_management_DiagnosticCommandImpl(), CHECK_NULL);
+ }
+ return _diagnosticCommandImpl_klass;
+}
+
+Klass* Management::sun_management_ManagementFactoryHelper_klass(TRAPS) {
+ if (_managementFactoryHelper_klass == NULL) {
+ _managementFactoryHelper_klass = load_and_initialize_klass(vmSymbols::sun_management_ManagementFactoryHelper(), CHECK_NULL);
+ }
+ return _managementFactoryHelper_klass;
+}
+
static void initialize_ThreadInfo_constructor_arguments(JavaCallArguments* args, ThreadSnapshot* snapshot, TRAPS) {
Handle snapshot_thread(THREAD, snapshot->threadObj());
@@ -2144,7 +2164,7 @@
JVM_ENTRY(jobjectArray, jmm_GetDiagnosticCommands(JNIEnv *env))
ResourceMark rm(THREAD);
- GrowableArray<const char *>* dcmd_list = DCmdFactory::DCmd_list();
+ GrowableArray<const char *>* dcmd_list = DCmdFactory::DCmd_list(DCmd_Source_MBean);
objArrayOop cmd_array_oop = oopFactory::new_objArray(SystemDictionary::String_klass(),
dcmd_list->length(), CHECK_NULL);
objArrayHandle cmd_array(THREAD, cmd_array_oop);
@@ -2173,7 +2193,7 @@
"Array element type is not String class");
}
- GrowableArray<DCmdInfo *>* info_list = DCmdFactory::DCmdInfo_list();
+ GrowableArray<DCmdInfo *>* info_list = DCmdFactory::DCmdInfo_list(DCmd_Source_MBean);
int num_cmds = cmds_ah->length();
for (int i = 0; i < num_cmds; i++) {
@@ -2196,6 +2216,10 @@
infoArray[i].name = info->name();
infoArray[i].description = info->description();
infoArray[i].impact = info->impact();
+ JavaPermission p = info->permission();
+ infoArray[i].permission_class = p._class;
+ infoArray[i].permission_name = p._name;
+ infoArray[i].permission_action = p._action;
infoArray[i].num_arguments = info->num_arguments();
infoArray[i].enabled = info->is_enabled();
}
@@ -2215,7 +2239,8 @@
"Command line content cannot be null.");
}
DCmd* dcmd = NULL;
- DCmdFactory*factory = DCmdFactory::factory(cmd_name, strlen(cmd_name));
+ DCmdFactory*factory = DCmdFactory::factory(DCmd_Source_MBean, cmd_name,
+ strlen(cmd_name));
if (factory != NULL) {
dcmd = factory->create_resource_instance(NULL);
}
@@ -2235,6 +2260,7 @@
infoArray[i].default_string = array->at(i)->default_string();
infoArray[i].mandatory = array->at(i)->is_mandatory();
infoArray[i].option = array->at(i)->is_option();
+ infoArray[i].multiple = array->at(i)->is_multiple();
infoArray[i].position = array->at(i)->position();
}
return;
@@ -2253,11 +2279,15 @@
"Command line content cannot be null.");
}
bufferedStream output;
- DCmd::parse_and_execute(&output, cmdline, ' ', CHECK_NULL);
+ DCmd::parse_and_execute(DCmd_Source_MBean, &output, cmdline, ' ', CHECK_NULL);
oop result = java_lang_String::create_oop_from_str(output.as_string(), CHECK_NULL);
return (jstring) JNIHandles::make_local(env, result);
JVM_END
+JVM_ENTRY(void, jmm_SetDiagnosticFrameworkNotificationEnabled(JNIEnv *env, jboolean enabled))
+ DCmdFactory::set_jmx_notification_enabled(enabled?true:false);
+JVM_END
+
jlong Management::ticks_to_ms(jlong ticks) {
assert(os::elapsed_frequency() > 0, "Must be non-zero");
return (jlong)(((double)ticks / (double)os::elapsed_frequency())
@@ -2304,7 +2334,8 @@
jmm_GetDiagnosticCommands,
jmm_GetDiagnosticCommandInfo,
jmm_GetDiagnosticCommandArgumentsInfo,
- jmm_ExecuteDiagnosticCommand
+ jmm_ExecuteDiagnosticCommand,
+ jmm_SetDiagnosticFrameworkNotificationEnabled
};
#endif // INCLUDE_MANAGEMENT
--- a/hotspot/src/share/vm/services/management.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/services/management.hpp Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2012, 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
@@ -50,6 +50,8 @@
static Klass* _garbageCollectorMXBean_klass;
static Klass* _managementFactory_klass;
static Klass* _garbageCollectorImpl_klass;
+ static Klass* _diagnosticCommandImpl_klass;
+ static Klass* _managementFactoryHelper_klass;
static Klass* _gcInfo_klass;
static Klass* load_and_initialize_klass(Symbol* sh, TRAPS);
@@ -99,6 +101,10 @@
NOT_MANAGEMENT_RETURN_(NULL);
static Klass* com_sun_management_GcInfo_klass(TRAPS)
NOT_MANAGEMENT_RETURN_(NULL);
+ static Klass* sun_management_DiagnosticCommandImpl_klass(TRAPS)
+ NOT_MANAGEMENT_RETURN_(NULL);
+ static Klass* sun_management_ManagementFactoryHelper_klass(TRAPS)
+ NOT_MANAGEMENT_RETURN_(NULL);
static instanceOop create_thread_info_instance(ThreadSnapshot* snapshot, TRAPS);
static instanceOop create_thread_info_instance(ThreadSnapshot* snapshot, objArrayHandle monitors_array, typeArrayHandle depths_array, objArrayHandle synchronizers_array, TRAPS);
--- a/hotspot/src/share/vm/services/memBaseline.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/services/memBaseline.cpp Thu May 16 11:47:51 2013 +0100
@@ -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;
}
--- a/hotspot/src/share/vm/services/memBaseline.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/services/memBaseline.hpp Thu May 16 11:47:51 2013 +0100
@@ -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();
--- a/hotspot/src/share/vm/services/memSnapshot.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/services/memSnapshot.cpp Thu May 16 11:47:51 2013 +0100
@@ -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
@@ -262,13 +262,28 @@
assert(cur->is_reserved_region() && cur->contains_region(rec),
"Sanity check");
if (rec->is_same_region(cur)) {
- // release whole reserved region
+
+ // In snapshot, the virtual memory records are sorted in following orders:
+ // 1. virtual memory's base address
+ // 2. virtual memory reservation record, followed by commit records within this reservation.
+ // The commit records are also in base address order.
+ // When a reserved region is released, we want to remove the reservation record and all
+ // commit records following it.
#ifdef ASSERT
- VMMemRegion* next_region = (VMMemRegion*)peek_next();
- // should not have any committed memory in this reserved region
- assert(next_region == NULL || !next_region->is_committed_region(), "Sanity check");
+ address low_addr = cur->addr();
+ address high_addr = low_addr + cur->size();
#endif
+ // remove virtual memory reservation record
remove();
+ // remove committed regions within above reservation
+ VMMemRegion* next_region = (VMMemRegion*)current();
+ while (next_region != NULL && next_region->is_committed_region()) {
+ assert(next_region->addr() >= low_addr &&
+ next_region->addr() + next_region->size() <= high_addr,
+ "Range check");
+ remove();
+ next_region = (VMMemRegion*)current();
+ }
} else if (rec->addr() == cur->addr() ||
rec->addr() + rec->size() == cur->addr() + cur->size()) {
// released region is at either end of this region
--- a/hotspot/src/share/vm/services/memTracker.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/services/memTracker.cpp Thu May 16 11:47:51 2013 +0100
@@ -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();
--- a/hotspot/src/share/vm/services/nmtDCmd.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/services/nmtDCmd.cpp Thu May 16 11:47:51 2013 +0100
@@ -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
@@ -71,7 +71,7 @@
_dcmdparser.add_dcmd_option(&_scale);
}
-void NMTDCmd::execute(TRAPS) {
+void NMTDCmd::execute(DCmdSource source, TRAPS) {
const char* scale_value = _scale.value();
size_t scale_unit;
if (strcmp(scale_value, "KB") == 0 || strcmp(scale_value, "kb") == 0) {
--- a/hotspot/src/share/vm/services/nmtDCmd.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/services/nmtDCmd.hpp Thu May 16 11:47:51 2013 +0100
@@ -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
@@ -52,10 +52,15 @@
return "Print native memory usage";
}
static const char* impact() {
- return "Medium:";
+ return "Medium";
+ }
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.lang.management.ManagementPermission",
+ "monitor", NULL};
+ return p;
}
static int num_arguments();
- virtual void execute(TRAPS);
+ virtual void execute(DCmdSource source, TRAPS);
};
#endif // SHARE_VM_SERVICES_NMT_DCMD_HPP
--- a/hotspot/src/share/vm/utilities/debug.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/utilities/debug.cpp Thu May 16 11:47:51 2013 +0100
@@ -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();
--- a/hotspot/src/share/vm/utilities/debug.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/utilities/debug.hpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/share/vm/utilities/ostream.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/utilities/ostream.hpp Thu May 16 11:47:51 2013 +0100
@@ -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;}
--- a/hotspot/src/share/vm/utilities/vmError.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/utilities/vmError.cpp Thu May 16 11:47:51 2013 +0100
@@ -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()));
+ }
+ }
}
}
--- a/hotspot/src/share/vm/utilities/vmError.hpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/utilities/vmError.hpp Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/hotspot/src/share/vm/utilities/workgroup.cpp Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/src/share/vm/utilities/workgroup.cpp Thu May 16 11:47:51 2013 +0100
@@ -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) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/ciReplay/TestSA.sh Thu May 16 11:47:51 2013 +0100
@@ -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
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/ciReplay/TestVM.sh Thu May 16 11:47:51 2013 +0100
@@ -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
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/ciReplay/TestVM_no_comp_level.sh Thu May 16 11:47:51 2013 +0100
@@ -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
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/ciReplay/common.sh Thu May 16 11:47:51 2013 +0100
@@ -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.<pid>' and 'core'
+ core_locations=`echo $core_locations | \
+ sed -e 's/\([^ ]*\) or \([^ ]*\)/\1 \2/'`
+ # add <core_path>/core.<pid> core.<pid>
+ 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
+}
+
--- a/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java Thu May 16 11:47:51 2013 +0100
@@ -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<Integer> callable;
--- a/hotspot/test/compiler/whitebox/MakeMethodNotCompilableTest.java Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/test/compiler/whitebox/MakeMethodNotCompilableTest.java Thu May 16 11:47:51 2013 +0100
@@ -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;
+ }
}
--- a/hotspot/test/gc/7072527/TestFullGCCount.java Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/test/gc/7072527/TestFullGCCount.java Thu May 16 11:47:51 2013 +0100
@@ -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<GarbageCollectorMXBean> 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<String, List> counts = new HashMap<String, List>();
+
+ // 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<Long>(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; i<iterations; i++) {
- System.gc();
- counts[i] = getCollectionCount();
- if (i>0) {
- if (counts[i] - counts[i-1] != 2) {
- diffAlways2 = false;
+ for (int i = 0; i < iterations - 1; i++) {
+ List<Long> 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<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans();
- List<GarbageCollectorMXBean> collectors = ManagementFactory.getGarbageCollectorMXBeans();
- for (int i=0; i<collectors.size(); i++) {
+ private static void addCollectionCount(HashMap<String, List> 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;
}
-
}
-
--- a/hotspot/test/gc/TestVerifyDuringStartup.java Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/test/gc/TestVerifyDuringStartup.java Thu May 16 11:47:51 2013 +0100
@@ -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<String> 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);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/concurrentMarkSweep/CheckAllocateAndSystemGC.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,66 @@
+/*
+ * 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 CheckAllocateAndSystemGC
+ * @summary CMS: assert(used() == used_after_gc && used_after_gc <= capacity()) failed: used: 0 used_after_gc: 292080 capacity: 1431699456
+ * @bug 8013032
+ * @key gc
+ * @key regression
+ * @library /testlibrary
+ * @run main/othervm CheckAllocateAndSystemGC
+ * @author jon.masamitsu@oracle.com
+ */
+
+import com.oracle.java.testlibrary.*;
+
+public class CheckAllocateAndSystemGC {
+ public static void main(String args[]) throws Exception {
+
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+ "-showversion",
+ "-XX:+UseConcMarkSweepGC",
+ "-Xmn4m",
+ "-XX:MaxTenuringThreshold=1",
+ "-XX:-UseCMSCompactAtFullCollection",
+ "CheckAllocateAndSystemGC$AllocateAndSystemGC"
+ );
+
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+ output.shouldNotContain("error");
+
+ output.shouldHaveExitValue(0);
+ }
+ static class AllocateAndSystemGC {
+ public static void main(String [] args) {
+ Integer x[] = new Integer [1000];
+ // Allocate enough objects to cause a minor collection.
+ // These allocations suffice for a 4m young geneneration.
+ for (int i = 0; i < 100; i++) {
+ Integer y[] = new Integer[10000];
+ }
+ System.gc();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/concurrentMarkSweep/GuardShrinkWarning.java Thu May 16 11:47:51 2013 +0100
@@ -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();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/concurrentMarkSweep/SystemGCOnForegroundCollector.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,67 @@
+/*
+ * 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 SystemGCOnForegroundCollector
+ * @summary CMS: Call reset_after_compaction() only if a compaction has been done
+ * @bug 8013184
+ * @key gc
+ * @key regression
+ * @library /testlibrary
+ * @run main/othervm SystemGCOnForegroundCollector
+ * @author jon.masamitsu@oracle.com
+ */
+
+import com.oracle.java.testlibrary.*;
+
+public class SystemGCOnForegroundCollector {
+ public static void main(String args[]) throws Exception {
+
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+ "-showversion",
+ "-XX:+UseConcMarkSweepGC",
+ "-XX:MaxTenuringThreshold=1",
+ "-XX:-UseCMSCompactAtFullCollection",
+ ThreePlusMSSystemGC.class.getName()
+ );
+
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+ output.shouldNotContain("error");
+
+ output.shouldHaveExitValue(0);
+ }
+
+ static class ThreePlusMSSystemGC {
+ public static void main(String [] args) {
+ // From running this test 3 System.gc() were always
+ // enough to see the failure but the cause of the failure
+ // depends on how objects are allocated in the CMS generation
+ // which is non-deterministic. Use 30 iterations for a more
+ // reliable test.
+ for (int i = 0; i < 30; i++) {
+ System.gc();
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/g1/TestRegionAlignment.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,36 @@
+/*
+ * 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 TestRegionAlignment.java
+ * @bug 8013791
+ * @summary Make sure that G1 ergonomics pick a heap size that is aligned with the region size
+ * @run main/othervm -XX:+UseG1GC -XX:G1HeapRegionSize=32m -XX:MaxRAM=555m TestRegionAlignment
+ *
+ * When G1 ergonomically picks a maximum heap size it must be aligned to the region size.
+ * This test tries to get the VM to pick a small and unaligned heap size (by using MaxRAM=555) and a
+ * large region size (by using -XX:G1HeapRegionSize=32m). This will fail without the fix for 8013791.
+ */
+public class TestRegionAlignment {
+ public static void main(String[] args) { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/g1/TestShrinkToOneRegion.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,37 @@
+/*
+ * 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 TestShrinkToOneRegion.java
+ * @bug 8013872
+ * @summary Shrinking the heap down to one region used to hit an assert
+ * @run main/othervm -XX:+UseG1GC -XX:G1HeapRegionSize=32m -Xmx256m TestShrinkToOneRegion
+ *
+ * Doing a System.gc() without having allocated many objects will shrink the heap.
+ * With a large region size we will shrink the heap to one region.
+ */
+public class TestShrinkToOneRegion {
+ public static void main(String[] args) {
+ System.gc();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/ClassUnload/KeepAliveClass.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,79 @@
+/*
+ * 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 KeepAliveClass
+ * @summary This test case uses a java.lang.Class instance to keep a class alive.
+ * @library /testlibrary /testlibrary/whitebox /runtime/testlibrary
+ * @library classes
+ * @build KeepAliveClass test.Empty
+ * @build ClassUnloadCommon
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI KeepAliveClass
+ */
+
+import java.lang.ref.SoftReference;
+import sun.hotspot.WhiteBox;
+
+/**
+ * Test that verifies that classes are not unloaded when specific types of references are kept to them.
+ */
+public class KeepAliveClass {
+ private static final String className = "test.Empty";
+ private static final WhiteBox wb = WhiteBox.getWhiteBox();
+ public static Object escape = null;
+
+ public static void main(String... args) throws Exception {
+ ClassLoader cl = ClassUnloadCommon.newClassLoader();
+ Class<?> c = cl.loadClass(className);
+ Object o = c.newInstance();
+ o = null; cl = null;
+ escape = c;
+
+ {
+ boolean isAlive = wb.isClassAlive(className);
+ System.out.println("testClass (1) alive: " + isAlive);
+
+ ClassUnloadCommon.failIf(!isAlive, "should be alive");
+ }
+
+ ClassUnloadCommon.triggerUnloading();
+
+ {
+ boolean isAlive = wb.isClassAlive(className);
+ System.out.println("testClass (2) alive: " + isAlive);
+
+ ClassUnloadCommon.failIf(!isAlive, "should be alive");
+ }
+ c = null;
+ escape = null;
+ ClassUnloadCommon.triggerUnloading();
+
+ {
+ boolean isAlive = wb.isClassAlive(className);
+ System.out.println("testClass (3) alive: " + isAlive);
+ ClassUnloadCommon.failIf(isAlive, "should be unloaded");
+ }
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/ClassUnload/KeepAliveClassLoader.java Thu May 16 11:47:51 2013 +0100
@@ -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 KeepAliveClassLoader
+ * @summary This test case uses a java.lang.ClassLoader instance to keep a class alive.
+ * @library /testlibrary /testlibrary/whitebox /runtime/testlibrary
+ * @library classes
+ * @build KeepAliveClassLoader test.Empty
+ * @build ClassUnloadCommon
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI KeepAliveClassLoader
+ */
+
+import sun.hotspot.WhiteBox;
+
+/**
+ * Test that verifies that classes are not unloaded when specific types of references are kept to them.
+ */
+public class KeepAliveClassLoader {
+ private static final String className = "test.Empty";
+ private static final WhiteBox wb = WhiteBox.getWhiteBox();
+ public static Object escape = null;
+
+
+ public static void main(String... args) throws Exception {
+ ClassLoader cl = ClassUnloadCommon.newClassLoader();
+ Class<?> c = cl.loadClass(className);
+ Object o = c.newInstance();
+ o = null; c = null;
+ escape = cl;
+
+ {
+ boolean isAlive = wb.isClassAlive(className);
+ System.out.println("testClassLoader (1) alive: " + isAlive);
+
+ ClassUnloadCommon.failIf(!isAlive, "should be alive");
+ }
+ ClassUnloadCommon.triggerUnloading();
+
+ {
+ boolean isAlive = wb.isClassAlive(className);
+ System.out.println("testClassLoader (2) alive: " + isAlive);
+
+ ClassUnloadCommon.failIf(!isAlive, "should be alive");
+ }
+ cl = null;
+ escape = null;
+ ClassUnloadCommon.triggerUnloading();
+
+ {
+ boolean isAlive = wb.isClassAlive(className);
+ System.out.println("testClassLoader (3) alive: " + isAlive);
+ ClassUnloadCommon.failIf(isAlive, "should be unloaded");
+ }
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/ClassUnload/KeepAliveObject.java Thu May 16 11:47:51 2013 +0100
@@ -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 KeepAliveObject
+ * @summary This test case uses a class instance to keep the class alive.
+ * @library /testlibrary /testlibrary/whitebox /runtime/testlibrary
+ * @library classes
+ * @build KeepAliveObject test.Empty
+ * @build ClassUnloadCommon
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI KeepAliveObject
+ */
+
+import sun.hotspot.WhiteBox;
+
+/**
+ * Test that verifies that classes are not unloaded when specific types of references are kept to them.
+ */
+public class KeepAliveObject {
+ private static final String className = "test.Empty";
+ private static final WhiteBox wb = WhiteBox.getWhiteBox();
+ public static Object escape = null;
+
+ public static void main(String... args) throws Exception {
+ ClassLoader cl = ClassUnloadCommon.newClassLoader();
+ Class<?> c = cl.loadClass(className);
+ Object o = c.newInstance();
+ cl = null; c = null;
+ escape = o;
+
+ {
+ boolean isAlive = wb.isClassAlive(className);
+ System.out.println("testObject (1) alive: " + isAlive);
+
+ ClassUnloadCommon.failIf(!isAlive, "should be alive");
+ }
+
+ ClassUnloadCommon.triggerUnloading();
+
+ {
+ boolean isAlive = wb.isClassAlive(className);
+ System.out.println("testObject (2) alive: " + isAlive);
+
+ ClassUnloadCommon.failIf(!isAlive, "should be alive");
+ }
+ o = null;
+ escape = null;
+ ClassUnloadCommon.triggerUnloading();
+
+ {
+ boolean isAlive = wb.isClassAlive(className);
+ System.out.println("testObject (3) alive: " + isAlive);
+ ClassUnloadCommon.failIf(isAlive, "should be unloaded");
+ }
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/ClassUnload/KeepAliveSoftReference.java Thu May 16 11:47:51 2013 +0100
@@ -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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 KeepAliveSoftReference
+ * @summary This test case uses a java.lang.ref.SoftReference referencing a class instance to keep a class alive.
+ * @library /testlibrary /testlibrary/whitebox /runtime/testlibrary
+ * @library classes
+ * @build KeepAliveSoftReference test.Empty
+ * @build ClassUnloadCommon
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI KeepAliveSoftReference
+ */
+
+import java.lang.ref.SoftReference;
+import sun.hotspot.WhiteBox;
+
+/**
+ * Test that verifies that classes are not unloaded when specific types of references are kept to them.
+ */
+public class KeepAliveSoftReference {
+ private static final String className = "test.Empty";
+ private static final WhiteBox wb = WhiteBox.getWhiteBox();
+
+ public static void main(String... args) throws Exception {
+ ClassLoader cl = ClassUnloadCommon.newClassLoader();
+ Class<?> c = cl.loadClass(className);
+ Object o = c.newInstance();
+ SoftReference<Object> sr = new SoftReference(o);
+ o = null; c = null; cl = null;
+
+ {
+ boolean isAlive = wb.isClassAlive(className);
+ System.out.println("testSoftReference (1) alive: " + isAlive);
+ boolean cleared = (sr.get() == null);
+ boolean shouldBeAlive = !cleared;
+ ClassUnloadCommon.failIf(isAlive != shouldBeAlive, "" + isAlive + " != " + shouldBeAlive);
+ }
+
+ ClassUnloadCommon.triggerUnloading();
+
+ {
+ boolean isAlive = wb.isClassAlive(className);
+ System.out.println("testSoftReference (2) alive: " + isAlive);
+ boolean cleared = (sr.get() == null);
+ boolean shouldBeAlive = !cleared;
+ ClassUnloadCommon.failIf(isAlive != shouldBeAlive, "" + isAlive + " != " + shouldBeAlive);
+ }
+ sr.clear();
+ ClassUnloadCommon.triggerUnloading();
+
+ {
+ boolean isAlive = wb.isClassAlive(className);
+ System.out.println("testSoftReference (3) alive: " + isAlive);
+ boolean cleared = (sr.get() == null);
+ boolean shouldBeAlive = !cleared;
+ ClassUnloadCommon.failIf(isAlive != shouldBeAlive, "" + isAlive + " != " + shouldBeAlive);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/ClassUnload/UnloadTest.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,66 @@
+/*
+ * 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 UnloadTest
+ * @library /runtime/testlibrary /testlibrary /testlibrary/whitebox
+ * @library classes
+ * @build ClassUnloadCommon test.Empty
+ * @build UnloadTest
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI UnloadTest
+ */
+import sun.hotspot.WhiteBox;
+
+/**
+ * Test that verifies that classes are unloaded when they are no longer reachable.
+ *
+ * The test creates a class loader, uses the loader to load a class and creates an instance
+ * of that class. The it nulls out all the references to the instance, class and class loader
+ * and tries to trigger class unloading. Then it verifies that the class is no longer
+ * loaded by the VM.
+ */
+public class UnloadTest {
+ private static String className = "test.Empty";
+
+ public static void main(String... args) throws Exception {
+ run();
+ }
+
+ private static void run() throws Exception {
+ final WhiteBox wb = WhiteBox.getWhiteBox();
+
+ ClassUnloadCommon.failIf(wb.isClassAlive(className), "is not expected to be alive yet");
+
+ ClassLoader cl = ClassUnloadCommon.newClassLoader();
+ Class<?> c = cl.loadClass(className);
+ Object o = c.newInstance();
+
+ ClassUnloadCommon.failIf(!wb.isClassAlive(className), "should be live here");
+
+ cl = null; c = null; o = null;
+ ClassUnloadCommon.triggerUnloading();
+ ClassUnloadCommon.failIf(wb.isClassAlive(className), "should have been unloaded");
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/ClassUnload/classes/test/Empty.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,5 @@
+package test;
+
+public class Empty {
+public String toString() { return "nothing"; }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/NMT/ReleaseCommittedMemory.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,50 @@
+/*
+ * 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 8013120
+ * @summary Release committed memory and make sure NMT handles it correctly
+ * @key nmt regression
+ * @library /testlibrary /testlibrary/whitebox
+ * @build ReleaseCommittedMemory
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail ReleaseCommittedMemory
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class ReleaseCommittedMemory {
+
+ public static void main(String args[]) throws Exception {
+ WhiteBox wb = WhiteBox.getWhiteBox();
+ long reserveSize = 256 * 1024;
+ long addr;
+
+ addr = wb.NMTReserveMemory(reserveSize);
+ wb.NMTCommitMemory(addr, 128*1024);
+ wb.NMTReleaseMemory(addr, reserveSize);
+ wb.NMTWaitForDataMerge();
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/lambda-features/PublicStaticInterfaceMethodHandling.java Thu May 16 11:47:51 2013 +0100
@@ -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();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/memory/ReserveMemory.java Thu May 16 11:47:51 2013 +0100
@@ -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");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/testlibrary/ClassUnloadCommon.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,68 @@
+/*
+ * 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.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+
+public class ClassUnloadCommon {
+ public static class TestFailure extends RuntimeException {
+ TestFailure(String msg) {
+ super(msg);
+ }
+ }
+
+ public static void failIf(boolean value, String msg) {
+ if (value) throw new TestFailure("Test failed: " + msg);
+ }
+
+ private static volatile Object dummy = null;
+ private static void allocateMemory(int kilobytes) {
+ ArrayList<byte[]> l = new ArrayList<>();
+ dummy = l;
+ for (int i = kilobytes; i > 0; i -= 1) {
+ l.add(new byte[1024]);
+ }
+ l = null;
+ dummy = null;
+ }
+
+ public static void triggerUnloading() {
+ allocateMemory(16 * 1024); // yg size is 8m with cms, force young collection
+ System.gc();
+ }
+
+ public static ClassLoader newClassLoader() {
+ try {
+ return new URLClassLoader(new URL[] {
+ Paths.get(System.getProperty("test.classes",".") + File.separatorChar + "classes").toUri().toURL(),
+ }, null);
+ } catch (MalformedURLException e){
+ throw new RuntimeException("Unexpected URL conversion failure", e);
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/sanity/WhiteBox.java Thu May 16 11:47:51 2013 +0100
@@ -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");
+ }
+ }
+}
--- a/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java Wed May 08 11:22:25 2013 +0100
+++ b/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java Thu May 16 11:47:51 2013 +0100
@@ -61,6 +61,9 @@
registerNatives();
}
+ // Arguments
+ public native void printHeapSizes();
+
// Memory
public native long getObjectAddress(Object o);
public native int getHeapOopSize();
@@ -111,6 +114,9 @@
// Intered strings
public native boolean isInStringTable(String str);
+ // Memory
+ public native long reserveMemory(long size);
+
// force Full GC
public native void fullGC();
}
--- a/jaxp/.hgtags Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/.hgtags Thu May 16 11:47:51 2013 +0100
@@ -209,3 +209,5 @@
41b50e2c5ea3f4aa1af729e1deb1678cb3e1ef9c jdk8-b85
ca71ec37b2efc9c3f0971ebabb3a6eb1213d76de jdk8-b86
eddbc8ad2435a89f64729512337c9f2669e4dd85 jdk8-b87
+7122f7bb0fcc8a39e5254402119b2ee3fa0ad313 jdk8-b88
+893d2ba8bbea3a8d090e51d8eaea285b390789ea jdk8-b89
--- a/jaxp/build.xml Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/build.xml Thu May 16 11:47:51 2013 +0100
@@ -176,4 +176,42 @@
<echo message="+---------------------------------------+"/>
</target>
+ <target name="javadoc" depends="init" description="Build basic Javadoc for public packages.">
+ <property name="javadoc.options" value=""/> <!-- default, can be overridden per user or per project -->
+ <!-- Note: even with this default value, includes/excludes
+ from share.src.dir get javadoc'd; see packageset below -->
+ <property name="javadoc.packagenames" value="none"/> <!-- default, can be overridden per user or per project -->
+ <property name="javadoc.dir" value="${build.dir}/docs/api"/>
+ <property name="includes" value="**"/>
+ <javadoc destdir="${javadoc.dir}" source="1.5"
+ windowtitle="UNOFFICIAL" failonerror="true" use="true"
+ author="false" version="false"
+ packagenames="${javadoc.packagenames}">
+ <header><![CDATA[<strong>Unofficial Javadoc</strong> generated from developer sources for preview purposes only]]></header>
+ <arg line="${javadoc.options}"/>
+ <bootclasspath>
+ <path location="${java.home}/lib/rt.jar"/>
+ <path location="${build.classes.dir}"/>
+ </bootclasspath>
+ <sourcepath>
+ <pathelement location="${jaxp.src.dir}"/>
+ </sourcepath>
+ <!-- XXX just <fileset> (restricted further to **/*.java) and no <packageset> -->
+ <!-- means that {@link some.package} will not work, which is no good. -->
+ <!-- (It correctly skips excluded single classes, but not if packageset is also included, -->
+ <!-- which also causes duplicates in the class index for included files.) -->
+ <packageset dir="${jaxp.src.dir}" includes="${includes}" excludes="${excludes}">
+ <or>
+ <filename name="javax/"/>
+ <filename name="org/w3c/"/>
+ <filename name="org/xml/sax/"/>
+ </or>
+ </packageset>
+ </javadoc>
+ </target>
+ <target name="javadoc-nb" depends="javadoc" if="netbeans.home">
+ <property name="javadoc.dir=" value="${build.dir}/docs/api"/>
+ <nbbrowse file="${javadoc.dir}/index.html"/>
+ </target>
+
</project>
--- a/jaxp/nbproject/project.xml Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/nbproject/project.xml Thu May 16 11:47:51 2013 +0100
@@ -15,6 +15,12 @@
<location>.</location>
<encoding>UTF-8</encoding>
</source-folder>
+ <source-folder>
+ <label>src</label>
+ <type>java</type>
+ <location>src</location>
+ <encoding>UTF-8</encoding>
+ </source-folder>
</folders>
<ide-actions>
<action name="build">
@@ -27,9 +33,16 @@
<target>clean</target>
<target>build</target>
</action>
+ <action name="javadoc">
+ <target>javadoc-nb</target>
+ </action>
</ide-actions>
<view>
<items>
+ <source-folder style="packages">
+ <label>src</label>
+ <location>src</location>
+ </source-folder>
<source-file>
<location>build.xml</location>
</source-file>
@@ -38,11 +51,16 @@
<ide-action name="build"/>
<ide-action name="rebuild"/>
<ide-action name="clean"/>
+ <ide-action name="javadoc"/>
</context-menu>
</view>
<subprojects/>
</general-data>
- <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/1"/>
+ <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/1">
+ <compilation-unit>
+ <package-root>src</package-root>
+ </compilation-unit>
+ </java-data>
<preferences xmlns="http://www.netbeans.org/ns/auxiliary-configuration-preferences/1">
<module name="org-netbeans-modules-editor-indent"/>
</preferences>
--- a/jaxp/src/javax/xml/datatype/DatatypeFactory.java Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/src/javax/xml/datatype/DatatypeFactory.java Thu May 16 11:47:51 2013 +0100
@@ -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 @@
/**
* <p>Factory that creates new <code>javax.xml.datatype</code> <code>Object</code>s that map XML to/from Java <code>Object</code>s.</p>
*
- * <p><a name="DatatypeFactory.newInstance"/>{@link #newInstance()} is used to create a new <code>DatatypeFactory</code>.
- * The following implementation resolution mechanisms are used in the following order:</p>
+ * <p>A new instance of the <code>DatatypeFactory</code> is created through the {@link #newInstance()} method
+ * that uses the following implementation resolution mechanisms to determine an implementation:</p>
* <ol>
* <li>
* If the system property specified by {@link #DATATYPEFACTORY_PROPERTY}, "<code>javax.xml.datatype.DatatypeFactory</code>",
- * 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}.
* </li>
* <li>
@@ -48,8 +48,12 @@
* and processed as documented in the prior step.
* </li>
* <li>
- * The services resolution mechanism is used, e.g. <code>META-INF/services/java.xml.datatype.DatatypeFactory</code>.
- * 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.
+ * <br>
+ * In case of {@link java.util.ServiceConfigurationError service
+ * configuration error} a {@link javax.xml.datatype.DatatypeConfigurationException}
+ * will be thrown.
* </li>
* <li>
* The final mechanism is to attempt to instantiate the <code>Class</code> specified by
@@ -67,26 +71,33 @@
*/
public abstract class DatatypeFactory {
- /**
- * <p>Default property name as defined in JSR 206: Java(TM) API for XML Processing (JAXP) 1.3.</p>
- *
- * <p>Default value is <code>javax.xml.datatype.DatatypeFactory</code>.</p>
- */
- public static final String DATATYPEFACTORY_PROPERTY = "javax.xml.datatype.DatatypeFactory";
+ /**
+ * <p>Default property name as defined in JSR 206: Java(TM) API for XML Processing (JAXP) 1.3.</p>
+ *
+ * <p>Default value is <code>javax.xml.datatype.DatatypeFactory</code>.</p>
+ */
+ 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";
- /**
- * <p>Default implementation class name as defined in
- * <em>JSR 206: Java(TM) API for XML Processing (JAXP) 1.3</em>.</p>
- *
- * <p>Implementers should specify the name of an appropriate class
- * to be instantiated if no other implementation resolution mechanism
- * succeeds.</p>
- *
- * <p>Users should not refer to this field; it is intended only to
- * document a factory implementation detail.
- * </p>
- */
- public static final String DATATYPEFACTORY_IMPLEMENTATION_CLASS = new String("com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl");
+ /**
+ * <p>Default implementation class name as defined in
+ * <em>JSR 206: Java(TM) API for XML Processing (JAXP) 1.3</em>.</p>
+ *
+ * <p>Implementers should specify the name of an appropriate class
+ * to be instantiated if no other implementation resolution mechanism
+ * succeeds.</p>
+ *
+ * <p>Users should not refer to this field; it is intended only to
+ * document a factory implementation detail.
+ * </p>
+ */
+ 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].*");
- /**
- * <p>Protected constructor to prevent instaniation outside of package.</p>
- *
- * <p>Use {@link #newInstance()} to create a <code>DatatypeFactory</code>.</p>
- */
- protected DatatypeFactory() {
- }
+ /**
+ * <p>Protected constructor to prevent instaniation outside of package.</p>
+ *
+ * <p>Use {@link #newInstance()} to create a <code>DatatypeFactory</code>.</p>
+ */
+ protected DatatypeFactory() {
+ }
- /**
- * <p>Obtain a new instance of a <code>DatatypeFactory</code>.</p>
- *
+ /**
+ * <p>Obtain a new instance of a <code>DatatypeFactory</code>.</p>
+ *
* <p>The implementation resolution mechanisms are <a href="#DatatypeFactory.newInstance">defined</a> in this
* <code>Class</code>'s documentation.</p>
- *
- * @return New instance of a <code>DatatypeFactory</code>
- *
- * @throws DatatypeConfigurationException If the implementation is not
- * available or cannot be instantiated.
+ *
+ * @return New instance of a <code>DatatypeFactory</code>
+ *
+ * @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);
+ }
/**
* <p>Obtain a new instance of a <code>DatatypeFactory</code> 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);
+ }
- /**
- * <p>Obtain a new instance of a <code>Duration</code>
- * specifying the <code>Duration</code> as its string representation, "PnYnMnDTnHnMnS",
- * as defined in XML Schema 1.0 section 3.2.6.1.</p>
- *
- * <p>XML Schema Part 2: Datatypes, 3.2.6 duration, defines <code>duration</code> as:</p>
- * <blockquote>
- * 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.
- * </blockquote>
- * <p>All six values are set and availabe from the created {@link Duration}</p>
+ /**
+ * <p>Obtain a new instance of a <code>Duration</code>
+ * specifying the <code>Duration</code> as its string representation, "PnYnMnDTnHnMnS",
+ * as defined in XML Schema 1.0 section 3.2.6.1.</p>
+ *
+ * <p>XML Schema Part 2: Datatypes, 3.2.6 duration, defines <code>duration</code> as:</p>
+ * <blockquote>
+ * 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.
+ * </blockquote>
+ * <p>All six values are set and available from the created {@link Duration}</p>
*
* <p>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.</p>
- *
- * @param lexicalRepresentation <code>String</code> representation of a <code>Duration</code>.
- *
- * @return New <code>Duration</code> created from parsing the <code>lexicalRepresentation</code>.
- *
- * @throws IllegalArgumentException If <code>lexicalRepresentation</code> is not a valid representation of a <code>Duration</code>.
- * @throws UnsupportedOperationException If implementation cannot support requested values.
- * @throws NullPointerException if <code>lexicalRepresentation</code> is <code>null</code>.
- */
- public abstract Duration newDuration(final String lexicalRepresentation);
+ *
+ * @param lexicalRepresentation <code>String</code> representation of a <code>Duration</code>.
+ *
+ * @return New <code>Duration</code> created from parsing the <code>lexicalRepresentation</code>.
+ *
+ * @throws IllegalArgumentException If <code>lexicalRepresentation</code> is not a valid representation of a <code>Duration</code>.
+ * @throws UnsupportedOperationException If implementation cannot support requested values.
+ * @throws NullPointerException if <code>lexicalRepresentation</code> is <code>null</code>.
+ */
+ public abstract Duration newDuration(final String lexicalRepresentation);
- /**
- * <p>Obtain a new instance of a <code>Duration</code>
- * specifying the <code>Duration</code> as milliseconds.</p>
- *
- * <p>XML Schema Part 2: Datatypes, 3.2.6 duration, defines <code>duration</code> as:</p>
- * <blockquote>
- * 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.
- * </blockquote>
+ /**
+ * <p>Obtain a new instance of a <code>Duration</code>
+ * specifying the <code>Duration</code> as milliseconds.</p>
+ *
+ * <p>XML Schema Part 2: Datatypes, 3.2.6 duration, defines <code>duration</code> as:</p>
+ * <blockquote>
+ * 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.
+ * </blockquote>
* <p>All six values are set by computing their values from the specified milliseconds
- * and are availabe using the <code>get</code> methods of the created {@link Duration}.
+ * and are available using the <code>get</code> methods of the created {@link Duration}.
* The values conform to and are defined by:</p>
* <ul>
* <li>ISO 8601:2000(E) Section 5.5.3.2 Alternative format</li>
@@ -231,25 +235,25 @@
* </li>
* <li>{@link XMLGregorianCalendar} Date/Time Datatype Field Mapping Between XML Schema 1.0 and Java Representation</li>
* </ul>
- *
- * <p>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.</p>
- *
- * @param durationInMilliSeconds Duration in milliseconds to create.
- *
- * @return New <code>Duration</code> representing <code>durationInMilliSeconds</code>.
- */
- public abstract Duration newDuration(final long durationInMilliSeconds);
+ *
+ * <p>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.</p>
+ *
+ * @param durationInMilliSeconds Duration in milliseconds to create.
+ *
+ * @return New <code>Duration</code> representing <code>durationInMilliSeconds</code>.
+ */
+ public abstract Duration newDuration(final long durationInMilliSeconds);
- /**
- * <p>Obtain a new instance of a <code>Duration</code>
- * specifying the <code>Duration</code> as isPositive, years, months, days, hours, minutes, seconds.</p>
- *
+ /**
+ * <p>Obtain a new instance of a <code>Duration</code>
+ * specifying the <code>Duration</code> as isPositive, years, months, days, hours, minutes, seconds.</p>
+ *
* <p>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 @@
*
* <p>A <code>null</code> value indicates that field is not set.</p>
*
- * @param isPositive Set to <code>false</code> to create a negative duration. When the length
- * of the duration is zero, this parameter will be ignored.
- * @param years of this <code>Duration</code>
- * @param months of this <code>Duration</code>
- * @param days of this <code>Duration</code>
- * @param hours of this <code>Duration</code>
- * @param minutes of this <code>Duration</code>
- * @param seconds of this <code>Duration</code>
- *
- * @return New <code>Duration</code> created from the specified values.
- *
- * @throws IllegalArgumentException If the values are not a valid representation of a
- * <code>Duration</code>: 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 <code>false</code> to create a negative duration. When the length
+ * of the duration is zero, this parameter will be ignored.
+ * @param years of this <code>Duration</code>
+ * @param months of this <code>Duration</code>
+ * @param days of this <code>Duration</code>
+ * @param hours of this <code>Duration</code>
+ * @param minutes of this <code>Duration</code>
+ * @param seconds of this <code>Duration</code>
+ *
+ * @return New <code>Duration</code> created from the specified values.
+ *
+ * @throws IllegalArgumentException If the values are not a valid representation of a
+ * <code>Duration</code>: 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);
- /**
- * <p>Obtain a new instance of a <code>Duration</code>
- * specifying the <code>Duration</code> as isPositive, years, months, days, hours, minutes, seconds.</p>
- *
+ /**
+ * <p>Obtain a new instance of a <code>Duration</code>
+ * specifying the <code>Duration</code> as isPositive, years, months, days, hours, minutes, seconds.</p>
+ *
* <p>A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.</p>
*
* @param isPositive Set to <code>false</code> to create a negative duration. When the length
@@ -297,113 +301,113 @@
* @param minutes of this <code>Duration</code>
* @param seconds of this <code>Duration</code>
*
- * @return New <code>Duration</code> created from the specified values.
- *
- * @throws IllegalArgumentException If the values are not a valid representation of a
- * <code>Duration</code>: 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 <code>Duration</code> created from the specified values.
+ *
+ * @throws IllegalArgumentException If the values are not a valid representation of a
+ * <code>Duration</code>: 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
+ );
+ }
- /**
- * <p>Create a <code>Duration</code> of type <code>xdt:dayTimeDuration</code> by parsing its <code>String</code> representation,
- * "<em>PnDTnHnMnS</em>", <a href="http://www.w3.org/TR/xpath-datamodel#dayTimeDuration">
- * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration</a>.</p>
- *
- * <p>The datatype <code>xdt:dayTimeDuration</code> is a subtype of <code>xs:duration</code>
- * whose lexical representation contains only day, hour, minute, and second components.
- * This datatype resides in the namespace <code>http://www.w3.org/2003/11/xpath-datatypes</code>.</p>
- *
- * <p>All four values are set and availabe from the created {@link Duration}</p>
- *
+ /**
+ * <p>Create a <code>Duration</code> of type <code>xdt:dayTimeDuration</code> by parsing its <code>String</code> representation,
+ * "<em>PnDTnHnMnS</em>", <a href="http://www.w3.org/TR/xpath-datamodel#dayTimeDuration">
+ * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration</a>.</p>
+ *
+ * <p>The datatype <code>xdt:dayTimeDuration</code> is a subtype of <code>xs:duration</code>
+ * whose lexical representation contains only day, hour, minute, and second components.
+ * This datatype resides in the namespace <code>http://www.w3.org/2003/11/xpath-datatypes</code>.</p>
+ *
+ * <p>All four values are set and available from the created {@link Duration}</p>
+ *
* <p>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.</p>
*
- * @param lexicalRepresentation Lexical representation of a duration.
- *
- * @return New <code>Duration</code> created using the specified <code>lexicalRepresentation</code>.
- *
- * @throws IllegalArgumentException If <code>lexicalRepresentation</code> is not a valid representation of a <code>Duration</code> expressed only in terms of days and time.
- * @throws UnsupportedOperationException If implementation cannot support requested values.
- * @throws NullPointerException If <code>lexicalRepresentation</code> is <code>null</code>.
- */
- 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 <code>Duration</code> created using the specified <code>lexicalRepresentation</code>.
+ *
+ * @throws IllegalArgumentException If <code>lexicalRepresentation</code> is not a valid representation of a <code>Duration</code> expressed only in terms of days and time.
+ * @throws UnsupportedOperationException If implementation cannot support requested values.
+ * @throws NullPointerException If <code>lexicalRepresentation</code> is <code>null</code>.
+ */
+ 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\"");
}
- /**
- * <p>Create a <code>Duration</code> of type <code>xdt:dayTimeDuration</code> using the specified milliseconds as defined in
- * <a href="http://www.w3.org/TR/xpath-datamodel#dayTimeDuration">
- * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration</a>.</p>
- *
- * <p>The datatype <code>xdt:dayTimeDuration</code> is a subtype of <code>xs:duration</code>
- * whose lexical representation contains only day, hour, minute, and second components.
- * This datatype resides in the namespace <code>http://www.w3.org/2003/11/xpath-datatypes</code>.</p>
- *
+ // 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);
+ }
+
+ /**
+ * <p>Create a <code>Duration</code> of type <code>xdt:dayTimeDuration</code> using the specified milliseconds as defined in
+ * <a href="http://www.w3.org/TR/xpath-datamodel#dayTimeDuration">
+ * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration</a>.</p>
+ *
+ * <p>The datatype <code>xdt:dayTimeDuration</code> is a subtype of <code>xs:duration</code>
+ * whose lexical representation contains only day, hour, minute, and second components.
+ * This datatype resides in the namespace <code>http://www.w3.org/2003/11/xpath-datatypes</code>.</p>
+ *
* <p>All four values are set by computing their values from the specified milliseconds
- * and are availabe using the <code>get</code> methods of the created {@link Duration}.
+ * and are available using the <code>get</code> methods of the created {@link Duration}.
* The values conform to and are defined by:</p>
* <ul>
* <li>ISO 8601:2000(E) Section 5.5.3.2 Alternative format</li>
@@ -412,39 +416,39 @@
* </li>
* <li>{@link XMLGregorianCalendar} Date/Time Datatype Field Mapping Between XML Schema 1.0 and Java Representation</li>
* </ul>
- *
- * <p>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.</p>
- *
+ *
+ * <p>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.</p>
+ *
* <p>Any remaining milliseconds after determining the day, hour, minute and second are discarded.</p>
*
- * @param durationInMilliseconds Milliseconds of <code>Duration</code> to create.
- *
- * @return New <code>Duration</code> created with the specified <code>durationInMilliseconds</code>.
- *
- * @see <a href="http://www.w3.org/TR/xpath-datamodel#dayTimeDuration">
- * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration</a>
- */
- public Duration newDurationDayTime(final long durationInMilliseconds) {
+ * @param durationInMilliseconds Milliseconds of <code>Duration</code> to create.
+ *
+ * @return New <code>Duration</code> created with the specified <code>durationInMilliseconds</code>.
+ *
+ * @see <a href="http://www.w3.org/TR/xpath-datamodel#dayTimeDuration">
+ * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration</a>
+ */
+ public Duration newDurationDayTime(final long durationInMilliseconds) {
- return newDuration(durationInMilliseconds);
- }
+ return newDuration(durationInMilliseconds);
+ }
- /**
- * <p>Create a <code>Duration</code> of type <code>xdt:dayTimeDuration</code> using the specified
- * <code>day</code>, <code>hour</code>, <code>minute</code> and <code>second</code> as defined in
- * <a href="http://www.w3.org/TR/xpath-datamodel#dayTimeDuration">
- * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration</a>.</p>
- *
- * <p>The datatype <code>xdt:dayTimeDuration</code> is a subtype of <code>xs:duration</code>
- * whose lexical representation contains only day, hour, minute, and second components.
- * This datatype resides in the namespace <code>http://www.w3.org/2003/11/xpath-datatypes</code>.</p>
- *
+ /**
+ * <p>Create a <code>Duration</code> of type <code>xdt:dayTimeDuration</code> using the specified
+ * <code>day</code>, <code>hour</code>, <code>minute</code> and <code>second</code> as defined in
+ * <a href="http://www.w3.org/TR/xpath-datamodel#dayTimeDuration">
+ * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration</a>.</p>
+ *
+ * <p>The datatype <code>xdt:dayTimeDuration</code> is a subtype of <code>xs:duration</code>
+ * whose lexical representation contains only day, hour, minute, and second components.
+ * This datatype resides in the namespace <code>http://www.w3.org/2003/11/xpath-datatypes</code>.</p>
+ *
* <p>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 <code>false</code> to create a negative duration. When the length
* of the duration is zero, this parameter will be ignored.
- * @param day Day of <code>Duration</code>.
- * @param hour Hour of <code>Duration</code>.
- * @param minute Minute of <code>Duration</code>.
- * @param second Second of <code>Duration</code>.
- *
- * @return New <code>Duration</code> created with the specified <code>day</code>, <code>hour</code>, <code>minute</code>
- * and <code>second</code>.
- *
- * @throws IllegalArgumentException If the values are not a valid representation of a
- * <code>Duration</code>: 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 <code>Duration</code>.
+ * @param hour Hour of <code>Duration</code>.
+ * @param minute Minute of <code>Duration</code>.
+ * @param second Second of <code>Duration</code>.
+ *
+ * @return New <code>Duration</code> created with the specified <code>day</code>, <code>hour</code>, <code>minute</code>
+ * and <code>second</code>.
+ *
+ * @throws IllegalArgumentException If the values are not a valid representation of a
+ * <code>Duration</code>: 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
+ );
+ }
- /**
- * <p>Create a <code>Duration</code> of type <code>xdt:dayTimeDuration</code> using the specified
- * <code>day</code>, <code>hour</code>, <code>minute</code> and <code>second</code> as defined in
- * <a href="http://www.w3.org/TR/xpath-datamodel#dayTimeDuration">
- * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration</a>.</p>
- *
- * <p>The datatype <code>xdt:dayTimeDuration</code> is a subtype of <code>xs:duration</code>
- * whose lexical representation contains only day, hour, minute, and second components.
- * This datatype resides in the namespace <code>http://www.w3.org/2003/11/xpath-datatypes</code>.</p>
- *
+ /**
+ * <p>Create a <code>Duration</code> of type <code>xdt:dayTimeDuration</code> using the specified
+ * <code>day</code>, <code>hour</code>, <code>minute</code> and <code>second</code> as defined in
+ * <a href="http://www.w3.org/TR/xpath-datamodel#dayTimeDuration">
+ * XQuery 1.0 and XPath 2.0 Data Model, xdt:dayTimeDuration</a>.</p>
+ *
+ * <p>The datatype <code>xdt:dayTimeDuration</code> is a subtype of <code>xs:duration</code>
+ * whose lexical representation contains only day, hour, minute, and second components.
+ * This datatype resides in the namespace <code>http://www.w3.org/2003/11/xpath-datatypes</code>.</p>
+ *
* <p>A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.</p>
*
* @param isPositive Set to <code>false</code> to create a negative duration. When the length
* of the duration is zero, this parameter will be ignored.
- * @param day Day of <code>Duration</code>.
- * @param hour Hour of <code>Duration</code>.
- * @param minute Minute of <code>Duration</code>.
- * @param second Second of <code>Duration</code>.
- *
- * @return New <code>Duration</code> created with the specified <code>day</code>, <code>hour</code>, <code>minute</code>
- * and <code>second</code>.
- *
- * @throws IllegalArgumentException If the values are not a valid representation of a
- * <code>Duration</code>: 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 <code>Duration</code>.
+ * @param hour Hour of <code>Duration</code>.
+ * @param minute Minute of <code>Duration</code>.
+ * @param second Second of <code>Duration</code>.
+ *
+ * @return New <code>Duration</code> created with the specified <code>day</code>, <code>hour</code>, <code>minute</code>
+ * and <code>second</code>.
+ *
+ * @throws IllegalArgumentException If the values are not a valid representation of a
+ * <code>Duration</code>: 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)
+ );
+ }
- /**
- * <p>Create a <code>Duration</code> of type <code>xdt:yearMonthDuration</code> by parsing its <code>String</code> representation,
- * "<em>PnYnM</em>", <a href="http://www.w3.org/TR/xpath-datamodel#yearMonthDuration">
- * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration</a>.</p>
- *
- * <p>The datatype <code>xdt:yearMonthDuration</code> is a subtype of <code>xs:duration</code>
- * whose lexical representation contains only year and month components.
- * This datatype resides in the namespace {@link javax.xml.XMLConstants#W3C_XPATH_DATATYPE_NS_URI}.</p>
- *
- * <p>Both values are set and availabe from the created {@link Duration}</p>
- *
+ /**
+ * <p>Create a <code>Duration</code> of type <code>xdt:yearMonthDuration</code> by parsing its <code>String</code> representation,
+ * "<em>PnYnM</em>", <a href="http://www.w3.org/TR/xpath-datamodel#yearMonthDuration">
+ * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration</a>.</p>
+ *
+ * <p>The datatype <code>xdt:yearMonthDuration</code> is a subtype of <code>xs:duration</code>
+ * whose lexical representation contains only year and month components.
+ * This datatype resides in the namespace {@link javax.xml.XMLConstants#W3C_XPATH_DATATYPE_NS_URI}.</p>
+ *
+ * <p>Both values are set and available from the created {@link Duration}</p>
+ *
* <p>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.</p>
*
- * @param lexicalRepresentation Lexical representation of a duration.
- *
- * @return New <code>Duration</code> created using the specified <code>lexicalRepresentation</code>.
- *
- * @throws IllegalArgumentException If <code>lexicalRepresentation</code> is not a valid representation of a <code>Duration</code> expressed only in terms of years and months.
- * @throws UnsupportedOperationException If implementation cannot support requested values.
- * @throws NullPointerException If <code>lexicalRepresentation</code> is <code>null</code>.
- */
+ * @param lexicalRepresentation Lexical representation of a duration.
+ *
+ * @return New <code>Duration</code> created using the specified <code>lexicalRepresentation</code>.
+ *
+ * @throws IllegalArgumentException If <code>lexicalRepresentation</code> is not a valid representation of a <code>Duration</code> expressed only in terms of years and months.
+ * @throws UnsupportedOperationException If implementation cannot support requested values.
+ * @throws NullPointerException If <code>lexicalRepresentation</code> is <code>null</code>.
+ */
public Duration newDurationYearMonth(
final String lexicalRepresentation) {
@@ -572,17 +576,17 @@
return newDuration(lexicalRepresentation);
}
- /**
- * <p>Create a <code>Duration</code> of type <code>xdt:yearMonthDuration</code> using the specified milliseconds as defined in
- * <a href="http://www.w3.org/TR/xpath-datamodel#yearMonthDuration">
- * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration</a>.</p>
- *
- * <p>The datatype <code>xdt:yearMonthDuration</code> is a subtype of <code>xs:duration</code>
- * whose lexical representation contains only year and month components.
- * This datatype resides in the namespace {@link javax.xml.XMLConstants#W3C_XPATH_DATATYPE_NS_URI}.</p>
- *
+ /**
+ * <p>Create a <code>Duration</code> of type <code>xdt:yearMonthDuration</code> using the specified milliseconds as defined in
+ * <a href="http://www.w3.org/TR/xpath-datamodel#yearMonthDuration">
+ * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration</a>.</p>
+ *
+ * <p>The datatype <code>xdt:yearMonthDuration</code> is a subtype of <code>xs:duration</code>
+ * whose lexical representation contains only year and month components.
+ * This datatype resides in the namespace {@link javax.xml.XMLConstants#W3C_XPATH_DATATYPE_NS_URI}.</p>
+ *
* <p>Both values are set by computing their values from the specified milliseconds
- * and are availabe using the <code>get</code> methods of the created {@link Duration}.
+ * and are available using the <code>get</code> methods of the created {@link Duration}.
* The values conform to and are defined by:</p>
* <ul>
* <li>ISO 8601:2000(E) Section 5.5.3.2 Alternative format</li>
@@ -592,20 +596,20 @@
* <li>{@link XMLGregorianCalendar} Date/Time Datatype Field Mapping Between XML Schema 1.0 and Java Representation</li>
* </ul>
*
- * <p>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.</p>
- *
+ * <p>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.</p>
+ *
* <p>Any remaining milliseconds after determining the year and month are discarded.</p>
- *
- * @param durationInMilliseconds Milliseconds of <code>Duration</code> to create.
- *
- * @return New <code>Duration</code> created using the specified <code>durationInMilliseconds</code>.
- */
+ *
+ * @param durationInMilliseconds Milliseconds of <code>Duration</code> to create.
+ *
+ * @return New <code>Duration</code> created using the specified <code>durationInMilliseconds</code>.
+ */
public Duration newDurationYearMonth(
final long durationInMilliseconds) {
@@ -624,12 +628,12 @@
return newDurationYearMonth(isPositive, years, months);
}
- /**
- * <p>Create a <code>Duration</code> of type <code>xdt:yearMonthDuration</code> using the specified
- * <code>year</code> and <code>month</code> as defined in
- * <a href="http://www.w3.org/TR/xpath-datamodel#yearMonthDuration">
- * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration</a>.</p>
- *
+ /**
+ * <p>Create a <code>Duration</code> of type <code>xdt:yearMonthDuration</code> using the specified
+ * <code>year</code> and <code>month</code> as defined in
+ * <a href="http://www.w3.org/TR/xpath-datamodel#yearMonthDuration">
+ * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration</a>.</p>
+ *
* <p>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 <code>false</code> to create a negative duration. When the length
* of the duration is zero, this parameter will be ignored.
- * @param year Year of <code>Duration</code>.
- * @param month Month of <code>Duration</code>.
- *
- * @return New <code>Duration</code> created using the specified <code>year</code> and <code>month</code>.
- *
- * @throws IllegalArgumentException If the values are not a valid representation of a
- * <code>Duration</code>: 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 <code>Duration</code>.
+ * @param month Month of <code>Duration</code>.
+ *
+ * @return New <code>Duration</code> created using the specified <code>year</code> and <code>month</code>.
+ *
+ * @throws IllegalArgumentException If the values are not a valid representation of a
+ * <code>Duration</code>: 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
+ );
+ }
- /**
- * <p>Create a <code>Duration</code> of type <code>xdt:yearMonthDuration</code> using the specified
- * <code>year</code> and <code>month</code> as defined in
- * <a href="http://www.w3.org/TR/xpath-datamodel#yearMonthDuration">
- * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration</a>.</p>
- *
+ /**
+ * <p>Create a <code>Duration</code> of type <code>xdt:yearMonthDuration</code> using the specified
+ * <code>year</code> and <code>month</code> as defined in
+ * <a href="http://www.w3.org/TR/xpath-datamodel#yearMonthDuration">
+ * XQuery 1.0 and XPath 2.0 Data Model, xdt:yearMonthDuration</a>.</p>
+ *
* <p>A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.</p>
*
* @param isPositive Set to <code>false</code> to create a negative duration. When the length
* of the duration is zero, this parameter will be ignored.
- * @param year Year of <code>Duration</code>.
- * @param month Month of <code>Duration</code>.
- *
- * @return New <code>Duration</code> created using the specified <code>year</code> and <code>month</code>.
- *
- * @throws IllegalArgumentException If the values are not a valid representation of a
- * <code>Duration</code>: 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 <code>Duration</code>.
+ * @param month Month of <code>Duration</code>.
+ *
+ * @return New <code>Duration</code> created using the specified <code>year</code> and <code>month</code>.
+ *
+ * @throws IllegalArgumentException If the values are not a valid representation of a
+ * <code>Duration</code>: 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));
+ }
- /**
- * <p>Create a new instance of an <code>XMLGregorianCalendar</code>.</p>
- *
+ /**
+ * <p>Create a new instance of an <code>XMLGregorianCalendar</code>.</p>
+ *
* <p>All date/time datatype fields set to {@link DatatypeConstants#FIELD_UNDEFINED} or null.</p>
*
* @return New <code>XMLGregorianCalendar</code> with all date/time datatype fields set to
* {@link DatatypeConstants#FIELD_UNDEFINED} or null.
- */
- public abstract XMLGregorianCalendar newXMLGregorianCalendar();
+ */
+ public abstract XMLGregorianCalendar newXMLGregorianCalendar();
- /**
- * <p>Create a new XMLGregorianCalendar by parsing the String as a lexical representation.</p>
- *
+ /**
+ * <p>Create a new XMLGregorianCalendar by parsing the String as a lexical representation.</p>
+ *
* <p>Parsing the lexical string representation is defined in
* <a href="http://www.w3.org/TR/xmlschema-2/#dateTime-order">XML Schema 1.0 Part 2, Section 3.2.[7-14].1,
* <em>Lexical Representation</em>.</a></p>
@@ -721,344 +725,344 @@
* <p>Except for the noted lexical/canonical representation mismatches
* listed in <a href="http://www.w3.org/2001/05/xmlschema-errata#e2-45">
* XML Schema 1.0 errata, Section 3.2.7.2</a>.</p>
- *
- * @param lexicalRepresentation Lexical representation of one the eight XML Schema date/time datatypes.
- *
- * @return <code>XMLGregorianCalendar</code> created from the <code>lexicalRepresentation</code>.
- *
- * @throws IllegalArgumentException If the <code>lexicalRepresentation</code> is not a valid <code>XMLGregorianCalendar</code>.
- * @throws NullPointerException If <code>lexicalRepresentation</code> is <code>null</code>.
- */
- public abstract XMLGregorianCalendar newXMLGregorianCalendar(final String lexicalRepresentation);
+ *
+ * @param lexicalRepresentation Lexical representation of one the eight XML Schema date/time datatypes.
+ *
+ * @return <code>XMLGregorianCalendar</code> created from the <code>lexicalRepresentation</code>.
+ *
+ * @throws IllegalArgumentException If the <code>lexicalRepresentation</code> is not a valid <code>XMLGregorianCalendar</code>.
+ * @throws NullPointerException If <code>lexicalRepresentation</code> is <code>null</code>.
+ */
+ public abstract XMLGregorianCalendar newXMLGregorianCalendar(final String lexicalRepresentation);
- /**
- * <p>Create an <code>XMLGregorianCalendar</code> from a {@link GregorianCalendar}.</p>
- *
- * <table border="2" rules="all" cellpadding="2">
- * <thead>
- * <tr>
- * <th align="center" colspan="2">
- * Field by Field Conversion from
- * {@link GregorianCalendar} to an {@link XMLGregorianCalendar}
- * </th>
- * </tr>
- * <tr>
- * <th><code>java.util.GregorianCalendar</code> field</th>
- * <th><code>javax.xml.datatype.XMLGregorianCalendar</code> field</th>
- * </tr>
- * </thead>
- * <tbody>
- * <tr>
- * <td><code>ERA == GregorianCalendar.BC ? -YEAR : YEAR</code></td>
- * <td>{@link XMLGregorianCalendar#setYear(int year)}</td>
- * </tr>
- * <tr>
- * <td><code>MONTH + 1</code></td>
- * <td>{@link XMLGregorianCalendar#setMonth(int month)}</td>
- * </tr>
- * <tr>
- * <td><code>DAY_OF_MONTH</code></td>
- * <td>{@link XMLGregorianCalendar#setDay(int day)}</td>
- * </tr>
- * <tr>
- * <td><code>HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND</code></td>
- * <td>{@link XMLGregorianCalendar#setTime(int hour, int minute, int second, BigDecimal fractional)}</td>
- * </tr>
- * <tr>
- * <td>
- * <code>(ZONE_OFFSET + DST_OFFSET) / (60*1000)</code><br/>
- * <em>(in minutes)</em>
- * </td>
- * <td>{@link XMLGregorianCalendar#setTimezone(int offset)}<sup><em>*</em></sup>
- * </td>
- * </tr>
- * </tbody>
- * </table>
- * <p><em>*</em>conversion loss of information. It is not possible to represent
- * a <code>java.util.GregorianCalendar</code> daylight savings timezone id in the
- * XML Schema 1.0 date/time datatype representation.</p>
- *
- * <p>To compute the return value's <code>TimeZone</code> field,
- * <ul>
- * <li>when <code>this.getTimezone() != FIELD_UNDEFINED</code>,
- * create a <code>java.util.TimeZone</code> with a custom timezone id
- * using the <code>this.getTimezone()</code>.</li>
- * <li>else use the <code>GregorianCalendar</code> default timezone value
- * for the host is defined as specified by
- * <code>java.util.TimeZone.getDefault()</code>.</li></p>
- *
- * @param cal <code>java.util.GregorianCalendar</code> used to create <code>XMLGregorianCalendar</code>
- *
- * @return <code>XMLGregorianCalendar</code> created from <code>java.util.GregorianCalendar</code>
- *
- * @throws NullPointerException If <code>cal</code> is <code>null</code>.
- */
- public abstract XMLGregorianCalendar newXMLGregorianCalendar(final GregorianCalendar cal);
+ /**
+ * <p>Create an <code>XMLGregorianCalendar</code> from a {@link GregorianCalendar}.</p>
+ *
+ * <table border="2" rules="all" cellpadding="2">
+ * <thead>
+ * <tr>
+ * <th align="center" colspan="2">
+ * Field by Field Conversion from
+ * {@link GregorianCalendar} to an {@link XMLGregorianCalendar}
+ * </th>
+ * </tr>
+ * <tr>
+ * <th><code>java.util.GregorianCalendar</code> field</th>
+ * <th><code>javax.xml.datatype.XMLGregorianCalendar</code> field</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td><code>ERA == GregorianCalendar.BC ? -YEAR : YEAR</code></td>
+ * <td>{@link XMLGregorianCalendar#setYear(int year)}</td>
+ * </tr>
+ * <tr>
+ * <td><code>MONTH + 1</code></td>
+ * <td>{@link XMLGregorianCalendar#setMonth(int month)}</td>
+ * </tr>
+ * <tr>
+ * <td><code>DAY_OF_MONTH</code></td>
+ * <td>{@link XMLGregorianCalendar#setDay(int day)}</td>
+ * </tr>
+ * <tr>
+ * <td><code>HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND</code></td>
+ * <td>{@link XMLGregorianCalendar#setTime(int hour, int minute, int second, BigDecimal fractional)}</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <code>(ZONE_OFFSET + DST_OFFSET) / (60*1000)</code><br/>
+ * <em>(in minutes)</em>
+ * </td>
+ * <td>{@link XMLGregorianCalendar#setTimezone(int offset)}<sup><em>*</em></sup>
+ * </td>
+ * </tr>
+ * </tbody>
+ * </table>
+ * <p><em>*</em>conversion loss of information. It is not possible to represent
+ * a <code>java.util.GregorianCalendar</code> daylight savings timezone id in the
+ * XML Schema 1.0 date/time datatype representation.</p>
+ *
+ * <p>To compute the return value's <code>TimeZone</code> field,
+ * <ul>
+ * <li>when <code>this.getTimezone() != FIELD_UNDEFINED</code>,
+ * create a <code>java.util.TimeZone</code> with a custom timezone id
+ * using the <code>this.getTimezone()</code>.</li>
+ * <li>else use the <code>GregorianCalendar</code> default timezone value
+ * for the host is defined as specified by
+ * <code>java.util.TimeZone.getDefault()</code>.</li></p>
+ *
+ * @param cal <code>java.util.GregorianCalendar</code> used to create <code>XMLGregorianCalendar</code>
+ *
+ * @return <code>XMLGregorianCalendar</code> created from <code>java.util.GregorianCalendar</code>
+ *
+ * @throws NullPointerException If <code>cal</code> is <code>null</code>.
+ */
+ public abstract XMLGregorianCalendar newXMLGregorianCalendar(final GregorianCalendar cal);
- /**
- * <p>Constructor allowing for complete value spaces allowed by
- * W3C XML Schema 1.0 recommendation for xsd:dateTime and related
- * builtin datatypes. Note that <code>year</code> parameter supports
- * arbitrarily large numbers and fractionalSecond has infinite
- * precision.</p>
- *
+ /**
+ * <p>Constructor allowing for complete value spaces allowed by
+ * W3C XML Schema 1.0 recommendation for xsd:dateTime and related
+ * builtin datatypes. Note that <code>year</code> parameter supports
+ * arbitrarily large numbers and fractionalSecond has infinite
+ * precision.</p>
+ *
* <p>A <code>null</code> value indicates that field is not set.</p>
*
- * @param year of <code>XMLGregorianCalendar</code> to be created.
- * @param month of <code>XMLGregorianCalendar</code> to be created.
- * @param day of <code>XMLGregorianCalendar</code> to be created.
- * @param hour of <code>XMLGregorianCalendar</code> to be created.
- * @param minute of <code>XMLGregorianCalendar</code> to be created.
- * @param second of <code>XMLGregorianCalendar</code> to be created.
- * @param fractionalSecond of <code>XMLGregorianCalendar</code> to be created.
- * @param timezone of <code>XMLGregorianCalendar</code> to be created.
- *
- * @return <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> to be created.
+ * @param month of <code>XMLGregorianCalendar</code> to be created.
+ * @param day of <code>XMLGregorianCalendar</code> to be created.
+ * @param hour of <code>XMLGregorianCalendar</code> to be created.
+ * @param minute of <code>XMLGregorianCalendar</code> to be created.
+ * @param second of <code>XMLGregorianCalendar</code> to be created.
+ * @param fractionalSecond of <code>XMLGregorianCalendar</code> to be created.
+ * @param timezone of <code>XMLGregorianCalendar</code> to be created.
+ *
+ * @return <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> 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);
- /**
- * <p>Constructor of value spaces that a
- * <code>java.util.GregorianCalendar</code> instance would need to convert to an
- * <code>XMLGregorianCalendar</code> instance.</p>
- *
- * <p><code>XMLGregorianCalendar eon</code> and
- * <code>fractionalSecond</code> are set to <code>null</code></p>
- *
+ /**
+ * <p>Constructor of value spaces that a
+ * <code>java.util.GregorianCalendar</code> instance would need to convert to an
+ * <code>XMLGregorianCalendar</code> instance.</p>
+ *
+ * <p><code>XMLGregorianCalendar eon</code> and
+ * <code>fractionalSecond</code> are set to <code>null</code></p>
+ *
* <p>A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.</p>
*
- * @param year of <code>XMLGregorianCalendar</code> to be created.
- * @param month of <code>XMLGregorianCalendar</code> to be created.
- * @param day of <code>XMLGregorianCalendar</code> to be created.
- * @param hour of <code>XMLGregorianCalendar</code> to be created.
- * @param minute of <code>XMLGregorianCalendar</code> to be created.
- * @param second of <code>XMLGregorianCalendar</code> to be created.
- * @param millisecond of <code>XMLGregorianCalendar</code> to be created.
- * @param timezone of <code>XMLGregorianCalendar</code> to be created.
- *
- * @return <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> to be created.
+ * @param month of <code>XMLGregorianCalendar</code> to be created.
+ * @param day of <code>XMLGregorianCalendar</code> to be created.
+ * @param hour of <code>XMLGregorianCalendar</code> to be created.
+ * @param minute of <code>XMLGregorianCalendar</code> to be created.
+ * @param second of <code>XMLGregorianCalendar</code> to be created.
+ * @param millisecond of <code>XMLGregorianCalendar</code> to be created.
+ * @param timezone of <code>XMLGregorianCalendar</code> to be created.
+ *
+ * @return <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> 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
+ );
+ }
- /**
- * <p>Create a Java representation of XML Schema builtin datatype <code>date</code> or <code>g*</code>.</p>
- *
- * <p>For example, an instance of <code>gYear</code> can be created invoking this factory
- * with <code>month</code> and <code>day</code> parameters set to
- * {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
- *
+ /**
+ * <p>Create a Java representation of XML Schema builtin datatype <code>date</code> or <code>g*</code>.</p>
+ *
+ * <p>For example, an instance of <code>gYear</code> can be created invoking this factory
+ * with <code>month</code> and <code>day</code> parameters set to
+ * {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
+ *
* <p>A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.</p>
*
- * @param year of <code>XMLGregorianCalendar</code> to be created.
- * @param month of <code>XMLGregorianCalendar</code> to be created.
- * @param day of <code>XMLGregorianCalendar</code> to be created.
- * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
- *
- * @return <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> to be created.
+ * @param month of <code>XMLGregorianCalendar</code> to be created.
+ * @param day of <code>XMLGregorianCalendar</code> to be created.
+ * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
+ *
+ * @return <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> 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);
+ }
- /**
- * <p>Create a Java instance of XML Schema builtin datatype <code>time</code>.</p>
- *
+ /**
+ * <p>Create a Java instance of XML Schema builtin datatype <code>time</code>.</p>
+ *
* <p>A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.</p>
*
- * @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 <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> 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);
+ }
- /**
- * <p>Create a Java instance of XML Schema builtin datatype time.</p>
- *
+ /**
+ * <p>Create a Java instance of XML Schema builtin datatype time.</p>
+ *
* <p>A <code>null</code> value indicates that field is not set.</p>
* <p>A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.</p>
*
- * @param hours number of hours
- * @param minutes number of minutes
- * @param seconds number of seconds
- * @param fractionalSecond value of <code>null</code> indicates that this optional field is not set.
- * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
- *
- * @return <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> 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 <code>null</code> indicates that this optional field is not set.
+ * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
+ *
+ * @return <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> 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);
+ }
- /**
- * <p>Create a Java instance of XML Schema builtin datatype time.</p>
- *
+ /**
+ * <p>Create a Java instance of XML Schema builtin datatype time.</p>
+ *
* <p>A {@link DatatypeConstants#FIELD_UNDEFINED} value indicates that field is not set.</p>
*
- * @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 <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> 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 <code>XMLGregorianCalendar</code> 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
+ );
+ }
}
--- a/jaxp/src/javax/xml/datatype/FactoryFinder.java Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/src/javax/xml/datatype/FactoryFinder.java Thu May 16 11:47:51 2013 +0100
@@ -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;
/**
* <p>Implements pluggable Datatypes.</p>
@@ -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
* <code>getProviderClass()</code> 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> T newInstance(Class<T> 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
* <code>getProviderClass()</code> 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> T newInstance(Class<T> 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> T find(Class<T> 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> T findServiceProvider(final Class<T> 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<T>() {
+ public T run() {
+ final ServiceLoader<T> serviceLoader = ServiceLoader.load(type);
+ final Iterator<T> 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;
}
}
--- a/jaxp/src/javax/xml/parsers/DocumentBuilderFactory.java Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/src/javax/xml/parsers/DocumentBuilderFactory.java Thu May 16 11:47:51 2013 +0100
@@ -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;
-
/**
* <p>Protected constructor to prevent instantiation.
* Use {@link #newInstance()}.</p>
@@ -85,14 +80,12 @@
* of any property in jaxp.properties after it has been read for the first time.
* </li>
* <li>
- * 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
- * <code>META-INF/services/javax.xml.parsers.DocumentBuilderFactory</code>
- * 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.
* </li>
* <li>
- * Platform default <code>DocumentBuilderFactory</code> instance.
+ * Otherwise, the system-default implementation is returned.
* </li>
* </ul>
*
@@ -113,21 +106,16 @@
*
* @return New instance of a <code>DocumentBuilderFactory</code>
*
- * @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;
- /**
- * <p>Set a feature for this <code>DocumentBuilderFactory</code> and <code>DocumentBuilder</code>s created by this factory.</p>
- *
- * <p>
- * Feature names are fully qualified {@link java.net.URI}s.
- * Implementations may define their own features.
- * A {@link ParserConfigurationException} is thrown if this <code>DocumentBuilderFactory</code> or the
- * <code>DocumentBuilder</code>s it creates cannot support the feature.
- * It is possible for a <code>DocumentBuilderFactory</code> to expose a feature value but be unable to change its state.
- * </p>
- *
- * <p>
- * All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature.
- * When the feature is:</p>
- * <ul>
- * <li>
- * <code>true</code>: 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)}.
- * </li>
- * <li>
- * <code>false</code>: the implementation will processing XML according to the XML specifications without
- * regard to possible implementation limits.
- * </li>
- * </ul>
- *
- * @param name Feature name.
- * @param value Is feature state <code>true</code> or <code>false</code>.
- *
- * @throws ParserConfigurationException if this <code>DocumentBuilderFactory</code> or the <code>DocumentBuilder</code>s
- * it creates cannot support this feature.
+ /**
+ * <p>Set a feature for this <code>DocumentBuilderFactory</code> and <code>DocumentBuilder</code>s created by this factory.</p>
+ *
+ * <p>
+ * Feature names are fully qualified {@link java.net.URI}s.
+ * Implementations may define their own features.
+ * A {@link ParserConfigurationException} is thrown if this <code>DocumentBuilderFactory</code> or the
+ * <code>DocumentBuilder</code>s it creates cannot support the feature.
+ * It is possible for a <code>DocumentBuilderFactory</code> to expose a feature value but be unable to change its state.
+ * </p>
+ *
+ * <p>
+ * All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature.
+ * When the feature is:</p>
+ * <ul>
+ * <li>
+ * <code>true</code>: 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)}.
+ * </li>
+ * <li>
+ * <code>false</code>: the implementation will processing XML according to the XML specifications without
+ * regard to possible implementation limits.
+ * </li>
+ * </ul>
+ *
+ * @param name Feature name.
+ * @param value Is feature state <code>true</code> or <code>false</code>.
+ *
+ * @throws ParserConfigurationException if this <code>DocumentBuilderFactory</code> or the <code>DocumentBuilder</code>s
+ * it creates cannot support this feature.
* @throws NullPointerException If the <code>name</code> parameter is null.
- */
- public abstract void setFeature(String name, boolean value)
- throws ParserConfigurationException;
+ */
+ public abstract void setFeature(String name, boolean value)
+ throws ParserConfigurationException;
- /**
- * <p>Get the state of the named feature.</p>
- *
- * <p>
- * Feature names are fully qualified {@link java.net.URI}s.
- * Implementations may define their own features.
- * An {@link ParserConfigurationException} is thrown if this <code>DocumentBuilderFactory</code> or the
- * <code>DocumentBuilder</code>s it creates cannot support the feature.
- * It is possible for an <code>DocumentBuilderFactory</code> to expose a feature value but be unable to change its state.
- * </p>
- *
- * @param name Feature name.
- *
- * @return State of the named feature.
- *
- * @throws ParserConfigurationException if this <code>DocumentBuilderFactory</code>
- * or the <code>DocumentBuilder</code>s it creates cannot support this feature.
- */
- public abstract boolean getFeature(String name)
- throws ParserConfigurationException;
-
-
- /** <p>Get current state of canonicalization.</p>
+ /**
+ * <p>Get the state of the named feature.</p>
*
- * @return current state canonicalization control
+ * <p>
+ * Feature names are fully qualified {@link java.net.URI}s.
+ * Implementations may define their own features.
+ * An {@link ParserConfigurationException} is thrown if this <code>DocumentBuilderFactory</code> or the
+ * <code>DocumentBuilder</code>s it creates cannot support the feature.
+ * It is possible for an <code>DocumentBuilderFactory</code> to expose a feature value but be unable to change its state.
+ * </p>
+ *
+ * @param name Feature name.
+ *
+ * @return State of the named feature.
+ *
+ * @throws ParserConfigurationException if this <code>DocumentBuilderFactory</code>
+ * or the <code>DocumentBuilder</code>s it creates cannot support this feature.
*/
- /*
- public boolean getCanonicalization() {
- return canonicalState;
- }
- */
+ public abstract boolean getFeature(String name)
+ throws ParserConfigurationException;
/**
@@ -488,17 +461,6 @@
}
- /* <p>Set canonicalization control to <code>true</code> or
- * </code>false</code>.</p>
- *
- * @param state of canonicalization
- */
- /*
- public void setCanonicalization(boolean state) {
- canonicalState = state;
- }
- */
-
/**
* <p>Set the {@link Schema} to be used by parsers created
* from this factory.
--- a/jaxp/src/javax/xml/parsers/FactoryFinder.java Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/src/javax/xml/parsers/FactoryFinder.java Thu May 16 11:47:51 2013 +0100
@@ -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;
/**
- * <p>Implements pluggable Datatypes.</p>
+ * <p>Implements pluggable Parsers.</p>
*
* <p>This class is duplicated for each JAXP subpackage so keep it in
* sync. It is package private for secure class loading.</p>
@@ -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
* <code>getProviderClass()</code> 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> T newInstance(Class<T> 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
* <code>getProviderClass()</code> 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> T newInstance(Class<T> 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> T find(Class<T> 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> T findServiceProvider(final Class<T> 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<T>() {
+ public T run() {
+ final ServiceLoader<T> serviceLoader = ServiceLoader.load(type);
+ final Iterator<T> 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;
}
}
--- a/jaxp/src/javax/xml/parsers/SAXParserFactory.java Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/src/javax/xml/parsers/SAXParserFactory.java Thu May 16 11:47:51 2013 +0100
@@ -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";
/**
* <p>Should Parsers be validating?</p>
@@ -87,14 +84,12 @@
* of any property in jaxp.properties after it has been read for the first time.
* </li>
* <li>
- * 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
- * <code>META-INF/services/javax.xml.parsers.SAXParserFactory</code>
- * 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.
* </li>
* <li>
- * Platform default <code>SAXParserFactory</code> instance.
+ * Otherwise the system-default implementation is returned.
* </li>
* </ul>
*
@@ -109,7 +104,7 @@
* this method to print a lot of debug messages
* to <code>System.err</code> about what it is doing and where it is looking at.</p>
*
- * <p> If you have problems loading {@link DocumentBuilder}s, try:</p>
+ * <p> If you have problems loading {@link SAXParser}s, try:</p>
* <pre>
* java -Djaxp.debug=1 YourProgram ....
* </pre>
@@ -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
* <a href="http://www.saxproject.org/">http://www.saxproject.org/</a></p>
*
- * <p>All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature.
- * When the feature is</p>
- * <ul>
- * <li>
- * <code>true</code>: 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} <code>parse</code> methods for handler specification.
- * </li>
- * <li>
- * When the feature is <code>false</code>, the implementation will processing XML according to the XML specifications without
- * regard to possible implementation limits.
- * </li>
- * </ul>
- *
+ * <p>All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature.
+ * When the feature is</p>
+ * <ul>
+ * <li>
+ * <code>true</code>: 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} <code>parse</code> methods for handler specification.
+ * </li>
+ * <li>
+ * When the feature is <code>false</code>, the implementation will processing XML according to the XML specifications without
+ * regard to possible implementation limits.
+ * </li>
+ * </ul>
+ *
* @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;
-
- /* <p>Get current state of canonicalization.</p>
- *
- * @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 @@
);
}
- /** <p>Set canonicalization control to <code>true</code> or
- * </code>false</code>.</p>
- *
- * @param state of canonicalization
- */
- /*
- public void setCanonicalization(boolean state) {
- canonicalState = state;
- }
- */
-
/**
* <p>Set the {@link Schema} to be used by parsers created
* from this factory.</p>
@@ -400,7 +365,7 @@
* Such configuration will cause a {@link SAXException}
* exception when those properties are set on a {@link SAXParser}.</p>
*
- * <h4>Note for implmentors</h4>
+ * <h4>Note for implementors</h4>
* <p>
* A parser must be able to work with any {@link Schema}
* implementation. However, parsers and schemas are allowed
--- a/jaxp/src/javax/xml/stream/FactoryFinder.java Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/src/javax/xml/stream/FactoryFinder.java Thu May 16 11:47:51 2013 +0100
@@ -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;
/**
- * <p>Implements pluggable Datatypes.</p>
+ * <p>Implements pluggable streams.</p>
*
* <p>This class is duplicated for each JAXP subpackage so keep it in
* sync. It is package private for secure class loading.</p>
@@ -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
* <code>getProviderClass()</code> 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> T newInstance(Class<T> 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
* <code>getProviderClass()</code> 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> T newInstance(Class<T> 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> T find(Class<T> 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> T find(Class<T> 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> T findServiceProvider(final Class<T> 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<T>() {
+ @Override
+ public T run() {
+ final ServiceLoader<T> serviceLoader = ServiceLoader.load(type);
+ final Iterator<T> 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;
+ }
+ }
}
--- a/jaxp/src/javax/xml/stream/XMLEventFactory.java Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/src/javax/xml/stream/XMLEventFactory.java Thu May 16 11:47:51 2013 +0100
@@ -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.
+ * <p>
* This static method creates a new factory instance.
* This method uses the following ordered lookup procedure to determine
* the XMLEventFactory implementation class to load:
+ * </p>
+ * <ul>
+ * <li>
* Use the javax.xml.stream.XMLEventFactory system property.
+ * </li>
+ * <li>
* 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.
- *
+ * </li>
+ * <li>
+ * 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.
+ * </li>
+ * <li>
+ * Otherwise, the system-default implementation is returned.
+ * </li>
+ * </ul>
+ * <p>
* Once an application has obtained a reference to a XMLEventFactory it
* can use the factory to configure and obtain stream instances.
- *
+ * </p>
+ * <p>
* 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
+ * </p>
+ * @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.
+ * <p>
+ * This method uses the following ordered lookup procedure to determine
+ * the XMLEventFactory implementation class to load:
+ * </p>
+ * <ul>
+ * <li>
+ * Use the value of the system property identified by {@code factoryId}.
+ * </li>
+ * <li>
+ * 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}.
+ * </li>
+ * <li>
+ * 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.
+ * </li>
+ * <li>
+ * Otherwise, throws a {@link FactoryConfigurationError}.
+ * </li>
+ * </ul>
*
+ * <p>
* 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.
+ * </p>
*
* @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);
}
/**
--- a/jaxp/src/javax/xml/stream/XMLInputFactory.java Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/src/javax/xml/stream/XMLInputFactory.java Thu May 16 11:47:51 2013 +0100
@@ -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.
+ * <p>
* This static method creates a new factory instance.
* This method uses the following ordered lookup procedure to determine
* the XMLInputFactory implementation class to load:
+ * </p>
+ * <ul>
+ * <li>
* Use the javax.xml.stream.XMLInputFactory system property.
+ * </li>
+ * <li>
* 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.
- *
+ * </li>
+ * <li>
+ * 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.
+ * </li>
+ * <li>
+ * Otherwise, the system-default implementation is returned.
+ * </li>
+ * </ul>
+ * <p>
* Once an application has obtained a reference to a XMLInputFactory it
* can use the factory to configure and obtain stream instances.
- *
+ * </p>
+ * <p>
* 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
+ * </p>
+ * @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.
+ * <p>
+ * This method uses the following ordered lookup procedure to determine
+ * the XMLInputFactory implementation class to load:
+ * </p>
+ * <ul>
+ * <li>
+ * Use the value of the system property identified by {@code factoryId}.
+ * </li>
+ * <li>
+ * 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}.
+ * </li>
+ * <li>
+ * 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.
+ * </li>
+ * <li>
+ * Otherwise, throws a {@link FactoryConfigurationError}.
+ * </li>
+ * </ul>
*
+ * <p>
* 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.
+ * </p>
*
* @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);
}
/**
--- a/jaxp/src/javax/xml/stream/XMLOutputFactory.java Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/src/javax/xml/stream/XMLOutputFactory.java Thu May 16 11:47:51 2013 +0100
@@ -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.
+ * <p>
* This static method creates a new factory instance. This method uses the
* following ordered lookup procedure to determine the XMLOutputFactory
* implementation class to load:
+ * </p>
+ * <ul>
+ * <li>
* Use the javax.xml.stream.XMLOutputFactory system property.
+ * </li>
+ * <li>
* 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.
- *
+ * </li>
+ * <li>
+ * 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.
+ * </li>
+ * <li>
+ * Otherwise, the system-default implementation is returned.
+ * </li>
+ * <p>
* Once an application has obtained a reference to a XMLOutputFactory it
* can use the factory to configure and obtain stream instances.
- *
+ * </p>
+ * <p>
* 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
+ * </p>
+ * @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.
+ * <p>
+ * This method uses the following ordered lookup procedure to determine
+ * the XMLOutputFactory implementation class to load:
+ * </p>
+ * <ul>
+ * <li>
+ * Use the value of the system property identified by {@code factoryId}.
+ * </li>
+ * <li>
+ * 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}.
+ * </li>
+ * <li>
+ * 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.
+ * </li>
+ * <li>
+ * Otherwise, throws a {@link FactoryConfigurationError}.
+ * </li>
+ * </ul>
*
- * No changes in behavior are defined by this replacement method relative
- * to the deprecated method.
- *
+ * <p>
+ * 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.
+ * </p>
*
* @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);
}
/**
--- a/jaxp/src/javax/xml/transform/FactoryFinder.java Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/src/javax/xml/transform/FactoryFinder.java Thu May 16 11:47:51 2013 +0100
@@ -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;
/**
* <p>Implements pluggable Datatypes.</p>
@@ -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
* <code>getProviderClass()</code> in order to load the class.
*
- * @param className Name of the concrete class corresponding to the
- * service provider
- *
- * @param cl <code>ClassLoader</code> used to load the factory class. If <code>null</code>
- * current <code>Thread</code>'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
- * <code>getProviderClass()</code> 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> T newInstance(Class<T> 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> T newInstanceNoServiceLoader(Class<T> 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> T find(Class<T> 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> T findServiceProvider(final Class<T> 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<T>() {
+ public T run() {
+ final ServiceLoader<T> serviceLoader = ServiceLoader.load(type);
+ final Iterator<T> 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;
}
}
-
}
--- a/jaxp/src/javax/xml/transform/TransformerFactory.java Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/src/javax/xml/transform/TransformerFactory.java Thu May 16 11:47:51 2013 +0100
@@ -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 @@
/**
* <p>Obtain a new instance of a <code>TransformerFactory</code>.
- * 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.</p>
+ * <p>This method uses the following ordered lookup procedure to determine
* the <code>TransformerFactory</code> implementation class to
* load:</p>
* <ul>
@@ -67,7 +67,7 @@
* </code> format and contains the fully qualified name of the
* implementation class with the key being the system property defined
* above.
- *
+ * <br>
* 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.
* </li>
* <li>
- * 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
- * <code>META-INF/services/javax.xml.transform.TransformerFactory</code>
- * 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.
* </li>
* <li>
- * Platform default <code>TransformerFactory</code> instance.
+ * Otherwise, the system-default implementation is returned.
* </li>
* </ul>
*
@@ -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);
}
/**
* <p>Process the <code>Source</code> into a <code>Transformer</code>
--- a/jaxp/src/javax/xml/validation/SchemaFactory.java Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/src/javax/xml/validation/SchemaFactory.java Thu May 16 11:47:51 2013 +0100
@@ -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 <em>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</em>.
@@ -147,14 +146,17 @@
* is looked for. If present, the value is processed just like above.
* </li>
* <li>
- * <p>The class loader is asked for service provider provider-configuration files matching
- * <code>javax.xml.validation.SchemaFactory</code> 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:</p>
- * <pre>
- * {@link #isSchemaLanguageSupported(String schemaLanguage)}
- * </pre>
- * 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.<br>
+ * Each potential service provider is required to implement the method
+ * {@link #isSchemaLanguageSupported(String schemaLanguage)}.
+ * <br>
+ * The first service provider found that supports the specified schema
+ * language is returned.
+ * <br>
+ * In case of {@link java.util.ServiceConfigurationError} a
+ * {@link SchemaFactoryConfigurationError} will be thrown.
* </li>
* <li>
* Platform default <code>SchemaFactory</code> is located
@@ -186,10 +188,12 @@
* If no implementation of the schema language is available.
* @throws NullPointerException
* If the <code>schemaLanguage</code> 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 @@
}
- /**
- * <p>Is specified schema supported by this <code>SchemaFactory</code>?</p>
- *
- * @param schemaLanguage Specifies the schema language which the returned <code>SchemaFactory</code> will understand.
+ /**
+ * <p>Is specified schema supported by this <code>SchemaFactory</code>?</p>
+ *
+ * @param schemaLanguage Specifies the schema language which the returned <code>SchemaFactory</code> will understand.
* <code>schemaLanguage</code> must specify a <a href="#schemaLanguage">valid</a> schema language.
- *
- * @return <code>true</code> if <code>SchemaFactory</code> supports <code>schemaLanguage</code>, else <code>false</code>.
- *
- * @throws NullPointerException If <code>schemaLanguage</code> is <code>null</code>.
- * @throws IllegalArgumentException If <code>schemaLanguage.length() == 0</code>
- * or <code>schemaLanguage</code> does not specify a <a href="#schemaLanguage">valid</a> schema language.
- */
- public abstract boolean isSchemaLanguageSupported(String schemaLanguage);
+ *
+ * @return <code>true</code> if <code>SchemaFactory</code> supports <code>schemaLanguage</code>, else <code>false</code>.
+ *
+ * @throws NullPointerException If <code>schemaLanguage</code> is <code>null</code>.
+ * @throws IllegalArgumentException If <code>schemaLanguage.length() == 0</code>
+ * or <code>schemaLanguage</code> does not specify a <a href="#schemaLanguage">valid</a> schema language.
+ */
+ public abstract boolean isSchemaLanguageSupported(String schemaLanguage);
/**
* Look up the value of a feature flag.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jaxp/src/javax/xml/validation/SchemaFactoryConfigurationError.java Thu May 16 11:47:51 2013 +0100
@@ -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 <code>SchemaFactoryConfigurationError</code> with no
+ * detail message.
+ */
+ public SchemaFactoryConfigurationError() {
+ }
+
+
+ /**
+ * Create a new <code>SchemaFactoryConfigurationError</code> with
+ * the <code>String</code> specified as an error message.
+ *
+ * @param message The error message for the exception.
+ */
+ public SchemaFactoryConfigurationError(String message) {
+ super(message);
+ }
+
+ /**
+ * Create a new <code>SchemaFactoryConfigurationError</code> with the
+ * given <code>Throwable</code> base cause.
+ *
+ * @param cause The exception or error to be encapsulated in a
+ * SchemaFactoryConfigurationError.
+ */
+ public SchemaFactoryConfigurationError(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Create a new <code>SchemaFactoryConfigurationError</code> with the
+ * given <code>Throwable</code> 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);
+ }
+
+}
--- a/jaxp/src/javax/xml/validation/SchemaFactoryFinder.java Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/src/javax/xml/validation/SchemaFactoryFinder.java Thu May 16 11:47:51 2013 +0100
@@ -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 @@
/**
*<p> Take care of restrictions imposed by java security model </p>
*/
- 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";
/**
* <p>Cache properties for performance.</p>
*/
- private static Properties cacheProps = new Properties();
+ private static final Properties cacheProps = new Properties();
- /**
- * <p>First time requires initialization overhead.</p>
- */
- private static volatile boolean firstTime = true;
+ /**
+ * <p>First time requires initialization overhead.</p>
+ */
+ 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 <code>schemaLanguage</code> 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 <code>null</code>.
*/
- 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;
}
/**
* <p>Creates an instance of the specified and returns it.</p>
*
* @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;
}
}
- /**
- * <p>Look up a value in a property file.</p>
- *
- * <p>Set <code>debug</code> to <code>true</code> to trace property evaluation.</p>
- *
- * @param schemaLanguage Schema Language to support.
- * @param inputName Name of <code>InputStream</code>.
- * @param in <code>InputStream</code> of properties.
- *
- * @return <code>SchemaFactory</code> as determined by <code>keyName</code> value or <code>null</code> if there was an error.
- *
- * @throws IOException If IO error reading from <code>in</code>.
- */
- 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<Boolean>() {
+ 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<SchemaFactory>() {
+ public SchemaFactory run() {
+ final ServiceLoader<SchemaFactory> 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<SchemaFactory> SERVICE_CLASS = SchemaFactory.class;
-
- private static String which( Class clazz ) {
+ private static String which( Class<?> clazz ) {
return which( clazz.getName(), clazz.getClassLoader() );
}
--- a/jaxp/src/javax/xml/xpath/XPathFactory.java Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/src/javax/xml/xpath/XPathFactory.java Thu May 16 11:47:51 2013 +0100
@@ -90,7 +90,7 @@
* @throws RuntimeException When there is a failure in creating an
* <code>XPathFactory</code> 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.
* </li>
* <li>
- * The class loader is asked for service provider provider-configuration files matching <code>javax.xml.xpath.XPathFactory</code>
- * 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:
- * <pre>
- * {@link #isObjectModelSupported(String objectModel)}
- * </pre>
- * 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.
+ * <br>
+ * 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.
+ * <br>
+ * In case of {@link java.util.ServiceConfigurationError} an
+ * {@link XPathFactoryConfigurationException} will be thrown.
* </li>
* <li>
* Platform default <code>XPathFactory</code> is located in a platform specific way.
@@ -152,43 +155,41 @@
*
* @return Instance of an <code>XPathFactory</code>.
*
- * @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 <code>uri</code> is <code>null</code>.
- * @throws IllegalArgumentException If <code>uri</code> is <code>null</code>
+ * @throws IllegalArgumentException If <code>uri</code> is <code>null</code>
* or <code>uri.length() == 0</code>.
*/
- 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");
}
}
- /**
- * <p>Is specified object model supported by this <code>XPathFactory</code>?</p>
- *
- * @param objectModel Specifies the object model which the returned <code>XPathFactory</code> will understand.
- *
- * @return <code>true</code> if <code>XPathFactory</code> supports <code>objectModel</code>, else <code>false</code>.
- *
- * @throws NullPointerException If <code>objectModel</code> is <code>null</code>.
- * @throws IllegalArgumentException If <code>objectModel.length() == 0</code>.
- */
- public abstract boolean isObjectModelSupported(String objectModel);
+ /**
+ * <p>Is specified object model supported by this <code>XPathFactory</code>?</p>
+ *
+ * @param objectModel Specifies the object model which the returned <code>XPathFactory</code> will understand.
+ *
+ * @return <code>true</code> if <code>XPathFactory</code> supports <code>objectModel</code>, else <code>false</code>.
+ *
+ * @throws NullPointerException If <code>objectModel</code> is <code>null</code>.
+ * @throws IllegalArgumentException If <code>objectModel.length() == 0</code>.
+ */
+ public abstract boolean isObjectModelSupported(String objectModel);
/**
* <p>Set a feature for this <code>XPathFactory</code> and
@@ -314,8 +314,8 @@
* it creates cannot support this feature.
* @throws NullPointerException if <code>name</code> is <code>null</code>.
*/
- public abstract void setFeature(String name, boolean value)
- throws XPathFactoryConfigurationException;
+ public abstract void setFeature(String name, boolean value)
+ throws XPathFactoryConfigurationException;
/**
* <p>Get the state of the named feature.</p>
@@ -339,8 +339,8 @@
* it creates cannot support this feature.
* @throws NullPointerException if <code>name</code> is <code>null</code>.
*/
- public abstract boolean getFeature(String name)
- throws XPathFactoryConfigurationException;
+ public abstract boolean getFeature(String name)
+ throws XPathFactoryConfigurationException;
/**
* <p>Establish a default variable resolver.</p>
@@ -359,19 +359,19 @@
public abstract void setXPathVariableResolver(XPathVariableResolver resolver);
/**
- * <p>Establish a default function resolver.</p>
- *
- * <p>Any <code>XPath</code> objects constructed from this factory will
- * use the specified resolver by default.</p>
- *
- * <p>A <code>NullPointerException</code> is thrown if
- * <code>resolver</code> is <code>null</code>.</p>
- *
- * @param resolver XPath function resolver.
- *
- * @throws NullPointerException If <code>resolver</code> is
- * <code>null</code>.
- */
+ * <p>Establish a default function resolver.</p>
+ *
+ * <p>Any <code>XPath</code> objects constructed from this factory will
+ * use the specified resolver by default.</p>
+ *
+ * <p>A <code>NullPointerException</code> is thrown if
+ * <code>resolver</code> is <code>null</code>.</p>
+ *
+ * @param resolver XPath function resolver.
+ *
+ * @throws NullPointerException If <code>resolver</code> is
+ * <code>null</code>.
+ */
public abstract void setXPathFunctionResolver(XPathFunctionResolver resolver);
/**
--- a/jaxp/src/javax/xml/xpath/XPathFactoryFinder.java Wed May 08 11:22:25 2013 +0100
+++ b/jaxp/src/javax/xml/xpath/XPathFactoryFinder.java Thu May 16 11:47:51 2013 +0100
@@ -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 @@
/**
* <p>Cache properties for performance.</p>
*/
- private static Properties cacheProps = new Properties();
+ private static final Properties cacheProps = new Properties();
- /**
- * <p>First time requires initialization overhead.</p>
- */
- private volatile static boolean firstTime = true;
+ /**
+ * <p>First time requires initialization overhead.</p>
+ */
+ private volatile static boolean firstTime = true;
/**
* <p>Conditional debug printing.</p>
@@ -93,9 +89,8 @@
* to find <code>XPathFactory</code>.</p>
*
* @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 @@
/**
* <p>Creates a new {@link XPathFactory} object for the specified
- * schema language.</p>
+ * object model.</p>
*
* @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 <code>null</code>.
*/
- 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;
}
/**
* <p>Creates an instance of the specified and returns it.</p>
*
* @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;
- }
- }
-
- /**
- * <p>Look up a value in a property file.</p>
- *
- * <p>Set <code>debug</code> to <code>true</code> to trace property evaluation.</p>
- *
- * @param objectModel URI of object model to support.
- * @param inputName Name of <code>InputStream</code>.
- * @param in <code>InputStream</code> of properties.
- *
- * @return <code>XPathFactory</code> as determined by <code>keyName</code> value or <code>null</code> if there was an error.
- *
- * @throws IOException If IO error reading from <code>in</code>.
- */
- 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<Boolean>() {
+ 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<XPathFactory>() {
+ public XPathFactory run() {
+ final ServiceLoader<XPathFactory> 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<XPathFactory> SERVICE_CLASS = XPathFactory.class;
private static String which( Class clazz ) {
return which( clazz.getName(), clazz.getClassLoader() );
--- a/jaxws/.hgtags Wed May 08 11:22:25 2013 +0100
+++ b/jaxws/.hgtags Thu May 16 11:47:51 2013 +0100
@@ -209,3 +209,5 @@
8c0b6bccfe474576d6b30d1582c4329029330150 jdk8-b85
a5e7c2f093c9996ab3419db1565094a07b059e9c jdk8-b86
72e03566f0a61282cc48ebc869803b256cccd66c jdk8-b87
+24fa5452e5d4e9df8b85196283275a6ca4b4adb4 jdk8-b88
+88838e08e4ef6a254867c8126070a1975683108d jdk8-b89
--- a/jdk/.hgtags Wed May 08 11:22:25 2013 +0100
+++ b/jdk/.hgtags Thu May 16 11:47:51 2013 +0100
@@ -210,3 +210,4 @@
7989cd0cc3a9149864589438ee2c949015d8aa9a jdk8-b86
d5228e624826a10ccc5b05f30ad8d839b58fe48d jdk8-b87
8dbb4b159e04de3c447c9242c70505e71f8624c7 jdk8-b88
+845025546e35519fbb8970e79fc2a834063a5e19 jdk8-b89
--- a/jdk/makefiles/CompileNativeLibraries.gmk Wed May 08 11:22:25 2013 +0100
+++ b/jdk/makefiles/CompileNativeLibraries.gmk Thu May 16 11:47:51 2013 +0100
@@ -63,7 +63,12 @@
# Use this variable to set DEBUG_SYMBOLS true on windows for all libraries, but
# not on other platforms.
ifeq ($(OPENJDK_TARGET_OS), windows)
- WINDOWS_ONLY := true
+ DEBUG_ALL_BINARIES := true
+endif
+
+# Build everything with debugging on OpenJDK
+ifdef OPENJDK
+ DEBUG_ALL_BINARIES := true
endif
#
@@ -91,7 +96,8 @@
-I$(JDK_TOPDIR)/src/share/native/java/lang/fdlibm/include,\
CFLAGS_windows_debug:=-DLOGGING,\
ARFLAGS:=$(ARFLAGS),\
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libfdlibm))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libfdlibm,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
else
@@ -105,7 +111,8 @@
CFLAGS:=$(CFLAGS_JDKLIB) \
-I$(JDK_TOPDIR)/src/share/native/java/lang/fdlibm/include,\
LDFLAGS:=-nostdlib -r -arch x86_64,\
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libfdlibm))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libfdlibm,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBFDLIBM := $(JDK_OUTPUTDIR)/objs/$(LIBRARY_PREFIX)fdlibm$(STATIC_LIBRARY_SUFFIX)
$(BUILD_LIBFDLIBM) : $(BUILD_LIBFDLIBM_MAC)
@@ -257,7 +264,7 @@
-D "JDK_FTYPE=0x2L",\
REORDER:=$(LIBJAVA_REORDER), \
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjava,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBJAVA)
@@ -308,7 +315,7 @@
-D "JDK_INTERNAL_NAME=mlib_image" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libmlib_image,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBMLIB_IMAGE) : $(BUILD_LIBJAVA)
@@ -431,7 +438,8 @@
$(BUILD_LIBMLIB_LDLIBS) -ljava -ljvm \
$(call SET_SHARED_LIBRARY_ORIGIN),\
LDFLAGS_SUFFIX_solaris:=-lc,\
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libmlib_image_v))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libmlib_image_v,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBMLIB_IMAGE_V): $(BUILD_LIBJAVA)
@@ -739,7 +747,7 @@
-D "JDK_INTERNAL_NAME=awt" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libawt,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBAWT) : $(BUILD_LIBJAVA)
@@ -895,7 +903,8 @@
-D "JDK_FNAME=xawt.dll" \
-D "JDK_INTERNAL_NAME=xawt" \
-D "JDK_FTYPE=0x2L",\
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libawt_xawt))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libawt_xawt,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBAWT_XAWT) : $(BUILD_LIBJAVA)
@@ -956,7 +965,7 @@
-D "JDK_INTERNAL_NAME=zip" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libzip,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBZIP) : $(BUILD_LIBJAVA)
@@ -986,7 +995,7 @@
-D "JDK_FNAME=unpack.dll" \
-D "JDK_INTERNAL_NAME=unpack" \
-D "JDK_FTYPE=0x2L",\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBUNPACK) : $(BUILD_LIBJAVA)
@@ -1100,7 +1109,7 @@
-D "JDK_INTERNAL_NAME=dt_shmem" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libdt_shmem,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBDT_SHMEM)
@@ -1134,7 +1143,7 @@
-D "JDK_INTERNAL_NAME=jdwp" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjdwp,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBJDWP) : $(BUILD_LIBJAVA)
@@ -1175,7 +1184,7 @@
-D "JDK_INTERNAL_NAME=$(LIBJAAS_NAME)" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjaas,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBJAAS) : $(BUILD_LIBJAVA)
@@ -1240,7 +1249,7 @@
-D "JDK_INTERNAL_NAME=lcms" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/liblcms,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBLCMS)
@@ -1300,7 +1309,7 @@
-D "JDK_FTYPE=0x2L",\
REORDER:=$(BUILD_LIBJPEG_REORDER),\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjpeg,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBJPEG) : $(BUILD_LIBJAVA)
@@ -1377,7 +1386,7 @@
-D "JDK_INTERNAL_NAME=fontmanager" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libfontmanager,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBFONTMANAGER) : $(BUILD_LIBAWT)
@@ -1434,7 +1443,7 @@
-D "JDK_INTERNAL_NAME=t2k" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libt2k,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
# t2k is linked against fontmanager
$(BUILD_LIBT2K) : $(BUILD_LIBFONTMANAGER)
@@ -1472,7 +1481,7 @@
-D "JDK_INTERNAL_NAME=jawt" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjawt,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBJAWT) : $(BUILD_LIBAWT)
@@ -1521,7 +1530,8 @@
LDFLAGS_SUFFIX_solaris:=$(JAWT_LIBS) $(LDFLAGS_JDKLIB_SUFFIX) -lXrender,\
LDFLAGS_SUFFIX_macosx:=-Xlinker -rpath -Xlinker @loader_path $(JAWT_LIBS) \
-framework Cocoa $(LDFLAGS_JDKLIB_SUFFIX),\
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjawt))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjawt,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
ifndef BUILD_HEADLESS_ONLY
$(BUILD_LIBJAWT) : $(BUILD_LIBAWT_XAWT)
@@ -1828,7 +1838,7 @@
-D "JDK_INTERNAL_NAME=net" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libnet,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBNET) : $(BUILD_LIBJAVA)
@@ -1965,7 +1975,7 @@
-D "JDK_INTERNAL_NAME=nio" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libnio,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBNIO)
@@ -2002,7 +2012,8 @@
LDFLAGS_SUFFIX_posix:=-lnio -lnet,\
LDFLAGS_SUFFIX_solaris:=-lsocket -ljava -ljvm -lc,\
LDFLAGS_SUFFIX_macosx:=-ljava -ljvm,\
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libsctp))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libsctp,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBSCTP)
@@ -2126,7 +2137,7 @@
-D "JDK_INTERNAL_NAME=jli" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjli,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBJLI)
@@ -2143,7 +2154,8 @@
OPTIMIZATION:=HIGH, \
CFLAGS:=$(STATIC_LIBRARY_FLAGS) $(LIBJLI_CFLAGS),\
ARFLAGS:=$(ARFLAGS),\
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjli_static))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjli_static,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBJLI_STATIC)
@@ -2161,7 +2173,8 @@
OPTIMIZATION:=HIGH, \
CFLAGS:=$(CFLAGS_JDKLIB) $(LIBJLI_CFLAGS),\
LDFLAGS:=-nostdlib -r,\
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjli_static))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjli_static,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(JDK_OUTPUTDIR)/objs/libjli_static.a : $(BUILD_LIBJLI_STATIC)
$(call install-file)
@@ -2192,7 +2205,7 @@
-D "JDK_INTERNAL_NAME=jfr" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjfr,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBJFR)
@@ -2244,7 +2257,7 @@
-D "JDK_INTERNAL_NAME=kcms" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libkcms,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBKCMS) : $(BUILD_LIBJAVA)
@@ -2279,7 +2292,8 @@
LDFLAGS:=$(LDFLAGS_JDKLIB) \
$(call SET_SHARED_LIBRARY_ORIGIN), \
LDFLAGS_SUFFIX:=-L$(OPENWIN_LIB)$(OPENJDK_TARGET_CPU_ISADIR) -R$(OPENWIN_LIB)$(OPENJDK_TARGET_CPU_ISADIR) -ldga -lX11 $(LIBDL) -lc, \
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libsunwjdga))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libsunwjdga,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBSUNWJDGA)
@@ -2369,7 +2383,8 @@
LDFLAGS_SUFFIX_linux:=-ljvm -lawt -lm $(LIBDL) -ljava,\
LDFLAGS_SUFFIX_solaris:=$(LIBDL) -ljvm -lawt -lm -ljava $(LIBCXX) -lc,\
LDFLAGS_SUFFIX_macosx:=-ljvm $(LIBCXX) -lawt $(LIBDL) -ljava,\
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libawt_headless))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libawt_headless,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBAWT_HEADLESS) : $(BUILD_LIBAWT)
@@ -2462,7 +2477,7 @@
-D "JDK_INTERNAL_NAME=splashscreen" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libsplashscreen,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(LIBSPLASHSCREEN)
@@ -2504,7 +2519,7 @@
-D "JDK_INTERNAL_NAME=dcpr" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libdcpr,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBDCPR) : $(BUILD_LIBJAVA)
@@ -2538,7 +2553,7 @@
-D "JDK_INTERNAL_NAME=j2pcsc" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libj2pcsc,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBJ2PCSC)
@@ -2561,7 +2576,8 @@
$(call SET_SHARED_LIBRARY_ORIGIN),\
LDFLAGS_SUFFIX:=$(LIBDL),\
LDFLAGS_SUFFIX_solaris:=-lc,\
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libj2gss))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libj2gss,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBJ2GSS)
endif
@@ -2601,7 +2617,7 @@
-D "JDK_INTERNAL_NAME=$(BUILD_LIBKRB5_NAME)" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libkrb5,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBKRB5)
endif
@@ -2627,7 +2643,7 @@
-D "JDK_INTERNAL_NAME=sunmscapi" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libsunmscapi,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBSUNMSCAPI)
endif
@@ -2659,7 +2675,7 @@
-D "JDK_INTERNAL_NAME=j2pkcs11" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libj2pkcs11,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBJ2PKCS11)
@@ -2705,7 +2721,7 @@
-D "JDK_INTERNAL_NAME=sunec" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libsunec,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBSUNEC)
endif
@@ -2849,7 +2865,7 @@
-D "JDK_INTERNAL_NAME=jsound" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjsound,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBJSOUND) : $(BUILD_LIBJAVA)
@@ -2884,7 +2900,8 @@
LDFLAGS:=$(LDFLAGS_JDKLIB)\
$(call SET_SHARED_LIBRARY_ORIGIN),\
LDFLAGS_SUFFIX:=-lasound -ljava -ljvm,\
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjsoundalsa))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjsoundalsa,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBJSOUNDALSA) : $(BUILD_LIBJAVA)
@@ -2917,7 +2934,7 @@
-D "JDK_INTERNAL_NAME=jsoundds" \
-D "JDK_FTYPE=0x2L",\
OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjsoundds,\
- DEBUG_SYMBOLS:=$(WINDOWS_ONLY)))
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBJSOUNDDS) : $(BUILD_LIBJAVA)
@@ -2942,7 +2959,8 @@
LDFLAGS:=$(LDFLAGS_JDKLIB),\
LDFLAGS_SUFFIX:=$(LIBDL),\
LDFLAGS_SUFFIX_solaris:=-lc,\
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libj2ucrypto))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libj2ucrypto,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBJ2UCRYPTO) : $(BUILD_LIBJAVA)
@@ -2972,7 +2990,8 @@
-F/System/Library/Frameworks/JavaVM.framework/Frameworks \
-framework JavaNativeFoundation \
$(LDFLAGS_JDKLIB_SUFFIX),\
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libAppleScriptEngine))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libAppleScriptEngine,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(BUILD_LIBAPPLESCRIPTENGINE) : $(BUILD_LIBJAVA)
@@ -3011,7 +3030,8 @@
-framework OpenGL \
-framework IOSurface \
-framework QuartzCore, \
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libosxapp))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libosxapp,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBOSXAPP)
@@ -3051,7 +3071,8 @@
-framework Security \
-framework SystemConfiguration \
$(LDFLAGS_JDKLIB_SUFFIX), \
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libosx))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libosx,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBOSX)
@@ -3184,7 +3205,8 @@
-framework JavaRuntimeSupport \
-framework OpenGL \
-framework QuartzCore -ljava,\
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libawt_lwawt))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libawt_lwawt,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBAWT_LWAWT)
@@ -3224,7 +3246,8 @@
-framework JavaNativeFoundation \
-framework JavaRuntimeSupport \
-ljava -ljvm,\
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libosxui))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libosxui,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
BUILD_LIBRARIES += $(BUILD_LIBOSXUI)
@@ -3264,7 +3287,8 @@
-F/System/Library/Frameworks/JavaVM.framework/Frameworks \
-framework JavaNativeFoundation \
-lffi, \
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjobjc32))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjobjc32,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(eval $(call SetupNativeCompilation,BUILD_LIBJOBJC64,\
LIBRARY:=JObjC,\
@@ -3288,7 +3312,8 @@
-F/System/Library/Frameworks/JavaVM.framework/Frameworks \
-framework JavaNativeFoundation \
-lffi, \
- OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjobjc64))
+ OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjobjc64,\
+ DEBUG_SYMBOLS:=$(DEBUG_ALL_BINARIES)))
$(INSTALL_LIBRARIES_HERE)/$(LIBRARY_PREFIX)JObjC$(SHARED_LIBRARY_SUFFIX): $(BUILD_LIBJOBJC32) $(BUILD_LIBJOBJC64)
$(LIPO) -create -output $@ $(BUILD_LIBJOBJC32) $(BUILD_LIBJOBJC64)
--- a/jdk/src/share/classes/java/util/Base64.java Wed May 08 11:22:25 2013 +0100
+++ b/jdk/src/share/classes/java/util/Base64.java Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2013 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
--- a/jdk/src/share/classes/java/util/StringJoiner.java Wed May 08 11:22:25 2013 +0100
+++ b/jdk/src/share/classes/java/util/StringJoiner.java Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ * 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
--- a/jdk/test/java/lang/CharSequence/DefaultTest.java Wed May 08 11:22:25 2013 +0100
+++ b/jdk/test/java/lang/CharSequence/DefaultTest.java Thu May 16 11:47:51 2013 +0100
@@ -1,12 +1,10 @@
/*
- * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute 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.
+ * published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
--- a/jdk/test/java/util/StringJoiner/StringJoinerTest.java Wed May 08 11:22:25 2013 +0100
+++ b/jdk/test/java/util/StringJoiner/StringJoinerTest.java Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ * 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
--- a/langtools/.hgtags Wed May 08 11:22:25 2013 +0100
+++ b/langtools/.hgtags Thu May 16 11:47:51 2013 +0100
@@ -209,3 +209,5 @@
4a48f31735349782ad13980267358c97076adc66 jdk8-b85
6ab578e141dfd17c4dc03869bb204aafa490c9f4 jdk8-b86
1329f9c38d93c8caf339d7687df8371d06fe9e56 jdk8-b87
+a1e10f3adc47c8602a72e43a41403a642e73e0b1 jdk8-b88
+ec434cfd2752a7742c875c2fe7d556d8b81c0f3a jdk8-b89
--- a/langtools/make/Makefile-classic Wed May 08 11:22:25 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,396 +0,0 @@
-#
-# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
-# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-#
-# This code is free software; you can redistribute 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.
-#
-
-#
-# Makefile for building the langtools workspace.
-#
-
-#
-# On Solaris, the standard 'make' utility will not work with these makefiles.
-# This little rule is only understood by Solaris make, and is harmless
-# when seen by the GNU make tool. If using Solaris make, this causes the
-# make command to fail.
-#
-SUN_MAKE_TEST:sh = @echo "ERROR: PLEASE USE GNU VERSION OF MAKE"; exit 33
-
-#----- cancel implicit rules
-
-%: %.o
-%: %.obj
-%: %.dll
-%: %.c
-%: %.cc
-%: %.C
-%: %.p
-%: %.f
-%: %.s
-%: %.F
-%: %.r
-%: %.S
-%: %.mod
-%: %.sh
-%: %,v
-%: RCS/%,v
-
-#----- imports
-
-ifdef ALT_BOOT_JAVA_HOME
- BOOT_JAVA_HOME = $(ALT_BOOT_JAVA_HOME)
-else
- ifdef ALT_BOOTDIR
- BOOT_JAVA_HOME = $(ALT_BOOTDIR)
- else
- BOOT_JAVA_HOME=/java/re/jdk/1.5.0/archive/fcs/binaries/solaris-sparc
- endif
-endif
-
-BOOT_JAVA=$(BOOT_JAVA_HOME)/bin/java
-BOOT_JAVAC=$(BOOT_JAVA_HOME)/bin/javac
-BOOTJAR=$(BOOT_JAVA_HOME)/bin/jar
-
-ifdef ALT_TESTJAVA_HOME
- TESTJAVA_HOME = $(ALT_TESTJAVA_HOME)
-else
- TESTJAVA_HOME=/java/re/jdk/1.6.0/archive/fcs/binaries/solaris-sparc
-endif
-
-TESTJAVA=$(TESTJAVA_HOME)/bin/java
-
-ifdef ALT_FINDBUGS_HOME
- FINDBUGS_HOME = $(ALT_FINDBUGS_HOME)
-else
- FINDBUGS_HOME = /java/devtools/share/findbugs/1.1.2-rc4
-endif
-
-FINDBUGS = $(FINDBUGS_HOME)/bin/findbugs
-
-#----- commands
-
-CHMOD = chmod
-CP = cp
-ECHO = echo # FIXME
-FIND = find
-MKDIR = mkdir
-SED = sed
-ZIP = zip
-
-#----- locations and deliverables
-
-TOPDIR = ..
-SRC_BIN_DIR = $(TOPDIR)/src/share/bin
-SRC_CLASSES_DIR = $(TOPDIR)/src/share/classes
-
-BUILD_DIR = $(TOPDIR)/build
-
-CLASSES_DIR = $(BUILD_DIR)/classes
-GENSRC_DIR = $(BUILD_DIR)/gensrc
-
-DIST_DIR = $(TOPDIR)/dist
-BIN_DIR = $(DIST_DIR)/bin
-LIB_DIR = $(DIST_DIR)/lib
-
-JAVAC_JAR = $(LIB_DIR)/javac.jar
-JAVADOC_JAR = $(LIB_DIR)/javadoc.jar
-JAVAH_JAR = $(LIB_DIR)/javah.jar
-JAVAP_JAR = $(LIB_DIR)/javap.jar
-
-CLASSES_JAR = $(DIST_DIR)/classes.jar
-SRC_ZIP = $(DIST_DIR)/src.zip
-
-BUILDTOOLSRC_DIR = tools
-BUILDTOOLCLASSES_DIR = $(BUILD_DIR)/toolclasses
-
-#-----
-
-ifndef JDK_MAJOR_VERSION
- JDK_MAJOR_VERSION = 1
-endif
-
-ifndef JDK_MINOR_VERSION
- JDK_MINOR_VERSION = 7
-endif
-
-ifndef JDK_MICRO_VERSION
- JDK_MICRO_VERSION = 0
-endif
-
-ifndef JDK_VERSION
- JDK_VERSION = $(JDK_MAJOR_VERSION).$(JDK_MINOR_VERSION).$(JDK_MICRO_VERSION)
-endif
-
-ifndef MILESTONE
- MILESTONE = internal
-endif
-
-# RELEASE is JDK_VERSION and -MILESTONE if MILESTONE is set
-ifneq ($(MILESTONE),fcs)
- RELEASE = $(JDK_VERSION)-$(MILESTONE)$(BUILD_VARIANT_RELEASE)
-else
- RELEASE = $(JDK_VERSION)$(BUILD_VARIANT_RELEASE)
-endif
-
-# FULL_VERSION is RELEASE and -BUILD_NUMBER if BUILD_NUMBER is set
-ifdef BUILD_NUMBER
- FULL_VERSION = $(RELEASE)-$(BUILD_NUMBER)
-else
- BUILD_NUMBER = b00
- USER_RELEASE_SUFFIX := $(shell echo $(USER)_`date '+%d_%b_%Y_%H_%M' | tr "A-Z" "a-z"`)
- FULL_VERSION = $(RELEASE)-$(USER_RELEASE_SUFFIX)-$(BUILD_NUMBER)
-endif
-
-#----- useful macros
-
-TOOLS = javac javadoc javah javap
-
-SOURCE_LEVEL = 5
-BOOTSTRAP_TARGET_LEVEL = 5
-TARGET_LEVEL = 6
-
-ifndef TARGET_JAVA
- TARGET_JAVA = java
-endif
-
-NO_PROPRIETARY_API_WARNINGS = -XDignore.symbol.file=true
-
-SELF = $(lastword $(MAKEFILE_LIST))
-
-#-----
-
-# the default is to generate the following:
-# dist/{bin,lib}:
-# lang tools compiled to run on the target JDK
-
-default:
- $(MAKE) -f $(SELF) \
- MILESTONE=bootstrap \
- TARGET_LEVEL=$(BOOTSTRAP_TARGET_LEVEL) \
- TARGET_JAVA=$(BOOT_JAVA_HOME)/bin/java \
- GENSRC_DIR=$(BUILD_DIR)/bootstrap/gensrc \
- CLASSES_DIR=$(BUILD_DIR)/bootstrap/classes \
- BIN_DIR=$(BUILD_DIR)/bootstrap/bin \
- LIB_DIR=$(BUILD_DIR)/bootstrap/lib \
- $(BUILD_DIR)/bootstrap/lib/javac.jar \
- $(BUILD_DIR)/bootstrap/bin/javac
- $(MAKE) -f $(SELF) \
- BOOT_JAVAC=$(BUILD_DIR)/bootstrap/bin/javac \
- tools
-
-# for jdk, we generate the following:
-# dist/bootstrap/{bin,lib}:
-# lang tools compiled to run on the boot JDK
-# dist/lib/classes.jar:
-# lang tools recompiled to run on the target JDK,
-# ready for inclusion in rt.jar and tools.jar
-# dist/lib/src.zip
-# .properties and .java files for classes in classes.jar,
-# ready for jdk src.zip
-
-jdk:
- $(MAKE) -f $(SELF) \
- MILESTONE=bootstrap \
- TARGET_LEVEL=$(BOOTSTRAP_TARGET_LEVEL) \
- TARGET_JAVA=$(BOOT_JAVA_HOME)/bin/java \
- GENSRC_DIR=$(BUILD_DIR)/bootstrap/gensrc \
- CLASSES_DIR=$(BUILD_DIR)/bootstrap/classes \
- BIN_DIR=$(DIST_DIR)/bootstrap/bin \
- LIB_DIR=$(DIST_DIR)/bootstrap/lib \
- tools
- $(MAKE) -f $(SELF) \
- BOOT_JAVAC=$(DIST_DIR)/bootstrap/bin/javac \
- LIB_DIR=$(BUILD_DIR)/jdk/lib \
- $(DIST_DIR)/lib/classes.jar \
- $(DIST_DIR)/lib/src.zip
-
-tools: $(TOOLS:%=$(LIB_DIR)/%.jar) $(TOOLS:%=$(BIN_DIR)/%)
-
-clean:
- $(RM) -r $(BUILD_DIR)
-
-really-clean: clean
- $(RM) -r $(DIST_DIR)
-
-jprt_product_build \
-jprt_debug_build \
-jprt_fastdebug_build: lib
-
-#----- javac
-
-JAVAC_DIRS = \
- javax/annotation/processing \
- javax/lang/model \
- javax/tools \
- jdk/ \
- com/sun/source \
- com/sun/tools/javac
-
-JAVAC_RESOURCE_FILES = \
- $(shell find $(patsubst %,$(SRC_CLASSES_DIR)/%,$(JAVAC_DIRS)) -name SCCS -prune -o -name \*.properties -print )
-
-JAVAC_JAVA_FILES = \
- $(shell find $(patsubst %,$(SRC_CLASSES_DIR)/%,$(JAVAC_DIRS)) -name SCCS -prune -o -name \*.java -print ) \
- $(patsubst $(SRC_CLASSES_DIR)/%.properties,$(GENSRC_DIR)/%.java,$(JAVAC_RESOURCE_FILES)) \
- $(GENSRC_DIR)/com/sun/tools/javac/resources/version.java
-
-$(JAVAC_JAR): $(JAVAC_JAVA_FILES)
- $(MKDIR) -p $(CLASSES_DIR) $(@D)
- $(BOOT_JAVAC) -d $(CLASSES_DIR) -target $(TARGET_LEVEL) $(NO_PROPRIETARY_API_WARNINGS) $(JAVAC_JAVA_FILES)
- ( $(ECHO) Main-Class: com.sun.tools.javac.Main ) > $(BUILD_DIR)/javac.mf
- $(BOOTJAR) -cfm $@ $(BUILD_DIR)/javac.mf $(patsubst %,-C $(CLASSES_DIR) %, $(JAVAC_DIRS))
-
-#----- javadoc
-
-### FIXME -- javadoc has a couple of extra non-property resource files
-### that need to be included
-
-JAVADOC_DIRS = \
- com/sun/javadoc \
- com/sun/tools/doclets \
- com/sun/tools/javadoc
-
-JAVADOC_RESOURCE_FILES = \
- $(shell find $(patsubst %,$(SRC_CLASSES_DIR)/%,$(JAVADOC_DIRS)) -name SCCS -prune -o -name \*.properties -print )
-
-JAVADOC_JAVA_FILES = \
- $(shell find $(patsubst %,$(SRC_CLASSES_DIR)/%,$(JAVADOC_DIRS)) -name SCCS -prune -o -name \*.java -print ) \
- $(patsubst $(SRC_CLASSES_DIR)/%.properties,$(GENSRC_DIR)/%.java,$(JAVADOC_RESOURCE_FILES))
-
-$(JAVADOC_JAR): $(JAVADOC_JAVA_FILES) $(JAVAC_JAR)
- $(MKDIR) -p $(CLASSES_DIR) $(@D)
- $(BOOT_JAVAC) -sourcepath "" -classpath $(JAVAC_JAR) -d $(CLASSES_DIR) -target $(TARGET_LEVEL) $(JAVADOC_JAVA_FILES)
- ( $(ECHO) Main-Class: com.sun.tools.javadoc.Main ; $(ECHO) Class-Path: javac.jar ) > $(BUILD_DIR)/javadoc.mf
- $(BOOTJAR) -cfm $@ $(BUILD_DIR)/javadoc.mf $(patsubst %,-C $(CLASSES_DIR) %, $(JAVADOC_DIRS))
-
-#----- javah
-
-JAVAH_DIRS = \
- com/sun/tools/javah
-
-JAVAH_RESOURCE_FILES = \
- $(shell find $(patsubst %,$(SRC_CLASSES_DIR)/%,$(JAVAH_DIRS)) -name SCCS -prune -o -name \*.properties -print )
-
-JAVAH_JAVA_FILES = \
- $(shell find $(patsubst %,$(SRC_CLASSES_DIR)/%,$(JAVAH_DIRS)) -name SCCS -prune -o -name \*.java -print ) \
- $(patsubst $(SRC_CLASSES_DIR)/%.properties,$(GENSRC_DIR)/%.java,$(JAVAH_RESOURCE_FILES))
-
-$(JAVAH_JAR): $(JAVAH_JAVA_FILES) $(JAVADOC_JAR)
- $(MKDIR) -p $(CLASSES_DIR) $(@D)
- $(BOOT_JAVAC) -sourcepath "" -classpath $(CLASSES_DIR) -d $(CLASSES_DIR) -target $(TARGET_LEVEL) $(JAVAH_JAVA_FILES)
- ( $(ECHO) Main-Class: com.sun.tools.javah.Main ; $(ECHO) Class-Path: javadoc.jar ) > $(BUILD_DIR)/javah.mf
- $(BOOTJAR) -cfm $@ $(BUILD_DIR)/javah.mf $(patsubst %,-C $(CLASSES_DIR) %, $(JAVAH_DIRS))
-
-#----- javap
-
-JAVAP_DIRS = \
- sun/tools/javap
-
-JAVAP_RESOURCE_FILES = \
- $(shell find $(patsubst %,$(SRC_CLASSES_DIR)/%,$(JAVAP_DIRS)) -name SCCS -prune -o -name \*.properties -print )
-
-JAVAP_JAVA_FILES = \
- $(shell find $(patsubst %,$(SRC_CLASSES_DIR)/%,$(JAVAP_DIRS)) -name SCCS -prune -o -name \*.java -print ) \
- $(patsubst $(SRC_CLASSES_DIR)/%.properties,$(GENSRC_DIR)/%.java,$(JAVAP_RESOURCE_FILES))
-
-$(JAVAP_JAR): $(JAVAP_JAVA_FILES) \
- $(patsubst $(SRC_CLASSES_DIR)/%.properties,$(GENSRC_DIR)/%.java,$(JAVAP_RESOURCE_FILES))
- $(MKDIR) -p $(CLASSES_DIR)
- $(BOOT_JAVAC) -sourcepath "" -classpath $(CLASSES_DIR) -d $(CLASSES_DIR) -target $(TARGET_LEVEL) $(JAVAP_JAVA_FILES)
- ( $(ECHO) Main-Class: sun.tools.javap.Main ) > $(BUILD_DIR)/javap.mf
- $(BOOTJAR) -cfm $@ $(BUILD_DIR)/javap.mf $(patsubst %,-C $(CLASSES_DIR) %, $(JAVAP_DIRS))
-
-#-----
-
-build-tools: $(BUILDTOOLCLASSES_DIR)/CompileProperties/CompileProperties.class
-
-$(GENSRC_DIR)/%.java: $(SRC_CLASSES_DIR)/%.properties $(BUILDTOOLCLASSES_DIR)/CompileProperties/CompileProperties.class
- $(MKDIR) -p $(@D)
- $(BOOT_JAVA) -cp $(BUILDTOOLCLASSES_DIR)/CompileProperties CompileProperties $< $(patsubst $(CLASSES_DIR)/%.class,$(GENSRC_DIR)/%.java,$@)
-
-$(GENSRC_DIR)/%.java: $(GENSRC_DIR)/%.properties $(BUILDTOOLCLASSES_DIR)/CompileProperties/CompileProperties.class
- $(MKDIR) -p $(@D)
- $(BOOT_JAVA) -cp $(BUILDTOOLCLASSES_DIR)/CompileProperties CompileProperties $< $(patsubst $(CLASSES_DIR)/%.class,$(GENSRC_DIR)/%.java,$@)
-
-$(GENSRC_DIR)/%.properties: $(SRC_CLASSES_DIR)/%.properties-template
- $(MKDIR) -p $(@D)
- $(SED) -e 's/$$(JDK_VERSION)/$(JDK_VERSION)/' \
- -e 's/$$(FULL_VERSION)/$(FULL_VERSION)/' \
- -e 's/$$(RELEASE)/$(RELEASE)/' \
- < $< > $@
-
-$(BUILDTOOLCLASSES_DIR)/%.class : $(BUILDTOOLSRC_DIR)/%.java
- $(MKDIR) -p $(@D)
- $(BOOT_JAVAC) -d $(@D) $<
-
-#----- all classes
-
-$(DIST_DIR)/%/classes.jar: $(JAVAC_JAR) $(JAVADOC_JAR) $(JAVAH_JAR) $(JAVAP_JAR)
- $(MKDIR) -p $(@D)
- $(BOOTJAR) -cf $@ -C $(CLASSES_DIR) .
-
-#----- src.zip
-
-SRC_ZIP_FILES = $(shell $(FIND) $(SRC_CLASSES_DIR) \( -name SCCS -o -name \*-template \) -prune -o -type f -print )
-
-$(DIST_DIR)/%/src.zip: $(SRC_ZIP_FILES)
- abs_src_zip=`cd $(@D) ; pwd`/$(@F) ; \
- ( cd $(SRC_CLASSES_DIR) ; $(FIND) . \( -name SCCS -o -name \*-template \) -prune -o -type f -print | $(ZIP) -q $$abs_src_zip -@ ) ; \
- ( cd $(SRC_CLASSES_DIR) ; $(FIND) . -name SCCS -prune -o -name \*-template -print | $(SED) -e 's/-template//' ) | ( cd $(GENSRC_DIR) ; $(ZIP) -q $$abs_src_zip -@ )
-
-#----- bin files
-
-$(BIN_DIR)/%: $(SRC_BIN_DIR)/launcher.sh-template
- $(MKDIR) -p $(@D)
- $(SED) -e 's|#PROGRAM#|$(@F)|' -e 's|#TARGET_JAVA#|$(TARGET_JAVA)|' $< > $@
- $(CHMOD) +x $@
-
-#-----
-
-findbugs: $(BUILD_DIR)/findbugs.txt
-
-$(BUILD_DIR)/findbugs.txt: $(CLASSES_JAR)
- $(MKDIR) -p $(@D)
- $(FINDBUGS) -textui -javahome $(BOOT_JAVA_HOME) -high -emacs -outputFile $@ $<
-
-#-----
-
-quick-check: $(patsubst %, $(DIST_LIB_DIR)/%.jar, $(TOOLS))
- $(TESTJAVA) -jar $(JAVAC_JAR) -version
- - $(TESTJAVA) -jar $(JAVADOC_JAR) -version
- $(TESTJAVA) -jar $(JAVAH_JAR) -version
- - $(TESTJAVA) -jar $(JAVAP_JAR) -version
-
-#-----
-
-.PHONY: \
- all \
- build \
- build-tools \
- clean \
- default \
- findbugs \
- jprt_product_build \
- jprt_debug_build \
- jprt_fastdebug_build \
- tools
-
--- a/langtools/make/build.xml Wed May 08 11:22:25 2013 +0100
+++ b/langtools/make/build.xml Thu May 16 11:47:51 2013 +0100
@@ -717,6 +717,29 @@
<target name="sjavac" depends="build-sjavac,jtreg-sjavac,findbugs-sjavac"/>
<!--
+ **** crules targets.
+ -->
+
+ <target name="build-crules" depends="-def-compilecrules,-def-build-jar-with-services">
+ <compilecrules/>
+ <build-jar-with-services
+ name="crules"
+ includes="crules/* crules/resources/*"
+ classes.dir="${build.toolclasses.dir}"
+ lib.dir="${build.toolclasses.dir}"
+ jarmainclass=""
+ jarclasspath="crules.jar"
+ service.type="com.sun.source.util.Plugin"
+ service.provider="crules.MutableFieldsAnalyzer"/>
+ <build-tool name="crules"/>
+ </target>
+
+ <target name="check-coding-rules" depends="build-bootstrap-javac,-create-import-jdk-stubs,build-crules">
+ <build-classes includes="${javac.includes}"
+ plugin.options="-J-Xbootclasspath/a:${build.toolclasses.dir}/crules.jar -Xplugin:mutable_fields_analyzer" />
+ </target>
+
+ <!--
**** Create import JDK stubs.
-->
@@ -811,6 +834,31 @@
</macrodef>
</target>
+ <target name="-def-build-jar-with-services">
+ <macrodef name="build-jar-with-services">
+ <attribute name="name"/>
+ <attribute name="includes"/>
+ <attribute name="classes.dir" default="${build.classes.dir}"/>
+ <attribute name="lib.dir" default="${dist.lib.dir}"/>
+ <attribute name="jarmainclass" default="com.sun.tools.@{name}.Main"/>
+ <attribute name="jarclasspath" default=""/>
+ <attribute name="service.type" default=""/>
+ <attribute name="service.provider" default=""/>
+ <sequential>
+ <mkdir dir="${build.toolclasses.dir}"/>
+ <jar destfile="@{lib.dir}/@{name}.jar"
+ basedir="@{classes.dir}"
+ includes="@{includes}">
+ <service type="@{service.type}" provider="@{service.provider}"/>
+ <manifest>
+ <attribute name="Main-Class" value="@{jarmainclass}"/>
+ <attribute name="Class-Path" value="@{jarclasspath}"/>
+ </manifest>
+ </jar>
+ </sequential>
+ </macrodef>
+ </target>
+
<target name="-def-build-classes" depends="-def-pcompile">
<macrodef name="build-classes">
<attribute name="includes"/>
@@ -826,6 +874,7 @@
<attribute name="target" default="${javac.target}"/>
<attribute name="release" default="${release}"/>
<attribute name="full.version" default="${full.version}"/>
+ <attribute name="plugin.options" default=""/>
<sequential>
<echo level="verbose" message="build-classes: excludes=@{excludes}"/>
<echo level="verbose" message="build-classes: bootclasspath.opt=@{bootclasspath.opt}"/>
@@ -868,6 +917,7 @@
<compilerarg line="${javac.no.jdk.warnings}"/>
<compilerarg line="${javac.version.opt}"/>
<compilerarg line="${javac.lint.opts}"/>
+ <compilerarg line="@{plugin.options}"/>
</javac>
<copy todir="@{classes.dir}" includeemptydirs="false">
<fileset dir="${src.classes.dir}" includes="@{includes}" excludes="@{excludes}">
@@ -935,6 +985,32 @@
classpath="${build.toolclasses.dir}/"/>
</target>
+ <target name="-def-compilecrules">
+ <macrodef name="compilecrules">
+ <sequential>
+ <mkdir dir="${build.toolclasses.dir}"/>
+ <javac fork="true"
+ source="${boot.javac.source}"
+ target="${boot.javac.target}"
+ executable="${boot.java.home}/bin/javac"
+ srcdir="${make.tools.dir}"
+ includes="crules/*"
+ destdir="${build.toolclasses.dir}/"
+ classpath="${ant.core.lib}"
+ bootclasspath="${boot.java.home}/jre/lib/rt.jar"
+ includeantruntime="false">
+ <compilerarg value="-Xbootclasspath/p:${build.bootstrap.dir}/classes"/>
+ <compilerarg line="${javac.lint.opts}"/>
+ </javac>
+ <copy todir="${build.toolclasses.dir}/" includeemptydirs="false">
+ <fileset dir="${make.tools.dir}">
+ <include name="**/*.properties"/>
+ </fileset>
+ </copy>
+ </sequential>
+ </macrodef>
+ </target>
+
<target name="-def-genstubs" depends="build-bootstrap-javac" if="require.import.jdk.stubs">
<mkdir dir="${build.toolclasses.dir}"/>
<javac fork="true"
--- a/langtools/make/netbeans/langtools/nbproject/project.xml Wed May 08 11:22:25 2013 +0100
+++ b/langtools/make/netbeans/langtools/nbproject/project.xml Thu May 16 11:47:51 2013 +0100
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright (c) 2007, 2009, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -67,7 +67,7 @@
</folders>
<ide-actions>
<!--
- Copyright (c) 2007, 2009, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -96,7 +96,7 @@
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
- <!--
+ <!--
This file defines the standard actions accepted by langtools projects.
It is normally included as an entity into a project's project.xml file.
@@ -104,7 +104,7 @@
- NetBeans: Setting Up Projects
at http://www.netbeans.org/kb/55/using-netbeans/project_setup.html
- NetBeans: Advanced Freeform Project Configuration
- at http://www.netbeans.org/kb/41/freeform-config.html
+ at http://www.netbeans.org/kb/41/freeform-config.html
-->
<action name="build">
<target>build</target>
@@ -144,7 +144,7 @@
</arity>
</context>
</action>
- <!--
+ <!--
Note: NetBeans does not appear to support context menu items
on shell scripts :-(
-->
@@ -178,7 +178,7 @@
</arity>
</context>
</action>
- <!--
+ <!--
Note: NetBeans does not appear to support context menu items
on shell scripts :-(
-->
@@ -239,10 +239,6 @@
<label>Build files</label>
<location>${root}/make</location>
</source-folder>
- <source-folder style="packages">
- <label>Source files</label>
- <location>${root}/src/share/classes</location>
- </source-folder>
<source-file>
<label>README</label>
<location>README</location>
@@ -250,7 +246,7 @@
</items>
<context-menu>
<!--
- Copyright (c) 2007, 2009, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -279,7 +275,7 @@
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
- <!--
+ <!--
This file defines the actions that will appear on the project's context
menu, in the Projects viewer.
It is normally included as an entity into a project's project.xml file.
@@ -288,7 +284,7 @@
- NetBeans: Setting Up Projects
at http://www.netbeans.org/kb/55/using-netbeans/project_setup.html
- NetBeans: Advanced Freeform Project Configuration
- at http://www.netbeans.org/kb/41/freeform-config.html
+ at http://www.netbeans.org/kb/41/freeform-config.html
-->
<ide-action name="select-tool"/>
<separator/>
@@ -305,11 +301,11 @@
</view>
<subprojects/>
</general-data>
- <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/2">
+ <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/3">
<compilation-unit>
<package-root>${root}/src/share/classes</package-root>
<built-to>${root}/build/classes</built-to>
- <source-level>1.5</source-level>
+ <source-level>1.7</source-level>
</compilation-unit>
</java-data>
</configuration>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/make/tools/crules/AbstractCodingRulesAnalyzer.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+package crules;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import javax.lang.model.element.TypeElement;
+import javax.tools.JavaFileObject;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.Plugin;
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskListener;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.api.BasicJavacTask;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.TreeScanner;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.Log;
+
+import static com.sun.source.util.TaskEvent.Kind;
+
+public abstract class AbstractCodingRulesAnalyzer implements Plugin {
+
+ protected Log log;
+ protected Trees trees;
+ protected TreeScanner treeVisitor;
+ protected Kind eventKind;
+ protected Messages messages;
+
+ public void init(JavacTask task, String... args) {
+ BasicJavacTask impl = (BasicJavacTask)task;
+ Context context = impl.getContext();
+ log = Log.instance(context);
+ trees = Trees.instance(task);
+ messages = new Messages();
+ task.addTaskListener(new PostAnalyzeTaskListener());
+ }
+
+ public class PostAnalyzeTaskListener implements TaskListener {
+
+ @Override
+ public void started(TaskEvent taskEvent) {}
+
+ @Override
+ public void finished(TaskEvent taskEvent) {
+ if (taskEvent.getKind().equals(eventKind)) {
+ TypeElement typeElem = taskEvent.getTypeElement();
+ Tree tree = trees.getTree(typeElem);
+ if (tree != null) {
+ JavaFileObject prevSource = log.currentSourceFile();
+ try {
+ log.useSource(taskEvent.getCompilationUnit().getSourceFile());
+ treeVisitor.scan((JCTree)tree);
+ } finally {
+ log.useSource(prevSource);
+ }
+ }
+ }
+ }
+ }
+
+ class Messages {
+ ResourceBundle bundle;
+
+ Messages() {
+ String name = getClass().getPackage().getName() + ".resources.crules";
+ bundle = ResourceBundle.getBundle(name, Locale.ENGLISH);
+ }
+
+ public void error(JCTree tree, String code, Object... args) {
+ String msg = (code == null) ? (String) args[0] : localize(code, args);
+ log.error(tree, "proc.messager", msg.toString());
+ }
+
+ private String localize(String code, Object... args) {
+ String msg = bundle.getString(code);
+ if (msg == null) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("message file broken: code=").append(code);
+ if (args.length > 0) {
+ sb.append(" arguments={0}");
+ for (int i = 1; i < args.length; i++) {
+ sb.append(", {").append(i).append("}");
+ }
+ }
+ msg = sb.toString();
+ }
+ return MessageFormat.format(msg, args);
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/make/tools/crules/MutableFieldsAnalyzer.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+package crules;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.tools.javac.code.Kinds;
+import com.sun.tools.javac.tree.TreeScanner;
+
+import static com.sun.source.util.TaskEvent.Kind;
+import static com.sun.tools.javac.code.Flags.*;
+import static com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+
+public class MutableFieldsAnalyzer extends AbstractCodingRulesAnalyzer {
+
+ public MutableFieldsAnalyzer() {
+ treeVisitor = new MutableFieldsVisitor();
+ eventKind = Kind.ANALYZE;
+ }
+
+ public String getName() {
+ return "mutable_fields_analyzer";
+ }
+
+ private boolean ignoreField(String className, String field) {
+ List<String> currentFieldsToIgnore =
+ classFieldsToIgnoreMap.get(className);
+ if (currentFieldsToIgnore != null) {
+ for (String fieldToIgnore : currentFieldsToIgnore) {
+ if (field.equals(fieldToIgnore)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ class MutableFieldsVisitor extends TreeScanner {
+
+ @Override
+ public void visitVarDef(JCVariableDecl tree) {
+ boolean isJavacPack = tree.sym.outermostClass().fullname.toString()
+ .contains(packageToCheck);
+ if (isJavacPack &&
+ (tree.sym.flags() & SYNTHETIC) == 0 &&
+ tree.sym.owner.kind == Kinds.TYP) {
+ if (!ignoreField(tree.sym.owner.flatName().toString(),
+ tree.getName().toString())) {
+ boolean enumClass = (tree.sym.owner.flags() & ENUM) != 0;
+ boolean nonFinalStaticEnumField =
+ (tree.sym.flags() & (ENUM | FINAL)) == 0;
+ boolean nonFinalStaticField =
+ (tree.sym.flags() & STATIC) != 0 &&
+ (tree.sym.flags() & FINAL) == 0;
+ if (enumClass ? nonFinalStaticEnumField : nonFinalStaticField) {
+ messages.error(tree, "crules.err.var.must.be.final", tree);
+ }
+ }
+ }
+ super.visitVarDef(tree);
+ }
+
+ }
+
+ private static final String packageToCheck = "com.sun.tools.javac";
+
+ private static final Map<String, List<String>> classFieldsToIgnoreMap =
+ new HashMap<String, List<String>>();
+
+ static {
+ classFieldsToIgnoreMap.
+ put("com.sun.tools.javac.util.JCDiagnostic",
+ Arrays.asList("fragmentFormatter"));
+ classFieldsToIgnoreMap.
+ put("com.sun.tools.javac.util.JavacMessages",
+ Arrays.asList("defaultBundle", "defaultMessages"));
+ classFieldsToIgnoreMap.
+ put("com.sun.tools.javac.file.ZipFileIndexCache",
+ Arrays.asList("sharedInstance"));
+ classFieldsToIgnoreMap.
+ put("com.sun.tools.javac.main.JavaCompiler",
+ Arrays.asList("versionRB"));
+ classFieldsToIgnoreMap.
+ put("com.sun.tools.javac.code.Type",
+ Arrays.asList("moreInfo"));
+ classFieldsToIgnoreMap.
+ put("com.sun.tools.javac.util.SharedNameTable",
+ Arrays.asList("freelist"));
+ classFieldsToIgnoreMap.
+ put("com.sun.tools.javac.util.Log",
+ Arrays.asList("useRawMessages"));
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/make/tools/crules/resources/crules.properties Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+# 0: symbol
+crules.err.var.must.be.final=\
+ Static variable {0} must be final
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/source/util/DocSourcePositions.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,98 @@
+/*
+ * 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 com.sun.source.util;
+
+import com.sun.source.doctree.DocCommentTree;
+import com.sun.source.doctree.DocTree;
+import com.sun.source.tree.CompilationUnitTree;
+
+/**
+ * Provides methods to obtain the position of a DocTree within a javadoc comment.
+ * A position is defined as a simple character offset from the start of a
+ * CompilationUnit where the first character is at offset 0.
+ *
+ * @since 1.8
+ */
+@jdk.Supported
+public interface DocSourcePositions extends SourcePositions {
+
+ /**
+ * Gets the starting position of the tree within the comment within the file. If tree is not found within
+ * file, or if the starting position is not available,
+ * return {@link javax.tools.Diagnostic#NOPOS}.
+ * The given tree should be under the given comment tree, and the given documentation
+ * comment tree should be returned from a {@link DocTrees#getDocCommentTree(com.sun.source.util.TreePath) }
+ * for a tree under the given file.
+ * The returned position must be at the start of the yield of this tree, that
+ * is for any sub-tree of this tree, the following must hold:
+ *
+ * <p>
+ * {@code tree.getStartPosition() <= subtree.getStartPosition()} or <br>
+ * {@code tree.getStartPosition() == NOPOS} or <br>
+ * {@code subtree.getStartPosition() == NOPOS}
+ * </p>
+ *
+ * @param file CompilationUnit in which to find tree.
+ * @param comment the comment tree that encloses the tree for which the
+ * position is being sought
+ * @param tree tree for which a position is sought.
+ * @return the start position of tree.
+ */
+ long getStartPosition(CompilationUnitTree file, DocCommentTree comment, DocTree tree);
+
+ /**
+ * Gets the ending position of the tree within the comment within the file. If tree is not found within
+ * file, or if the ending position is not available,
+ * return {@link javax.tools.Diagnostic#NOPOS}.
+ * The given tree should be under the given comment tree, and the given documentation
+ * comment tree should be returned from a {@link DocTrees#getDocCommentTree(com.sun.source.util.TreePath) }
+ * for a tree under the given file.
+ * The returned position must be at the end of the yield of this tree,
+ * that is for any sub-tree of this tree, the following must hold:
+ *
+ * <p>
+ * {@code tree.getEndPosition() >= subtree.getEndPosition()} or <br>
+ * {@code tree.getEndPosition() == NOPOS} or <br>
+ * {@code subtree.getEndPosition() == NOPOS}
+ * </p>
+ *
+ * In addition, the following must hold:
+ *
+ * <p>
+ * {@code tree.getStartPosition() <= tree.getEndPosition()} or <br>
+ * {@code tree.getStartPosition() == NOPOS} or <br>
+ * {@code tree.getEndPosition() == NOPOS}
+ * </p>
+ *
+ * @param file CompilationUnit in which to find tree.
+ * @param comment the comment tree that encloses the tree for which the
+ * position is being sought
+ * @param tree tree for which a position is sought.
+ * @return the start position of tree.
+ */
+ long getEndPosition(CompilationUnitTree file, DocCommentTree comment, DocTree tree);
+
+}
--- a/langtools/src/share/classes/com/sun/source/util/DocTrees.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/source/util/DocTrees.java Thu May 16 11:47:51 2013 +0100
@@ -72,6 +72,8 @@
*/
public abstract Element getElement(TreePath path, ReferenceTree reference);
+ public abstract DocSourcePositions getSourcePositions();
+
/**
* Prints a message of the specified kind at the location of the
* tree within the provided compilation unit
--- a/langtools/src/share/classes/com/sun/source/util/SourcePositions.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/source/util/SourcePositions.java Thu May 16 11:47:51 2013 +0100
@@ -59,7 +59,7 @@
/**
* Gets the ending position of tree within file. If tree is not found within
- * file, or if the starting position is not available,
+ * file, or if the ending position is not available,
* return {@link javax.tools.Diagnostic#NOPOS}.
* The returned position must be at the end of the yield of this tree,
* that is for any sub-tree of this tree, the following must hold:
--- a/langtools/src/share/classes/com/sun/tools/classfile/Dependencies.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/classfile/Dependencies.java Thu May 16 11:47:51 2013 +0100
@@ -315,7 +315,7 @@
static class SimpleLocation implements Location {
public SimpleLocation(String name) {
this.name = name;
- this.className = name.replace('/', '.').replace('$', '.');
+ this.className = name.replace('/', '.');
}
public String getName() {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/classfile/ReferenceFinder.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,240 @@
+/*
+ * 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 com.sun.tools.classfile;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import com.sun.tools.classfile.Instruction.TypeKind;
+import static com.sun.tools.classfile.ConstantPool.*;
+
+/**
+ * A utility class to find where in a ClassFile references
+ * a {@link CONSTANT_Methodref_info method},
+ * a {@link CONSTANT_InterfaceMethodref_info interface method,
+ * or a {@link CONSTANT_Fieldref_info field}.
+ */
+public final class ReferenceFinder {
+ /**
+ * Filter for ReferenceFinder of what constant pool entries for reference lookup.
+ */
+ public interface Filter {
+ /**
+ * Decides if the given CPRefInfo entry should be accepted or filtered.
+ *
+ * @param cpool ConstantPool of the ClassFile being parsed
+ * @param cpref constant pool entry representing a reference to
+ * a fields method, and interface method.
+ * @return {@code true} if accepted; otherwise {@code false}
+ */
+ boolean accept(ConstantPool cpool, CPRefInfo cpref);
+ }
+
+ /**
+ * Visitor of individual method of a ClassFile that references the
+ * accepted field, method, or interface method references.
+ */
+ public interface Visitor {
+ /**
+ * Invoked for a method containing one or more accepted CPRefInfo entries
+ *
+ * @param cf ClassFile
+ * @param method Method that does the references the accepted references
+ * @param refs Accepted constant pool method/field reference
+ */
+ void visit(ClassFile cf, Method method, List<CPRefInfo> refConstantPool);
+ }
+
+ private final Filter filter;
+ private final Visitor visitor;
+
+ /**
+ * Constructor.
+ */
+ public ReferenceFinder(Filter filter, Visitor visitor) {
+ this.filter = Objects.requireNonNull(filter);
+ this.visitor = Objects.requireNonNull(visitor);
+ }
+
+ /**
+ * Parses a given ClassFile and invoke the visitor if there is any reference
+ * to the constant pool entries referencing field, method, or
+ * interface method that are accepted. This method will return
+ * {@code true} if there is one or more accepted constant pool entries
+ * to lookup; otherwise, it will return {@code false}.
+ *
+ * @param cf ClassFile
+ * @return {@code true} if the given class file is processed to lookup
+ * references
+ * @throws ConstantPoolException if an error of the constant pool
+ */
+ public boolean parse(ClassFile cf) throws ConstantPoolException {
+ List<Integer> cprefs = new ArrayList<Integer>();
+ int index = 1;
+ for (ConstantPool.CPInfo cpInfo : cf.constant_pool.entries()) {
+ if (cpInfo.accept(cpVisitor, cf.constant_pool)) {
+ cprefs.add(index);
+ }
+ index += cpInfo.size();
+ }
+
+ if (cprefs.isEmpty()) {
+ return false;
+ }
+
+ for (Method m : cf.methods) {
+ Set<Integer> ids = new HashSet<Integer>();
+ 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) {
+ ids.add(idx);
+ }
+ }
+ }
+ if (ids.size() > 0) {
+ List<CPRefInfo> refInfos = new ArrayList<CPRefInfo>(ids.size());
+ for (int id : ids) {
+ refInfos.add(CPRefInfo.class.cast(cf.constant_pool.get(id)));
+ }
+ visitor.visit(cf, m, refInfos);
+ }
+ }
+ return true;
+ }
+
+ private ConstantPool.Visitor<Boolean,ConstantPool> cpVisitor =
+ new ConstantPool.Visitor<Boolean,ConstantPool>()
+ {
+ public Boolean visitClass(CONSTANT_Class_info info, ConstantPool cpool) {
+ return false;
+ }
+
+ public Boolean visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, ConstantPool cpool) {
+ return filter.accept(cpool, info);
+ }
+
+ public Boolean visitMethodref(CONSTANT_Methodref_info info, ConstantPool cpool) {
+ return filter.accept(cpool, info);
+ }
+
+ public Boolean visitFieldref(CONSTANT_Fieldref_info info, ConstantPool cpool) {
+ return filter.accept(cpool, info);
+ }
+
+ public Boolean visitDouble(CONSTANT_Double_info info, ConstantPool cpool) {
+ return false;
+ }
+
+ public Boolean visitFloat(CONSTANT_Float_info info, ConstantPool cpool) {
+ return false;
+ }
+
+ public Boolean visitInteger(CONSTANT_Integer_info info, ConstantPool cpool) {
+ return false;
+ }
+
+ public Boolean visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, ConstantPool cpool) {
+ return false;
+ }
+
+ public Boolean visitLong(CONSTANT_Long_info info, ConstantPool cpool) {
+ return false;
+ }
+
+ public Boolean visitNameAndType(CONSTANT_NameAndType_info info, ConstantPool cpool) {
+ return false;
+ }
+
+ public Boolean visitMethodHandle(CONSTANT_MethodHandle_info info, ConstantPool cpool) {
+ return false;
+ }
+
+ public Boolean visitMethodType(CONSTANT_MethodType_info info, ConstantPool cpool) {
+ return false;
+ }
+
+ public Boolean visitString(CONSTANT_String_info info, ConstantPool cpool) {
+ return false;
+ }
+
+ public Boolean visitUtf8(CONSTANT_Utf8_info info, ConstantPool cpool) {
+ return false;
+ }
+ };
+
+ private Instruction.KindVisitor<Integer, List<Integer>> codeVisitor =
+ new Instruction.KindVisitor<Integer, List<Integer>>()
+ {
+ public Integer visitNoOperands(Instruction instr, List<Integer> p) {
+ return 0;
+ }
+
+ public Integer visitArrayType(Instruction instr, TypeKind kind, List<Integer> p) {
+ return 0;
+ }
+
+ public Integer visitBranch(Instruction instr, int offset, List<Integer> p) {
+ return 0;
+ }
+
+ public Integer visitConstantPoolRef(Instruction instr, int index, List<Integer> p) {
+ return p.contains(index) ? index : 0;
+ }
+
+ public Integer visitConstantPoolRefAndValue(Instruction instr, int index, int value, List<Integer> p) {
+ return p.contains(index) ? index : 0;
+ }
+
+ public Integer visitLocal(Instruction instr, int index, List<Integer> p) {
+ return 0;
+ }
+
+ public Integer visitLocalAndValue(Instruction instr, int index, int value, List<Integer> p) {
+ return 0;
+ }
+
+ public Integer visitLookupSwitch(Instruction instr, int default_, int npairs, int[] matches, int[] offsets, List<Integer> p) {
+ return 0;
+ }
+
+ public Integer visitTableSwitch(Instruction instr, int default_, int low, int high, int[] offsets, List<Integer> p) {
+ return 0;
+ }
+
+ public Integer visitValue(Instruction instr, int value, List<Integer> p) {
+ return 0;
+ }
+
+ public Integer visitUnknown(Instruction instr, List<Integer> p) {
+ return 0;
+ }
+ };
+}
+
--- a/langtools/src/share/classes/com/sun/tools/javac/api/JavacTrees.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/api/JavacTrees.java Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2012, 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
@@ -43,14 +43,16 @@
import javax.tools.JavaFileObject;
import com.sun.source.doctree.DocCommentTree;
+import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.ReferenceTree;
import com.sun.source.tree.CatchTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.Scope;
import com.sun.source.tree.Tree;
+import com.sun.source.util.DocSourcePositions;
+import com.sun.source.util.DocTreeScanner;
import com.sun.source.util.DocTrees;
import com.sun.source.util.JavacTask;
-import com.sun.source.util.SourcePositions;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Kinds;
@@ -76,8 +78,14 @@
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.DCTree;
+import com.sun.tools.javac.tree.DCTree.DCBlockTag;
import com.sun.tools.javac.tree.DCTree.DCDocComment;
+import com.sun.tools.javac.tree.DCTree.DCEndPosTree;
+import com.sun.tools.javac.tree.DCTree.DCErroneous;
+import com.sun.tools.javac.tree.DCTree.DCIdentifier;
+import com.sun.tools.javac.tree.DCTree.DCParam;
import com.sun.tools.javac.tree.DCTree.DCReference;
+import com.sun.tools.javac.tree.DCTree.DCText;
import com.sun.tools.javac.tree.EndPosTable;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.*;
@@ -94,6 +102,7 @@
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
import com.sun.tools.javac.util.Pair;
+import com.sun.tools.javac.util.Position;
import static com.sun.tools.javac.code.TypeTag.*;
/**
@@ -166,8 +175,8 @@
javacTaskImpl = (JavacTaskImpl) t;
}
- public SourcePositions getSourcePositions() {
- return new SourcePositions() {
+ public DocSourcePositions getSourcePositions() {
+ return new DocSourcePositions() {
public long getStartPosition(CompilationUnitTree file, Tree tree) {
return TreeInfo.getStartPos((JCTree) tree);
}
@@ -176,9 +185,80 @@
EndPosTable endPosTable = ((JCCompilationUnit) file).endPositions;
return TreeInfo.getEndPos((JCTree) tree, endPosTable);
}
+
+ public long getStartPosition(CompilationUnitTree file, DocCommentTree comment, DocTree tree) {
+ return ((DCTree) tree).getSourcePosition((DCDocComment) comment);
+ }
+ @SuppressWarnings("fallthrough")
+ public long getEndPosition(CompilationUnitTree file, DocCommentTree comment, DocTree tree) {
+ DCDocComment dcComment = (DCDocComment) comment;
+ if (tree instanceof DCEndPosTree) {
+ int endPos = ((DCEndPosTree) tree).getEndPos(dcComment);
+
+ if (endPos != Position.NOPOS) {
+ return endPos;
+ }
+ }
+ int correction = 0;
+ switch (tree.getKind()) {
+ case TEXT:
+ DCText text = (DCText) tree;
+
+ return dcComment.comment.getSourcePos(text.pos + text.text.length());
+ case ERRONEOUS:
+ DCErroneous err = (DCErroneous) tree;
+
+ return dcComment.comment.getSourcePos(err.pos + err.body.length());
+ case IDENTIFIER:
+ DCIdentifier ident = (DCIdentifier) tree;
+
+ return dcComment.comment.getSourcePos(ident.pos + (ident.name != names.error ? ident.name.length() : 0));
+ case PARAM:
+ DCParam param = (DCParam) tree;
+
+ if (param.isTypeParameter && param.getDescription().isEmpty()) {
+ correction = 1;
+ }
+ case AUTHOR: case DEPRECATED: case RETURN: case SEE:
+ case SERIAL: case SERIAL_DATA: case SERIAL_FIELD: case SINCE:
+ case THROWS: case UNKNOWN_BLOCK_TAG: case VERSION: {
+ DocTree last = getLastChild(tree);
+
+ if (last != null) {
+ return getEndPosition(file, comment, last) + correction;
+ }
+
+ DCBlockTag block = (DCBlockTag) tree;
+
+ return dcComment.comment.getSourcePos(block.pos + block.getTagName().length() + 1);
+ }
+ default:
+ DocTree last = getLastChild(tree);
+
+ if (last != null) {
+ return getEndPosition(file, comment, last);
+ }
+ break;
+ }
+
+ return Position.NOPOS;
+ }
};
}
+ private DocTree getLastChild(DocTree tree) {
+ final DocTree[] last = new DocTree[] {null};
+
+ tree.accept(new DocTreeScanner<Void, Void>() {
+ @Override public Void scan(DocTree node, Void p) {
+ if (node != null) last[0] = node;
+ return null;
+ }
+ }, null);
+
+ return last[0];
+ }
+
public JCClassDecl getTree(TypeElement element) {
return (JCClassDecl) getTree((Element) element);
}
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java Thu May 16 11:47:51 2013 +0100
@@ -465,19 +465,12 @@
* This is the implementation for {@code
* javax.lang.model.element.Element.getAnnotationMirrors()}.
*/
- public final List<? extends AnnotationMirror> getAnnotationMirrors() {
+ @Override
+ public List<Attribute.Compound> getAnnotationMirrors() {
return getRawAttributes();
}
/**
- * TODO: Should there be a {@code
- * javax.lang.model.element.Element.getTypeAnnotationMirrors()}.
- */
- public final List<Attribute.TypeCompound> getTypeAnnotationMirrors() {
- return getRawTypeAttributes();
- }
-
- /**
* @deprecated this method should never be used by javac internally.
*/
@Deprecated
@@ -657,6 +650,24 @@
}
@Override
+ public List<Attribute.Compound> getAnnotationMirrors() {
+ return onlyTypeVariableAnnotations(owner.getRawTypeAttributes());
+ }
+
+ private List<Attribute.Compound> onlyTypeVariableAnnotations(
+ List<Attribute.TypeCompound> candidates) {
+ // Declaration annotations on TypeParameters are stored in type attributes
+ List<Attribute.Compound> res = List.nil();
+ for (Attribute.TypeCompound a : candidates) {
+ if (a.position.type == TargetType.CLASS_TYPE_PARAMETER ||
+ a.position.type == TargetType.METHOD_TYPE_PARAMETER)
+ res = res.prepend(a);
+ }
+
+ return res = res.reverse();
+ }
+
+ @Override
public <R, P> R accept(ElementVisitor<R, P> v, P p) {
return v.visitTypeParameter(this, p);
}
--- a/langtools/src/share/classes/com/sun/tools/javac/code/TypeAnnotations.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/TypeAnnotations.java Thu May 16 11:47:51 2013 +0100
@@ -206,7 +206,7 @@
sym.getKind() == ElementKind.EXCEPTION_PARAMETER) {
// Make sure all type annotations from the symbol are also
// on the owner.
- sym.owner.annotations.appendUniqueTypes(sym.getTypeAnnotationMirrors());
+ sym.owner.annotations.appendUniqueTypes(sym.getRawTypeAttributes());
}
}
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java Thu May 16 11:47:51 2013 +0100
@@ -1078,7 +1078,8 @@
mask = MethodFlags;
}
// Imply STRICTFP if owner has STRICTFP set.
- if (((flags|implicit) & Flags.ABSTRACT) == 0)
+ if (((flags|implicit) & Flags.ABSTRACT) == 0 ||
+ ((flags) & Flags.DEFAULT) != 0)
implicit |= sym.owner.flags_field & STRICTFP;
break;
case TYP:
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java Thu May 16 11:47:51 2013 +0100
@@ -35,7 +35,6 @@
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.code.Symbol.*;
-import com.sun.tools.javac.comp.Resolve;
import com.sun.tools.javac.tree.JCTree.*;
import static com.sun.tools.javac.code.Flags.*;
@@ -277,6 +276,15 @@
}
/**
+ * Utility method to reset several Bits instances.
+ */
+ private void resetBits(Bits... bits) {
+ for (Bits b : bits) {
+ b.reset();
+ }
+ }
+
+ /**
* Base visitor class for all visitors implementing dataflow analysis logic.
* This class define the shared logic for handling jumps (break/continue statements).
*/
@@ -1294,11 +1302,11 @@
/** The set of definitely assigned variables.
*/
- Bits inits;
+ final Bits inits;
/** The set of definitely unassigned variables.
*/
- Bits uninits;
+ final Bits uninits;
/** The set of variables that are definitely unassigned everywhere
* in current try block. This variable is maintained lazily; it is
@@ -1308,15 +1316,15 @@
* anywhere in current try block, intersect uninitsTry and
* uninits.
*/
- Bits uninitsTry;
+ final Bits uninitsTry;
/** When analyzing a condition, inits and uninits are null.
* Instead we have:
*/
- Bits initsWhenTrue;
- Bits initsWhenFalse;
- Bits uninitsWhenTrue;
- Bits uninitsWhenFalse;
+ final Bits initsWhenTrue;
+ final Bits initsWhenFalse;
+ final Bits uninitsWhenTrue;
+ final Bits uninitsWhenFalse;
/** A mapping from addresses to variable symbols.
*/
@@ -1348,15 +1356,25 @@
/** The starting position of the analysed tree */
int startPos;
+ AssignAnalyzer() {
+ inits = new Bits();
+ uninits = new Bits();
+ uninitsTry = new Bits();
+ initsWhenTrue = new Bits(true);
+ initsWhenFalse = new Bits(true);
+ uninitsWhenTrue = new Bits(true);
+ uninitsWhenFalse = new Bits(true);
+ }
+
class AssignPendingExit extends BaseAnalyzer.PendingExit {
- Bits exit_inits;
- Bits exit_uninits;
+ final Bits exit_inits = new Bits(true);
+ final Bits exit_uninits = new Bits(true);
- AssignPendingExit(JCTree tree, Bits inits, Bits uninits) {
+ AssignPendingExit(JCTree tree, final Bits inits, final Bits uninits) {
super(tree);
- this.exit_inits = inits.dup();
- this.exit_uninits = uninits.dup();
+ this.exit_inits.assign(inits);
+ this.exit_uninits.assign(uninits);
}
void resolveJump() {
@@ -1476,19 +1494,20 @@
/** Split (duplicate) inits/uninits into WhenTrue/WhenFalse sets
*/
void split(boolean setToNull) {
- initsWhenFalse = inits.dup();
- uninitsWhenFalse = uninits.dup();
- initsWhenTrue = inits;
- uninitsWhenTrue = uninits;
- if (setToNull)
- inits = uninits = null;
+ initsWhenFalse.assign(inits);
+ uninitsWhenFalse.assign(uninits);
+ initsWhenTrue.assign(inits);
+ uninitsWhenTrue.assign(uninits);
+ if (setToNull) {
+ resetBits(inits, uninits);
+ }
}
/** Merge (intersect) inits/uninits from WhenTrue/WhenFalse sets.
*/
void merge() {
- inits = initsWhenFalse.andSet(initsWhenTrue);
- uninits = uninitsWhenFalse.andSet(uninitsWhenTrue);
+ inits.assign(initsWhenFalse.andSet(initsWhenTrue));
+ uninits.assign(uninitsWhenFalse.andSet(uninitsWhenTrue));
}
/* ************************************************************************
@@ -1501,7 +1520,7 @@
void scanExpr(JCTree tree) {
if (tree != null) {
scan(tree);
- if (inits == null) merge();
+ if (inits.isReset()) merge();
}
}
@@ -1518,28 +1537,29 @@
*/
void scanCond(JCTree tree) {
if (tree.type.isFalse()) {
- if (inits == null) merge();
- initsWhenTrue = inits.dup();
+ if (inits.isReset()) merge();
+ initsWhenTrue.assign(inits);
initsWhenTrue.inclRange(firstadr, nextadr);
- uninitsWhenTrue = uninits.dup();
+ uninitsWhenTrue.assign(uninits);
uninitsWhenTrue.inclRange(firstadr, nextadr);
- initsWhenFalse = inits;
- uninitsWhenFalse = uninits;
+ initsWhenFalse.assign(inits);
+ uninitsWhenFalse.assign(uninits);
} else if (tree.type.isTrue()) {
- if (inits == null) merge();
- initsWhenFalse = inits.dup();
+ if (inits.isReset()) merge();
+ initsWhenFalse.assign(inits);
initsWhenFalse.inclRange(firstadr, nextadr);
- uninitsWhenFalse = uninits.dup();
+ uninitsWhenFalse.assign(uninits);
uninitsWhenFalse.inclRange(firstadr, nextadr);
- initsWhenTrue = inits;
- uninitsWhenTrue = uninits;
+ initsWhenTrue.assign(inits);
+ uninitsWhenTrue.assign(uninits);
} else {
scan(tree);
- if (inits != null)
+ if (!inits.isReset())
split(tree.type != syms.unknownType);
}
- if (tree.type != syms.unknownType)
- inits = uninits = null;
+ if (tree.type != syms.unknownType) {
+ resetBits(inits, uninits);
+ }
}
/* ------------ Visitor methods for various sorts of trees -------------*/
@@ -1619,8 +1639,8 @@
public void visitMethodDef(JCMethodDecl tree) {
if (tree.body == null) return;
- Bits initsPrev = inits.dup();
- Bits uninitsPrev = uninits.dup();
+ final Bits initsPrev = new Bits(inits);
+ final Bits uninitsPrev = new Bits(uninits);
int nextadrPrev = nextadr;
int firstadrPrev = firstadr;
int returnadrPrev = returnadr;
@@ -1658,14 +1678,14 @@
exits = exits.tail;
Assert.check(exit.tree.hasTag(RETURN), exit.tree);
if (isInitialConstructor) {
- inits = exit.exit_inits;
+ inits.assign(exit.exit_inits);
for (int i = firstadr; i < nextadr; i++)
checkInit(exit.tree.pos(), vars[i]);
}
}
} finally {
- inits = initsPrev;
- uninits = uninitsPrev;
+ inits.assign(initsPrev);
+ uninits.assign(uninitsPrev);
nextadr = nextadrPrev;
firstadr = firstadrPrev;
returnadr = returnadrPrev;
@@ -1698,31 +1718,31 @@
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
FlowKind prevFlowKind = flowKind;
flowKind = FlowKind.NORMAL;
- Bits initsSkip = null;
- Bits uninitsSkip = null;
+ final Bits initsSkip = new Bits(true);
+ final Bits uninitsSkip = new Bits(true);
pendingExits = new ListBuffer<AssignPendingExit>();
int prevErrors = log.nerrors;
do {
- Bits uninitsEntry = uninits.dup();
+ final Bits uninitsEntry = new Bits(uninits);
uninitsEntry.excludeFrom(nextadr);
scan(tree.body);
resolveContinues(tree);
scanCond(tree.cond);
if (!flowKind.isFinal()) {
- initsSkip = initsWhenFalse;
- uninitsSkip = uninitsWhenFalse;
+ initsSkip.assign(initsWhenFalse);
+ uninitsSkip.assign(uninitsWhenFalse);
}
if (log.nerrors != prevErrors ||
flowKind.isFinal() ||
- uninitsEntry.dup().diffSet(uninitsWhenTrue).nextBit(firstadr)==-1)
+ new Bits(uninitsEntry).diffSet(uninitsWhenTrue).nextBit(firstadr)==-1)
break;
- inits = initsWhenTrue;
- uninits = uninitsEntry.andSet(uninitsWhenTrue);
+ inits.assign(initsWhenTrue);
+ uninits.assign(uninitsEntry.andSet(uninitsWhenTrue));
flowKind = FlowKind.SPECULATIVE_LOOP;
} while (true);
flowKind = prevFlowKind;
- inits = initsSkip;
- uninits = uninitsSkip;
+ inits.assign(initsSkip);
+ uninits.assign(uninitsSkip);
resolveBreaks(tree, prevPendingExits);
}
@@ -1730,34 +1750,34 @@
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
FlowKind prevFlowKind = flowKind;
flowKind = FlowKind.NORMAL;
- Bits initsSkip = null;
- Bits uninitsSkip = null;
+ final Bits initsSkip = new Bits(true);
+ final Bits uninitsSkip = new Bits(true);
pendingExits = new ListBuffer<AssignPendingExit>();
int prevErrors = log.nerrors;
- Bits uninitsEntry = uninits.dup();
+ final Bits uninitsEntry = new Bits(uninits);
uninitsEntry.excludeFrom(nextadr);
do {
scanCond(tree.cond);
if (!flowKind.isFinal()) {
- initsSkip = initsWhenFalse;
- uninitsSkip = uninitsWhenFalse;
+ initsSkip.assign(initsWhenFalse) ;
+ uninitsSkip.assign(uninitsWhenFalse);
}
- inits = initsWhenTrue;
- uninits = uninitsWhenTrue;
+ inits.assign(initsWhenTrue);
+ uninits.assign(uninitsWhenTrue);
scan(tree.body);
resolveContinues(tree);
if (log.nerrors != prevErrors ||
flowKind.isFinal() ||
- uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
+ new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1)
break;
- uninits = uninitsEntry.andSet(uninits);
+ uninits.assign(uninitsEntry.andSet(uninits));
flowKind = FlowKind.SPECULATIVE_LOOP;
} while (true);
flowKind = prevFlowKind;
//a variable is DA/DU after the while statement, if it's DA/DU assuming the
//branch is not taken AND if it's DA/DU before any break statement
- inits = initsSkip;
- uninits = uninitsSkip;
+ inits.assign(initsSkip);
+ uninits.assign(uninitsSkip);
resolveBreaks(tree, prevPendingExits);
}
@@ -1767,25 +1787,25 @@
flowKind = FlowKind.NORMAL;
int nextadrPrev = nextadr;
scan(tree.init);
- Bits initsSkip = null;
- Bits uninitsSkip = null;
+ final Bits initsSkip = new Bits(true);
+ final Bits uninitsSkip = new Bits(true);
pendingExits = new ListBuffer<AssignPendingExit>();
int prevErrors = log.nerrors;
do {
- Bits uninitsEntry = uninits.dup();
+ final Bits uninitsEntry = new Bits(uninits);
uninitsEntry.excludeFrom(nextadr);
if (tree.cond != null) {
scanCond(tree.cond);
if (!flowKind.isFinal()) {
- initsSkip = initsWhenFalse;
- uninitsSkip = uninitsWhenFalse;
+ initsSkip.assign(initsWhenFalse);
+ uninitsSkip.assign(uninitsWhenFalse);
}
- inits = initsWhenTrue;
- uninits = uninitsWhenTrue;
+ inits.assign(initsWhenTrue);
+ uninits.assign(uninitsWhenTrue);
} else if (!flowKind.isFinal()) {
- initsSkip = inits.dup();
+ initsSkip.assign(inits);
initsSkip.inclRange(firstadr, nextadr);
- uninitsSkip = uninits.dup();
+ uninitsSkip.assign(uninits);
uninitsSkip.inclRange(firstadr, nextadr);
}
scan(tree.body);
@@ -1793,16 +1813,16 @@
scan(tree.step);
if (log.nerrors != prevErrors ||
flowKind.isFinal() ||
- uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
+ new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1)
break;
- uninits = uninitsEntry.andSet(uninits);
+ uninits.assign(uninitsEntry.andSet(uninits));
flowKind = FlowKind.SPECULATIVE_LOOP;
} while (true);
flowKind = prevFlowKind;
//a variable is DA/DU after a for loop, if it's DA/DU assuming the
//branch is not taken AND if it's DA/DU before any break statement
- inits = initsSkip;
- uninits = uninitsSkip;
+ inits.assign(initsSkip);
+ uninits.assign(uninitsSkip);
resolveBreaks(tree, prevPendingExits);
nextadr = nextadrPrev;
}
@@ -1815,27 +1835,27 @@
flowKind = FlowKind.NORMAL;
int nextadrPrev = nextadr;
scan(tree.expr);
- Bits initsStart = inits.dup();
- Bits uninitsStart = uninits.dup();
+ final Bits initsStart = new Bits(inits);
+ final Bits uninitsStart = new Bits(uninits);
letInit(tree.pos(), tree.var.sym);
pendingExits = new ListBuffer<AssignPendingExit>();
int prevErrors = log.nerrors;
do {
- Bits uninitsEntry = uninits.dup();
+ final Bits uninitsEntry = new Bits(uninits);
uninitsEntry.excludeFrom(nextadr);
scan(tree.body);
resolveContinues(tree);
if (log.nerrors != prevErrors ||
flowKind.isFinal() ||
- uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1)
+ new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1)
break;
- uninits = uninitsEntry.andSet(uninits);
+ uninits.assign(uninitsEntry.andSet(uninits));
flowKind = FlowKind.SPECULATIVE_LOOP;
} while (true);
flowKind = prevFlowKind;
- inits = initsStart;
- uninits = uninitsStart.andSet(uninits);
+ inits.assign(initsStart);
+ uninits.assign(uninitsStart.andSet(uninits));
resolveBreaks(tree, prevPendingExits);
nextadr = nextadrPrev;
}
@@ -1852,12 +1872,12 @@
pendingExits = new ListBuffer<AssignPendingExit>();
int nextadrPrev = nextadr;
scanExpr(tree.selector);
- Bits initsSwitch = inits;
- Bits uninitsSwitch = uninits.dup();
+ final Bits initsSwitch = new Bits(inits);
+ final Bits uninitsSwitch = new Bits(uninits);
boolean hasDefault = false;
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
- inits = initsSwitch.dup();
- uninits = uninits.andSet(uninitsSwitch);
+ inits.assign(initsSwitch);
+ uninits.assign(uninits.andSet(uninitsSwitch));
JCCase c = l.head;
if (c.pat == null)
hasDefault = true;
@@ -1875,8 +1895,8 @@
}
// where
/** Add any variables defined in stats to inits and uninits. */
- private void addVars(List<JCStatement> stats, Bits inits,
- Bits uninits) {
+ private void addVars(List<JCStatement> stats, final Bits inits,
+ final Bits uninits) {
for (;stats.nonEmpty(); stats = stats.tail) {
JCTree stat = stats.head;
if (stat.hasTag(VARDEF)) {
@@ -1889,11 +1909,11 @@
public void visitTry(JCTry tree) {
ListBuffer<JCVariableDecl> resourceVarDecls = ListBuffer.lb();
- Bits uninitsTryPrev = uninitsTry;
+ final Bits uninitsTryPrev = new Bits(uninitsTry);
ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<AssignPendingExit>();
- Bits initsTry = inits.dup();
- uninitsTry = uninits.dup();
+ final Bits initsTry = new Bits(inits);
+ uninitsTry.assign(uninits);
for (JCTree resource : tree.resources) {
if (resource instanceof JCVariableDecl) {
JCVariableDecl vdecl = (JCVariableDecl) resource;
@@ -1908,8 +1928,8 @@
}
scan(tree.body);
uninitsTry.andSet(uninits);
- Bits initsEnd = inits;
- Bits uninitsEnd = uninits;
+ final Bits initsEnd = new Bits(inits);
+ final Bits uninitsEnd = new Bits(uninits);
int nextadrCatch = nextadr;
if (!resourceVarDecls.isEmpty() &&
@@ -1925,8 +1945,8 @@
for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
JCVariableDecl param = l.head.param;
- inits = initsTry.dup();
- uninits = uninitsTry.dup();
+ inits.assign(initsTry);
+ uninits.assign(uninitsTry);
scan(param);
inits.incl(param.sym.adr);
uninits.excl(param.sym.adr);
@@ -1936,8 +1956,8 @@
nextadr = nextadrCatch;
}
if (tree.finalizer != null) {
- inits = initsTry.dup();
- uninits = uninitsTry.dup();
+ inits.assign(initsTry);
+ uninits.assign(uninitsTry);
ListBuffer<AssignPendingExit> exits = pendingExits;
pendingExits = prevPendingExits;
scan(tree.finalizer);
@@ -1958,8 +1978,8 @@
inits.orSet(initsEnd);
}
} else {
- inits = initsEnd;
- uninits = uninitsEnd;
+ inits.assign(initsEnd);
+ uninits.assign(uninitsEnd);
ListBuffer<AssignPendingExit> exits = pendingExits;
pendingExits = prevPendingExits;
while (exits.nonEmpty()) pendingExits.append(exits.next());
@@ -1969,10 +1989,10 @@
public void visitConditional(JCConditional tree) {
scanCond(tree.cond);
- Bits initsBeforeElse = initsWhenFalse;
- Bits uninitsBeforeElse = uninitsWhenFalse;
- inits = initsWhenTrue;
- uninits = uninitsWhenTrue;
+ final Bits initsBeforeElse = new Bits(initsWhenFalse);
+ final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse);
+ inits.assign(initsWhenTrue);
+ uninits.assign(uninitsWhenTrue);
if (tree.truepart.type.hasTag(BOOLEAN) &&
tree.falsepart.type.hasTag(BOOLEAN)) {
// if b and c are boolean valued, then
@@ -1980,12 +2000,12 @@
// v is (un)assigned after b when true and
// v is (un)assigned after c when true
scanCond(tree.truepart);
- Bits initsAfterThenWhenTrue = initsWhenTrue.dup();
- Bits initsAfterThenWhenFalse = initsWhenFalse.dup();
- Bits uninitsAfterThenWhenTrue = uninitsWhenTrue.dup();
- Bits uninitsAfterThenWhenFalse = uninitsWhenFalse.dup();
- inits = initsBeforeElse;
- uninits = uninitsBeforeElse;
+ final Bits initsAfterThenWhenTrue = new Bits(initsWhenTrue);
+ final Bits initsAfterThenWhenFalse = new Bits(initsWhenFalse);
+ final Bits uninitsAfterThenWhenTrue = new Bits(uninitsWhenTrue);
+ final Bits uninitsAfterThenWhenFalse = new Bits(uninitsWhenFalse);
+ inits.assign(initsBeforeElse);
+ uninits.assign(uninitsBeforeElse);
scanCond(tree.falsepart);
initsWhenTrue.andSet(initsAfterThenWhenTrue);
initsWhenFalse.andSet(initsAfterThenWhenFalse);
@@ -1993,10 +2013,10 @@
uninitsWhenFalse.andSet(uninitsAfterThenWhenFalse);
} else {
scanExpr(tree.truepart);
- Bits initsAfterThen = inits.dup();
- Bits uninitsAfterThen = uninits.dup();
- inits = initsBeforeElse;
- uninits = uninitsBeforeElse;
+ final Bits initsAfterThen = new Bits(inits);
+ final Bits uninitsAfterThen = new Bits(uninits);
+ inits.assign(initsBeforeElse);
+ uninits.assign(uninitsBeforeElse);
scanExpr(tree.falsepart);
inits.andSet(initsAfterThen);
uninits.andSet(uninitsAfterThen);
@@ -2005,16 +2025,16 @@
public void visitIf(JCIf tree) {
scanCond(tree.cond);
- Bits initsBeforeElse = initsWhenFalse;
- Bits uninitsBeforeElse = uninitsWhenFalse;
- inits = initsWhenTrue;
- uninits = uninitsWhenTrue;
+ final Bits initsBeforeElse = new Bits(initsWhenFalse);
+ final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse);
+ inits.assign(initsWhenTrue);
+ uninits.assign(uninitsWhenTrue);
scan(tree.thenpart);
if (tree.elsepart != null) {
- Bits initsAfterThen = inits.dup();
- Bits uninitsAfterThen = uninits.dup();
- inits = initsBeforeElse;
- uninits = uninitsBeforeElse;
+ final Bits initsAfterThen = new Bits(inits);
+ final Bits uninitsAfterThen = new Bits(uninits);
+ inits.assign(initsBeforeElse);
+ uninits.assign(uninitsBeforeElse);
scan(tree.elsepart);
inits.andSet(initsAfterThen);
uninits.andSet(uninitsAfterThen);
@@ -2055,8 +2075,8 @@
@Override
public void visitLambda(JCLambda tree) {
- Bits prevUninits = uninits;
- Bits prevInits = inits;
+ final Bits prevUninits = new Bits(uninits);
+ final Bits prevInits = new Bits(inits);
int returnadrPrev = returnadr;
ListBuffer<AssignPendingExit> prevPending = pendingExits;
try {
@@ -2076,8 +2096,8 @@
}
finally {
returnadr = returnadrPrev;
- uninits = prevUninits;
- inits = prevInits;
+ uninits.assign(prevUninits);
+ inits.assign(prevInits);
pendingExits = prevPending;
}
}
@@ -2088,17 +2108,17 @@
}
public void visitAssert(JCAssert tree) {
- Bits initsExit = inits.dup();
- Bits uninitsExit = uninits.dup();
+ final Bits initsExit = new Bits(inits);
+ final Bits uninitsExit = new Bits(uninits);
scanCond(tree.cond);
uninitsExit.andSet(uninitsWhenTrue);
if (tree.detail != null) {
- inits = initsWhenFalse;
- uninits = uninitsWhenFalse;
+ inits.assign(initsWhenFalse);
+ uninits.assign(uninitsWhenFalse);
scanExpr(tree.detail);
}
- inits = initsExit;
- uninits = uninitsExit;
+ inits.assign(initsExit);
+ uninits.assign(uninitsExit);
}
public void visitAssign(JCAssign tree) {
@@ -2120,12 +2140,12 @@
switch (tree.getTag()) {
case NOT:
scanCond(tree.arg);
- Bits t = initsWhenFalse;
- initsWhenFalse = initsWhenTrue;
- initsWhenTrue = t;
- t = uninitsWhenFalse;
- uninitsWhenFalse = uninitsWhenTrue;
- uninitsWhenTrue = t;
+ final Bits t = new Bits(initsWhenFalse);
+ initsWhenFalse.assign(initsWhenTrue);
+ initsWhenTrue.assign(t);
+ t.assign(uninitsWhenFalse);
+ uninitsWhenFalse.assign(uninitsWhenTrue);
+ uninitsWhenTrue.assign(t);
break;
case PREINC: case POSTINC:
case PREDEC: case POSTDEC:
@@ -2141,20 +2161,20 @@
switch (tree.getTag()) {
case AND:
scanCond(tree.lhs);
- Bits initsWhenFalseLeft = initsWhenFalse;
- Bits uninitsWhenFalseLeft = uninitsWhenFalse;
- inits = initsWhenTrue;
- uninits = uninitsWhenTrue;
+ final Bits initsWhenFalseLeft = new Bits(initsWhenFalse);
+ final Bits uninitsWhenFalseLeft = new Bits(uninitsWhenFalse);
+ inits.assign(initsWhenTrue);
+ uninits.assign(uninitsWhenTrue);
scanCond(tree.rhs);
initsWhenFalse.andSet(initsWhenFalseLeft);
uninitsWhenFalse.andSet(uninitsWhenFalseLeft);
break;
case OR:
scanCond(tree.lhs);
- Bits initsWhenTrueLeft = initsWhenTrue;
- Bits uninitsWhenTrueLeft = uninitsWhenTrue;
- inits = initsWhenFalse;
- uninits = uninitsWhenFalse;
+ final Bits initsWhenTrueLeft = new Bits(initsWhenTrue);
+ final Bits uninitsWhenTrueLeft = new Bits(uninitsWhenTrue);
+ inits.assign(initsWhenFalse);
+ uninits.assign(uninitsWhenFalse);
scanCond(tree.rhs);
initsWhenTrue.andSet(initsWhenTrueLeft);
uninitsWhenTrue.andSet(uninitsWhenTrueLeft);
@@ -2200,11 +2220,7 @@
attrEnv = env;
Flow.this.make = make;
startPos = tree.pos().getStartPosition();
- inits = new Bits();
- uninits = new Bits();
- uninitsTry = new Bits();
- initsWhenTrue = initsWhenFalse =
- uninitsWhenTrue = uninitsWhenFalse = null;
+
if (vars == null)
vars = new VarSymbol[32];
else
@@ -2219,9 +2235,8 @@
} finally {
// note that recursive invocations of this method fail hard
startPos = -1;
- inits = uninits = uninitsTry = null;
- initsWhenTrue = initsWhenFalse =
- uninitsWhenTrue = uninitsWhenFalse = null;
+ resetBits(inits, uninits, uninitsTry, initsWhenTrue,
+ initsWhenFalse, uninitsWhenTrue, uninitsWhenFalse);
if (vars != null) for (int i=0; i<vars.length; i++)
vars[i] = null;
firstadr = 0;
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Thu May 16 11:47:51 2013 +0100
@@ -40,10 +40,9 @@
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
-import com.sun.tools.javac.code.Type.ClassType;
import com.sun.tools.javac.code.Type.MethodType;
import com.sun.tools.javac.code.Types;
-import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzer.*;
+import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzerPreprocessor.*;
import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector;
import com.sun.tools.javac.jvm.*;
import com.sun.tools.javac.util.*;
@@ -81,7 +80,7 @@
private Env<AttrContext> attrEnv;
/** the analyzer scanner */
- private LambdaAnalyzer analyzer;
+ private LambdaAnalyzerPreprocessor analyzer;
/** map from lambda trees to translation contexts */
private Map<JCTree, TranslationContext<?>> contextMap;
@@ -156,7 +155,7 @@
make = TreeMaker.instance(context);
types = Types.instance(context);
transTypes = TransTypes.instance(context);
- analyzer = new LambdaAnalyzer();
+ analyzer = new LambdaAnalyzerPreprocessor();
}
// </editor-fold>
@@ -206,7 +205,7 @@
public void visitClassDef(JCClassDecl tree) {
if (tree.sym.owner.kind == PCK) {
//analyze class
- analyzer.analyzeClass(tree);
+ tree = analyzer.analyzeAndPreprocessClass(tree);
}
KlassInfo prevKlassInfo = kInfo;
try {
@@ -531,16 +530,25 @@
/** Make an attributed class instance creation expression.
* @param ctype The class type.
* @param args The constructor arguments.
+ * @param cons The constructor symbol
*/
- JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
+ JCNewClass makeNewClass(Type ctype, List<JCExpression> args, Symbol cons) {
JCNewClass tree = make.NewClass(null,
null, make.QualIdent(ctype.tsym), args, null);
- tree.constructor = rs.resolveConstructor(
- null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil());
+ tree.constructor = cons;
tree.type = ctype;
return tree;
}
+ /** Make an attributed class instance creation expression.
+ * @param ctype The class type.
+ * @param args The constructor arguments.
+ */
+ JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
+ return makeNewClass(ctype, args,
+ rs.resolveConstructor(null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil()));
+ }
+
private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym,
DiagnosticPosition pos, List<Object> staticArgs, MethodType indyType) {
String functionalInterfaceClass = classSig(targetType);
@@ -1019,8 +1027,9 @@
* This visitor collects information about translation of a lambda expression.
* More specifically, it keeps track of the enclosing contexts and captured locals
* accessed by the lambda being translated (as well as other useful info).
+ * It also translates away problems for LambdaToMethod.
*/
- class LambdaAnalyzer extends TreeScanner {
+ class LambdaAnalyzerPreprocessor extends TreeTranslator {
/** the frame stack - used to reconstruct translation info about enclosing scopes */
private List<Frame> frameStack;
@@ -1047,10 +1056,10 @@
private Map<ClassSymbol, Symbol> clinits =
new HashMap<ClassSymbol, Symbol>();
- private void analyzeClass(JCClassDecl tree) {
+ private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) {
frameStack = List.nil();
localClassDefs = new HashMap<Symbol, JCClassDecl>();
- scan(tree);
+ return translate(tree);
}
@Override
@@ -1154,7 +1163,7 @@
frameStack.head.addLocal(param.sym);
}
contextMap.put(tree, context);
- scan(tree.body);
+ super.visitLambda(tree);
context.complete();
}
finally {
@@ -1220,12 +1229,47 @@
};
fvc.scan(localCDef);
}
- }
+ }
+ /**
+ * Method references to local class constructors, may, if the local
+ * class references local variables, have implicit constructor
+ * parameters added in Lower; As a result, the invokedynamic bootstrap
+ * information added in the LambdaToMethod pass will have the wrong
+ * signature. Hooks between Lower and LambdaToMethod have been added to
+ * handle normal "new" in this case. This visitor converts potentially
+ * effected method references into a lambda containing a normal "new" of
+ * the class.
+ *
+ * @param tree
+ */
@Override
public void visitReference(JCMemberReference tree) {
- scan(tree.getQualifierExpression());
- contextMap.put(tree, makeReferenceContext(tree));
+ if (tree.getMode() == ReferenceMode.NEW
+ && tree.kind != ReferenceKind.ARRAY_CTOR
+ && tree.sym.owner.isLocal()) {
+ MethodSymbol consSym = (MethodSymbol) tree.sym;
+ List<Type> ptypes = ((MethodType) consSym.type).getParameterTypes();
+ Type classType = consSym.owner.type;
+
+ // Make new-class call
+ List<JCVariableDecl> params = make.Params(ptypes, owner());
+ JCNewClass nc = makeNewClass(classType, make.Idents(params));
+ nc.pos = tree.pos;
+
+ // Make lambda holding the new-class call
+ JCLambda slam = make.Lambda(params, nc);
+ slam.descriptorType = tree.descriptorType;
+ slam.targets = tree.targets;
+ slam.type = tree.type;
+ slam.pos = tree.pos;
+
+ // Now it is a lambda, process as such
+ visitLambda(slam);
+ } else {
+ super.visitReference(tree);
+ contextMap.put(tree, makeReferenceContext(tree));
+ }
}
@Override
@@ -1240,10 +1284,8 @@
}
localContext = localContext.prev;
}
- scan(tree.selected);
- } else {
- super.visitSelect(tree);
}
+ super.visitSelect(tree);
}
@Override
--- a/langtools/src/share/classes/com/sun/tools/javac/jvm/Code.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/Code.java Thu May 16 11:47:51 2013 +0100
@@ -1647,7 +1647,7 @@
State dup() {
try {
State state = (State)super.clone();
- state.defined = defined.dup();
+ state.defined = new Bits(defined);
state.stack = stack.clone();
if (locks != null) state.locks = locks.clone();
if (debugCode) {
@@ -1775,7 +1775,7 @@
}
State join(State other) {
- defined = defined.andSet(other.defined);
+ defined.andSet(other.defined);
Assert.check(stacksize == other.stacksize
&& nlocks == other.nlocks);
for (int i=0; i<stacksize; ) {
@@ -1887,7 +1887,7 @@
/** Set the current variable defined state. */
public void setDefined(Bits newDefined) {
if (alive && newDefined != state.defined) {
- Bits diff = state.defined.dup().xorSet(newDefined);
+ Bits diff = new Bits(state.defined).xorSet(newDefined);
for (int adr = diff.nextBit(0);
adr >= 0;
adr = diff.nextBit(adr+1)) {
--- a/langtools/src/share/classes/com/sun/tools/javac/model/JavacAnnoConstructs.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/model/JavacAnnoConstructs.java Thu May 16 11:47:51 2013 +0100
@@ -33,10 +33,13 @@
import com.sun.tools.javac.code.Kinds;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.TypeVariableSymbol;
+import com.sun.tools.javac.code.TargetType;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.AnnotatedType;
import com.sun.tools.javac.util.ListBuffer;
import static com.sun.tools.javac.code.TypeTag.CLASS;
+import com.sun.tools.javac.util.List;
/**
* Utility methods for operating on annotated constructs.
@@ -61,8 +64,12 @@
throw new IllegalArgumentException("Not an annotation type: "
+ annoType);
Attribute.Compound c;
- if (annotated.kind == Kinds.TYP && annotated instanceof ClassSymbol) {
+ if (annotated.kind == Kinds.TYP &&
+ annotated instanceof ClassSymbol) {
c = getAttributeOnClass((ClassSymbol)annotated, annoType);
+ } else if (annotated.kind == Kinds.TYP &&
+ annotated instanceof TypeVariableSymbol) {
+ c = getAttributeOnTypeVariable((TypeVariableSymbol)annotated, annoType);
} else {
c = getAttribute(annotated, annoType);
}
@@ -83,6 +90,24 @@
}
// Helper to getAnnotation[s]
+ private static <A extends Annotation> Attribute.Compound
+ getAttributeOnTypeVariable(TypeVariableSymbol annotated, Class<A> annoType) {
+ String name = annoType.getName();
+
+ // Declaration annotations on type variables are stored in type attributes
+ // on the owner of the TypeVariableSymbol
+ List<Attribute.Compound> res = List.nil();
+ List<Attribute.TypeCompound> candidates = annotated.owner.getRawTypeAttributes();
+ for (Attribute.TypeCompound anno : candidates)
+ if (anno.position.type == TargetType.CLASS_TYPE_PARAMETER ||
+ anno.position.type == TargetType.METHOD_TYPE_PARAMETER)
+ if (name.equals(anno.type.tsym.flatName().toString()))
+ return anno;
+
+ return null;
+ }
+
+ // Helper to getAnnotation[s]
private static <A extends Annotation> Attribute.Compound getAttributeOnClass(ClassSymbol annotated,
Class<A> annoType) {
boolean inherited = annoType.isAnnotationPresent(Inherited.class);
--- a/langtools/src/share/classes/com/sun/tools/javac/model/JavacElements.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/model/JavacElements.java Thu May 16 11:47:51 2013 +0100
@@ -402,9 +402,10 @@
* @param e the element being examined
* @return all annotations of the element
*/
+ @Override
public List<Attribute.Compound> getAllAnnotationMirrors(Element e) {
Symbol sym = cast(Symbol.class, e);
- List<Attribute.Compound> annos = sym.getRawAttributes();
+ List<Attribute.Compound> annos = sym.getAnnotationMirrors();
while (sym.getKind() == ElementKind.CLASS) {
Type sup = ((ClassSymbol) sym).getSuperclass();
if (!sup.hasTag(CLASS) || sup.isErroneous() ||
@@ -413,7 +414,7 @@
}
sym = sup.tsym;
List<Attribute.Compound> oldAnnos = annos;
- List<Attribute.Compound> newAnnos = sym.getRawAttributes();
+ List<Attribute.Compound> newAnnos = sym.getAnnotationMirrors();
for (Attribute.Compound anno : newAnnos) {
if (isInherited(anno.type) &&
!containsAnnoOfType(oldAnnos, anno.type)) {
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/DocCommentParser.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/DocCommentParser.java Thu May 16 11:47:51 2013 +0100
@@ -41,6 +41,7 @@
import com.sun.tools.javac.tree.DCTree.DCAttribute;
import com.sun.tools.javac.tree.DCTree.DCDocComment;
import com.sun.tools.javac.tree.DCTree.DCEndElement;
+import com.sun.tools.javac.tree.DCTree.DCEndPosTree;
import com.sun.tools.javac.tree.DCTree.DCErroneous;
import com.sun.tools.javac.tree.DCTree.DCIdentifier;
import com.sun.tools.javac.tree.DCTree.DCReference;
@@ -336,12 +337,12 @@
DCTree text = inlineText();
if (text != null) {
nextChar();
- return m.at(p).UnknownInlineTag(name, List.of(text));
+ return m.at(p).UnknownInlineTag(name, List.of(text)).setEndPos(bp);
}
} else if (tp.getKind() == TagParser.Kind.INLINE) {
- DCTree tree = tp.parse(p);
+ DCEndPosTree<?> tree = (DCEndPosTree<?>) tp.parse(p);
if (tree != null) {
- return tree;
+ return tree.setEndPos(bp);
}
} else {
inlineText(); // skip content
@@ -509,7 +510,7 @@
fac.log.popDiagnosticHandler(deferredDiagnosticHandler);
}
- return m.at(pos).Reference(sig, qualExpr, member, paramTypes);
+ return m.at(pos).Reference(sig, qualExpr, member, paramTypes).setEndPos(bp);
}
JCTree parseType(String s) throws ParseException {
@@ -741,7 +742,7 @@
}
if (ch == '>') {
nextChar();
- return m.at(p).StartElement(name, attrs, selfClosing);
+ return m.at(p).StartElement(name, attrs, selfClosing).setEndPos(bp);
}
}
} else if (ch == '/') {
--- a/langtools/src/share/classes/com/sun/tools/javac/tree/DCTree.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/tree/DCTree.java Thu May 16 11:47:51 2013 +0100
@@ -36,6 +36,7 @@
import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Position;
import java.io.IOException;
import java.io.StringWriter;
import javax.tools.JavaFileObject;
@@ -82,8 +83,24 @@
return s.toString();
}
+ public static abstract class DCEndPosTree<T extends DCEndPosTree<T>> extends DCTree {
+
+ private int endPos = Position.NOPOS;
+
+ public int getEndPos(DCDocComment dc) {
+ return dc.comment.getSourcePos(endPos);
+ }
+
+ @SuppressWarnings("unchecked")
+ public T setEndPos(int endPos) {
+ this.endPos = endPos;
+ return (T) this;
+ }
+
+ }
+
public static class DCDocComment extends DCTree implements DocCommentTree {
- final Comment comment; // required for the implicit source pos table
+ public final Comment comment; // required for the implicit source pos table
public final List<DCTree> firstSentence;
public final List<DCTree> body;
@@ -125,7 +142,7 @@
}
}
- public static abstract class DCInlineTag extends DCTree implements InlineTagTree {
+ public static abstract class DCInlineTag extends DCEndPosTree<DCInlineTag> implements InlineTagTree {
public String getTagName() {
return getKind().tagName;
}
@@ -345,6 +362,7 @@
public int getEndPosition(EndPosTable endPosTable) {
return pos + body.length();
}
+
}
public static class DCIdentifier extends DCTree implements IdentifierTree {
@@ -478,7 +496,7 @@
}
}
- public static class DCReference extends DCTree implements ReferenceTree {
+ public static class DCReference extends DCEndPosTree<DCReference> implements ReferenceTree {
public final String signature;
// The following are not directly exposed through ReferenceTree
@@ -663,7 +681,7 @@
}
}
- public static class DCStartElement extends DCTree implements StartElementTree {
+ public static class DCStartElement extends DCEndPosTree<DCStartElement> implements StartElementTree {
public final Name name;
public final List<DCTree> attrs;
public final boolean selfClosing;
--- a/langtools/src/share/classes/com/sun/tools/javac/util/Bits.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/util/Bits.java Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2012, 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
@@ -27,6 +27,8 @@
import java.util.Arrays;
+import static com.sun.tools.javac.util.Bits.BitsOpKind.*;
+
/** A class for extensible, mutable bit sets.
*
* <p><b>This is NOT part of any supported API.
@@ -36,31 +38,114 @@
*/
public class Bits {
+ public enum BitsOpKind {
+ INIT,
+ CLEAR,
+ INCL_BIT,
+ EXCL_BIT,
+ ASSIGN,
+ AND_SET,
+ OR_SET,
+ DIFF_SET,
+ XOR_SET,
+ INCL_RANGE,
+ EXCL_RANGE,
+ }
+
+ // ____________ reset _________
+ // / UNKNOWN \ <-------- / UNINIT \
+ // \____________/ | \_________/
+ // | | |
+ // |assign | | any
+ // | ___________ |
+ // ------> / NORMAL \ <----
+ // \___________/ |
+ // | |
+ // | |
+ // -----------
+ // any
+ private enum BitsState {
+ /* A Bits instance is in UNKNOWN state if it has been explicitly reset.
+ * It is possible to get to this state from any other by calling the
+ * reset method. An instance in the UNKNOWN state can pass to the
+ * NORMAL state after being assigned another Bits instance.
+ */
+ UNKNOWN,
+ /* A Bits instance is in UNINIT when it is created with the default
+ * constructor but it isn't explicitly reset. The main objective of this
+ * internal state is to save some memory.
+ */
+ UNINIT,
+ /* The normal state is reached after creating a Bits instance from an
+ * existing one or after applying any operation to an instance on UNINIT
+ * or NORMAL state. From this state a bits instance can pass to the
+ * UNKNOWN state by calling the reset method.
+ */
+ NORMAL;
+
+ static BitsState getState(int[] someBits, boolean reset) {
+ if (reset) {
+ return UNKNOWN;
+ } else {
+ if (someBits != unassignedBits) {
+ return NORMAL;
+ } else {
+ return UNINIT;
+ }
+ }
+ }
+
+ }
private final static int wordlen = 32;
private final static int wordshift = 5;
private final static int wordmask = wordlen - 1;
- private int[] bits;
+ public int[] bits = null;
+ // This field will store last version of bits after every change.
+ public int[] oldBits = null;
+
+ public BitsOpKind lastOperation = null;
+
+ private static final int[] unassignedBits = new int[0];
+
+ private BitsState currentState;
/** Construct an initially empty set.
*/
public Bits() {
- this(new int[1]);
+ this(false);
+ }
+
+ public Bits(Bits someBits) {
+ this(someBits.dup().bits, BitsState.getState(someBits.bits, false));
+ }
+
+ public Bits(boolean reset) {
+ this(unassignedBits, BitsState.getState(unassignedBits, reset));
}
/** Construct a set consisting initially of given bit vector.
*/
- public Bits(int[] bits) {
+ private Bits(int[] bits, BitsState initState) {
this.bits = bits;
+ this.currentState = initState;
+ switch (initState) {
+ case UNKNOWN:
+ reset(); //this will also set current state;
+ break;
+ case NORMAL:
+ Assert.check(bits != unassignedBits);
+ lastOperation = INIT;
+ break;
+ }
}
- /** Construct a set consisting initially of given range.
+ /** This method will be called after any operation that causes a change to
+ * the bits. Subclasses can thus override it in order to extract information
+ * from the changes produced to the bits by the given operation.
*/
- public Bits(int start, int limit) {
- this();
- inclRange(start, limit);
- }
+ public void changed() {}
private void sizeTo(int len) {
if (bits.length < len) {
@@ -71,57 +156,110 @@
/** This set = {}.
*/
public void clear() {
+ Assert.check(currentState != BitsState.UNKNOWN);
+ oldBits = bits;
+ lastOperation = CLEAR;
for (int i = 0; i < bits.length; i++) bits[i] = 0;
+ changed();
+ currentState = BitsState.NORMAL;
+ }
+
+ public void reset() {
+ bits = null;
+ oldBits = null;
+ currentState = BitsState.UNKNOWN;
+ }
+
+ public boolean isReset() {
+ return currentState == BitsState.UNKNOWN;
+ }
+
+ public Bits assign(Bits someBits) {
+ lastOperation = ASSIGN;
+ oldBits = bits;
+ bits = someBits.dup().bits;
+ changed();
+ currentState = BitsState.NORMAL;
+ return this;
}
/** Return a copy of this set.
*/
- public Bits dup() {
- int[] newbits = new int[bits.length];
- System.arraycopy(bits, 0, newbits, 0, bits.length);
- return new Bits(newbits);
+ private Bits dup() {
+ Assert.check(currentState != BitsState.UNKNOWN);
+ Bits tmp = new Bits();
+ if (currentState != BitsState.NORMAL) {
+ tmp.bits = bits;
+ } else {
+ tmp.bits = new int[bits.length];
+ System.arraycopy(bits, 0, tmp.bits, 0, bits.length);
+ }
+ currentState = BitsState.NORMAL;
+ return tmp;
}
/** Include x in this set.
*/
public void incl(int x) {
+ Assert.check(currentState != BitsState.UNKNOWN);
Assert.check(x >= 0);
+ oldBits = bits;
+ lastOperation = INCL_BIT;
sizeTo((x >>> wordshift) + 1);
bits[x >>> wordshift] = bits[x >>> wordshift] |
(1 << (x & wordmask));
+ changed();
+ currentState = BitsState.NORMAL;
}
/** Include [start..limit) in this set.
*/
public void inclRange(int start, int limit) {
+ Assert.check(currentState != BitsState.UNKNOWN);
+ oldBits = bits;
+ lastOperation = INCL_RANGE;
sizeTo((limit >>> wordshift) + 1);
- for (int x = start; x < limit; x++)
+ for (int x = start; x < limit; x++) {
bits[x >>> wordshift] = bits[x >>> wordshift] |
(1 << (x & wordmask));
+ }
+ changed();
+ currentState = BitsState.NORMAL;
}
/** Exclude [start...end] from this set.
*/
public void excludeFrom(int start) {
+ Assert.check(currentState != BitsState.UNKNOWN);
+ oldBits = bits;
+ lastOperation = EXCL_RANGE;
Bits temp = new Bits();
temp.sizeTo(bits.length);
temp.inclRange(0, start);
- andSet(temp);
+ internalAndSet(temp);
+ changed();
+ currentState = BitsState.NORMAL;
}
/** Exclude x from this set.
*/
public void excl(int x) {
+ Assert.check(currentState != BitsState.UNKNOWN);
Assert.check(x >= 0);
+ oldBits = bits;
+ lastOperation = EXCL_BIT;
sizeTo((x >>> wordshift) + 1);
bits[x >>> wordshift] = bits[x >>> wordshift] &
~(1 << (x & wordmask));
+ changed();
+ currentState = BitsState.NORMAL;
}
/** Is x an element of this set?
*/
public boolean isMember(int x) {
+ Assert.check(currentState != BitsState.UNKNOWN);
return
0 <= x && x < (bits.length << wordshift) &&
(bits[x >>> wordshift] & (1 << (x & wordmask))) != 0;
@@ -130,38 +268,66 @@
/** {@literal this set = this set & xs}.
*/
public Bits andSet(Bits xs) {
+ Assert.check(currentState != BitsState.UNKNOWN);
+ oldBits = bits;
+ lastOperation = AND_SET;
+ internalAndSet(xs);
+ changed();
+ currentState = BitsState.NORMAL;
+ return this;
+ }
+
+ private void internalAndSet(Bits xs) {
+ Assert.check(currentState != BitsState.UNKNOWN);
sizeTo(xs.bits.length);
- for (int i = 0; i < xs.bits.length; i++)
+ for (int i = 0; i < xs.bits.length; i++) {
bits[i] = bits[i] & xs.bits[i];
- return this;
+ }
}
/** this set = this set | xs.
*/
public Bits orSet(Bits xs) {
+ Assert.check(currentState != BitsState.UNKNOWN);
+ oldBits = bits;
+ lastOperation = OR_SET;
sizeTo(xs.bits.length);
- for (int i = 0; i < xs.bits.length; i++)
+ for (int i = 0; i < xs.bits.length; i++) {
bits[i] = bits[i] | xs.bits[i];
+ }
+ changed();
+ currentState = BitsState.NORMAL;
return this;
}
/** this set = this set \ xs.
*/
public Bits diffSet(Bits xs) {
+ Assert.check(currentState != BitsState.UNKNOWN);
+ oldBits = bits;
+ lastOperation = DIFF_SET;
for (int i = 0; i < bits.length; i++) {
if (i < xs.bits.length) {
bits[i] = bits[i] & ~xs.bits[i];
}
}
+ changed();
+ currentState = BitsState.NORMAL;
return this;
}
/** this set = this set ^ xs.
*/
public Bits xorSet(Bits xs) {
+ Assert.check(currentState != BitsState.UNKNOWN);
+ oldBits = bits;
+ lastOperation = XOR_SET;
sizeTo(xs.bits.length);
- for (int i = 0; i < xs.bits.length; i++)
+ for (int i = 0; i < xs.bits.length; i++) {
bits[i] = bits[i] ^ xs.bits[i];
+ }
+ changed();
+ currentState = BitsState.NORMAL;
return this;
}
@@ -187,6 +353,7 @@
* }</pre>
*/
public int nextBit(int x) {
+ Assert.check(currentState != BitsState.UNKNOWN);
int windex = x >>> wordshift;
if (windex >= bits.length) return -1;
int word = bits[windex] & ~((1 << (x & wordmask))-1);
@@ -202,17 +369,20 @@
/** a string representation of this set.
*/
public String toString() {
- char[] digits = new char[bits.length * wordlen];
- for (int i = 0; i < bits.length * wordlen; i++)
- digits[i] = isMember(i) ? '1' : '0';
- return new String(digits);
+ if (bits.length > 0) {
+ char[] digits = new char[bits.length * wordlen];
+ for (int i = 0; i < bits.length * wordlen; i++)
+ digits[i] = isMember(i) ? '1' : '0';
+ return new String(digits);
+ } else {
+ return "[]";
+ }
}
/** Test Bits.nextBit(int). */
public static void main(String[] args) {
java.util.Random r = new java.util.Random();
Bits bits = new Bits();
- int dupCount = 0;
for (int i=0; i<125; i++) {
int k;
do {
--- a/langtools/src/share/classes/com/sun/tools/javadoc/ClassDocImpl.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javadoc/ClassDocImpl.java Thu May 16 11:47:51 2013 +0100
@@ -341,9 +341,14 @@
* </pre>
*/
public String name() {
- return getClassName(tsym, false);
+ if (name == null) {
+ name = getClassName(tsym, false);
+ }
+ return name;
}
+ private String name;
+
/**
* Return the qualified class name as a String.
* <pre>
@@ -354,9 +359,14 @@
* </pre>
*/
public String qualifiedName() {
- return getClassName(tsym, true);
+ if (qualifiedName == null) {
+ qualifiedName = getClassName(tsym, true);
+ }
+ return qualifiedName;
}
+ private String qualifiedName;
+
/**
* Return unqualified name of type excluding any dimension information.
* <p>
@@ -380,9 +390,14 @@
* Return the simple name of this type.
*/
public String simpleTypeName() {
- return tsym.name.toString();
+ if (simpleTypeName == null) {
+ simpleTypeName = tsym.name.toString();
+ }
+ return simpleTypeName;
}
+ private String simpleTypeName;
+
/**
* Return the qualified name and any type parameters.
* Each parameter is a type variable with optional bounds.
--- a/langtools/src/share/classes/com/sun/tools/javadoc/FieldDocImpl.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javadoc/FieldDocImpl.java Thu May 16 11:47:51 2013 +0100
@@ -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
@@ -252,13 +252,23 @@
}
public String name() {
- return sym.name.toString();
+ if (name == null) {
+ name = sym.name.toString();
+ }
+ return name;
}
+ private String name;
+
public String qualifiedName() {
- return sym.enclClass().getQualifiedName() + "." + name();
+ if (qualifiedName == null) {
+ qualifiedName = sym.enclClass().getQualifiedName() + "." + name();
+ }
+ return qualifiedName;
}
+ private String qualifiedName;
+
/**
* Return the source position of the entity, or null if
* no position is available.
--- a/langtools/src/share/classes/com/sun/tools/javadoc/MethodDocImpl.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javadoc/MethodDocImpl.java Thu May 16 11:47:51 2013 +0100
@@ -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
@@ -203,13 +203,23 @@
public String name() {
- return sym.name.toString();
+ if (name == null) {
+ name = sym.name.toString();
+ }
+ return name;
}
+ private String name;
+
public String qualifiedName() {
- return sym.enclClass().getQualifiedName() + "." + sym.name;
+ if (qualifiedName == null) {
+ qualifiedName = sym.enclClass().getQualifiedName() + "." + sym.name;
+ }
+ return qualifiedName;
}
+ private String qualifiedName;
+
/**
* Returns a string representation of this method. Includes the
* qualified signature, the qualified method name, and any type
--- a/langtools/src/share/classes/com/sun/tools/javadoc/PackageDocImpl.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javadoc/PackageDocImpl.java Thu May 16 11:47:51 2013 +0100
@@ -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
@@ -334,12 +334,17 @@
* Get package name.
*/
public String qualifiedName() {
- Name fullname = sym.getQualifiedName();
- // Some bogus tests depend on the interned "" being returned.
- // See 6457276.
- return fullname.isEmpty() ? "" : fullname.toString();
+ if (qualifiedName == null) {
+ Name fullname = sym.getQualifiedName();
+ // Some bogus tests depend on the interned "" being returned.
+ // See 6457276.
+ qualifiedName = fullname.isEmpty() ? "" : fullname.toString();
+ }
+ return qualifiedName;
}
+ private String qualifiedName;
+
/**
* set doc path for an unzipped directory
*/
--- a/langtools/test/tools/javac/api/TestJavacTaskScanner.java Wed May 08 11:22:25 2013 +0100
+++ b/langtools/test/tools/javac/api/TestJavacTaskScanner.java Thu May 16 11:47:51 2013 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2010, 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
@@ -93,7 +93,7 @@
System.out.println("#allMembers: " + numAllMembers);
check(numTokens, "#Tokens", 1222);
- check(numParseTypeElements, "#parseTypeElements", 136);
+ check(numParseTypeElements, "#parseTypeElements", 158);
check(numAllMembers, "#allMembers", 52);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/defaultMethods/CheckACC_STRICTFlagOnDefaultMethodTest.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8012723
+ * @summary strictfp interface misses strictfp modifer on default method
+ * @run main CheckACC_STRICTFlagOnDefaultMethodTest
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+import java.io.File;
+import java.io.IOException;
+
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.ConstantPoolException;
+import com.sun.tools.classfile.Descriptor;
+import com.sun.tools.classfile.Descriptor.InvalidDescriptor;
+import com.sun.tools.classfile.Method;
+
+import static com.sun.tools.classfile.AccessFlags.ACC_STRICT;
+
+public class CheckACC_STRICTFlagOnDefaultMethodTest {
+ private static final String AssertionErrorMessage =
+ "All methods should have the ACC_STRICT access flag " +
+ "please check output";
+ private static final String offendingMethodErrorMessage =
+ "Method %s of class %s doesn't have the ACC_STRICT access flag";
+
+ private List<String> errors = new ArrayList<>();
+
+ public static void main(String[] args)
+ throws IOException, ConstantPoolException, InvalidDescriptor {
+ new CheckACC_STRICTFlagOnDefaultMethodTest().run();
+ }
+
+ private void run()
+ throws IOException, ConstantPoolException, InvalidDescriptor {
+ String testClasses = System.getProperty("test.classes");
+ check(testClasses,
+ "CheckACC_STRICTFlagOnDefaultMethodTest$StrictfpInterface.class");
+ if (errors.size() > 0) {
+ for (String error: errors) {
+ System.err.println(error);
+ }
+ throw new AssertionError(AssertionErrorMessage);
+ }
+ }
+
+ void check(String dir, String... fileNames)
+ throws
+ IOException,
+ ConstantPoolException,
+ Descriptor.InvalidDescriptor {
+ for (String fileName : fileNames) {
+ ClassFile classFileToCheck = ClassFile.read(new File(dir, fileName));
+
+ for (Method method : classFileToCheck.methods) {
+ if ((method.access_flags.flags & ACC_STRICT) == 0) {
+ errors.add(String.format(offendingMethodErrorMessage,
+ method.getName(classFileToCheck.constant_pool),
+ classFileToCheck.getName()));
+ }
+ }
+ }
+ }
+
+ strictfp interface StrictfpInterface {
+ default void default_interface_method() {}
+ static void static_interface_method() {}
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/doctree/positions/TestPosition.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2010, 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 8008174
+ * @summary proper source positions for doc comments
+ * @build TestPosition
+ * @compile/ref=TestPosition.out -processor TestPosition -proc:only TestPositionSource.java
+ */
+
+import com.sun.source.doctree.DocCommentTree;
+import com.sun.source.doctree.DocTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.util.DocSourcePositions;
+import com.sun.source.util.DocTreeScanner;
+import com.sun.source.util.DocTrees;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.TreePathScanner;
+import java.io.IOException;
+import java.util.Set;
+import javax.annotation.processing.*;
+import javax.lang.model.*;
+import javax.lang.model.element.*;
+
+@SupportedAnnotationTypes("*")
+public class TestPosition extends AbstractProcessor {
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ TypeElement source = processingEnv.getElementUtils().getTypeElement("TestPositionSource");
+
+ if (source == null) throw new IllegalStateException();
+
+ if (!roundEnv.getRootElements().contains(source)) return false;
+
+ final DocTrees trees = DocTrees.instance(processingEnv);
+ final TreePath testElement = trees.getPath(source);
+
+ if (testElement == null) throw new IllegalStateException();
+
+ String code;
+
+ try {
+ code = testElement.getCompilationUnit().getSourceFile().getCharContent(false).toString();
+ } catch ( IOException ex) {
+ throw new IllegalStateException(ex);
+ }
+
+ new TreePathScanner<Void, Void>() {
+ @Override public Void visitMethod(MethodTree node, Void p) {
+ final DocCommentTree docCommentTree = trees.getDocCommentTree(getCurrentPath());
+
+ if (docCommentTree != null) {
+ System.out.println(node.getName() + ":");
+ new DocTreeScanner<Void, Void>() {
+ @Override public Void scan(DocTree node, Void p) {
+ if (node != null) {
+ DocSourcePositions sp = (DocSourcePositions) trees.getSourcePositions(); //XXX: the cast???
+ int start = (int) sp.getStartPosition(testElement.getCompilationUnit(), docCommentTree, node);
+ int end = (int) sp.getEndPosition(testElement.getCompilationUnit(), docCommentTree, node);
+ String snippet = code.substring(start, end).replace(" \n", "!trailing-whitespace!\n");
+
+ if (snippet.endsWith(" ")) {
+ snippet = snippet.substring(0, snippet.length() - 1) + "!trailing-whitespace!";
+ }
+ System.out.println(node.getKind().name() + ":" + snippet);
+ }
+ return super.scan(node, p);
+ }
+ }.scan(docCommentTree, null);
+ }
+
+ return super.visitMethod(node, p);
+ }
+ }.scan(testElement, null);
+
+ return false;
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latest();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/doctree/positions/TestPosition.out Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,99 @@
+valid:
+DOC_COMMENT:First sentence.
+ *
+ * <p>Description with {@link java.io.InputStream link}
+ *
+ * @param first description
+ * @param second description
+ * @return whatever
+ * @throws IllegalStateException why?
+ * @since 1.15
+ * @see java.util.List
+TEXT:First sentence.
+START_ELEMENT:<p>
+TEXT:Description with!trailing-whitespace!
+LINK:{@link java.io.InputStream link}
+REFERENCE:java.io.InputStream
+TEXT:link
+PARAM:@param first description
+IDENTIFIER:first
+TEXT:description
+PARAM:@param second description
+IDENTIFIER:second
+TEXT:description
+RETURN:@return whatever
+TEXT:whatever
+THROWS:@throws IllegalStateException why?
+REFERENCE:IllegalStateException
+TEXT:why?
+SINCE:@since 1.15
+TEXT:1.15
+SEE:@see java.util.List
+REFERENCE:java.util.List
+erroneous:
+DOC_COMMENT:First sentence.
+ *
+ * <p>Description with {@link}, {@link java.util.List}, {@link
+ *
+ * @param
+ * @param second
+ * @return
+ * @throws
+ * @throws IllegalStateException
+ * @since
+ * @see
+TEXT:First sentence.
+START_ELEMENT:<p>
+TEXT:Description with!trailing-whitespace!
+LINK:{@link}
+TEXT:,!trailing-whitespace!
+LINK:{@link java.util.List}
+REFERENCE:java.util.List
+TEXT:,!trailing-whitespace!
+ERRONEOUS:{@link
+ERRONEOUS:@param
+PARAM:@param second
+IDENTIFIER:second
+RETURN:@return
+ERRONEOUS:@throws
+THROWS:@throws IllegalStateException
+REFERENCE:IllegalStateException
+SINCE:@since
+ERRONEOUS:@see
+withWhiteSpaces:
+DOC_COMMENT:First sentence.
+ *
+ * <p>Description with {@link }, {@link java.util.List#add( int )},
+ * {@link java.util.List#add( int ) some text with whitespaces}, {@link
+ *
+ * @param first
+ * @param second some text with trailing whitespace
+ * @return some return
+ * @throws java.lang.IllegalStateException
+ * @throws java.lang.IllegalStateException some text
+TEXT:First sentence.
+START_ELEMENT:<p>
+TEXT:Description with!trailing-whitespace!
+LINK:{@link }
+TEXT:,!trailing-whitespace!
+LINK:{@link java.util.List#add( int )}
+REFERENCE:java.util.List#add( int )
+TEXT:,
+ *!trailing-whitespace!
+LINK:{@link java.util.List#add( int ) some text with whitespaces}
+REFERENCE:java.util.List#add( int )
+TEXT:some text with whitespaces
+TEXT:,!trailing-whitespace!
+ERRONEOUS:{@link
+PARAM:@param first
+IDENTIFIER:first
+PARAM:@param second some text with trailing whitespace
+IDENTIFIER:second
+TEXT:some text with trailing whitespace
+RETURN:@return some return
+TEXT:some return
+THROWS:@throws java.lang.IllegalStateException
+REFERENCE:java.lang.IllegalStateException
+THROWS:@throws java.lang.IllegalStateException some text
+REFERENCE:java.lang.IllegalStateException
+TEXT:some text
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/doctree/positions/TestPositionSource.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+public class TestPositionSource {
+
+ /**First sentence.
+ *
+ * <p>Description with {@link java.io.InputStream link}
+ *
+ * @param first description
+ * @param second description
+ * @return whatever
+ * @throws IllegalStateException why?
+ * @since 1.15
+ * @see java.util.List
+ */
+ public boolean valid(int first, int second) throws IllegalStateException {
+ return true;
+ }
+
+ /**First sentence.
+ *
+ * <p>Description with {@link}, {@link java.util.List}, {@link
+ *
+ * @param
+ * @param second
+ * @return
+ * @throws
+ * @throws IllegalStateException
+ * @since
+ * @see
+ */
+ public boolean erroneous(int first, int second) throws IllegalStateException {
+ return true;
+ }
+
+ /**First sentence.
+ *
+ * <p>Description with {@link }, {@link java.util.List#add( int )},
+ * {@link java.util.List#add( int ) some text with whitespaces}, {@link
+ *
+ * @param first
+ * @param second some text with trailing whitespace
+ * @return some return
+ * @throws java.lang.IllegalStateException
+ * @throws java.lang.IllegalStateException some text
+ */
+ public boolean withWhiteSpaces(int first, int second) throws IllegalStateException {
+ return true;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/methodReferenceExecution/MethodReferenceTestNewInnerImplicitArgs.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+/**
+ * @test
+ * @bug 8011591
+ * @summary BootstrapMethodError when capturing constructor ref to local classes
+ * @run testng MethodReferenceTestNewInnerImplicitArgs
+ */
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/**
+ * Test the case that a constructor has implicit parameters added to
+ * access local variables and that this constructor is used in a
+ * method reference.
+ * @author Robert Field
+ */
+
+@Test
+public class MethodReferenceTestNewInnerImplicitArgs {
+
+
+ static class S {
+ String b;
+ S(String s, String s2) { b = s + s2; }
+ }
+
+ interface I {
+ S m();
+ }
+
+ interface I2 {
+ S m(int i, int j);
+ }
+
+ public static void testConstructorReferenceImplicitParameters() {
+ String title = "Hey";
+ String a2 = "!!!";
+ class MS extends S {
+ MS() {
+ super(title, a2);
+ }
+ }
+
+ I result = MS::new;
+ assertEquals(result.m().b, "Hey!!!");
+
+ class MS2 extends S {
+ MS2(int x, int y) {
+ super(title+x, a2+y);
+ }
+ }
+
+ I2 result2 = MS2::new;
+ assertEquals(result2.m(8, 4).b, "Hey8!!!4");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/model/element/TestTypeParameterAnnotations.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,190 @@
+/*
+ * 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 8011027
+ * @library /tools/javac/lib
+ * @build JavacTestingAbstractProcessor TestTypeParameterAnnotations
+ * @compile -processor TestTypeParameterAnnotations -proc:only TestTypeParameterAnnotations.java
+ */
+
+import java.util.*;
+import java.lang.annotation.*;
+import javax.annotation.processing.*;
+import javax.lang.model.element.*;
+import javax.lang.model.util.*;
+import javax.tools.*;
+
+public class TestTypeParameterAnnotations<@Foo @Bar @Baz T> extends JavacTestingAbstractProcessor {
+ int round = 0;
+
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (++round == 1) {
+ int found = (new Scanner()).scan(roundEnv.getRootElements(), null);
+ if (found == expect) {
+ ; //nop
+ } else {
+ error("unexpected number of results: expected " + expect
+ + ", found " + found);
+ }
+
+ }
+ return true;
+ }
+
+ class Scanner extends JavacTestingAbstractProcessor.ElementScanner<Integer,Void> {
+ @Override
+ public Integer visitExecutable(ExecutableElement e, Void p) {
+ super.visitExecutable(e, p);
+ found += check(e, e.getTypeParameters());
+ return found;
+ }
+
+ @Override
+ public Integer visitType(TypeElement e, Void p) {
+ super.visitType(e, p);
+ found += check(e, e.getTypeParameters());
+ return found;
+ }
+
+ int found;
+ }
+
+ int check(Element e, List<? extends TypeParameterElement> typarams) {
+ if (typarams.isEmpty())
+ return 0;
+ if (typarams.size() != 1)
+ return 0;
+
+ for (TypeParameterElement tpe: typarams) {
+ boolean b1 = checkAnnotationMirrors(tpe, tpe.getAnnotationMirrors());
+ boolean b2 = checkAnnotationMirrors(tpe, elements.getAllAnnotationMirrors(tpe));
+ boolean b3 = checkGetAnnotation(tpe);
+ boolean b4 = checkGetAnnotations(tpe);
+ return b1 && b2 && b3 && b4 ? 1 : 0;
+ }
+ return 0;
+ }
+
+ boolean checkAnnotationMirrors(TypeParameterElement tpe, List<? extends AnnotationMirror> l) {
+ if (l.size() != 3) {
+ error("To few annotations, got " + l.size() +
+ ", should be 3", tpe);
+ return false;
+ }
+
+ AnnotationMirror m = l.get(0);
+ if (!m.getAnnotationType().asElement().equals(elements.getTypeElement("Foo"))) {
+ error("Wrong type of annotation, was expecting @Foo", m.getAnnotationType().asElement());
+ return false;
+ }
+ m = l.get(1);
+ if (!m.getAnnotationType().asElement().equals(elements.getTypeElement("Bar"))) {
+ error("Wrong type of annotation, was expecting @Bar", m.getAnnotationType().asElement());
+ return false;
+ }
+ m = l.get(2);
+ if (!m.getAnnotationType().asElement().equals(elements.getTypeElement("Baz"))) {
+ error("Wrong type of annotation, was expecting @Baz", m.getAnnotationType().asElement());
+ return false;
+ }
+ return true;
+ }
+
+ boolean checkGetAnnotation(TypeParameterElement tpe) {
+ Foo f = tpe.getAnnotation(Foo.class);
+ if (f == null)
+ error("Expecting @Foo to be present in getAnnotation()", tpe);
+
+ Bar b = tpe.getAnnotation(Bar.class);
+ if (b == null)
+ error("Expecting @Bar to be present in getAnnotation()", tpe);
+
+ Baz z = tpe.getAnnotation(Baz.class);
+ if (z == null)
+ error("Expecting @Baz to be present in getAnnotation()", tpe);
+
+ return f != null &&
+ b != null &&
+ z != null;
+ }
+
+ boolean checkGetAnnotations(TypeParameterElement tpe) {
+ Foo[] f = tpe.getAnnotationsByType(Foo.class);
+ if (f.length != 1) {
+ error("Expecting 1 @Foo to be present in getAnnotationsByType()", tpe);
+ return false;
+ }
+
+ Bar[] b = tpe.getAnnotationsByType(Bar.class);
+ if (b.length != 1) {
+ error("Expecting 1 @Bar to be present in getAnnotationsByType()", tpe);
+ return false;
+ }
+
+ Baz[] z = tpe.getAnnotationsByType(Baz.class);
+ if (z.length != 1) {
+ error("Expecting 1 @Baz to be present in getAnnotationsByType()", tpe);
+ return false;
+ }
+
+ return true;
+ }
+
+ void note(String msg) {
+ messager.printMessage(Diagnostic.Kind.NOTE, msg);
+ }
+
+ void note(String msg, Element e) {
+ messager.printMessage(Diagnostic.Kind.NOTE, msg, e);
+ }
+
+ void error(String msg, Element e) {
+ messager.printMessage(Diagnostic.Kind.ERROR, msg, e);
+ }
+
+ void error(String msg) {
+ messager.printMessage(Diagnostic.Kind.ERROR, msg);
+ }
+
+ // additional generic elements to test
+ <@Foo @Bar @Baz X> X m(X x) { return x; }
+
+ interface Intf<@Foo @Bar @Baz X> { X m() ; }
+
+ class Class<@Foo @Bar @Baz X> {
+ <@Foo @Bar @Baz Y> Class() { }
+ }
+
+ final int expect = 5; // top level class, plus preceding examples
+}
+
+@Target(ElementType.TYPE_PARAMETER)
+@interface Foo {}
+
+@Target(ElementType.TYPE_PARAMETER)
+@interface Bar {}
+
+@Target(ElementType.TYPE_PARAMETER)
+@interface Baz {}
--- a/nashorn/.hgtags Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/.hgtags Thu May 16 11:47:51 2013 +0100
@@ -197,3 +197,5 @@
e0378f0a50dafdcfb7b04f6401d320f89884baa1 jdk8-b85
002ad9d6735f36d1204e133324c73058c8abb1b0 jdk8-b86
774aeaa89bc15f4365e3c2fc36f6a3a0da70ba28 jdk8-b87
+40c107d1ae6f81a62e35dfe618b827897405e9b2 jdk8-b88
+45ce27fbe2720d80070095c0db7344ec41e2833d jdk8-b89
--- a/nashorn/bin/verbose_octane.sh Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/bin/verbose_octane.sh Thu May 16 11:47:51 2013 +0100
@@ -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}"
--- a/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java Thu May 16 11:47:51 2013 +0100
@@ -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);
}
}
--- a/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java Thu May 16 11:47:51 2013 +0100
@@ -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);
--- a/nashorn/make/build-nasgen.xml Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/make/build-nasgen.xml Thu May 16 11:47:51 2013 +0100
@@ -37,6 +37,7 @@
<pathelement location="${basedir}/buildtools/nasgen/dist/nasgen.jar"/>
<pathelement path="${basedir}/build/classes"/>
</classpath>
+ <jvmarg value="-Djava.ext.dirs="/>
<arg value="${basedir}/build/classes"/>
<arg value="jdk.nashorn.internal.objects"/>
<arg value="${basedir}/build/classes"/>
--- a/nashorn/make/project.properties Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/make/project.properties Thu May 16 11:47:51 2013 +0100
@@ -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}
--- a/nashorn/src/jdk/internal/dynalink/beans/StaticClassIntrospector.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/internal/dynalink/beans/StaticClassIntrospector.java Thu May 16 11:47:51 2013 +0100
@@ -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();
--- a/nashorn/src/jdk/internal/dynalink/beans/StaticClassLinker.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/internal/dynalink/beans/StaticClassLinker.java Thu May 16 11:47:51 2013 +0100
@@ -144,7 +144,7 @@
}
private static MethodHandle drop(MethodHandle mh) {
- return MethodHandles.dropArguments(mh, 0, StaticClass.class);
+ return StaticClassIntrospector.dropReceiver(mh, StaticClass.class);
}
@Override
--- a/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Thu May 16 11:47:51 2013 +0100
@@ -397,10 +397,7 @@
}
setContextVariables(ctxt);
- final Object val = ctxt.getAttribute(ScriptEngine.FILENAME);
- final String fileName = (val != null) ? val.toString() : "<eval>";
- 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");
--- a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java Thu May 16 11:47:51 2013 +0100
@@ -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<String> localDefs;
+ private final Deque<Set<String>> 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<String> localUses;
+ private final Deque<Set<String>> localUses;
- private final LexicalContext lexicalContext = new LexicalContext();
+ private final Deque<Type> 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<String> savedLocalDefs = localDefs;
- final Set<String> 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<Node> 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<Symbol> 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<Block> 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<Node> 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<Node> 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<Node> newStatements = new ArrayList<>(functionNode.getStatements());
- for(ListIterator<Node> 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<Block> 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<LexicalContextNode> 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<LexicalContextNode> 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<FunctionNode> fns = lexicalContext.getFunctions(); fns.hasNext();) {
- fns.next().setUsesAncestorScope();
+ for (final Iterator<FunctionNode> 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<Block> blocks = lexicalContext.getBlocks(block); blocks.hasNext();) {
+ for(final Iterator<Block> 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<CaseNode> 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<Node> 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<Block> 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<Node> 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<String>());
+ localUses.push(new HashSet<String>());
+ }
+
+ private void pushLocalsBlock() {
+ localDefs.push(localDefs.isEmpty() ? new HashSet<String>() : new HashSet<>(localDefs.peek()));
+ localUses.push(localUses.isEmpty() ? new HashSet<String>() : 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<Symbol> 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(" <NO SYMBOL>");
@@ -1746,7 +1805,7 @@
}
LOG.unindent();
- LOG.info(sb.toString());
+ LOG.info(sb);
}
return node;
--- a/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java Thu May 16 11:47:51 2013 +0100
@@ -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<Flag> 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 <clinit>
*/
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 <init>()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 <init>()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 <init>(...)V
*/
MethodEmitter init(final EnumSet<Flag> 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<Flag> flags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
+ return cw.visitMethod(Flag.getValue(flags), methodName, methodDescriptor(rtype, ptypes), null, null);
+ }
+
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Thu May 16 11:47:51 2013 +0100
@@ -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<SharedScopeCall, SharedScopeCall> 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<CompileUnit> compileUnits = new ArrayDeque<>();
+
+ /** Method emitter stack - every time we start a sub method (e.g. a split) we push one */
+ private final Deque<MethodEmitter> 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<Node> 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 extends LexicalContextNode> T push(T node) {
+ if(isDynamicScopeBoundary(node)) {
+ ++dynamicScopeCount;
+ }
+ return super.push(node);
+ }
+
+ @Override
+ public <T extends LexicalContextNode> 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<FunctionNode> 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<LexicalContextNode> 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<Block> blocks = lexicalContext.getBlocks(startingBlock); blocks.hasNext();) {
+ final String name = symbol.getName();
+ for(final Iterator<Block> 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<Symbol> 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<Node> 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<Node> 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<Node>(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<Symbol> 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<Symbol> 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<String> nameList = new ArrayList<>();
final List<Symbol> locals = new ArrayList<>();
-
// Initalize symbols and values
final List<Symbol> newSymbols = new ArrayList<>();
final List<Symbol> values = new ArrayList<>();
final boolean hasArguments = function.needsArguments();
- for (final Symbol symbol : symbols) {
- if (symbol.isInternal() || symbol.isThis()) {
+
+ final Iterator<Symbol> 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<Symbol> iter = block.symbolIterator();
+ final List<Symbol> 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 ? "<anonymous>" : function.getIdent().getName()));
+ printSymbols(block, (isFunctionBody ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : 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<Node> 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<Node> 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<Label> targets = method.getExternalTargets();
+
try {
// Wrap up this method.
- method.loadResult();
- method._return(getCurrentFunctionNode().getReturnType());
+
+ method.loadCompilerConstant(RETURN);
+ method._return(getLexicalContext().getCurrentFunction().getReturnType());
method.end();
+
+ pop(splitNode.getCompileUnit());
+ popMethodEmitter(method);
+
} catch (final Throwable t) {
Context.printStackTrace(t);
- final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getCurrentFunctionNode().getSource().getName());
+ final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getLexicalContext().getCurrentFunction().getSource().getName());
e.initCause(t);
throw e;
}
// Handle return from split method if there was one.
- final MethodEmitter caller = splitNode.getCaller();
- final List<Label> targets = splitNode.getExternalTargets();
- final int targetCount = targets.size();
-
- if (splitNode.hasReturn() || targetCount > 0) {
-
- caller.loadScope();
- caller.checkcast(Scope.class);
- caller.invoke(Scope.GET_SPLIT_STATE);
-
- // Split state is -1 for no split state, 0 for return, 1..n+1 for break/continue
- final Label breakLabel = new Label("no_split_state");
- final int low = splitNode.hasReturn() ? 0 : 1;
- final int labelCount = targetCount + 1 - low;
- final Label[] labels = new Label[labelCount];
+ final MethodEmitter caller = method;
+ final int targetCount = targets.size();
+
+ //no external jump targets or return in switch node
+ if (!hasReturn && targets.isEmpty()) {
+ return splitNode;
+ }
+
+ caller.loadCompilerConstant(SCOPE);
+ caller.checkcast(Scope.class);
+ caller.invoke(Scope.GET_SPLIT_STATE);
+
+ final Label breakLabel = new Label("no_split_state");
+ // Split state is -1 for no split state, 0 for return, 1..n+1 for break/continue
+
+ //the common case is that we don't need a switch
+ if (targetCount == 0) {
+ assert hasReturn;
+ caller.ifne(breakLabel);
+ //has to be zero
+ caller.label(new Label("split_return"));
+ method.loadCompilerConstant(RETURN);
+ caller._return(getLexicalContext().getCurrentFunction().getReturnType());
+ caller.label(breakLabel);
+ } else {
+ assert !targets.isEmpty();
+
+ final int low = hasReturn ? 0 : 1;
+ final int labelCount = targetCount + 1 - low;
+ final Label[] labels = new Label[labelCount];
for (int i = 0; i < labelCount; i++) {
- labels[i] = new Label("split_state_" + i);
+ labels[i] = new Label(i == 0 ? "split_return" : "split_" + targets.get(i - 1));
}
-
caller.tableswitch(low, targetCount, breakLabel, labels);
for (int i = low; i <= targetCount; i++) {
caller.label(labels[i - low]);
if (i == 0) {
- caller.loadResult();
- caller._return(getCurrentFunctionNode().getReturnType());
+ caller.loadCompilerConstant(RETURN);
+ caller._return(getLexicalContext().getCurrentFunction().getReturnType());
} else {
// Clear split state.
- caller.loadScope();
+ caller.loadCompilerConstant(SCOPE);
caller.checkcast(Scope.class);
caller.load(-1);
caller.invoke(Scope.SET_SPLIT_STATE);
- caller.splitAwareGoto(targets.get(i - 1));
+ caller.splitAwareGoto(getLexicalContext(), targets.get(i - 1));
}
}
-
caller.label(breakLabel);
}
@@ -1788,11 +1884,7 @@
}
@Override
- public Node enterSwitchNode(final SwitchNode switchNode) {
- if (switchNode.testResolved()) {
- return null;
- }
-
+ public boolean enterSwitchNode(final SwitchNode switchNode) {
final Node expression = switchNode.getExpression();
final Symbol tag = switchNode.getTag();
final boolean allInteger = tag.getSymbolType().isInteger();
@@ -1810,7 +1902,7 @@
if (cases.isEmpty()) {
method.label(breakLabel);
- return null;
+ return false;
}
if (allInteger) {
@@ -1916,15 +2008,11 @@
method.label(breakLabel);
}
- return null;
+ return false;
}
@Override
- public Node enterThrowNode(final ThrowNode throwNode) {
- if (throwNode.testResolved()) {
- return null;
- }
-
+ public boolean enterThrowNode(final ThrowNode throwNode) {
method._new(ECMAException.class).dup();
final Node expression = throwNode.getExpression();
@@ -1943,15 +2031,11 @@
method.athrow();
- return null;
+ return false;
}
@Override
- public Node enterTryNode(final TryNode tryNode) {
- if (tryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterTryNode(final TryNode tryNode) {
final Block body = tryNode.getBody();
final List<Block> catchBlocks = tryNode.getCatchBlocks();
final Symbol symbol = tryNode.getException();
@@ -1974,74 +2058,68 @@
method.store(symbol);
for (int i = 0; i < catchBlocks.size(); i++) {
- final Block saveBlock = getCurrentBlock();
final Block catchBlock = catchBlocks.get(i);
- setCurrentBlock(catchBlock);
-
- try {
- enterBlock(catchBlock);
-
- final CatchNode catchNode = (CatchNode)catchBlocks.get(i).getStatements().get(0);
- final IdentNode exception = catchNode.getException();
- final Node exceptionCondition = catchNode.getExceptionCondition();
- final Block catchBody = catchNode.getBody();
-
- if (catchNode.isSyntheticRethrow()) {
- // Generate catch body (inlined finally) and rethrow exception
- catchBody.accept(this);
- method.load(symbol).athrow();
- lexicalContext.pop(catchBlock);
- continue;
+ //TODO this is very ugly - try not to call enter/leave methods directly
+ //better to use the implicit lexical context scoping given by the visitor's
+ //accept method.
+ getLexicalContext().push(catchBlock);
+ enterBlock(catchBlock);
+
+ final CatchNode catchNode = (CatchNode)catchBlocks.get(i).getStatements().get(0);
+ final IdentNode exception = catchNode.getException();
+ final Node exceptionCondition = catchNode.getExceptionCondition();
+ final Block catchBody = catchNode.getBody();
+
+ new Store<IdentNode>(exception) {
+ @Override
+ protected void storeNonDiscard() {
+ return;
}
-
- new Store<IdentNode>(exception) {
- @Override
- protected void evaluate() {
- /*
- * If caught object is an instance of ECMAException, then
- * bind obj.thrown to the script catch var. Or else bind the
- * caught object itself to the script catch var.
- */
- final Label notEcmaException = new Label("no_ecma_exception");
-
- method.load(symbol).dup()._instanceof(ECMAException.class).ifeq(notEcmaException);
- method.checkcast(ECMAException.class); //TODO is this necessary?
- method.getField(ECMAException.THROWN);
- method.label(notEcmaException);
- }
- }.store();
-
- final Label next;
-
- if (exceptionCondition != null) {
- next = new Label("next");
- load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next);
+ @Override
+ protected void evaluate() {
+ /*
+ * If caught object is an instance of ECMAException, then
+ * bind obj.thrown to the script catch var. Or else bind the
+ * caught object itself to the script catch var.
+ */
+ final Label notEcmaException = new Label("no_ecma_exception");
+
+ method.load(symbol).dup()._instanceof(ECMAException.class).ifeq(notEcmaException);
+ method.checkcast(ECMAException.class); //TODO is this necessary?
+ method.getField(ECMAException.THROWN);
+ method.label(notEcmaException);
+ }
+ }.store();
+
+ final Label next;
+
+ if (exceptionCondition != null) {
+ next = new Label("next");
+ load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next);
+ } else {
+ next = null;
+ }
+
+ catchBody.accept(this);
+
+ if (i + 1 != catchBlocks.size() && !catchBody.hasTerminalFlags()) {
+ method._goto(skip);
+ }
+
+ if (next != null) {
+ if (i + 1 == catchBlocks.size()) {
+ // no next catch block - rethrow if condition failed
+ method._goto(skip);
+ method.label(next);
+ method.load(symbol).athrow();
} else {
- next = null;
- }
-
- catchBody.accept(this);
-
- if (i + 1 != catchBlocks.size() && !catchBody.hasTerminalFlags()) {
- method._goto(skip);
+ method.label(next);
}
-
- if (next != null) {
- if (i + 1 == catchBlocks.size()) {
- // no next catch block - rethrow if condition failed
- method._goto(skip);
- method.label(next);
- method.load(symbol).athrow();
- } else {
- method.label(next);
- }
- }
-
- leaveBlock(catchBlock);
- } finally {
- setCurrentBlock(saveBlock);
}
+
+ leaveBlock(catchBlock);
+ getLexicalContext().pop(catchBlock);
}
method.label(skip);
@@ -2049,15 +2127,15 @@
// Finally body is always inlined elsewhere so it doesn't need to be emitted
- return null;
+ return false;
}
@Override
- public Node enterVarNode(final VarNode varNode) {
+ public boolean enterVarNode(final VarNode varNode) {
final Node init = varNode.getInit();
- if (varNode.testResolved() || init == null) {
- return null;
+ if (init == null) {
+ return false;
}
final Symbol varSymbol = varNode.getSymbol();
@@ -2067,7 +2145,7 @@
final boolean needsScope = varSymbol.isScope();
if (needsScope) {
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
}
load(init);
@@ -2087,22 +2165,18 @@
method.store(varSymbol);
}
- return null;
+ return false;
}
@Override
- public Node enterWhileNode(final WhileNode whileNode) {
- if (whileNode.testResolved()) {
- return null;
- }
-
+ public boolean enterWhileNode(final WhileNode whileNode) {
final Node test = whileNode.getTest();
final Block body = whileNode.getBody();
final Label breakLabel = whileNode.getBreakLabel();
final Label continueLabel = whileNode.getContinueLabel();
final Label loopLabel = new Label("loop");
- if (!(whileNode instanceof DoWhileNode)) {
+ if (!whileNode.isDoWhile()) {
method._goto(continueLabel);
}
@@ -2114,90 +2188,95 @@
method.label(breakLabel);
}
- return null;
+ return false;
}
private void closeWith() {
- method.loadScope();
- method.invoke(ScriptRuntime.CLOSE_WITH);
- method.storeScope();
+ if(method.hasScope()) {
+ method.loadCompilerConstant(SCOPE);
+ method.invoke(ScriptRuntime.CLOSE_WITH);
+ method.storeCompilerConstant(SCOPE);
+ }
}
@Override
- public Node enterWithNode(final WithNode withNode) {
- if (withNode.testResolved()) {
- return null;
- }
-
+ public boolean enterWithNode(final WithNode withNode) {
final Node expression = withNode.getExpression();
final Node body = withNode.getBody();
- final Label tryLabel = new Label("with_try");
- final Label endLabel = new Label("with_end");
- final Label catchLabel = new Label("with_catch");
- final Label exitLabel = new Label("with_exit");
-
- method.label(tryLabel);
-
- method.loadScope();
+ // It is possible to have a "pathological" case where the with block does not reference *any* identifiers. It's
+ // pointless, but legal. In that case, if nothing else in the method forced the assignment of a slot to the
+ // scope object, its' possible that it won't have a slot assigned. In this case we'll only evaluate expression
+ // for its side effect and visit the body, and not bother opening and closing a WithObject.
+ final boolean hasScope = method.hasScope();
+
+ final Label tryLabel;
+ if(hasScope) {
+ tryLabel = new Label("with_try");
+ method.label(tryLabel);
+ method.loadCompilerConstant(SCOPE);
+ } else {
+ tryLabel = null;
+ }
+
load(expression);
-
assert expression.getType().isObject() : "with expression needs to be object: " + expression;
- method.invoke(ScriptRuntime.OPEN_WITH);
- method.storeScope();
-
+ if(hasScope) {
+ // Construct a WithObject if we have a scope
+ method.invoke(ScriptRuntime.OPEN_WITH);
+ method.storeCompilerConstant(SCOPE);
+ } else {
+ // We just loaded the expression for its side effect; discard it
+ method.pop();
+ }
+
+
+ // Always process body
body.accept(this);
- if (!body.isTerminal()) {
+ if(hasScope) {
+ // Ensure we always close the WithObject
+ final Label endLabel = new Label("with_end");
+ final Label catchLabel = new Label("with_catch");
+ final Label exitLabel = new Label("with_exit");
+
+ if (!body.isTerminal()) {
+ closeWith();
+ method._goto(exitLabel);
+ }
+
+ method.label(endLabel);
+
+ method._catch(catchLabel);
closeWith();
- method._goto(exitLabel);
+ method.athrow();
+
+ method.label(exitLabel);
+
+ method._try(tryLabel, endLabel, catchLabel);
}
-
- method.label(endLabel);
-
- method._catch(catchLabel);
- closeWith();
- method.athrow();
-
- method.label(exitLabel);
-
- method._try(tryLabel, endLabel, catchLabel);
-
- return null;
+ return false;
}
@Override
- public Node enterADD(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterADD(final UnaryNode unaryNode) {
load(unaryNode.rhs());
assert unaryNode.rhs().getType().isNumber();
method.store(unaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterBIT_NOT(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterBIT_NOT(final UnaryNode unaryNode) {
load(unaryNode.rhs()).convert(Type.INT).load(-1).xor().store(unaryNode.getSymbol());
-
- return null;
+ return false;
}
// do this better with convert calls to method. TODO
@Override
- public Node enterCONVERT(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterCONVERT(final UnaryNode unaryNode) {
final Node rhs = unaryNode.rhs();
final Type to = unaryNode.getType();
@@ -2230,15 +2309,11 @@
method.store(unaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterDECINC(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterDECINC(final UnaryNode unaryNode) {
final Node rhs = unaryNode.rhs();
final Type type = unaryNode.getType();
final TokenType tokenType = unaryNode.tokenType();
@@ -2282,32 +2357,28 @@
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterDISCARD(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterDISCARD(final UnaryNode unaryNode) {
final Node rhs = unaryNode.rhs();
+ // System.err.println("**** Enter discard " + unaryNode);
+ discard.push(rhs);
load(rhs);
- if (rhs.shouldDiscard()) {
+ if (discard.peek() == rhs) {
+ assert !rhs.isAssignment();
method.pop();
+ discard.pop();
}
-
- return null;
+ // System.err.println("**** Leave discard " + unaryNode);
+ return false;
}
@Override
- public Node enterNEW(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterNEW(final UnaryNode unaryNode) {
final CallNode callNode = (CallNode)unaryNode.rhs();
final List<Node> args = callNode.getArgs();
@@ -2317,15 +2388,11 @@
method.dynamicNew(1 + loadArgs(args), getCallSiteFlags());
method.store(unaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterNOT(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterNOT(final UnaryNode unaryNode) {
final Node rhs = unaryNode.rhs();
load(rhs);
@@ -2342,18 +2409,14 @@
method.label(afterLabel);
method.store(unaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterSUB(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterSUB(final UnaryNode unaryNode) {
load(unaryNode.rhs()).neg().store(unaryNode.getSymbol());
- return null;
+ return false;
}
private Node enterNumericAdd(final Node lhs, final Node rhs, final Type type, final Symbol symbol) {
@@ -2366,11 +2429,7 @@
}
@Override
- public Node enterADD(final BinaryNode binaryNode) {
- if (binaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterADD(final BinaryNode binaryNode) {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
@@ -2384,14 +2443,10 @@
method.store(binaryNode.getSymbol());
}
- return null;
+ return false;
}
- private Node enterAND_OR(final BinaryNode binaryNode) {
- if (binaryNode.testResolved()) {
- return null;
- }
-
+ private boolean enterAND_OR(final BinaryNode binaryNode) {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
@@ -2410,20 +2465,16 @@
method.label(skip);
method.store(binaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterAND(final BinaryNode binaryNode) {
+ public boolean enterAND(final BinaryNode binaryNode) {
return enterAND_OR(binaryNode);
}
@Override
- public Node enterASSIGN(final BinaryNode binaryNode) {
- if (binaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterASSIGN(final BinaryNode binaryNode) {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
@@ -2442,7 +2493,7 @@
}
}.store();
- return null;
+ return false;
}
/**
@@ -2473,14 +2524,6 @@
this.opType = opType;
}
- @Override
- public void store() {
- if (assignNode.testResolved()) {
- return;
- }
- super.store();
- }
-
protected abstract void op();
@Override
@@ -2493,35 +2536,43 @@
}
@Override
- public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
assert RuntimeNode.Request.ADD.canSpecialize();
+ final Type lhsType = binaryNode.lhs().getType();
+ final Type rhsType = binaryNode.rhs().getType();
final boolean specialize = binaryNode.getType() == Type.OBJECT;
new AssignOp(binaryNode) {
- @Override
- protected boolean isSelfModifying() {
- return !specialize;
- }
@Override
protected void op() {
- method.add();
+ if (specialize) {
+ method.dynamicRuntimeCall(
+ new SpecializedRuntimeNode(
+ Request.ADD,
+ new Type[] {
+ lhsType,
+ rhsType,
+ },
+ Type.OBJECT).getInitialName(),
+ Type.OBJECT,
+ Request.ADD);
+ } else {
+ method.add();
+ }
}
@Override
protected void evaluate() {
- if (specialize && specializationCheck(Request.ADD, assignNode, Arrays.asList(assignNode.lhs(), assignNode.rhs()))) {
- return;
- }
super.evaluate();
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
@@ -2529,11 +2580,11 @@
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
@@ -2541,11 +2592,11 @@
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
@@ -2553,11 +2604,11 @@
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
new AssignOp(binaryNode) {
@Override
protected void op() {
@@ -2565,11 +2616,11 @@
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
new AssignOp(binaryNode) {
@Override
protected void op() {
@@ -2577,11 +2628,11 @@
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
new AssignOp(binaryNode) {
@Override
protected void op() {
@@ -2589,11 +2640,11 @@
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
@@ -2601,11 +2652,11 @@
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
@@ -2613,24 +2664,24 @@
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
method.shr();
- method.convert(Type.LONG).load(0xffff_ffffL).and();
+ method.convert(Type.LONG).load(JSType.MAX_UINT).and();
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
new AssignOp(binaryNode) {
@Override
protected void op() {
@@ -2638,7 +2689,7 @@
}
}.store();
- return null;
+ return false;
}
/**
@@ -2649,9 +2700,6 @@
protected abstract void op();
protected void evaluate(final BinaryNode node) {
- if (node.testResolved()) {
- return;
- }
load(node.lhs());
load(node.rhs());
op();
@@ -2660,7 +2708,7 @@
}
@Override
- public Node enterBIT_AND(final BinaryNode binaryNode) {
+ public boolean enterBIT_AND(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2668,11 +2716,11 @@
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterBIT_OR(final BinaryNode binaryNode) {
+ public boolean enterBIT_OR(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2680,11 +2728,11 @@
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterBIT_XOR(final BinaryNode binaryNode) {
+ public boolean enterBIT_XOR(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2692,14 +2740,10 @@
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
- private Node enterComma(final BinaryNode binaryNode) {
- if (binaryNode.testResolved()) {
- return null;
- }
-
+ private boolean enterComma(final BinaryNode binaryNode) {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
@@ -2707,21 +2751,21 @@
load(rhs);
method.store(binaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterCOMMARIGHT(final BinaryNode binaryNode) {
+ public boolean enterCOMMARIGHT(final BinaryNode binaryNode) {
return enterComma(binaryNode);
}
@Override
- public Node enterCOMMALEFT(final BinaryNode binaryNode) {
+ public boolean enterCOMMALEFT(final BinaryNode binaryNode) {
return enterComma(binaryNode);
}
@Override
- public Node enterDIV(final BinaryNode binaryNode) {
+ public boolean enterDIV(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2729,10 +2773,10 @@
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
- private Node enterCmp(final Node lhs, final Node rhs, final Condition cond, final Type type, final Symbol symbol) {
+ private boolean enterCmp(final Node lhs, final Node rhs, final Condition cond, final Type type, final Symbol symbol) {
final Type lhsType = lhs.getType();
final Type rhsType = rhs.getType();
@@ -2758,48 +2802,45 @@
method.convert(type);
method.store(symbol);
- return null;
+ return false;
}
- private Node enterCmp(final BinaryNode binaryNode, final Condition cond) {
- if (binaryNode.testResolved()) {
- return null;
- }
+ private boolean enterCmp(final BinaryNode binaryNode, final Condition cond) {
return enterCmp(binaryNode.lhs(), binaryNode.rhs(), cond, binaryNode.getType(), binaryNode.getSymbol());
}
@Override
- public Node enterEQ(final BinaryNode binaryNode) {
+ public boolean enterEQ(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.EQ);
}
@Override
- public Node enterEQ_STRICT(final BinaryNode binaryNode) {
+ public boolean enterEQ_STRICT(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.EQ);
}
@Override
- public Node enterGE(final BinaryNode binaryNode) {
+ public boolean enterGE(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.GE);
}
@Override
- public Node enterGT(final BinaryNode binaryNode) {
+ public boolean enterGT(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.GT);
}
@Override
- public Node enterLE(final BinaryNode binaryNode) {
+ public boolean enterLE(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.LE);
}
@Override
- public Node enterLT(final BinaryNode binaryNode) {
+ public boolean enterLT(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.LT);
}
@Override
- public Node enterMOD(final BinaryNode binaryNode) {
+ public boolean enterMOD(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2807,11 +2848,11 @@
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterMUL(final BinaryNode binaryNode) {
+ public boolean enterMUL(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2819,26 +2860,26 @@
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterNE(final BinaryNode binaryNode) {
+ public boolean enterNE(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.NE);
}
@Override
- public Node enterNE_STRICT(final BinaryNode binaryNode) {
+ public boolean enterNE_STRICT(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.NE);
}
@Override
- public Node enterOR(final BinaryNode binaryNode) {
+ public boolean enterOR(final BinaryNode binaryNode) {
return enterAND_OR(binaryNode);
}
@Override
- public Node enterSAR(final BinaryNode binaryNode) {
+ public boolean enterSAR(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2846,11 +2887,11 @@
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterSHL(final BinaryNode binaryNode) {
+ public boolean enterSHL(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2858,24 +2899,24 @@
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterSHR(final BinaryNode binaryNode) {
+ public boolean enterSHR(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
method.shr();
- method.convert(Type.LONG).load(0xffff_ffffL).and();
+ method.convert(Type.LONG).load(JSType.MAX_UINT).and();
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterSUB(final BinaryNode binaryNode) {
+ public boolean enterSUB(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2883,18 +2924,11 @@
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
- /*
- * Ternary visits.
- */
@Override
- public Node enterTernaryNode(final TernaryNode ternaryNode) {
- if (ternaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterTernaryNode(final TernaryNode ternaryNode) {
final Node lhs = ternaryNode.lhs();
final Node rhs = ternaryNode.rhs();
final Node third = ternaryNode.third();
@@ -2926,7 +2960,7 @@
method.label(exitLabel);
method.store(symbol);
- return null;
+ return false;
}
/**
@@ -2955,7 +2989,7 @@
if (scopeCalls.containsKey(scopeCall)) {
return scopeCalls.get(scopeCall);
}
- scopeCall.setClassAndName(getCurrentCompileUnit(), getCurrentFunctionNode().uniqueName("scopeCall"));
+ scopeCall.setClassAndName(unit, getLexicalContext().getCurrentFunction().uniqueName("scopeCall"));
scopeCalls.put(scopeCall, scopeCall);
return scopeCall;
}
@@ -2974,7 +3008,7 @@
if (scopeCalls.containsKey(scopeCall)) {
return scopeCalls.get(scopeCall);
}
- scopeCall.setClassAndName(getCurrentCompileUnit(), getCurrentFunctionNode().uniqueName("scopeCall"));
+ scopeCall.setClassAndName(unit, getLexicalContext().getCurrentFunction().uniqueName("scopeCall"));
scopeCalls.put(scopeCall, scopeCall);
return scopeCall;
}
@@ -3037,9 +3071,6 @@
/** The target node to store to, e.g. x */
private final Node target;
- /** Should the result always be discarded, no matter what? */
- private final boolean alwaysDiscard;
-
/** How deep on the stack do the arguments go if this generates an indy call */
private int depth;
@@ -3055,7 +3086,6 @@
protected Store(final T assignNode, final Node target) {
this.assignNode = assignNode;
this.target = target;
- this.alwaysDiscard = assignNode == target;
}
/**
@@ -3077,21 +3107,21 @@
private void prologue() {
final Symbol targetSymbol = target.getSymbol();
- final Symbol scopeSymbol = getCurrentFunctionNode().getScopeNode().getSymbol();
+ final Symbol scopeSymbol = getLexicalContext().getCurrentFunction().compilerConstant(SCOPE);
/**
* This loads the parts of the target, e.g base and index. they are kept
* on the stack throughout the store and used at the end to execute it
*/
- target.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
+ target.accept(new NodeVisitor() {
@Override
- public Node enterIdentNode(final IdentNode node) {
+ public boolean enterIdentNode(final IdentNode node) {
if (targetSymbol.isScope()) {
method.load(scopeSymbol);
depth++;
}
- return null;
+ return false;
}
private void enterBaseNode() {
@@ -3109,13 +3139,13 @@
}
@Override
- public Node enterAccessNode(final AccessNode node) {
+ public boolean enterAccessNode(final AccessNode node) {
enterBaseNode();
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode node) {
+ public boolean enterIndexNode(final IndexNode node) {
enterBaseNode();
final Node index = node.getIndex();
@@ -3131,14 +3161,14 @@
method.dup(1);
}
- return null;
+ return false;
}
});
}
private Symbol quickSymbol(final Type type) {
- return quickSymbol(type, QUICK_PREFIX.tag());
+ return quickSymbol(type, QUICK_PREFIX.symbolName());
}
/**
@@ -3151,22 +3181,28 @@
* @return the quick symbol
*/
private Symbol quickSymbol(final Type type, final String prefix) {
- final String name = getCurrentFunctionNode().uniqueName(prefix);
- final Symbol symbol = new Symbol(name, IS_TEMP | IS_INTERNAL, null, null);
+ final String name = getLexicalContext().getCurrentFunction().uniqueName(prefix);
+ final Symbol symbol = new Symbol(name, IS_TEMP | IS_INTERNAL);
symbol.setType(type);
- symbol.setSlot(getCurrentBlock().getFrame().getSlotCount());
+ final int quickSlot = nextFreeSlots[nextFreeSlotsSize - 1];
+ nextFreeSlots[nextFreeSlotsSize - 1] = quickSlot + symbol.slotCount();
+ symbol.setSlot(quickSlot);
return symbol;
}
// store the result that "lives on" after the op, e.g. "i" in i++ postfix.
protected void storeNonDiscard() {
- if (assignNode.shouldDiscard() || alwaysDiscard) {
- assignNode.setDiscard(false);
+ if (discard.peek() == assignNode) {
+ assert assignNode.isAssignment();
+ discard.pop();
return;
}
+ //System.err.println("Store with out discard that shouldn't just return " + assignNode);
+ //new Throwable().printStackTrace();
+
final Symbol symbol = assignNode.getSymbol();
if (symbol.hasSlot()) {
method.dup().store(symbol);
@@ -3191,22 +3227,22 @@
*/
method.convert(target.getType());
- target.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
+ target.accept(new NodeVisitor() {
@Override
- protected Node enterDefault(Node node) {
+ protected boolean enterDefault(Node node) {
throw new AssertionError("Unexpected node " + node + " in store epilogue");
}
@Override
- public Node enterUnaryNode(final UnaryNode node) {
- if(node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) {
+ public boolean enterUnaryNode(final UnaryNode node) {
+ if (node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) {
method.convert(node.rhs().getType());
}
- return node;
+ return true;
}
@Override
- public Node enterIdentNode(final IdentNode node) {
+ public boolean enterIdentNode(final IdentNode node) {
final Symbol symbol = node.getSymbol();
assert symbol != null;
if (symbol.isScope()) {
@@ -3218,20 +3254,20 @@
} else {
method.store(symbol);
}
- return null;
+ return false;
}
@Override
- public Node enterAccessNode(final AccessNode node) {
+ public boolean enterAccessNode(final AccessNode node) {
method.dynamicSet(node.getProperty().getType(), node.getProperty().getName(), getCallSiteFlags());
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode node) {
+ public boolean enterIndexNode(final IndexNode node) {
method.dynamicSetIndex(getCallSiteFlags());
- return null;
+ return false;
}
});
@@ -3250,10 +3286,23 @@
method.load(quick);
}
}
-
}
- private void newFunctionObject(final FunctionNode functionNode) {
+ private void newFunctionObject(final FunctionNode functionNode, final FunctionNode originalFunctionNode) {
+ final LexicalContext lc = getLexicalContext();
+ assert lc.peek() == functionNode;
+ // We don't emit a ScriptFunction on stack for:
+ // 1. the outermost compiled function (as there's no code being generated in its outer context that'd need it
+ // as a callee), and
+ // 2. for functions that are immediately called upon definition and they don't need a callee, e.g. (function(){})().
+ // Such immediately-called functions are invoked using INVOKESTATIC (see enterFunctionNode() of the embedded
+ // visitor of enterCallNode() for details), and if they don't need a callee, they don't have it on their
+ // static method's parameter list.
+ if(lc.getOutermostFunction() == functionNode ||
+ (!functionNode.needsCallee()) && lc.isFunctionDefinedInCurrentCall(originalFunctionNode)) {
+ return;
+ }
+
final boolean isLazy = functionNode.isLazy();
new ObjectCreator(this, new ArrayList<String>(), new ArrayList<Symbol>(), false, false) {
@@ -3265,7 +3314,7 @@
loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), Compiler.binaryName(getClassName()), makeMap()));
if (isLazy || functionNode.needsParentScope()) {
- m.loadScope();
+ m.loadCompilerConstant(SCOPE);
} else {
m.loadNull();
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java Thu May 16 11:47:51 2013 +0100
@@ -15,6 +15,7 @@
import java.util.HashSet;
import java.util.Set;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
@@ -29,8 +30,8 @@
import jdk.nashorn.internal.runtime.Timing;
/**
- * A compilation phase is a step in the processes of turning a JavaScript FunctionNode
- * into bytecode. It has an optional return value.
+ * A compilation phase is a step in the processes of turning a JavaScript
+ * FunctionNode into bytecode. It has an optional return value.
*/
enum CompilationPhase {
@@ -41,77 +42,87 @@
*/
LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn0) {
/*
- * For lazy compilation, we might be given a node previously marked as lazy
- * to compile as the outermost function node in the compiler. Unmark it
- * so it can be compiled and not cause recursion. Make sure the return type
- * is unknown so it can be correctly deduced. Return types are always
- * Objects in Lazy nodes as we haven't got a change to generate code for
- * them and decude its parameter specialization
+ * For lazy compilation, we might be given a node previously marked
+ * as lazy to compile as the outermost function node in the
+ * compiler. Unmark it so it can be compiled and not cause
+ * recursion. Make sure the return type is unknown so it can be
+ * correctly deduced. Return types are always Objects in Lazy nodes
+ * as we haven't got a change to generate code for them and decude
+ * its parameter specialization
*
- * TODO: in the future specializations from a callsite will be passed here
- * so we can generate a better non-lazy version of a function from a trampoline
+ * TODO: in the future specializations from a callsite will be
+ * passed here so we can generate a better non-lazy version of a
+ * function from a trampoline
*/
- //compute the signature from the callsite - todo - now just clone object params
+
final FunctionNode outermostFunctionNode = compiler.getFunctionNode();
- outermostFunctionNode.setIsLazy(false);
- outermostFunctionNode.setReturnType(Type.UNKNOWN);
+ assert outermostFunctionNode == fn0;
final Set<FunctionNode> neverLazy = new HashSet<>();
- final Set<FunctionNode> lazy = new HashSet<>();
+ final Set<FunctionNode> lazy = new HashSet<>();
+
+ FunctionNode newFunctionNode = outermostFunctionNode;
- outermostFunctionNode.accept(new NodeVisitor() {
- // self references are done with invokestatic and thus cannot have trampolines - never lazy
+ newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor() {
+ // self references are done with invokestatic and thus cannot
+ // have trampolines - never lazy
@Override
- public Node enterCallNode(final CallNode node) {
+ public boolean enterCallNode(final CallNode node) {
final Node callee = node.getFunction();
if (callee instanceof FunctionNode) {
neverLazy.add(((FunctionNode)callee));
- return null;
+ return false;
}
- return node;
+ return true;
}
+ //any function that isn't the outermost one must be marked as lazy
@Override
- public Node enterFunctionNode(final FunctionNode node) {
- if (node == outermostFunctionNode) {
- return node;
- }
+ public boolean enterFunctionNode(final FunctionNode node) {
assert compiler.isLazy();
lazy.add(node);
-
- //also needs scope, potentially needs arguments etc etc
-
- return node;
+ return true;
}
});
+ //at least one method is non lazy - the outermost one
+ neverLazy.add(newFunctionNode);
+
for (final FunctionNode node : neverLazy) {
- Compiler.LOG.fine("Marking " + node.getName() + " as non lazy, as it's a self reference");
- node.setIsLazy(false);
+ Compiler.LOG.fine(
+ "Marking ",
+ node.getName(),
+ " as non lazy, as it's a self reference");
lazy.remove(node);
}
- outermostFunctionNode.accept(new NodeOperatorVisitor() {
- private final LexicalContext lexicalContext = new LexicalContext();
+ newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeOperatorVisitor() {
@Override
- public Node enterFunctionNode(FunctionNode functionNode) {
- lexicalContext.push(functionNode);
- if(lazy.contains(functionNode)) {
- Compiler.LOG.fine("Marking " + functionNode.getName() + " as lazy");
- functionNode.setIsLazy(true);
- lexicalContext.getParentFunction(functionNode).setHasLazyChildren();
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+ final LexicalContext lc = getLexicalContext();
+ if (lazy.contains(functionNode)) {
+ Compiler.LOG.fine(
+ "Marking ",
+ functionNode.getName(),
+ " as lazy");
+ final FunctionNode parent = lc.getParentFunction(functionNode);
+ assert parent != null;
+ lc.setFlag(parent, FunctionNode.HAS_LAZY_CHILDREN);
+ lc.setFlag(parent.getBody(), Block.NEEDS_SCOPE);
+ lc.setFlag(functionNode, FunctionNode.IS_LAZY);
+ return functionNode;
}
- return functionNode;
- }
- @Override
- public Node leaveFunctionNode(FunctionNode functionNode) {
- lexicalContext.pop(functionNode);
- return functionNode;
+
+ return functionNode.
+ clearFlag(lc, FunctionNode.IS_LAZY).
+ setReturnType(lc, Type.UNKNOWN);
}
});
+
+ return newFunctionNode;
}
@Override
@@ -121,13 +132,13 @@
},
/*
- * Constant folding pass
- * Simple constant folding that will make elementary constructs go away
+ * Constant folding pass Simple constant folding that will make elementary
+ * constructs go away
*/
CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
- fn.accept(new FoldConstants());
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+ return (FunctionNode)fn.accept(new FoldConstants());
}
@Override
@@ -137,18 +148,16 @@
},
/*
- * Lower (Control flow pass)
- * Finalizes the control flow. Clones blocks for finally constructs and
- * similar things. Establishes termination criteria for nodes
- * Guarantee return instructions to method making sure control flow
- * cannot fall off the end. Replacing high level nodes with lower such
- * as runtime nodes where applicable.
- *
+ * Lower (Control flow pass) Finalizes the control flow. Clones blocks for
+ * finally constructs and similar things. Establishes termination criteria
+ * for nodes Guarantee return instructions to method making sure control
+ * flow cannot fall off the end. Replacing high level nodes with lower such
+ * as runtime nodes where applicable.
*/
LOWERING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
- fn.accept(new Lower());
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+ return (FunctionNode)fn.accept(new Lower());
}
@Override
@@ -158,13 +167,27 @@
},
/*
- * Attribution
- * Assign symbols and types to all nodes.
+ * Attribution Assign symbols and types to all nodes.
*/
ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
- fn.accept(new Attr());
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+ return (FunctionNode)initReturnTypes(fn).accept(new Attr());
+ }
+
+ /**
+ * Pessimistically set all lazy functions' return types to Object
+ * @param functionNode node where to start iterating
+ */
+ private FunctionNode initReturnTypes(final FunctionNode functionNode) {
+ return (FunctionNode)functionNode.accept(new NodeVisitor() {
+ @Override
+ public Node leaveFunctionNode(final FunctionNode node) {
+ return node.isLazy() ?
+ node.setReturnType(getLexicalContext(), Type.OBJECT) :
+ node.setReturnType(getLexicalContext(), Type.UNKNOWN);
+ }
+ });
}
@Override
@@ -174,25 +197,35 @@
},
/*
- * Splitter
- * Split the AST into several compile units based on a size heuristic
- * Splitter needs attributed AST for weight calculations (e.g. is
- * a + b a ScriptRuntime.ADD with call overhead or a dadd with much
- * less). Split IR can lead to scope information being changed.
+ * Splitter Split the AST into several compile units based on a size
+ * heuristic Splitter needs attributed AST for weight calculations (e.g. is
+ * a + b a ScriptRuntime.ADD with call overhead or a dadd with much less).
+ * Split IR can lead to scope information being changed.
*/
SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
- new Splitter(compiler, fn, outermostCompileUnit).split();
+ final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn);
- assert fn.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + fn.getCompileUnit() + ") != " + outermostCompileUnit;
+ assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
- if (fn.isStrictMode()) {
+ if (newFunctionNode.isStrict()) {
assert compiler.getStrictMode();
compiler.setStrictMode(true);
}
+
+ /*
+ newFunctionNode.accept(new NodeVisitor() {
+ @Override
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ assert functionNode.getCompileUnit() != null : functionNode.getName() + " " + Debug.id(functionNode) + " has no compile unit";
+ return true;
+ }
+ });*/
+
+ return newFunctionNode;
}
@Override
@@ -204,30 +237,32 @@
/*
* FinalizeTypes
*
- * This pass finalizes the types for nodes. If Attr created wider types than
- * known during the first pass, convert nodes are inserted or access nodes
- * are specialized where scope accesses.
+ * This pass finalizes the types for nodes. If Attr created wider types than
+ * known during the first pass, convert nodes are inserted or access nodes
+ * are specialized where scope accesses.
*
- * Runtime nodes may be removed and primitivized or reintroduced depending
- * on information that was established in Attr.
+ * Runtime nodes may be removed and primitivized or reintroduced depending
+ * on information that was established in Attr.
*
* Contract: all variables must have slot assignments and scope assignments
* before type finalization.
*/
TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final ScriptEnvironment env = compiler.getEnv();
- fn.accept(new FinalizeTypes());
+ final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes());
if (env._print_lower_ast) {
- env.getErr().println(new ASTWriter(fn));
+ env.getErr().println(new ASTWriter(newFunctionNode));
}
if (env._print_lower_parse) {
- env.getErr().println(new PrintVisitor(fn));
- }
+ env.getErr().println(new PrintVisitor(newFunctionNode));
+ }
+
+ return newFunctionNode;
}
@Override
@@ -239,31 +274,21 @@
/*
* Bytecode generation:
*
- * Generate the byte code class(es) resulting from the compiled FunctionNode
+ * Generate the byte code class(es) resulting from the compiled FunctionNode
*/
BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final ScriptEnvironment env = compiler.getEnv();
+ FunctionNode newFunctionNode = fn;
try {
final CodeGenerator codegen = new CodeGenerator(compiler);
- fn.accept(codegen);
+ newFunctionNode = (FunctionNode)newFunctionNode.accept(codegen);
codegen.generateScopeCalls();
- fn.accept(new NodeOperatorVisitor() {
- @Override
- public Node enterFunctionNode(FunctionNode functionNode) {
- if(functionNode.isLazy()) {
- functionNode.resetResolved();
- return null;
- }
- return fn;
- }
- });
-
} catch (final VerifyError e) {
if (env._verify_code || env._print_code) {
- env.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
+ env.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
if (env._dump_on_error) {
e.printStackTrace(env.getErr());
}
@@ -283,25 +308,25 @@
compiler.addClass(className, bytecode);
- //should could be printed to stderr for generate class?
+ // should could be printed to stderr for generate class?
if (env._print_code) {
final StringBuilder sb = new StringBuilder();
- sb.append("class: " + className).
- append('\n').
- append(ClassEmitter.disassemble(bytecode)).
- append("=====");
+ sb.append("class: " + className).append('\n')
+ .append(ClassEmitter.disassemble(bytecode))
+ .append("=====");
env.getErr().println(sb);
}
- //should we verify the generated code?
+ // should we verify the generated code?
if (env._verify_code) {
compiler.getCodeInstaller().verify(bytecode);
}
- //should code be dumped to disk - only valid in compile_only mode?
+ // should code be dumped to disk - only valid in compile_only
+ // mode?
if (env._dest_dir != null && env._compile_only) {
final String fileName = className.replace('.', File.separatorChar) + ".class";
- final int index = fileName.lastIndexOf(File.separatorChar);
+ final int index = fileName.lastIndexOf(File.separatorChar);
if (index != -1) {
final File dir = new File(fileName.substring(0, index));
@@ -314,11 +339,18 @@
fos.write(bytecode);
}
} catch (final IOException e) {
- Compiler.LOG.warning("Skipping class dump for " + className + ": " + ECMAErrors.getMessage("io.error.cant.write", dir.toString()));
+ Compiler.LOG.warning("Skipping class dump for ",
+ className,
+ ": ",
+ ECMAErrors.getMessage(
+ "io.error.cant.write",
+ dir.toString()));
}
}
}
}
+
+ return newFunctionNode;
}
@Override
@@ -340,26 +372,28 @@
return functionNode.hasState(pre);
}
- protected void begin(final FunctionNode functionNode) {
+ protected FunctionNode begin(final FunctionNode functionNode) {
if (pre != null) {
- //check that everything in pre is present
+ // check that everything in pre is present
for (final CompilationState state : pre) {
assert functionNode.hasState(state);
}
- //check that nothing else is present
+ // check that nothing else is present
for (final CompilationState state : CompilationState.values()) {
assert !(functionNode.hasState(state) && !pre.contains(state));
}
}
startTime = System.currentTimeMillis();
+ return functionNode;
}
- protected void end(final FunctionNode functionNode) {
+ protected FunctionNode end(final FunctionNode functionNode) {
endTime = System.currentTimeMillis();
Timing.accumulateTime(toString(), endTime - startTime);
isFinished = true;
+ return functionNode;
}
boolean isFinished() {
@@ -374,15 +408,13 @@
return endTime;
}
- abstract void transform(final Compiler compiler, final FunctionNode functionNode) throws CompilationException;
+ abstract FunctionNode transform(final Compiler compiler, final FunctionNode functionNode) throws CompilationException;
- final void apply(final Compiler compiler, final FunctionNode functionNode) throws CompilationException {
+ final FunctionNode apply(final Compiler compiler, final FunctionNode functionNode) throws CompilationException {
if (!isApplicable(functionNode)) {
- throw new CompilationException("compile phase not applicable: " + this);
+ throw new CompilationException("compile phase not applicable: " + this + " to " + functionNode.getName() + " state=" + functionNode.getState());
}
- begin(functionNode);
- transform(compiler, functionNode);
- end(functionNode);
+ return end(transform(compiler, begin(functionNode)));
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Thu May 16 11:47:51 2013 +0100
@@ -25,12 +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.CONSTANTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.DEFAULT_SCRIPT_NAME;
import static jdk.nashorn.internal.codegen.CompilerConstants.LAZY;
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
import java.io.File;
import java.lang.reflect.Field;
@@ -46,13 +50,12 @@
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
+
import jdk.internal.dynalink.support.NameCodec;
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
-import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.CodeInstaller;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
@@ -80,8 +83,6 @@
private final ConstantData constantData;
- private final FunctionNode functionNode;
-
private final CompilationSequence sequence;
private final ScriptEnvironment env;
@@ -90,6 +91,8 @@
private boolean strict;
+ private FunctionNode functionNode;
+
private CodeInstaller<ScriptEnvironment> installer;
/** logger for compiler, trampolines, splits and related code generation events
@@ -103,8 +106,12 @@
* during a compile.
*/
private static String[] RESERVED_NAMES = {
- SCOPE.tag(),
- THIS.tag()
+ SCOPE.symbolName(),
+ THIS.symbolName(),
+ RETURN.symbolName(),
+ CALLEE.symbolName(),
+ VARARGS.symbolName(),
+ ARGUMENTS.symbolName()
};
/**
@@ -186,7 +193,7 @@
private static String lazyTag(final FunctionNode functionNode) {
if (functionNode.isLazy()) {
- return '$' + LAZY.tag() + '$' + functionNode.getName();
+ return '$' + LAZY.symbolName() + '$' + functionNode.getName();
}
return "";
}
@@ -205,13 +212,13 @@
this.functionNode = functionNode;
this.sequence = sequence;
this.installer = installer;
- this.strict = strict || functionNode.isStrictMode();
+ this.strict = strict || functionNode.isStrict();
this.constantData = new ConstantData();
this.compileUnits = new HashSet<>();
this.bytecode = new HashMap<>();
final StringBuilder sb = new StringBuilder();
- sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.tag() + lazyTag(functionNode))).
+ sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.symbolName() + lazyTag(functionNode))).
append('$').
append(safeSourceName(functionNode.getSource()));
@@ -253,9 +260,9 @@
* Execute the compilation this Compiler was created with
* @params param types if known, for specialization
* @throws CompilationException if something goes wrong
- * @return this compiler, for possible chaining
+ * @return function node that results from code transforms
*/
- public Compiler compile() throws CompilationException {
+ public FunctionNode compile() throws CompilationException {
return compile(null);
}
@@ -263,9 +270,9 @@
* Execute the compilation this Compiler was created with
* @param paramTypes param types if known, for specialization
* @throws CompilationException if something goes wrong
- * @return this compiler, for possible chaining
+ * @return function node that results from code transforms
*/
- public Compiler compile(final Class<?> paramTypes) throws CompilationException {
+ public FunctionNode compile(final Class<?> paramTypes) throws CompilationException {
for (final String reservedName : RESERVED_NAMES) {
functionNode.uniqueName(reservedName);
}
@@ -276,7 +283,7 @@
long time = 0L;
for (final CompilationPhase phase : sequence) {
- phase.apply(this, functionNode);
+ this.functionNode = phase.apply(this, functionNode);
final long duration = Timing.isEnabled() ? (phase.getEndTime() - phase.getStartTime()) : 0L;
time += duration;
@@ -295,7 +302,7 @@
append(" ms ");
}
- LOG.fine(sb.toString());
+ LOG.fine(sb);
}
}
@@ -311,14 +318,14 @@
append(" ms");
}
- LOG.info(sb.toString());
+ LOG.info(sb);
}
- return this;
+ return functionNode;
}
private Class<?> install(final String className, final byte[] code) {
- LOG.fine("Installing class " + className);
+ LOG.fine("Installing class ", className);
final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
@@ -330,8 +337,8 @@
@Override
public Void run() throws Exception {
//use reflection to write source and constants table to installed classes
- final Field sourceField = clazz.getDeclaredField(SOURCE.tag());
- final Field constantsField = clazz.getDeclaredField(CONSTANTS.tag());
+ final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName());
+ final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
sourceField.setAccessible(true);
constantsField.setAccessible(true);
sourceField.set(null, source);
@@ -380,17 +387,6 @@
unit.setCode(installedClasses.get(unit.getUnitClassName()));
}
- functionNode.accept(new NodeVisitor() {
- @Override
- public Node enterFunctionNode(final FunctionNode node) {
- if (node.isLazy()) {
- return null;
- }
- node.setState(CompilationState.INSTALLED);
- return node;
- }
- });
-
final StringBuilder sb;
if (LOG.isEnabled()) {
sb = new StringBuilder();
@@ -416,7 +412,7 @@
}
if (sb != null) {
- LOG.info(sb.toString());
+ LOG.info(sb);
}
return rootClass;
@@ -495,7 +491,7 @@
private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) {
final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight);
compileUnits.add(compileUnit);
- LOG.fine("Added compile unit " + compileUnit);
+ LOG.fine("Added compile unit ", compileUnit);
return compileUnit;
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java Thu May 16 11:47:51 2013 +0100
@@ -52,9 +52,6 @@
/** lazy prefix for classes of jitted methods */
LAZY("Lazy"),
- /** leaf tag used for functions that require no scope */
- LEAF("__leaf__"),
-
/** constructor name */
INIT("<init>"),
@@ -90,55 +87,55 @@
THIS("this"),
/** this debugger symbol */
- THIS_DEBUGGER("__this__"),
+ THIS_DEBUGGER(":this"),
/** scope name, type and slot */
- SCOPE("__scope__", ScriptObject.class, 2),
+ SCOPE(":scope", ScriptObject.class, 2),
/** the return value variable name were intermediate results are stored for scripts */
- SCRIPT_RETURN("__return__"),
+ RETURN(":return"),
/** the callee value variable when necessary */
- CALLEE("__callee__", ScriptFunction.class),
+ CALLEE(":callee", ScriptFunction.class),
/** the varargs variable when necessary */
- VARARGS("__varargs__"),
+ VARARGS(":varargs"),
/** the arguments vector when necessary and the slot */
ARGUMENTS("arguments", Object.class, 2),
/** prefix for iterators for for (x in ...) */
- ITERATOR_PREFIX("$iter"),
+ ITERATOR_PREFIX(":iter"),
/** prefix for tag variable used for switch evaluation */
- SWITCH_TAG_PREFIX("$tag"),
+ SWITCH_TAG_PREFIX(":tag"),
/** prefix for all exceptions */
- EXCEPTION_PREFIX("$exception"),
+ EXCEPTION_PREFIX(":exception"),
/** prefix for quick slots generated in Store */
- QUICK_PREFIX("$quick"),
+ QUICK_PREFIX(":quick"),
/** prefix for temporary variables */
- TEMP_PREFIX("$temp"),
+ TEMP_PREFIX(":temp"),
/** prefix for literals */
- LITERAL_PREFIX("$lit"),
-
- /** prefix for map */
- MAP("$map", 1),
+ LITERAL_PREFIX(":lit"),
/** prefix for regexps */
- REGEX_PREFIX("$regex"),
+ REGEX_PREFIX(":regex"),
/** "this" used in non-static Java methods; always in slot 0 */
- JAVA_THIS("this", 0),
+ JAVA_THIS(null, 0),
+
+ /** Map parameter in scope object constructors; always in slot 1 */
+ INIT_MAP(null, 1),
- /** init scope */
- INIT_SCOPE("$scope", 2),
+ /** Parent scope parameter in scope object constructors; always in slot 2 */
+ INIT_SCOPE(null, 2),
- /** init arguments */
- INIT_ARGUMENTS("$arguments", 3),
+ /** Arguments parameter in scope object constructors; in slot 3 when present */
+ INIT_ARGUMENTS(null, 3),
/** prefix for all ScriptObject subclasses with fields, @see ObjectGenerator */
JS_OBJECT_PREFIX("JO"),
@@ -167,30 +164,30 @@
/** get array suffix */
GET_ARRAY_SUFFIX("$array");
- private final String tag;
+ private final String symbolName;
private final Class<?> type;
private final int slot;
private CompilerConstants() {
- this.tag = name();
+ this.symbolName = name();
this.type = null;
this.slot = -1;
}
- private CompilerConstants(final String tag) {
- this(tag, -1);
+ private CompilerConstants(final String symbolName) {
+ this(symbolName, -1);
}
- private CompilerConstants(final String tag, final int slot) {
- this(tag, null, slot);
+ private CompilerConstants(final String symbolName, final int slot) {
+ this(symbolName, null, slot);
}
- private CompilerConstants(final String tag, final Class<?> type) {
- this(tag, type, -1);
+ private CompilerConstants(final String symbolName, final Class<?> type) {
+ this(symbolName, type, -1);
}
- private CompilerConstants(final String tag, final Class<?> type, final int slot) {
- this.tag = tag;
+ private CompilerConstants(final String symbolName, final Class<?> type, final int slot) {
+ this.symbolName = symbolName;
this.type = type;
this.slot = slot;
}
@@ -202,8 +199,8 @@
*
* @return the tag
*/
- public final String tag() {
- return tag;
+ public final String symbolName() {
+ return symbolName;
}
/**
@@ -277,7 +274,7 @@
* @return Call representing void constructor for type
*/
public static Call constructorNoLookup(final Class<?> clazz) {
- return specialCallNoLookup(clazz, INIT.tag(), void.class);
+ return specialCallNoLookup(clazz, INIT.symbolName(), void.class);
}
/**
@@ -290,7 +287,7 @@
* @return Call representing constructor for type
*/
public static Call constructorNoLookup(final String className, final Class<?>... ptypes) {
- return specialCallNoLookup(className, INIT.tag(), methodDescriptor(void.class, ptypes));
+ return specialCallNoLookup(className, INIT.symbolName(), methodDescriptor(void.class, ptypes));
}
/**
@@ -303,7 +300,7 @@
* @return Call representing constructor for type
*/
public static Call constructorNoLookup(final Class<?> clazz, final Class<?>... ptypes) {
- return specialCallNoLookup(clazz, INIT.tag(), void.class, ptypes);
+ return specialCallNoLookup(clazz, INIT.symbolName(), void.class, ptypes);
}
/**
--- a/nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Thu May 16 11:47:51 2013 +0100
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import static jdk.nashorn.internal.codegen.types.Type.OBJECT;
@@ -86,7 +87,7 @@
* @param method the method emitter to use
*/
protected void loadScope(final MethodEmitter method) {
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
}
/**
@@ -105,7 +106,7 @@
loadScope(method);
if (hasArguments()) {
- method.loadArguments();
+ method.loadCompilerConstant(ARGUMENTS);
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class, ARGUMENTS.type()));
} else {
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class));
--- a/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Thu May 16 11:47:51 2013 +0100
@@ -25,7 +25,12 @@
package jdk.nashorn.internal.codegen;
+import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
+
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
@@ -33,10 +38,8 @@
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.DoWhileNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
@@ -85,18 +88,11 @@
private static final DebugLogger LOG = new DebugLogger("finalize");
- private final LexicalContext lexicalContext = new LexicalContext();
-
FinalizeTypes() {
}
@Override
public Node leaveCallNode(final CallNode callNode) {
- final EvalArgs evalArgs = callNode.getEvalArgs();
- if (evalArgs != null) {
- evalArgs.setCode(evalArgs.getCode().accept(this));
- }
-
// AccessSpecializer - call return type may change the access for this location
final Node function = callNode.getFunction();
if (function instanceof FunctionNode) {
@@ -133,8 +129,7 @@
@Override
public Node leaveNEW(final UnaryNode unaryNode) {
assert unaryNode.getSymbol() != null && unaryNode.getSymbol().getSymbolType().isObject();
- ((CallNode)unaryNode.rhs()).setIsNew();
- return unaryNode;
+ return unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew());
}
@Override
@@ -254,7 +249,7 @@
@Override
public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
assert binaryNode.getSymbol() != null;
- final BinaryNode newBinaryNode = (BinaryNode)binaryNode.setRHS(discard(binaryNode.rhs()));
+ final BinaryNode newBinaryNode = binaryNode.setRHS(discard(binaryNode.rhs()));
// AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed
// in that case, update the node type as well
propagateType(newBinaryNode, newBinaryNode.lhs().getType());
@@ -354,41 +349,30 @@
}
@Override
- public Node enterBlock(final Block block) {
- lexicalContext.push(block);
+ public boolean enterBlock(final Block block) {
updateSymbols(block);
- return block;
+ return true;
}
+ /*
@Override
- public Node leaveBlock(Block block) {
- lexicalContext.pop(block);
- return super.leaveBlock(block);
- }
+ public Node leaveBlock(final Block block) {
+ final LexicalContext lc = getLexicalContext();
+ return block;//.setFlag(lc, lc.getFlags(block));
+ }*/
@Override
public Node leaveCatchNode(final CatchNode catchNode) {
final Node exceptionCondition = catchNode.getExceptionCondition();
if (exceptionCondition != null) {
- catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
+ return catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
}
return catchNode;
}
@Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- return enterWhileNode(doWhileNode);
- }
-
- @Override
- public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
- return leaveWhileNode(doWhileNode);
- }
-
- @Override
public Node leaveExecuteNode(final ExecuteNode executeNode) {
- executeNode.setExpression(discard(executeNode.getExpression()));
- return executeNode;
+ return executeNode.setExpression(discard(executeNode.getExpression()));
}
@Override
@@ -397,69 +381,54 @@
final Node test = forNode.getTest();
final Node modify = forNode.getModify();
- if (forNode.isForIn()) {
- forNode.setModify(convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
- return forNode;
- }
-
- if (init != null) {
- forNode.setInit(discard(init));
- }
+ final LexicalContext lc = getLexicalContext();
- if (test != null) {
- forNode.setTest(convert(test, Type.BOOLEAN));
- } else {
- assert forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getCurrentFunctionNode();
+ if (forNode.isForIn()) {
+ return forNode.setModify(lc, convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
}
+ assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getLexicalContext().getCurrentFunction();
- if (modify != null) {
- forNode.setModify(discard(modify));
- }
-
- return forNode;
+ return forNode.
+ setInit(lc, init == null ? null : discard(init)).
+ setTest(lc, test == null ? null : convert(test, Type.BOOLEAN)).
+ setModify(lc, modify == null ? null : discard(modify));
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
if (functionNode.isLazy()) {
- return null;
+ return false;
}
- lexicalContext.push(functionNode);
// If the function doesn't need a callee, we ensure its __callee__ symbol doesn't get a slot. We can't do
// this earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the
// need for the callee.
if (!functionNode.needsCallee()) {
- functionNode.getCalleeNode().getSymbol().setNeedsSlot(false);
+ functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
}
// Similar reasoning applies to __scope__ symbol: if the function doesn't need either parent scope or its
// own scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope earlier than
// this phase.
- if (!(functionNode.needsScope() || functionNode.needsParentScope())) {
- functionNode.getScopeNode().getSymbol().setNeedsSlot(false);
+ if (!(functionNode.getBody().needsScope() || functionNode.needsParentScope())) {
+ functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
}
- updateSymbols(functionNode);
- functionNode.setState(CompilationState.FINALIZED);
-
- return functionNode;
+ return true;
}
@Override
- public Node leaveFunctionNode(FunctionNode functionNode) {
- lexicalContext.pop(functionNode);
- return super.leaveFunctionNode(functionNode);
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+ return functionNode.setState(getLexicalContext(), CompilationState.FINALIZED);
}
@Override
public Node leaveIfNode(final IfNode ifNode) {
- ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
- return ifNode;
+ return ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
}
@SuppressWarnings("rawtypes")
@Override
- public Node enterLiteralNode(final LiteralNode literalNode) {
+ public boolean enterLiteralNode(final LiteralNode literalNode) {
if (literalNode instanceof ArrayLiteralNode) {
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
final Node[] array = arrayLiteralNode.getValue();
@@ -473,14 +442,14 @@
}
}
- return null;
+ return false;
}
@Override
public Node leaveReturnNode(final ReturnNode returnNode) {
final Node expr = returnNode.getExpression();
if (expr != null) {
- returnNode.setExpression(convert(expr, getCurrentFunctionNode().getReturnType()));
+ return returnNode.setExpression(convert(expr, getLexicalContext().getCurrentFunction().getReturnType()));
}
return returnNode;
}
@@ -496,21 +465,24 @@
@Override
public Node leaveSwitchNode(final SwitchNode switchNode) {
+ final boolean allInteger = switchNode.getTag().getSymbolType().isInteger();
+
+ if (allInteger) {
+ return switchNode;
+ }
+
final Node expression = switchNode.getExpression();
final List<CaseNode> cases = switchNode.getCases();
- final boolean allInteger = switchNode.getTag().getSymbolType().isInteger();
+ final List<CaseNode> newCases = new ArrayList<>();
- if (!allInteger) {
- switchNode.setExpression(convert(expression, Type.OBJECT));
- for (final CaseNode caseNode : cases) {
- final Node test = caseNode.getTest();
- if (test != null) {
- caseNode.setTest(convert(test, Type.OBJECT));
- }
- }
+ for (final CaseNode caseNode : cases) {
+ final Node test = caseNode.getTest();
+ newCases.add(test != null ? caseNode.setTest(convert(test, Type.OBJECT)) : caseNode);
}
- return switchNode;
+ return switchNode.
+ setExpression(getLexicalContext(), convert(expression, Type.OBJECT)).
+ setCases(getLexicalContext(), newCases);
}
@Override
@@ -520,8 +492,7 @@
@Override
public Node leaveThrowNode(final ThrowNode throwNode) {
- throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
- return throwNode;
+ return throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
}
@Override
@@ -544,23 +515,24 @@
public Node leaveWhileNode(final WhileNode whileNode) {
final Node test = whileNode.getTest();
if (test != null) {
- whileNode.setTest(convert(test, Type.BOOLEAN));
+ return whileNode.setTest(getLexicalContext(), convert(test, Type.BOOLEAN));
}
return whileNode;
}
@Override
public Node leaveWithNode(final WithNode withNode) {
- withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT));
- return withNode;
+ return withNode.setExpression(getLexicalContext(), convert(withNode.getExpression(), Type.OBJECT));
}
private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) {
- if (!symbol.isScope()) {
- LOG.finest("updateSymbols: " + symbol + " => scope, because all vars in " + functionNode.getName() + " are in scope");
- }
- if (loseSlot && symbol.hasSlot()) {
- LOG.finest("updateSymbols: " + symbol + " => no slot, because all vars in " + functionNode.getName() + " are in scope");
+ if (LOG.isEnabled()) {
+ if (!symbol.isScope()) {
+ LOG.finest("updateSymbols: ", symbol, " => scope, because all vars in ", functionNode.getName(), " are in scope");
+ }
+ if (loseSlot && symbol.hasSlot()) {
+ LOG.finest("updateSymbols: ", symbol, " => no slot, because all vars in ", functionNode.getName(), " are in scope");
+ }
}
}
@@ -574,29 +546,28 @@
return; // nothing to do
}
- final FunctionNode functionNode = lexicalContext.getFunction(block);
- assert !(block instanceof FunctionNode) || functionNode == block;
+ final LexicalContext lc = getLexicalContext();
+ final FunctionNode functionNode = lc.getFunction(block);
+ final boolean allVarsInScope = functionNode.allVarsInScope();
+ final boolean isVarArg = functionNode.isVarArg();
- final List<Symbol> symbols = block.getFrame().getSymbols();
- final boolean allVarsInScope = functionNode.allVarsInScope();
- final boolean isVarArg = functionNode.isVarArg();
-
- for (final Symbol symbol : symbols) {
- if (symbol.isInternal() || symbol.isThis()) {
+ for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
+ final Symbol symbol = iter.next();
+ if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
continue;
}
if (symbol.isVar()) {
if (allVarsInScope || symbol.isScope()) {
updateSymbolsLog(functionNode, symbol, true);
- symbol.setIsScope();
+ Symbol.setSymbolIsScope(lc, symbol);
symbol.setNeedsSlot(false);
} else {
assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
}
} else if (symbol.isParam() && (allVarsInScope || isVarArg || symbol.isScope())) {
updateSymbolsLog(functionNode, symbol, isVarArg);
- symbol.setIsScope();
+ Symbol.setSymbolIsScope(lc, symbol);
symbol.setNeedsSlot(!isVarArg);
}
}
@@ -636,11 +607,7 @@
//fallthru
default:
if (newRuntimeNode || widest.isObject()) {
- final RuntimeNode runtimeNode = new RuntimeNode(binaryNode, request);
- if (finalized) {
- runtimeNode.setIsFinal();
- }
- return runtimeNode;
+ return new RuntimeNode(binaryNode, request).setIsFinal(finalized);
}
break;
}
@@ -667,7 +634,8 @@
}
private Node leaveBinary(final BinaryNode binaryNode, final Type lhsType, final Type rhsType) {
- return binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType));
+ Node b = binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType));
+ return b;
}
/**
@@ -683,28 +651,28 @@
node.accept(new NodeVisitor() {
private void setCanBePrimitive(final Symbol symbol) {
- LOG.info("*** can be primitive symbol " + symbol + " " + Debug.id(symbol));
+ LOG.info("*** can be primitive symbol ", symbol, " ", Debug.id(symbol));
symbol.setCanBePrimitive(to);
}
@Override
- public Node enterIdentNode(final IdentNode identNode) {
+ public boolean enterIdentNode(final IdentNode identNode) {
if (!exclude.contains(identNode)) {
setCanBePrimitive(identNode.getSymbol());
}
- return null;
+ return false;
}
@Override
- public Node enterAccessNode(final AccessNode accessNode) {
+ public boolean enterAccessNode(final AccessNode accessNode) {
setCanBePrimitive(accessNode.getProperty().getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode indexNode) {
+ public boolean enterIndexNode(final IndexNode indexNode) {
exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine
- return indexNode;
+ return true;
}
});
}
@@ -785,12 +753,12 @@
private static <T extends Node> T setTypeOverride(final T node, final Type to) {
final Type from = node.getType();
if (!node.getType().equals(to)) {
- LOG.info("Changing call override type for '" + node + "' from " + node.getType() + " to " + to);
+ LOG.info("Changing call override type for '", node, "' from ", node.getType(), " to ", to);
if (!to.isObject() && from.isObject()) {
setCanBePrimitive(node, to);
}
}
- LOG.info("Type override for lhs in '" + node + "' => " + to);
+ LOG.info("Type override for lhs in '", node, "' => ", to);
return ((TypeOverride<T>)node).setType(to);
}
@@ -814,8 +782,8 @@
private Node convert(final Node node, final Type to) {
assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass();
assert node != null : "node is null";
- assert node.getSymbol() != null : "node " + node + " has no symbol!";
- assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getCurrentFunctionNode();
+ assert node.getSymbol() != null : "node " + node + " " + node.getClass() + " has no symbol! " + getLexicalContext().getCurrentFunction() + " " + node.getSource();
+ assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getLexicalContext().getCurrentFunction();
final Type from = node.getType();
@@ -842,23 +810,23 @@
resultNode = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.CONVERT), node);
}
- LOG.info("CONVERT('" + node + "', " + to + ") => '" + resultNode + "'");
+ LOG.info("CONVERT('", node, "', ", to, ") => '", resultNode, "'");
+ final LexicalContext lc = getLexicalContext();
//This is the only place in this file that can create new temporaries
//FinalizeTypes may not introduce ANY node that is not a conversion.
- getCurrentFunctionNode().newTemporary(getCurrentBlock().getFrame(), to, resultNode);
- resultNode.copyTerminalFlags(node);
+ lc.getCurrentFunction().ensureSymbol(lc.getCurrentBlock(), to, resultNode);
+
+ assert !node.isTerminal();
return resultNode;
}
private static Node discard(final Node node) {
- node.setDiscard(true);
-
if (node.getSymbol() != null) {
final Node discard = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.DISCARD), node);
//discard never has a symbol in the discard node - then it would be a nop
- discard.copyTerminalFlags(node);
+ assert !node.isTerminal();
return discard;
}
@@ -883,7 +851,7 @@
final Symbol symbol = node.getSymbol();
if (symbol.isTemp()) {
symbol.setTypeOverride(to);
- LOG.info("Type override for temporary in '" + node + "' => " + to);
+ LOG.info("Type override for temporary in '", node, "' => ", to);
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java Thu May 16 11:47:51 2013 +0100
@@ -57,7 +57,7 @@
public Node leaveUnaryNode(final UnaryNode unaryNode) {
final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval();
if (literalNode != null) {
- LOG.info("Unary constant folded " + unaryNode + " to " + literalNode);
+ LOG.info("Unary constant folded ", unaryNode, " to ", literalNode);
return literalNode;
}
return unaryNode;
@@ -67,24 +67,20 @@
public Node leaveBinaryNode(final BinaryNode binaryNode) {
final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval();
if (literalNode != null) {
- LOG.info("Binary constant folded " + binaryNode + " to " + literalNode);
+ LOG.info("Binary constant folded ", binaryNode, " to ", literalNode);
return literalNode;
}
return binaryNode;
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
- if (functionNode.isLazy()) {
- return null;
- }
- return functionNode;
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ return !functionNode.isLazy();
}
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
- functionNode.setState(CompilationState.CONSTANT_FOLDED);
- return functionNode;
+ return functionNode.setState(getLexicalContext(), CompilationState.CONSTANT_FOLDED);
}
@Override
@@ -251,7 +247,7 @@
value = lhs.getNumber() - rhs.getNumber();
break;
case SHR:
- return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & 0xffff_ffffL);
+ return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & JSType.MAX_UINT);
case SAR:
return LiteralNode.newInstance(source, token, finish, lhs.getInt32() >> rhs.getInt32());
case SHL:
--- a/nashorn/src/jdk/nashorn/internal/codegen/Frame.java Wed May 08 11:22:25 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,196 +0,0 @@
-/*
- * Copyright (c) 2010, 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 jdk.nashorn.internal.codegen;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import jdk.nashorn.internal.ir.Symbol;
-
-/**
- * Tracks the variable area state.
- *
- */
-public final class Frame {
- /** Previous frame. */
- private Frame previous;
-
- /** Current variables. */
- private final ArrayList<Symbol> symbols;
-
- /** Number of slots in previous frame. */
- private int baseCount;
-
- /** Number of slots in this frame. */
- private int count;
-
- /**
- * Constructor.
- *
- * @param previous frame, the parent variable frame
- */
- public Frame(final Frame previous) {
- this.previous = previous;
- this.symbols = new ArrayList<>();
- this.baseCount = getBaseCount();
- this.count = 0;
- }
-
- /**
- * Copy constructor
- * @param frame
- * @param symbols
- */
- private Frame(final Frame frame, final List<Symbol> symbols) {
- this.previous = frame.getPrevious() == null ? null : new Frame(frame.getPrevious(), frame.getPrevious().getSymbols());
- this.symbols = new ArrayList<>(frame.getSymbols());
- this.baseCount = frame.getBaseCount();
- this.count = frame.getCount();
- }
-
- /**
- * Copy the frame
- *
- * @return a new frame with the identical contents
- */
- public Frame copy() {
- return new Frame(this, getSymbols());
- }
-
- /**
- * Add a new variable to the frame.
- * @param symbol Symbol representing variable.
- */
- public void addSymbol(final Symbol symbol) {
- final int slot = symbol.getSlot();
- if (slot < 0) {
- symbols.add(symbol);
- count += symbol.slotCount();
- }
- }
-
- /**
- * Realign slot numbering prior to code generation.
- * @return Number of slots in frame.
- */
- public int realign() {
- baseCount = getBaseCount();
- count = 0;
-
- for (final Symbol symbol : symbols) {
- if (symbol.hasSlot()) {
- symbol.setSlot(baseCount + count);
- count += symbol.slotCount();
- }
- }
-
- return count;
- }
-
- /**
- * Return the slot count of previous frames.
- * @return Number of slots in previous frames.
- */
- private int getBaseCount() {
- return previous != null ? previous.getSlotCount() : 0;
- }
-
- /**
- * Determine the number of slots to top of frame.
- * @return Number of slots in total.
- */
- public int getSlotCount() {
- return baseCount + count;
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder();
- Frame f = this;
- boolean hasPrev = false;
- int pos = 0;
-
- do {
- if (hasPrev) {
- sb.append("\n");
- }
-
- sb.append("#").
- append(pos++).
- append(" {baseCount:").
- append(baseCount).
- append(", ").
- append("count:").
- append(count).
- append("} ");
-
- for (final Symbol var : f.getSymbols()) {
- sb.append('[').
- append(var.toString()).
- append(' ').
- append(var.hashCode()).
- append("] ");
- }
-
- f = f.getPrevious();
- hasPrev = true;
- } while (f != null);
-
- return sb.toString();
- }
-
- /**
- * Get variable count for this frame
- * @return variable count
- */
- public int getCount() {
- return count;
- }
-
- /**
- * Get previous frame
- * @return previous frame
- */
- public Frame getPrevious() {
- return previous;
- }
-
- /**
- * Set previous frame
- * @param previous previous frame
- */
- public void setPrevious(final Frame previous) {
- this.previous = previous;
- }
-
- /**
- * Get symbols in frame
- * @return a list of symbols in this frame
- */
- public List<Symbol> getSymbols() {
- return Collections.unmodifiableList(symbols);
- }
- }
--- a/nashorn/src/jdk/nashorn/internal/codegen/Lower.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Lower.java Thu May 16 11:47:51 2013 +0100
@@ -25,29 +25,21 @@
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.EVAL;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SCRIPT_RETURN;
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
-import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
-import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Deque;
-import java.util.Iterator;
import java.util.List;
import jdk.nashorn.internal.ir.BaseNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
+import jdk.nashorn.internal.ir.BlockLexicalContext;
import jdk.nashorn.internal.ir.BreakNode;
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;
@@ -56,10 +48,10 @@
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.LabelNode;
-import jdk.nashorn.internal.ir.LabeledNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LineNumberNode;
import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.SwitchNode;
@@ -90,356 +82,167 @@
final class Lower extends NodeOperatorVisitor {
- /**
- * Nesting level stack. Currently just used for loops to avoid the problem
- * with terminal bodies that end with throw/return but still do continues to
- * outer loops or same loop.
- */
- private final Deque<Node> nesting;
-
private static final DebugLogger LOG = new DebugLogger("lower");
- private Node lastStatement;
-
- private List<Node> statements;
-
- private LexicalContext lexicalContext = new LexicalContext();
-
/**
* Constructor.
*
* @param compiler the compiler
*/
Lower() {
- this.nesting = new ArrayDeque<>();
- this.statements = new ArrayList<>();
+ super(new BlockLexicalContext() {
+
+ @Override
+ public List<Node> popStatements() {
+ List<Node> newStatements = new ArrayList<>();
+ boolean terminated = false;
+
+ final List<Node> statements = super.popStatements();
+ for (final Node statement : statements) {
+ if (!terminated) {
+ newStatements.add(statement);
+ if (statement.isTerminal()) {
+ terminated = true;
+ }
+ } else {
+ if (statement instanceof VarNode) {
+ newStatements.add(((VarNode)statement).setInit(null));
+ }
+ }
+ }
+ return newStatements;
+ }
+ });
+ }
+
+ @Override
+ public boolean enterBlock(final Block block) {
+ final LexicalContext lc = getLexicalContext();
+ if (lc.isFunctionBody() && lc.getCurrentFunction().isProgram() && !lc.getCurrentFunction().hasDeclaredFunctions()) {
+ new ExecuteNode(block.getSource(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this);
+ }
+ return true;
}
@Override
- public Node enterBlock(final Block block) {
- final Node savedLastStatement = lastStatement;
- final List<Node> savedStatements = statements;
- lexicalContext.push(block);
- try {
- this.statements = new ArrayList<>();
- NodeVisitor visitor = this;
- for (final Node statement : block.getStatements()) {
- statement.accept(visitor);
- /*
- * This is slightly unsound, for example if we have a loop with
- * a guarded statement like if (x) continue in the body and the
- * body ends with TERMINAL, e.g. return; we removed the continue
- * before we had the loop stack, as all we cared about was a
- * return last in the loop.
- *
- * @see NASHORN-285
- */
- if (lastStatement != null && lastStatement.isTerminal()) {
- copyTerminal(block, lastStatement);
- visitor = new DeadCodeVarDeclarationVisitor();
- }
- }
- block.setStatements(statements);
+ public Node leaveBlock(final Block block) {
+ //now we have committed the entire statement list to the block, but we need to truncate
+ //whatever is after the last terminal. block append won't append past it
+
+ final BlockLexicalContext lc = (BlockLexicalContext)getLexicalContext();
+
+ Node last = lc.getLastStatement();
- } finally {
- this.statements = savedStatements;
- this.lastStatement = savedLastStatement;
- lexicalContext.pop(block);
+ if (lc.isFunctionBody()) {
+ final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
+ final boolean isProgram = currentFunction.isProgram();
+ final ReturnNode returnNode = new ReturnNode(
+ currentFunction.getSource(),
+ currentFunction.getToken(),
+ currentFunction.getFinish(),
+ isProgram ?
+ compilerConstant(RETURN) :
+ LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED));
+
+ last = returnNode.accept(this);
}
- return null;
+ if (last != null && last.isTerminal()) {
+ return block.setIsTerminal(lc, true);
+ }
+
+ return block;
}
@Override
- public Node enterBreakNode(final BreakNode breakNode) {
- return enterBreakOrContinue(breakNode);
+ public boolean enterBreakNode(final BreakNode breakNode) {
+ addStatement(breakNode);
+ return false;
}
@Override
- public Node enterCallNode(final CallNode callNode) {
- final Node function = markerFunction(callNode.getFunction());
- callNode.setFunction(function);
- checkEval(callNode); //check if this is an eval call and store the information
- return callNode;
- }
-
- @Override
- public Node leaveCaseNode(final CaseNode caseNode) {
- caseNode.copyTerminalFlags(caseNode.getBody());
- return caseNode;
+ public Node leaveCallNode(final CallNode callNode) {
+ return checkEval(callNode.setFunction(markerFunction(callNode.getFunction())));
}
@Override
public Node leaveCatchNode(final CatchNode catchNode) {
- catchNode.copyTerminalFlags(catchNode.getBody());
- addStatement(catchNode);
- return catchNode;
- }
-
- @Override
- public Node enterContinueNode(final ContinueNode continueNode) {
- return enterBreakOrContinue(continueNode);
+ return addStatement(catchNode);
}
@Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- return enterWhileNode(doWhileNode);
+ public boolean enterContinueNode(final ContinueNode continueNode) {
+ addStatement(continueNode);
+ return false;
}
@Override
- public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
- return leaveWhileNode(doWhileNode);
- }
-
- @Override
- public Node enterEmptyNode(final EmptyNode emptyNode) {
- return null;
+ public boolean enterEmptyNode(final EmptyNode emptyNode) {
+ return false;
}
@Override
public Node leaveExecuteNode(final ExecuteNode executeNode) {
final Node expr = executeNode.getExpression();
+ ExecuteNode node = executeNode;
- if (getCurrentFunctionNode().isProgram()) {
+ final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
+
+ if (currentFunction.isProgram()) {
if (!(expr instanceof Block) || expr instanceof FunctionNode) { // it's not a block, but can be a function
if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) {
- executeNode.setExpression(new BinaryNode(executeNode.getSource(), Token.recast(executeNode.getToken(), TokenType.ASSIGN),
- getCurrentFunctionNode().getResultNode(),
- expr));
+ node = executeNode.setExpression(
+ new BinaryNode(
+ executeNode.getSource(),
+ Token.recast(
+ executeNode.getToken(),
+ TokenType.ASSIGN),
+ compilerConstant(RETURN),
+ expr));
}
}
}
- copyTerminal(executeNode, executeNode.getExpression());
- addStatement(executeNode);
-
- return executeNode;
- }
-
- @Override
- public Node enterForNode(final ForNode forNode) {
- nest(forNode);
- return forNode;
+ return addStatement(node);
}
@Override
public Node leaveForNode(final ForNode forNode) {
- final Node test = forNode.getTest();
- final Block body = forNode.getBody();
+ ForNode newForNode = forNode;
- if (!forNode.isForIn() && test == null) {
- setHasGoto(forNode);
- }
-
- final boolean escapes = controlFlowEscapes(body);
- if (escapes) {
- setTerminal(body, false);
+ final Node test = forNode.getTest();
+ if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
+ newForNode = forNode.setTest(getLexicalContext(), null);
}
- // pop the loop from the loop context
- unnest(forNode);
-
- if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
- forNode.setTest(null);
- setHasGoto(forNode);
- setTerminal(forNode, !escapes);
- }
-
- addStatement(forNode);
-
- return forNode;
+ return addStatement(checkEscape(newForNode));
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
- LOG.info("START FunctionNode: " + functionNode.getName());
-
- if (functionNode.isLazy()) {
- LOG.info("LAZY: " + functionNode.getName());
- return null;
- }
- lexicalContext.push(functionNode);
- initFunctionNode(functionNode);
-
- nest(functionNode);
-
- /*
- * As we are evaluating a nested structure, we need to store the
- * statement list for the surrounding block and restore it when the
- * function is done
- */
- final List<Node> savedStatements = statements;
- final Node savedLastStatement = lastStatement;
-
- statements = new ArrayList<>();
- lastStatement = null;
-
- if (functionNode.needsSelfSymbol()) {
- //function needs to start with var funcIdent = __callee_;
- statements.add(functionNode.getSelfSymbolInit().accept(this));
- }
-
- NodeVisitor visitor = this;
- try {
- //do the statements - this fills the block with code
- boolean needsInitialEvalResult = functionNode.isProgram();
- for (final Node statement : functionNode.getStatements()) {
- // If this function is a program, then insert an assignment to the initial eval result after all
- // function declarations.
- if(needsInitialEvalResult && !(statement instanceof LineNumberNode || (statement instanceof VarNode && ((VarNode)statement).isFunctionDeclaration()))) {
- addInitialEvalResult(functionNode);
- needsInitialEvalResult = false;
- }
- statement.accept(visitor);
- //If there are unused terminated endpoints in the function, we need
- // to add a "return undefined" in those places for correct semantics
- LOG.info("Checking lastStatement="+lastStatement+" for terminal flags");
- if (lastStatement != null && lastStatement.hasTerminalFlags()) {
- copyTerminal(functionNode, lastStatement);
- assert !needsInitialEvalResult;
- visitor = new DeadCodeVarDeclarationVisitor();
- }
- }
- if(needsInitialEvalResult) {
- addInitialEvalResult(functionNode);
- }
- functionNode.setStatements(statements);
-
- if (!functionNode.isTerminal()) {
- guaranteeReturn(functionNode);
- }
- } finally {
- statements = savedStatements;
- lastStatement = savedLastStatement;
- }
-
- LOG.info("END FunctionNode: " + functionNode.getName());
- unnest(functionNode);
- lexicalContext.pop(functionNode);
-
- functionNode.setState(CompilationState.LOWERED);
-
- return null;
- }
-
- /**
- * This visitor is used to go over statements after a terminal statement. Those statements are dead code, but the
- * var declarations in them still have the effect of declaring a local variable on the function level. Therefore,
- * they aren't really dead code and must be preserved. Note that they're only preserved as no-op declarations; their
- * initializers are wiped out as those are, in fact, dead code.
- */
- private class DeadCodeVarDeclarationVisitor extends NodeOperatorVisitor {
- DeadCodeVarDeclarationVisitor() {
- }
-
- @Override
- public Node enterVarNode(VarNode varNode) {
- // Can't ever see a function declaration, as this visitor is only ever used after a terminal statement was
- // encountered, and all function declarations precede any terminal statements.
- assert !varNode.isFunctionDeclaration();
- if(varNode.getInit() == null) {
- // No initializer, just pass it to Lower.
- return varNode.accept(Lower.this);
- }
- // Wipe out the initializer and then pass it to Lower.
- return varNode.setInit(null).accept(Lower.this);
- }
- }
-
- private void addInitialEvalResult(final FunctionNode functionNode) {
- new ExecuteNode(functionNode.getSource(), functionNode.getFirstToken(), functionNode.getFinish(),
- getInitialEvalResult(functionNode)).accept(this);
- }
-
- /**
- * Result of initial result of evaluating a particular program, which is either the last function it declares, or
- * undefined if it doesn't declare any functions.
- * @param program
- * @return the initial result of evaluating the program
- */
- private static Node getInitialEvalResult(final FunctionNode program) {
- IdentNode lastFnName = null;
- for (final FunctionNode fn : program.getDeclaredFunctions()) {
- assert fn.isDeclared();
- final IdentNode fnName = fn.getIdent();
- if(fnName != null) {
- lastFnName = fnName;
- }
- }
- return lastFnName != null ? new IdentNode(lastFnName) : LiteralNode.newInstance(program, ScriptRuntime.UNDEFINED);
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ return !functionNode.isLazy();
}
@Override
- public Node enterIfNode(final IfNode ifNode) {
- return nest(ifNode);
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+ LOG.info("END FunctionNode: ", functionNode.getName());
+ return functionNode.setState(getLexicalContext(), CompilationState.LOWERED);
}
@Override
public Node leaveIfNode(final IfNode ifNode) {
- final Node pass = ifNode.getPass();
- final Node fail = ifNode.getFail();
-
- if (pass.isTerminal() && fail != null && fail.isTerminal()) {
- setTerminal(ifNode, true);
- }
-
- addStatement(ifNode);
- unnest(ifNode);
-
- return ifNode;
- }
-
- @Override
- public Node enterLabelNode(LabelNode labelNode) {
- final Block body = labelNode.getBody();
- body.accept(this);
- copyTerminal(labelNode, body);
- addStatement(labelNode);
- return null;
- }
-
- @Override
- public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
- addStatement(lineNumberNode, false); // don't put it in lastStatement cache
- return null;
+ return addStatement(ifNode);
}
@Override
- public Node enterReturnNode(final ReturnNode returnNode) {
- final TryNode tryNode = returnNode.getTryChain();
- final Node expr = returnNode.getExpression();
-
- if (tryNode != null) {
- //we are inside a try block - we don't necessarily have a result node yet. attr will do that.
- if (expr != null) {
- final Source source = getCurrentFunctionNode().getSource();
-
- //we need to evaluate the result of the return in case it is complex while
- //still in the try block, store it in a result value and return it afterwards
- final long token = returnNode.getToken();
- final Node resultNode = new IdentNode(getCurrentFunctionNode().getResultNode());
- final Node assignResult = new BinaryNode(source, Token.recast(token, TokenType.ASSIGN), resultNode, expr);
-
- //add return_in_try = expr; to try block
- new ExecuteNode(source, token, Token.descPosition(token), assignResult).accept(this);
+ public Node leaveLabelNode(final LabelNode labelNode) {
+ return addStatement(labelNode);
+ }
- //splice in the finally code, inlining it here
- if (copyFinally(tryNode, null)) {
- return null;
- }
-
- //make sure that the return node now returns 'return_in_try'
- returnNode.setExpression(resultNode);
- } else if (copyFinally(tryNode, null)) {
- return null;
- }
- } else if (expr != null) {
- returnNode.setExpression(expr.accept(this));
- }
-
- addStatement(returnNode);
-
- return null;
+ @Override
+ public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
+ addStatement(lineNumberNode); // don't put it in lastStatement cache
+ return false;
}
@Override
@@ -448,31 +251,10 @@
return returnNode;
}
- @Override
- public Node enterSwitchNode(final SwitchNode switchNode) {
- nest(switchNode);
- return switchNode;
- }
@Override
public Node leaveSwitchNode(final SwitchNode switchNode) {
- unnest(switchNode);
-
- final List<CaseNode> cases = switchNode.getCases();
- final CaseNode defaultCase = switchNode.getDefaultCase();
-
- boolean allTerminal = !cases.isEmpty();
- for (final CaseNode caseNode : switchNode.getCases()) {
- allTerminal &= caseNode.isTerminal();
- }
-
- if (allTerminal && defaultCase != null && defaultCase.isTerminal()) {
- setTerminal(switchNode, true);
- }
-
- addStatement(switchNode);
-
- return switchNode;
+ return addStatement(switchNode);
}
@Override
@@ -481,208 +263,234 @@
return throwNode;
}
- @Override
- public Node enterTryNode(final TryNode tryNode) {
- final Block finallyBody = tryNode.getFinallyBody();
- final long token = tryNode.getToken();
- final int finish = tryNode.getFinish();
+ private static Node ensureUniqueLabelsIn(final Node node) {
+ return node.accept(new NodeVisitor() {
+ @Override
+ public Node leaveDefault(final Node labelledNode) {
+ return labelledNode.ensureUniqueLabels(getLexicalContext());
+ }
+ });
+ }
+
+ private static List<Node> copyFinally(final Block finallyBody) {
+ final List<Node> newStatements = new ArrayList<>();
+ for (final Node statement : finallyBody.getStatements()) {
+ newStatements.add(ensureUniqueLabelsIn(statement));
+ if (statement.hasTerminalFlags()) {
+ return newStatements;
+ }
+ }
+ return newStatements;
+ }
+
+ private Block catchAllBlock(final TryNode tryNode) {
+ final Source source = tryNode.getSource();
+ final long token = tryNode.getToken();
+ final int finish = tryNode.getFinish();
+
+ final IdentNode exception = new IdentNode(source, token, finish, getLexicalContext().getCurrentFunction().uniqueName("catch_all"));
+
+ final Block catchBody = new Block(source, token, finish, new ThrowNode(source, token, finish, new IdentNode(exception))).
+ setIsTerminal(getLexicalContext(), true); //ends with throw, so terminal
+
+ final CatchNode catchAllNode = new CatchNode(source, token, finish, new IdentNode(exception), null, catchBody);
+ final Block catchAllBlock = new Block(source, token, finish, catchAllNode);
+
+ //catchallblock -> catchallnode (catchnode) -> exception -> throw
+
+ return (Block)catchAllBlock.accept(this); //not accepted. has to be accepted by lower
+ }
+
+ private IdentNode compilerConstant(final CompilerConstants cc) {
+ final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
+ return new IdentNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
+ }
+
+ private static boolean isTerminal(final List<Node> statements) {
+ return !statements.isEmpty() && statements.get(statements.size() - 1).hasTerminalFlags();
+ }
+
+ /**
+ * Splice finally code into all endpoints of a trynode
+ * @param tryNode the try node
+ * @param list of rethrowing throw nodes from synthetic catch blocks
+ * @param finallyBody the code in the original finally block
+ * @return new try node after splicing finally code (same if nop)
+ */
+ private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) {
+ final Source source = tryNode.getSource();
+ final int finish = tryNode.getFinish();
+
+ assert tryNode.getFinallyBody() == null;
+
+ final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor() {
+ final List<Node> insideTry = new ArrayList<>();
- nest(tryNode);
+ @Override
+ public boolean enterDefault(final Node node) {
+ insideTry.add(node);
+ return true;
+ }
+
+ @Override
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ // do not enter function nodes - finally code should not be inlined into them
+ return false;
+ }
+
+ @Override
+ public Node leaveThrowNode(final ThrowNode throwNode) {
+ if (rethrows.contains(throwNode)) {
+ final List<Node> newStatements = copyFinally(finallyBody);
+ if (!isTerminal(newStatements)) {
+ newStatements.add(throwNode);
+ }
+ return new Block(source, throwNode.getToken(), throwNode.getFinish(), newStatements);
+ }
+ return throwNode;
+ }
+
+ @Override
+ public Node leaveBreakNode(final BreakNode breakNode) {
+ return copy(breakNode, Lower.this.getLexicalContext().getBreakable(breakNode.getLabel()));
+ }
+
+ @Override
+ public Node leaveContinueNode(final ContinueNode continueNode) {
+ return copy(continueNode, Lower.this.getLexicalContext().getContinueTo(continueNode.getLabel()));
+ }
- if (finallyBody == null) {
- //do nothing if no finally exists
- return tryNode;
+ @Override
+ public Node leaveReturnNode(final ReturnNode returnNode) {
+ final Node expr = returnNode.getExpression();
+ final List<Node> newStatements = new ArrayList<>();
+
+ final Node resultNode;
+ if (expr != null) {
+ //we need to evaluate the result of the return in case it is complex while
+ //still in the try block, store it in a result value and return it afterwards
+ resultNode = new IdentNode(Lower.this.compilerConstant(RETURN));
+ newStatements.add(new ExecuteNode(new BinaryNode(source, Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr)));
+ } else {
+ resultNode = null;
+ }
+
+ newStatements.addAll(copyFinally(finallyBody));
+ if (!isTerminal(newStatements)) {
+ newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode));
+ }
+
+ return new ExecuteNode(new Block(source, returnNode.getToken(), getLexicalContext().getCurrentBlock().getFinish(), newStatements));
+ }
+
+ private Node copy(final Node endpoint, final Node targetNode) {
+ if (!insideTry.contains(targetNode)) {
+ final List<Node> newStatements = copyFinally(finallyBody);
+ if (!isTerminal(newStatements)) {
+ newStatements.add(endpoint);
+ }
+ return new ExecuteNode(new Block(source, endpoint.getToken(), finish, newStatements));
+ }
+ return endpoint;
+ }
+ });
+
+ addStatement(newTryNode);
+ for (final Node statement : finallyBody.getStatements()) {
+ addStatement(statement);
}
- /*
- * We have a finally clause.
- *
- * Transform to do finally tail duplication as follows:
- *
- * <pre>
- * try {
- * try_body
- * } catch e1 {
- * catchbody_1
- * }
- * ...
- * } catch en {
- * catchbody_n
- * } finally {
- * finally_body
- * }
- *
- * (where e1 ... en are optional)
- *
- * turns into
- *
- * try {
- * try {
- * try_body
- * } catch e1 {
- * catchbody1
- * //nothing inlined explicitly here, return, break other
- * //terminals may inline the finally body
- * ...
- * } catch en {
- * catchbody2
- * //nothing inlined explicitly here, return, break other
- * //terminals may inline the finally body
- * }
- * } catch all ex {
- * finally_body_inlined
- * rethrow ex
- * }
- * finally_body_inlined
- * </pre>
- *
- * If tries are catches are terminal, visitors for return, break &
- * continue will handle the tail duplications. Throw needs to be
- * treated specially with the catchall as described in the above
- * ASCII art.
- *
- * If the try isn't terminal we do the finally_body_inlined at the
- * end. If the try is terminated with continue/break/return the
- * existing visitor logic will inline the finally before that
- * operation. if the try is terminated with a throw, the catches e1
- * ... en will have a chance to process the exception. If the
- * appropriate catch e1..en is non terminal we fall through to the
- * last finally_body_inlined. if the catch e1...en IS terminal with
- * continue/break/return existing visitor logic will fix it. If they
- * are terminal with another throw it goes to the catchall and the
- * finally_body_inlined marked (*) will fix it before rethrowing
- * whatever problem there was for identical semantic.
- */
- final Source source = getCurrentFunctionNode().getSource();
-
- // if try node does not contain a catch we can skip creation of a new
- // try node and just append our synthetic catch to the existing try node.
- if (!tryNode.getCatchBlocks().isEmpty()) {
- // insert an intermediate try-catch* node, where we move the body and all catch blocks.
- // the original try node become a try-finally container for the new try-catch* node.
- // because we don't clone (to avoid deep copy), we have to fix the block chain in the end.
- final TryNode innerTryNode;
- innerTryNode = new TryNode(source, token, finish, tryNode.getNext());
- innerTryNode.setBody(tryNode.getBody());
- innerTryNode.setCatchBlocks(tryNode.getCatchBlocks());
-
- // set outer tryNode's body to innerTryNode
- final Block outerBody;
- outerBody = new Block(source, token, finish);
- outerBody.setStatements(new ArrayList<Node>(Arrays.asList(innerTryNode)));
- tryNode.setBody(outerBody);
- tryNode.setCatchBlocks(null);
- }
-
- // create a catch-all that inlines finally and rethrows
-
- final Block catchBlock = new Block(source, token, finish);
- //this catch block should get define symbol
-
- final Block catchBody = new Block(source, token, finish);
- final Node catchAllFinally = finallyBody.copy();
-
- catchBody.addStatement(new ExecuteNode(source, finallyBody.getToken(), finallyBody.getFinish(), catchAllFinally));
- setTerminal(catchBody, true);
-
- final CatchNode catchAllNode;
- final IdentNode exception;
-
- exception = new IdentNode(source, token, finish, getCurrentFunctionNode().uniqueName("catch_all"));
- catchAllNode = new CatchNode(source, token, finish, new IdentNode(exception), null, catchBody);
- catchAllNode.setIsSyntheticRethrow();
-
- catchBlock.addStatement(catchAllNode);
-
- // replace all catches of outer tryNode with the catch-all
- tryNode.setCatchBlocks(new ArrayList<>(Arrays.asList(catchBlock)));
-
- /*
- * We leave the finally block for the original try in place for now
- * so that children visitations will work. It is removed and placed
- * afterwards in the else case below, after all children are visited
- */
-
- return tryNode;
+ return newTryNode;
}
@Override
public Node leaveTryNode(final TryNode tryNode) {
- final Block finallyBody = tryNode.getFinallyBody();
+ final Block finallyBody = tryNode.getFinallyBody();
- boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal());
-
- for (final Block catchBlock : tryNode.getCatchBlocks()) {
- allTerminal &= catchBlock.isTerminal();
+ if (finallyBody == null) {
+ return addStatement(tryNode);
}
- tryNode.setIsTerminal(allTerminal);
-
- addStatement(tryNode);
- unnest(tryNode);
+ /*
+ * create a new trynode
+ * if we have catches:
+ *
+ * try try
+ * x try
+ * catch x
+ * y catch
+ * finally z y
+ * catchall
+ * rethrow
+ *
+ * otheriwse
+ *
+ * try try
+ * x x
+ * finally catchall
+ * y rethrow
+ *
+ *
+ * now splice in finally code wherever needed
+ *
+ */
+ TryNode newTryNode;
- // if finally body is present, place it after the tryNode
- if (finallyBody != null) {
- tryNode.setFinallyBody(null);
- addStatement(finallyBody);
+ final Block catchAll = catchAllBlock(tryNode);
+
+ final List<ThrowNode> rethrows = new ArrayList<>();
+ catchAll.accept(new NodeVisitor() {
+ @Override
+ public boolean enterThrowNode(final ThrowNode throwNode) {
+ rethrows.add(throwNode);
+ return true;
+ }
+ });
+ assert rethrows.size() == 1;
+
+ if (tryNode.getCatchBlocks().isEmpty()) {
+ newTryNode = tryNode.setFinallyBody(null);
+ } else {
+ Block outerBody = new Block(tryNode.getSource(), tryNode.getToken(), tryNode.getFinish(), new ArrayList<Node>(Arrays.asList(tryNode.setFinallyBody(null))));
+ newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null);
}
- return tryNode;
+ newTryNode = newTryNode.setCatchBlocks(Arrays.asList(catchAll)).setFinallyBody(null);
+
+ /*
+ * Now that the transform is done, we have to go into the try and splice
+ * the finally block in front of any statement that is outside the try
+ */
+ return spliceFinally(newTryNode, rethrows, finallyBody);
}
@Override
public Node leaveVarNode(final VarNode varNode) {
addStatement(varNode);
+ if (varNode.getFlag(VarNode.IS_LAST_FUNCTION_DECLARATION) && getLexicalContext().getCurrentFunction().isProgram()) {
+ new ExecuteNode(varNode.getSource(), varNode.getToken(), varNode.getFinish(), new IdentNode(varNode.getName())).accept(this);
+ }
return varNode;
}
@Override
- public Node enterWhileNode(final WhileNode whileNode) {
- return nest(whileNode);
- }
-
- @Override
public Node leaveWhileNode(final WhileNode whileNode) {
final Node test = whileNode.getTest();
-
- if (test == null) {
- setHasGoto(whileNode);
- }
+ final Block body = whileNode.getBody();
- final Block body = whileNode.getBody();
- final boolean escapes = controlFlowEscapes(body);
- if (escapes) {
- setTerminal(body, false);
+ if (conservativeAlwaysTrue(test)) {
+ //turn it into a for node without a test.
+ final ForNode forNode = (ForNode)new ForNode(whileNode.getSource(), whileNode.getToken(), whileNode.getFinish(), null, null, body, null, ForNode.IS_FOR).accept(this);
+ getLexicalContext().replace(whileNode, forNode);
+ return forNode;
}
- Node node = whileNode;
-
- if (body.isTerminal()) {
- if (whileNode instanceof DoWhileNode) {
- setTerminal(whileNode, true);
- } else if (conservativeAlwaysTrue(test)) {
- node = new ForNode(whileNode.getSource(), whileNode.getToken(), whileNode.getFinish());
- ((ForNode)node).setBody(body);
- node.accept(this);
- setTerminal(node, !escapes);
- }
- }
-
- // pop the loop from the loop context
- unnest(whileNode);
- addStatement(node);
-
- return node;
+ return addStatement(checkEscape(whileNode));
}
@Override
public Node leaveWithNode(final WithNode withNode) {
- if (withNode.getBody().isTerminal()) {
- setTerminal(withNode, true);
- }
- addStatement(withNode);
-
- return withNode;
+ return addStatement(withNode);
}
@Override
@@ -741,23 +549,25 @@
*
* @param callNode call node to check if it's an eval
*/
- private void checkEval(final CallNode callNode) {
+ private CallNode checkEval(final CallNode callNode) {
if (callNode.getFunction() instanceof IdentNode) {
final List<Node> args = callNode.getArgs();
final IdentNode callee = (IdentNode)callNode.getFunction();
// 'eval' call with at least one argument
- if (args.size() >= 1 && EVAL.tag().equals(callee.getName())) {
- final CallNode.EvalArgs evalArgs =
+ if (args.size() >= 1 && EVAL.symbolName().equals(callee.getName())) {
+ final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
+ return callNode.setEvalArgs(
new CallNode.EvalArgs(
- args.get(0).copy().accept(this), //clone as we use this for the "is eval case". original evaluated separately for "is not eval case"
- getCurrentFunctionNode().getThisNode(),
+ ensureUniqueLabelsIn(args.get(0)).accept(this),
+ compilerConstant(THIS),
evalLocation(callee),
- getCurrentFunctionNode().isStrictMode());
- callNode.setEvalArgs(evalArgs);
+ currentFunction.isStrict()));
}
}
+
+ return callNode;
}
private static boolean conservativeAlwaysTrue(final Node node) {
@@ -773,7 +583,7 @@
* @param loopBody the loop body to check
* @return true if control flow may escape the loop
*/
- private boolean controlFlowEscapes(final Node loopBody) {
+ private static boolean controlFlowEscapes(final LexicalContext lex, final Block loopBody) {
final List<Node> escapes = new ArrayList<>();
loopBody.accept(new NodeVisitor() {
@@ -786,7 +596,7 @@
@Override
public Node leaveContinueNode(final ContinueNode node) {
// all inner loops have been popped.
- if (nesting.contains(node.getTargetNode())) {
+ if (lex.contains(lex.getContinueTo(node.getLabel()))) {
escapes.add(node);
}
return node;
@@ -796,136 +606,24 @@
return !escapes.isEmpty();
}
- private void guaranteeReturn(final FunctionNode functionNode) {
- Node resultNode;
-
- if (functionNode.isProgram()) {
- resultNode = functionNode.getResultNode(); // the eval result, symbol assigned in Attr
- } else {
- if (lastStatement != null && lastStatement.isTerminal() || lastStatement instanceof ReturnNode) {
- return; //already in place or not needed, as it should be for a non-undefined returning function
- }
- resultNode = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED);
+ private LoopNode checkEscape(final LoopNode loopNode) {
+ final LexicalContext lc = getLexicalContext();
+ final boolean escapes = controlFlowEscapes(lc, loopNode.getBody());
+ if (escapes) {
+ return loopNode.
+ setBody(lc, loopNode.getBody().setIsTerminal(lc, false)).
+ setControlFlowEscapes(lc, escapes);
}
-
- //create a return statement
- final Node returnNode = new ReturnNode(functionNode.getSource(), functionNode.getLastToken(), functionNode.getFinish(), resultNode, null);
- returnNode.accept(this);
+ return loopNode;
}
- private Node nest(final Node node) {
- LOG.info("Nesting: " + node);
- LOG.indent();
- nesting.push(node);
- return node;
- }
-
- private void unnest(final Node node) {
- LOG.unindent();
- assert nesting.getFirst() == node : "inconsistent nesting order : " + nesting.getFirst() + " != " + node;
- LOG.info("Unnesting: " + nesting);
- nesting.pop();
- }
-
- private static void setTerminal(final Node node, final boolean isTerminal) {
- LOG.info("terminal = " + isTerminal + " for " + node);
- node.setIsTerminal(isTerminal);
- }
-
- private static void setHasGoto(final Node node) { //, final boolean hasGoto) {
- LOG.info("hasGoto = true for " + node);
- node.setHasGoto();
- }
-
- private static void copyTerminal(final Node node, final Node sourceNode) {
- LOG.info("copy terminal flags " + sourceNode + " -> " + node);
- node.copyTerminalFlags(sourceNode);
- }
-
- private void addStatement(final Node statement, final boolean storeInLastStatement) {
- LOG.info("add statement = " + statement + " (lastStatement = " + lastStatement + ")");
- statements.add(statement);
- if (storeInLastStatement) {
- lastStatement = statement;
- }
- }
-
- private void addStatement(final Node statement) {
- addStatement(statement, true);
+ private Node addStatement(final Node statement) {
+ ((BlockLexicalContext)getLexicalContext()).appendStatement(statement);
+ return statement;
}
/**
- * Determine if Try block is inside target block.
- *
- * @param tryNode Try node to test.
- * @param target Target block.
- *
- * @return true if try block is inside the target, false otherwise.
- */
- private boolean isNestedTry(final TryNode tryNode, final Block target) {
- for(Iterator<Block> blocks = lexicalContext.getBlocks(getCurrentBlock()); blocks.hasNext();) {
- final Block block = blocks.next();
- if(block == target) {
- return false;
- }
- if(tryNode.isChildBlock(block)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Clones the body of the try finallys up to the target block.
- *
- * @param node first try node in the chain.
- * @param targetNode target block of the break/continue statement or null for return
- *
- * @return true if terminates.
- */
- private boolean copyFinally(final TryNode node, final Node targetNode) {
- Block target = null;
-
- if (targetNode instanceof Block) {
- target = (Block)targetNode;
- }
-
- for (TryNode tryNode = node; tryNode != null; tryNode = tryNode.getNext()) {
- if (target != null && !isNestedTry(tryNode, target)) {
- return false;
- }
-
- Block finallyBody = tryNode.getFinallyBody();
- if (finallyBody == null) {
- continue;
- }
-
- finallyBody = (Block)finallyBody.copy();
- final boolean hasTerminalFlags = finallyBody.hasTerminalFlags();
-
- new ExecuteNode(finallyBody.getSource(), finallyBody.getToken(), finallyBody.getFinish(), finallyBody).accept(this);
-
- if (hasTerminalFlags) {
- getCurrentBlock().copyTerminalFlags(finallyBody);
- return true;
- }
- }
-
- return false;
- }
-
- private Node enterBreakOrContinue(final LabeledNode labeledNode) {
- final TryNode tryNode = labeledNode.getTryChain();
- if (tryNode != null && copyFinally(tryNode, labeledNode.getTargetNode())) {
- return null;
- }
- addStatement(labeledNode);
- return null;
- }
-
-
- /**
* An internal expression has a symbol that is tagged internal. Check if
* this is such a node
*
@@ -939,40 +637,21 @@
/**
* Is this an assignment to the special variable that hosts scripting eval
- * results?
+ * results, i.e. __return__?
*
* @param expression expression to check whether it is $evalresult = X
* @return true if an assignment to eval result, false otherwise
*/
- private boolean isEvalResultAssignment(final Node expression) {
+ private static boolean isEvalResultAssignment(final Node expression) {
Node e = expression;
- if (e.tokenType() == TokenType.DISCARD) {
- e = ((UnaryNode)expression).rhs();
- }
- final Node resultNode = getCurrentFunctionNode().getResultNode();
- return e instanceof BinaryNode && ((BinaryNode)e).lhs().equals(resultNode);
- }
-
- /**
- * Prepare special function nodes.
- * TODO : only create those that are needed.
- * TODO : make sure slot numbering is not hardcoded in {@link CompilerConstants} - now creation order is significant
- */
- private static void initFunctionNode(final FunctionNode functionNode) {
- final Source source = functionNode.getSource();
- final long token = functionNode.getToken();
- final int finish = functionNode.getFinish();
-
- functionNode.setThisNode(new IdentNode(source, token, finish, THIS.tag()));
- functionNode.setScopeNode(new IdentNode(source, token, finish, SCOPE.tag()));
- functionNode.setResultNode(new IdentNode(source, token, finish, SCRIPT_RETURN.tag()));
- functionNode.setCalleeNode(new IdentNode(source, token, finish, CALLEE.tag()));
- if (functionNode.isVarArg()) {
- functionNode.setVarArgsNode(new IdentNode(source, token, finish, VARARGS.tag()));
- if (functionNode.needsArguments()) {
- functionNode.setArgumentsNode(new IdentNode(source, token, finish, ARGUMENTS.tag()));
+ assert e.tokenType() != TokenType.DISCARD; //there are no discards this early anymore
+ if (e instanceof BinaryNode) {
+ final Node lhs = ((BinaryNode)e).lhs();
+ if (lhs instanceof IdentNode) {
+ return ((IdentNode)lhs).getName().equals(RETURN.symbolName());
}
}
+ return false;
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/MethodEmitter.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/MethodEmitter.java Thu May 16 11:47:51 2013 +0100
@@ -53,9 +53,12 @@
import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS_DEBUGGER;
+import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
@@ -67,6 +70,7 @@
import java.util.ArrayDeque;
import java.util.EnumSet;
import java.util.Iterator;
+import java.util.List;
import jdk.internal.dynalink.support.NameCodec;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.MethodVisitor;
@@ -79,14 +83,14 @@
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
+import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.RuntimeNode;
-import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.ArgumentSetter;
+import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.internal.runtime.Scope;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
@@ -116,10 +120,10 @@
private final ClassEmitter classEmitter;
/** FunctionNode representing this method, or null if none exists */
- private FunctionNode functionNode;
+ protected FunctionNode functionNode;
- /** SplitNode representing the current split, or null if none exists */
- private SplitNode splitNode;
+ /** Check whether this emitter ever has a function return point */
+ private boolean hasReturn;
/** The script environment */
private final ScriptEnvironment env;
@@ -203,7 +207,7 @@
@Override
public String toString() {
- return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + stack;
+ return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + Debug.id(this);
}
/**
@@ -476,8 +480,8 @@
String name = symbol.getName();
- if (name.equals(THIS.tag())) {
- name = THIS_DEBUGGER.tag();
+ if (name.equals(THIS.symbolName())) {
+ name = THIS_DEBUGGER.symbolName();
}
method.visitLocalVariable(name, symbol.getSymbolType().getDescriptor(), null, start, end, symbol.getSlot());
@@ -654,7 +658,7 @@
* @return this method emitter
*/
MethodEmitter loadConstants() {
- getStatic(classEmitter.getUnitClassName(), CONSTANTS.tag(), CONSTANTS.descriptor());
+ getStatic(classEmitter.getUnitClassName(), CONSTANTS.symbolName(), CONSTANTS.descriptor());
assert peekType().isArray() : peekType();
return this;
}
@@ -669,7 +673,7 @@
* @return the method emitter
*/
MethodEmitter loadUndefined(final Type type) {
- debug("load undefined " + type);
+ debug("load undefined ", type);
pushType(type.loadUndefined(method));
return this;
}
@@ -681,7 +685,7 @@
* @return the method emitter
*/
MethodEmitter loadEmpty(final Type type) {
- debug("load empty " + type);
+ debug("load empty ", type);
pushType(type.loadEmpty(method));
return this;
}
@@ -814,7 +818,7 @@
}
/**
- * Push an local variable to the stack. If the symbol representing
+ * Push a local variable to the stack. If the symbol representing
* the local variable doesn't have a slot, this is a NOP
*
* @param symbol the symbol representing the local variable.
@@ -835,13 +839,13 @@
if (functionNode.needsArguments()) {
// ScriptObject.getArgument(int) on arguments
debug("load symbol", symbol.getName(), " arguments index=", index);
- loadArguments();
+ loadCompilerConstant(ARGUMENTS);
load(index);
ScriptObject.GET_ARGUMENT.invoke(this);
} else {
// array load from __varargs__
debug("load symbol", symbol.getName(), " array index=", index);
- loadVarArgs();
+ loadCompilerConstant(VARARGS);
load(symbol.getFieldIndex());
arrayload();
}
@@ -870,48 +874,13 @@
if(functionNode == null) {
return slot == CompilerConstants.JAVA_THIS.slot();
}
- final int thisSlot = functionNode.getThisNode().getSymbol().getSlot();
+ final int thisSlot = compilerConstant(THIS).getSlot();
assert !functionNode.needsCallee() || thisSlot == 1; // needsCallee -> thisSlot == 1
assert functionNode.needsCallee() || thisSlot == 0; // !needsCallee -> thisSlot == 0
return slot == thisSlot;
}
/**
- * Push the this object to the stack.
- *
- * @return the method emitter
- */
- MethodEmitter loadThis() {
- load(functionNode.getThisNode().getSymbol());
- return this;
- }
-
- /**
- * Push the scope object to the stack.
- *
- * @return the method emitter
- */
- MethodEmitter loadScope() {
- if (peekType() == Type.SCOPE) {
- dup();
- return this;
- }
- load(functionNode.getScopeNode().getSymbol());
- return this;
- }
-
- /**
- * Push the return object to the stack.
- *
- * @return the method emitter
- */
- MethodEmitter loadResult() {
- load(functionNode.getResultNode().getSymbol());
- return this;
- }
-
-
- /**
* Push a method handle to the stack
*
* @param className class name
@@ -927,62 +896,33 @@
return this;
}
- /**
- * Push the varargs object to the stack
- *
- * @return the method emitter
- */
- MethodEmitter loadVarArgs() {
- debug("load var args " + functionNode.getVarArgsNode().getSymbol());
- return load(functionNode.getVarArgsNode().getSymbol());
- }
-
- /**
- * Push the arguments array to the stack
- *
- * @return the method emitter
- */
- MethodEmitter loadArguments() {
- debug("load arguments ", functionNode.getArgumentsNode().getSymbol());
- assert functionNode.getArgumentsNode().getSymbol().getSlot() != 0;
- return load(functionNode.getArgumentsNode().getSymbol());
+ private Symbol compilerConstant(final CompilerConstants cc) {
+ return functionNode.getBody().getExistingSymbol(cc.symbolName());
}
/**
- * Push the callee object to the stack
- *
- * @return the method emitter
+ * True if this method has a slot allocated for the scope variable (meaning, something in the method actually needs
+ * the scope).
+ * @return if this method has a slot allocated for the scope variable.
*/
- MethodEmitter loadCallee() {
- final Symbol calleeSymbol = functionNode.getCalleeNode().getSymbol();
- debug("load callee ", calleeSymbol);
- assert calleeSymbol.getSlot() == 0 : "callee has wrong slot " + calleeSymbol.getSlot() + " in " + functionNode.getName();
-
- return load(calleeSymbol);
+ boolean hasScope() {
+ return compilerConstant(SCOPE).hasSlot();
}
- /**
- * Pop the scope from the stack and store it in its predefined slot
- */
- void storeScope() {
- debug("store scope");
- store(functionNode.getScopeNode().getSymbol());
+ MethodEmitter loadCompilerConstant(final CompilerConstants cc) {
+ final Symbol symbol = compilerConstant(cc);
+ if (cc == SCOPE && peekType() == Type.SCOPE) {
+ dup();
+ return this;
+ }
+ debug("load compiler constant ", symbol);
+ return load(symbol);
}
- /**
- * Pop the return from the stack and store it in its predefined slot
- */
- void storeResult() {
- debug("store result");
- store(functionNode.getResultNode().getSymbol());
- }
-
- /**
- * Pop the arguments array from the stack and store it in its predefined slot
- */
- void storeArguments() {
- debug("store arguments");
- store(functionNode.getArgumentsNode().getSymbol());
+ void storeCompilerConstant(final CompilerConstants cc) {
+ final Symbol symbol = compilerConstant(cc);
+ debug("store compiler constant ", symbol);
+ store(symbol);
}
/**
@@ -1030,13 +970,13 @@
final int index = symbol.getFieldIndex();
if (functionNode.needsArguments()) {
debug("store symbol", symbol.getName(), " arguments index=", index);
- loadArguments();
+ loadCompilerConstant(ARGUMENTS);
load(index);
ArgumentSetter.SET_ARGUMENT.invoke(this);
} else {
// varargs without arguments object - just do array store to __varargs__
debug("store symbol", symbol.getName(), " array index=", index);
- loadVarArgs();
+ loadCompilerConstant(VARARGS);
load(index);
ArgumentSetter.SET_ARRAY_ELEMENT.invoke(this);
}
@@ -1144,7 +1084,7 @@
* @return the method emitter
*/
MethodEmitter newarray(final ArrayType arrayType) {
- debug("newarray ", "arrayType=" + arrayType);
+ debug("newarray ", "arrayType=", arrayType);
popType(Type.INT); //LENGTH
pushType(arrayType.newarray(method));
return this;
@@ -1223,7 +1163,7 @@
* @return the method emitter
*/
MethodEmitter invokespecial(final String className, final String methodName, final String methodDescriptor) {
- debug("invokespecial", className + "." + methodName + methodDescriptor);
+ debug("invokespecial", className, ".", methodName, methodDescriptor);
return invoke(INVOKESPECIAL, className, methodName, methodDescriptor, true);
}
@@ -1237,7 +1177,7 @@
* @return the method emitter
*/
MethodEmitter invokevirtual(final String className, final String methodName, final String methodDescriptor) {
- debug("invokevirtual", className + "." + methodName + methodDescriptor + " " + stack);
+ debug("invokevirtual", className, ".", methodName, methodDescriptor, " ", stack);
return invoke(INVOKEVIRTUAL, className, methodName, methodDescriptor, true);
}
@@ -1251,7 +1191,7 @@
* @return the method emitter
*/
MethodEmitter invokestatic(final String className, final String methodName, final String methodDescriptor) {
- debug("invokestatic", className + "." + methodName + methodDescriptor);
+ debug("invokestatic", className, ".", methodName, methodDescriptor);
invoke(INVOKESTATIC, className, methodName, methodDescriptor, false);
return this;
}
@@ -1284,7 +1224,7 @@
* @return the method emitter
*/
MethodEmitter invokeinterface(final String className, final String methodName, final String methodDescriptor) {
- debug("invokeinterface", className + "." + methodName + methodDescriptor);
+ debug("invokeinterface", className, ".", methodName, methodDescriptor);
return invoke(INVOKEINTERFACE, className, methodName, methodDescriptor, true);
}
@@ -1336,15 +1276,20 @@
*/
void conditionalJump(final Condition cond, final boolean isCmpG, final Label trueLabel) {
if (peekType().isCategory2()) {
- debug("[ld]cmp isCmpG=" + isCmpG);
+ debug("[ld]cmp isCmpG=", isCmpG);
pushType(get2n().cmp(method, isCmpG));
jump(Condition.toUnary(cond), trueLabel, 1);
} else {
- debug("if" + cond);
+ debug("if", cond);
jump(Condition.toBinary(cond, peekType().isObject()), trueLabel, 2);
}
}
+ MethodEmitter registerReturn() {
+ this.hasReturn = true;
+ return this;
+ }
+
/**
* Perform a non void return, popping the type from the stack
*
@@ -1385,22 +1330,7 @@
*
* @param label destination label
*/
- void splitAwareGoto(final Label label) {
-
- if (splitNode != null) {
- final int index = splitNode.getExternalTargets().indexOf(label);
-
- if (index > -1) {
- loadScope();
- checkcast(Scope.class);
- load(index + 1);
- invoke(Scope.SET_SPLIT_STATE);
- loadUndefined(Type.OBJECT);
- _return(functionNode.getReturnType());
- return;
- }
- }
-
+ void splitAwareGoto(final LexicalContext lc, final Label label) {
_goto(label);
}
@@ -1595,7 +1525,7 @@
*/
private void mergeStackTo(final Label label) {
final ArrayDeque<Type> labelStack = label.getStack();
- //debug(labelStack == null ? " >> Control flow - first visit " + label : " >> Control flow - JOIN with " + labelStack + " at " + label);
+ //debug(labelStack == null ? " >> Control flow - first visit ", label : " >> Control flow - JOIN with ", labelStack, " at ", label);
if (labelStack == null) {
assert stack != null;
label.setStack(stack.clone());
@@ -1788,7 +1718,7 @@
* @return the method emitter
*/
MethodEmitter dynamicNew(final int argCount, final int flags) {
- debug("dynamic_new", "argcount=" + argCount);
+ debug("dynamic_new", "argcount=", argCount);
final String signature = getDynamicSignature(Type.OBJECT, argCount);
method.visitInvokeDynamicInsn("dyn:new", signature, LINKERBOOTSTRAP, flags);
pushType(Type.OBJECT); //TODO fix result type
@@ -1805,7 +1735,7 @@
* @return the method emitter
*/
MethodEmitter dynamicCall(final Type returnType, final int argCount, final int flags) {
- debug("dynamic_call", "args=" + argCount, "returnType=" + returnType);
+ debug("dynamic_call", "args=", argCount, "returnType=", returnType);
final String signature = getDynamicSignature(returnType, argCount); // +1 because the function itself is the 1st parameter for dynamic calls (what you call - call target)
debug(" signature", signature);
method.visitInvokeDynamicInsn("dyn:call", signature, LINKERBOOTSTRAP, flags);
@@ -1824,7 +1754,7 @@
* @return the method emitter
*/
MethodEmitter dynamicRuntimeCall(final String name, final Type returnType, final RuntimeNode.Request request) {
- debug("dynamic_runtime_call", name, "args=" + request.getArity(), "returnType=" + returnType);
+ debug("dynamic_runtime_call", name, "args=", request.getArity(), "returnType=", returnType);
final String signature = getDynamicSignature(returnType, request.getArity());
debug(" signature", signature);
method.visitInvokeDynamicInsn(name, signature, RUNTIMEBOOTSTRAP);
@@ -1895,7 +1825,7 @@
* @return the method emitter
*/
MethodEmitter dynamicGetIndex(final Type result, final int flags, final boolean isMethod) {
- debug("dynamic_get_index", peekType(1) + "[" + peekType() + "]");
+ debug("dynamic_get_index", peekType(1), "[", peekType(), "]");
Type resultType = result;
if (result.isBoolean()) {
@@ -1931,7 +1861,7 @@
* @param flags call site flags for setter
*/
void dynamicSetIndex(final int flags) {
- debug("dynamic_set_index", peekType(2) + "[" + peekType(1) + "] =", peekType());
+ debug("dynamic_set_index", peekType(2), "[", peekType(1), "] =", peekType());
Type value = peekType();
if (value.isObject() || value.isBoolean()) {
@@ -2031,7 +1961,7 @@
* @return the method emitter
*/
MethodEmitter getField(final String className, final String fieldName, final String fieldDescriptor) {
- debug("getfield", "receiver=" + peekType(), className + "." + fieldName + fieldDescriptor);
+ debug("getfield", "receiver=", peekType(), className, ".", fieldName, fieldDescriptor);
final Type receiver = popType();
assert receiver.isObject();
method.visitFieldInsn(GETFIELD, className, fieldName, fieldDescriptor);
@@ -2049,7 +1979,7 @@
* @return the method emitter
*/
MethodEmitter getStatic(final String className, final String fieldName, final String fieldDescriptor) {
- debug("getstatic", className + "." + fieldName + "." + fieldDescriptor);
+ debug("getstatic", className, ".", fieldName, ".", fieldDescriptor);
method.visitFieldInsn(GETSTATIC, className, fieldName, fieldDescriptor);
pushType(fieldType(fieldDescriptor));
return this;
@@ -2063,7 +1993,7 @@
* @param fieldDescriptor field descriptor
*/
void putField(final String className, final String fieldName, final String fieldDescriptor) {
- debug("putfield", "receiver=" + peekType(1), "value=" + peekType());
+ debug("putfield", "receiver=", peekType(1), "value=", peekType());
popType(fieldType(fieldDescriptor));
popType(Type.OBJECT);
method.visitFieldInsn(PUTFIELD, className, fieldName, fieldDescriptor);
@@ -2077,7 +2007,7 @@
* @param fieldDescriptor field descriptor
*/
void putStatic(final String className, final String fieldName, final String fieldDescriptor) {
- debug("putfield", "value=" + peekType());
+ debug("putfield", "value=", peekType());
popType(fieldType(fieldDescriptor));
method.visitFieldInsn(PUTSTATIC, className, fieldName, fieldDescriptor);
}
@@ -2237,7 +2167,7 @@
}
if (env != null) { //early bootstrap code doesn't have inited context yet
- LOG.info(sb.toString());
+ LOG.info(sb);
if (DEBUG_TRACE_LINE == linePrefix) {
new Throwable().printStackTrace(LOG.getOutputStream());
}
@@ -2254,21 +2184,12 @@
this.functionNode = functionNode;
}
- /**
- * Get the split node for this method emitter, if this is code
- * generation due to splitting large methods
- *
- * @return split node
- */
- SplitNode getSplitNode() {
- return splitNode;
+ boolean hasReturn() {
+ return hasReturn;
}
- /**
- * Set the split node for this method emitter
- * @param splitNode split node
- */
- void setSplitNode(final SplitNode splitNode) {
- this.splitNode = splitNode;
+ List<Label> getExternalTargets() {
+ return null;
}
+
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/Namespace.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Namespace.java Thu May 16 11:47:51 2013 +0100
@@ -53,7 +53,7 @@
*/
public Namespace(final Namespace parent) {
this.parent = parent;
- directory = new HashMap<>();
+ this.directory = new HashMap<>();
}
/**
@@ -65,10 +65,6 @@
return parent;
}
- private HashMap<String, Integer> getDirectory() {
- return directory;
- }
-
/**
* Create a uniqueName name in the namespace in the form base$n where n varies
* .
@@ -78,7 +74,7 @@
*/
public String uniqueName(final String base) {
for (Namespace namespace = this; namespace != null; namespace = namespace.getParent()) {
- final HashMap<String, Integer> namespaceDirectory = namespace.getDirectory();
+ final HashMap<String, Integer> namespaceDirectory = namespace.directory;
final Integer counter = namespaceDirectory.get(base);
if (counter != null) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java Thu May 16 11:47:51 2013 +0100
@@ -28,10 +28,10 @@
import static jdk.nashorn.internal.codegen.Compiler.SCRIPTS_PACKAGE;
import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_ARGUMENTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.JAVA_THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_PREFIX;
-import static jdk.nashorn.internal.codegen.CompilerConstants.MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.lookup.Lookup.MH;
@@ -204,8 +204,8 @@
* @return The class name.
*/
public static String getClassName(final int fieldCount) {
- return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag() + fieldCount :
- SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag();
+ return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount :
+ SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName();
}
/**
@@ -218,7 +218,23 @@
* @return The class name.
*/
public static String getClassName(final int fieldCount, final int paramCount) {
- return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag() + fieldCount + SCOPE_MARKER + paramCount;
+ return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount + SCOPE_MARKER + paramCount;
+ }
+
+ /**
+ * Returns the number of fields in the JavaScript scope class. Its name had to be generated using either
+ * {@link #getClassName(int)} or {@link #getClassName(int, int)}.
+ * @param clazz the JavaScript scope class.
+ * @return the number of fields in the scope class.
+ */
+ public static int getFieldCount(Class<?> clazz) {
+ final String name = clazz.getSimpleName();
+ final String prefix = JS_OBJECT_PREFIX.symbolName();
+ if(prefix.equals(name)) {
+ return 0;
+ }
+ final int scopeMarker = name.indexOf(SCOPE_MARKER);
+ return Integer.parseInt(scopeMarker == -1 ? name.substring(prefix.length()) : name.substring(prefix.length(), scopeMarker));
}
/**
@@ -387,7 +403,7 @@
final MethodEmitter init = classEmitter.init(PropertyMap.class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
- init.load(Type.OBJECT, MAP.slot());
+ init.load(Type.OBJECT, INIT_MAP.slot());
init.invoke(constructorNoLookup(ScriptObject.class, PropertyMap.class));
return init;
@@ -402,7 +418,7 @@
final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
- init.load(Type.OBJECT, MAP.slot());
+ init.load(Type.OBJECT, INIT_MAP.slot());
init.load(Type.OBJECT, INIT_SCOPE.slot());
init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class));
@@ -418,7 +434,7 @@
final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class, Object.class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
- init.load(Type.OBJECT, MAP.slot());
+ init.load(Type.OBJECT, INIT_MAP.slot());
init.load(Type.OBJECT, INIT_SCOPE.slot());
init.load(Type.OBJECT, INIT_ARGUMENTS.slot());
init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class, Object.class));
@@ -449,7 +465,7 @@
* @param className Name of JavaScript class.
*/
private static void newAllocate(final ClassEmitter classEmitter, final String className) {
- final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.tag(), ScriptObject.class, PropertyMap.class);
+ final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.symbolName(), ScriptObject.class, PropertyMap.class);
allocate.begin();
allocate._new(className);
allocate.dup();
--- a/nashorn/src/jdk/nashorn/internal/codegen/ObjectCreator.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ObjectCreator.java Thu May 16 11:47:51 2013 +0100
@@ -36,7 +36,7 @@
public abstract class ObjectCreator {
/** Compile unit for this ObjectCreator, see CompileUnit */
- protected final CompileUnit compileUnit;
+ //protected final CompileUnit compileUnit;
/** List of keys to initiate in this ObjectCreator */
protected final List<String> keys;
@@ -66,7 +66,6 @@
*/
protected ObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final boolean isScope, final boolean hasArguments) {
this.codegen = codegen;
- this.compileUnit = codegen.getCurrentCompileUnit();
this.keys = keys;
this.symbols = symbols;
this.isScope = isScope;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2010, 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 jdk.nashorn.internal.codegen;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
+
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.LexicalContext;
+import jdk.nashorn.internal.ir.SplitNode;
+import jdk.nashorn.internal.runtime.Scope;
+
+/**
+ * Emitter used for splitting methods. Needs to keep track of if there are jump targets
+ * outside the current split node. All external jump targets encountered at method
+ * emission are logged, and {@code CodeGenerator#leaveSplitNode(SplitNode)} creates
+ * an appropriate jump table when the SplitNode has been iterated through
+ */
+public class SplitMethodEmitter extends MethodEmitter {
+
+ private final SplitNode splitNode;
+
+ private final List<Label> externalTargets = new ArrayList<>();
+
+ SplitMethodEmitter(final ClassEmitter classEmitter, final MethodVisitor mv, SplitNode splitNode) {
+ super(classEmitter, mv);
+ this.splitNode = splitNode;
+ }
+
+ @Override
+ void splitAwareGoto(final LexicalContext lc, final Label label) {
+ assert splitNode != null;
+ final int index = findExternalTarget(lc, label);
+ if (index >= 0) {
+ loadCompilerConstant(SCOPE);
+ checkcast(Scope.class);
+ load(index + 1);
+ invoke(Scope.SET_SPLIT_STATE);
+ loadUndefined(Type.OBJECT);
+ _return(functionNode.getReturnType());
+ return;
+ }
+ super.splitAwareGoto(lc, label);
+ }
+
+ private int findExternalTarget(final LexicalContext lc, final Label label) {
+ final int index = externalTargets.indexOf(label);
+
+ if (index >= 0) {
+ return index;
+ }
+
+ if (lc.isExternalTarget(splitNode, label)) {
+ externalTargets.add(label);
+ return externalTargets.size() - 1;
+ }
+ return -1;
+ }
+
+ @Override
+ MethodEmitter registerReturn() {
+ super.registerReturn();
+ loadCompilerConstant(SCOPE);
+ checkcast(Scope.class);
+ load(0);
+ invoke(Scope.SET_SPLIT_STATE);
+ return this;
+ }
+
+ @Override
+ final List<Label> getExternalTargets() {
+ return externalTargets;
+ }
+}
--- a/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java Thu May 16 11:47:51 2013 +0100
@@ -28,29 +28,18 @@
import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
import java.util.ArrayList;
-import java.util.Deque;
import java.util.HashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import jdk.nashorn.internal.ir.Block;
-import jdk.nashorn.internal.ir.BreakNode;
-import jdk.nashorn.internal.ir.ContinueNode;
-import jdk.nashorn.internal.ir.DoWhileNode;
-import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
-import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext;
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.Node;
-import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.SplitNode;
-import jdk.nashorn.internal.ir.SwitchNode;
-import jdk.nashorn.internal.ir.WhileNode;
-import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.Source;
@@ -64,7 +53,7 @@
private final Compiler compiler;
/** IR to be broken down. */
- private final FunctionNode functionNode;
+ private FunctionNode outermost;
/** Compile unit for the main script. */
private final CompileUnit outermostCompileUnit;
@@ -72,8 +61,6 @@
/** Cache for calculated block weights. */
private final Map<Node, Long> weightCache = new HashMap<>();
- private final LexicalContext lexicalContext = new LexicalContext();
-
/** Weight threshold for when to start a split. */
public static final long SPLIT_THRESHOLD = Options.getIntProperty("nashorn.compiler.splitter.threshold", 32 * 1024);
@@ -88,70 +75,92 @@
*/
public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit outermostCompileUnit) {
this.compiler = compiler;
- this.functionNode = functionNode;
+ this.outermost = functionNode;
this.outermostCompileUnit = outermostCompileUnit;
}
/**
* Execute the split
*/
- void split() {
+ FunctionNode split(final FunctionNode fn) {
+ FunctionNode functionNode = fn;
+
if (functionNode.isLazy()) {
- LOG.finest("Postponing split of '" + functionNode.getName() + "' as it's lazy");
- return;
+ LOG.finest("Postponing split of '", functionNode.getName(), "' as it's lazy");
+ return functionNode;
}
- LOG.finest("Initiating split of '" + functionNode.getName() + "'");
+ LOG.finest("Initiating split of '", functionNode.getName(), "'");
+
+ final LexicalContext lc = getLexicalContext();
long weight = WeighNodes.weigh(functionNode);
+ final boolean top = compiler.getFunctionNode() == outermost;
if (weight >= SPLIT_THRESHOLD) {
- LOG.finest("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
-
- functionNode.accept(this);
+ LOG.finest("Splitting '", functionNode.getName(), "' as its weight ", weight, " exceeds split threshold ", SPLIT_THRESHOLD);
+ functionNode = (FunctionNode)functionNode.accept(this);
if (functionNode.isSplit()) {
// Weight has changed so weigh again, this time using block weight cache
weight = WeighNodes.weigh(functionNode, weightCache);
+ functionNode = functionNode.setBody(lc, functionNode.getBody().setNeedsScope(lc));
}
if (weight >= SPLIT_THRESHOLD) {
- weight = splitBlock(functionNode, functionNode);
- }
-
- if (functionNode.isSplit()) {
- functionNode.accept(new SplitFlowAnalyzer());
+ functionNode = functionNode.setBody(lc, splitBlock(functionNode.getBody(), functionNode));
+ weight = WeighNodes.weigh(functionNode.getBody(), weightCache);
}
}
- assert functionNode.getCompileUnit() == null : "compile unit already set";
+ assert functionNode.getCompileUnit() == null : "compile unit already set for " + functionNode.getName();
- if (compiler.getFunctionNode() == functionNode) { //functionNode.isScript()) {
+ if (top) {
assert outermostCompileUnit != null : "outermost compile unit is null";
-
- functionNode.setCompileUnit(outermostCompileUnit);
+ functionNode = functionNode.setCompileUnit(lc, outermostCompileUnit);
outermostCompileUnit.addWeight(weight + WeighNodes.FUNCTION_WEIGHT);
} else {
- functionNode.setCompileUnit(findUnit(weight));
+ functionNode = functionNode.setCompileUnit(lc, findUnit(weight));
}
- // Recursively split nested functions
- functionNode.accept(new NodeOperatorVisitor() {
+ final Block body = functionNode.getBody();
+ final List<FunctionNode> dc = directChildren(functionNode);
+
+ final Block newBody = (Block)body.accept(new NodeVisitor() {
+ @Override
+ public boolean enterFunctionNode(final FunctionNode nestedFunction) {
+ return dc.contains(nestedFunction);
+ }
+
+ @Override
+ public Node leaveFunctionNode(final FunctionNode nestedFunction) {
+ FunctionNode split = new Splitter(compiler, nestedFunction, outermostCompileUnit).split(nestedFunction);
+ getLexicalContext().replace(nestedFunction, split);
+ return split;
+ }
+ });
+ functionNode = functionNode.setBody(lc, newBody);
+
+ assert functionNode.getCompileUnit() != null;
+
+ return functionNode.setState(lc, CompilationState.SPLIT);
+ }
+
+ private static List<FunctionNode> directChildren(final FunctionNode functionNode) {
+ final List<FunctionNode> dc = new ArrayList<>();
+ functionNode.accept(new NodeVisitor() {
@Override
- public Node enterFunctionNode(FunctionNode function) {
- if(function == functionNode) {
- // Don't process outermost function (it was already processed) but descend into it to find nested
- // functions.
- return function;
+ public boolean enterFunctionNode(final FunctionNode child) {
+ if (child == functionNode) {
+ return true;
}
- // Process a nested function
- new Splitter(compiler, function, outermostCompileUnit).split();
- // Don't descend into a a nested function; Splitter.split() has taken care of nested-in-nested functions.
- return null;
+ if (getLexicalContext().getParentFunction(child) == functionNode) {
+ dc.add(child);
+ }
+ return false;
}
});
-
- functionNode.setState(CompilationState.SPLIT);
+ return dc;
}
/**
@@ -170,8 +179,8 @@
*
* @return new weight for the resulting block.
*/
- private long splitBlock(final Block block, final FunctionNode function) {
- functionNode.setIsSplit();
+ private Block splitBlock(final Block block, final FunctionNode function) {
+ getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
final List<Node> splits = new ArrayList<>();
List<Node> statements = new ArrayList<>();
@@ -186,7 +195,6 @@
statements = new ArrayList<>();
statementsWeight = 0;
}
-
}
if (statement.isTerminal()) {
@@ -201,9 +209,7 @@
splits.add(createBlockSplitNode(block, function, statements, statementsWeight));
}
- block.setStatements(splits);
-
- return WeighNodes.weigh(block, weightCache);
+ return block.setStatements(getLexicalContext(), splits);
}
/**
@@ -218,51 +224,44 @@
final Source source = parent.getSource();
final long token = parent.getToken();
final int finish = parent.getFinish();
- final String name = function.uniqueName(SPLIT_PREFIX.tag());
-
- final Block newBlock = new Block(source, token, finish);
- newBlock.setFrame(new Frame(parent.getFrame()));
- newBlock.setStatements(statements);
+ final String name = function.uniqueName(SPLIT_PREFIX.symbolName());
- final SplitNode splitNode = new SplitNode(name, functionNode, newBlock);
+ final Block newBlock = new Block(source, token, finish, statements);
- splitNode.setCompileUnit(compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT));
-
- return splitNode;
+ return new SplitNode(name, newBlock, compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT));
}
@Override
- public Node enterBlock(final Block block) {
+ public boolean enterBlock(final Block block) {
if (block.isCatchBlock()) {
- return null;
+ return false;
}
- lexicalContext.push(block);
final long weight = WeighNodes.weigh(block, weightCache);
if (weight < SPLIT_THRESHOLD) {
weightCache.put(block, weight);
- lexicalContext.pop(block);
- return null;
+ return false;
}
- return block;
+ return true;
}
@Override
public Node leaveBlock(final Block block) {
assert !block.isCatchBlock();
+ Block newBlock = block;
+
// Block was heavier than SLIT_THRESHOLD in enter, but a sub-block may have
// been split already, so weigh again before splitting.
long weight = WeighNodes.weigh(block, weightCache);
if (weight >= SPLIT_THRESHOLD) {
- weight = splitBlock(block, lexicalContext.getFunction(block));
+ newBlock = splitBlock(block, getLexicalContext().getFunction(block));
+ weight = WeighNodes.weigh(newBlock, weightCache);
}
- weightCache.put(block, weight);
-
- lexicalContext.pop(block);
- return block;
+ weightCache.put(newBlock, weight);
+ return newBlock;
}
@SuppressWarnings("rawtypes")
@@ -274,7 +273,7 @@
return literal;
}
- functionNode.setIsSplit();
+ getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
if (literal instanceof ArrayLiteralNode) {
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal;
@@ -312,123 +311,12 @@
}
@Override
- public Node enterFunctionNode(final FunctionNode node) {
- if(node == functionNode && !node.isLazy()) {
- lexicalContext.push(node);
- node.visitStatements(this);
- lexicalContext.pop(node);
- }
- return null;
- }
-
- static class SplitFlowAnalyzer extends NodeVisitor {
-
- /** Stack of visited Split nodes, deepest node first. */
- private final Deque<SplitNode> splitStack;
-
- /** Map of possible jump targets to containing split node */
- private final Map<Node,SplitNode> targetNodes = new HashMap<>();
-
- SplitFlowAnalyzer() {
- this.splitStack = new LinkedList<>();
- }
-
- @Override
- public Node enterLabelNode(final LabelNode labelNode) {
- registerJumpTarget(labelNode.getBreakNode());
- registerJumpTarget(labelNode.getContinueNode());
- return labelNode;
- }
-
- @Override
- public Node enterWhileNode(final WhileNode whileNode) {
- registerJumpTarget(whileNode);
- return whileNode;
- }
-
- @Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- registerJumpTarget(doWhileNode);
- return doWhileNode;
- }
-
- @Override
- public Node enterForNode(final ForNode forNode) {
- registerJumpTarget(forNode);
- return forNode;
- }
-
- @Override
- public Node enterSwitchNode(final SwitchNode switchNode) {
- registerJumpTarget(switchNode);
- return switchNode;
- }
-
- @Override
- public Node enterReturnNode(final ReturnNode returnNode) {
- for (final SplitNode split : splitStack) {
- split.setHasReturn(true);
- }
- return returnNode;
+ public boolean enterFunctionNode(final FunctionNode node) {
+ //only go into the function node for this splitter. any subfunctions are rejected
+ if (node == outermost && !node.isLazy()) {
+ return true;
}
-
- @Override
- public Node enterContinueNode(final ContinueNode continueNode) {
- searchJumpTarget(continueNode.getTargetNode(), continueNode.getTargetLabel());
- return continueNode;
- }
-
- @Override
- public Node enterBreakNode(final BreakNode breakNode) {
- searchJumpTarget(breakNode.getTargetNode(), breakNode.getTargetLabel());
- return breakNode;
- }
-
- @Override
- public Node enterSplitNode(final SplitNode splitNode) {
- splitStack.addFirst(splitNode);
- return splitNode;
- }
-
- @Override
- public Node leaveSplitNode(final SplitNode splitNode) {
- assert splitNode == splitStack.peekFirst();
- splitStack.removeFirst();
- return splitNode;
- }
-
- /**
- * Register the split node containing a potential jump target.
- * @param targetNode a potential target node.
- */
- private void registerJumpTarget(final Node targetNode) {
- final SplitNode splitNode = splitStack.peekFirst();
- if (splitNode != null) {
- targetNodes.put(targetNode, splitNode);
- }
- }
-
- /**
- * Check if a jump target is outside the current split node and its parent split nodes.
- * @param targetNode the jump target node.
- * @param targetLabel the jump target label.
- */
- private void searchJumpTarget(final Node targetNode, final Label targetLabel) {
-
- final SplitNode targetSplit = targetNodes.get(targetNode);
- // Note that targetSplit may be null, indicating that targetNode is in top level method.
- // In this case we have to add the external jump target to all split nodes.
-
- for (final SplitNode split : splitStack) {
- if (split == targetSplit) {
- break;
- }
- final List<Label> externalTargets = split.getExternalTargets();
- if (!externalTargets.contains(targetLabel)) {
- split.addExternalTarget(targetLabel);
- }
- }
- }
+ return false;
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java Thu May 16 11:47:51 2013 +0100
@@ -35,7 +35,6 @@
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode;
-import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
@@ -101,7 +100,7 @@
* @param weightCache cache of already calculated block weights
*/
private WeighNodes(FunctionNode topFunction, final Map<Node, Long> weightCache) {
- super(null, null);
+ super();
this.topFunction = topFunction;
this.weightCache = weightCache;
}
@@ -123,13 +122,13 @@
}
@Override
- public Node enterBlock(final Block block) {
+ public boolean enterBlock(final Block block) {
if (weightCache != null && weightCache.containsKey(block)) {
weight += weightCache.get(block);
- return null;
+ return false;
}
- return block;
+ return true;
}
@Override
@@ -157,12 +156,6 @@
}
@Override
- public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
- weight += LOOP_WEIGHT;
- return doWhileNode;
- }
-
- @Override
public Node leaveExecuteNode(final ExecuteNode executeNode) {
return executeNode;
}
@@ -174,15 +167,15 @@
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
- if(functionNode == topFunction) {
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ if (functionNode == topFunction) {
// the function being weighted; descend into its statements
- functionNode.visitStatements(this);
- } else {
- // just a reference to inner function from outer function
- weight += FUNC_EXPR_WEIGHT;
+ return true;
+// functionNode.visitStatements(this);
}
- return null;
+ // just a reference to inner function from outer function
+ weight += FUNC_EXPR_WEIGHT;
+ return false;
}
@Override
@@ -205,7 +198,7 @@
@SuppressWarnings("rawtypes")
@Override
- public Node enterLiteralNode(final LiteralNode literalNode) {
+ public boolean enterLiteralNode(final LiteralNode literalNode) {
weight += LITERAL_WEIGHT;
if (literalNode instanceof ArrayLiteralNode) {
@@ -224,10 +217,10 @@
}
}
- return null;
+ return false;
}
- return literalNode;
+ return true;
}
@Override
@@ -249,9 +242,9 @@
}
@Override
- public Node enterSplitNode(final SplitNode splitNode) {
+ public boolean enterSplitNode(final SplitNode splitNode) {
weight += SPLIT_WEIGHT;
- return null;
+ return false;
}
@Override
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/BooleanType.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/BooleanType.java Thu May 16 11:47:51 2013 +0100
@@ -93,12 +93,6 @@
}
@Override
- public Type loadEmpty(final MethodVisitor method) {
- assert false : "unsupported operation";
- return null;
- }
-
- @Override
public void _return(final MethodVisitor method) {
method.visitInsn(IRETURN);
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/IntType.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/IntType.java Thu May 16 11:47:51 2013 +0100
@@ -241,12 +241,6 @@
}
@Override
- public Type loadEmpty(final MethodVisitor method) {
- assert false : "unsupported operation";
- return null;
- }
-
- @Override
public Type cmp(final MethodVisitor method, final boolean isCmpG) {
assert false : "unsupported operation";
return null;
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/LongType.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/LongType.java Thu May 16 11:47:51 2013 +0100
@@ -217,12 +217,6 @@
}
@Override
- public Type loadEmpty(final MethodVisitor method) {
- assert false : "unsupported operation";
- return null;
- }
-
- @Override
public Type cmp(final MethodVisitor method, final boolean isCmpG) {
return cmp(method);
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/NumberType.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/NumberType.java Thu May 16 11:47:51 2013 +0100
@@ -89,12 +89,6 @@
}
@Override
- public Type loadEmpty(final MethodVisitor method) {
- assert false : "unsupported operation";
- return null;
- }
-
- @Override
public Type ldc(final MethodVisitor method, final Object c) {
assert c instanceof Double;
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java Thu May 16 11:47:51 2013 +0100
@@ -616,6 +616,12 @@
return this;
}
+ @Override
+ public Type loadEmpty(final MethodVisitor method) {
+ assert false : "unsupported operation";
+ return null;
+ }
+
/**
* Superclass logic for pop for all types
*
@@ -663,7 +669,6 @@
method.visitInsn(SWAP);
}
}
-
}
/**
@@ -841,12 +846,6 @@
}
@Override
- public Type loadEmpty(final MethodVisitor method) {
- assert false : "unsupported operation";
- return null;
- }
-
- @Override
public Type convert(final MethodVisitor method, final Type to) {
assert false : "unsupported operation";
return null;
--- a/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,23 +25,18 @@
package jdk.nashorn.internal.ir;
-import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
-
-import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of a property access (period operator.)
- *
*/
-public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
+@Immutable
+public final class AccessNode extends BaseNode {
/** Property ident. */
- private IdentNode property;
-
- /** Does this node have a type override */
- private boolean hasCallSiteType;
+ private final IdentNode property;
/**
* Constructor
@@ -53,49 +48,13 @@
* @param property property
*/
public AccessNode(final Source source, final long token, final int finish, final Node base, final IdentNode property) {
- super(source, token, finish, base);
-
- this.start = base.getStart();
+ super(source, token, finish, base, false, false);
this.property = property.setIsPropertyName();
}
- /**
- * Copy constructor
- *
- * @param accessNode source node
- */
- public AccessNode(final AccessNode accessNode) {
- this(accessNode, new CopyState());
- }
-
- /**
- * Internal copy constructor
- *
- * @param accessNode source node
- * @param cs copy state
- */
- protected AccessNode(final AccessNode accessNode, final CopyState cs) {
- super(accessNode, cs);
- this.property = (IdentNode)cs.existingOrCopy(accessNode.getProperty());
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new AccessNode(this, cs);
- }
-
- @Override
- public boolean equals(final Object other) {
- if (!super.equals(other)) {
- return false;
- }
- final AccessNode accessNode = (AccessNode)other;
- return property.equals(accessNode.getProperty());
- }
-
- @Override
- public int hashCode() {
- return super.hashCode() ^ property.hashCode();
+ private AccessNode(final AccessNode accessNode, final Node base, final IdentNode property, final boolean isFunction, final boolean hasCallSiteType) {
+ super(accessNode, base, isFunction, hasCallSiteType);
+ this.property = property;
}
/**
@@ -104,12 +63,11 @@
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterAccessNode(this) != null) {
- base = base.accept(visitor);
- property = (IdentNode)property.accept(visitor);
- return visitor.leaveAccessNode(this);
+ if (visitor.enterAccessNode(this)) {
+ return visitor.leaveAccessNode(
+ setBase(base.accept(visitor)).
+ setProperty((IdentNode)property.accept(visitor)));
}
-
return this;
}
@@ -117,7 +75,7 @@
public void toString(final StringBuilder sb) {
final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true);
- if (hasCallSiteType) {
+ if (hasCallSiteType()) {
sb.append('{');
final String desc = getType().getDescriptor();
sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
@@ -147,19 +105,34 @@
return property;
}
- @Override
- public AccessNode setType(final Type type) {
- if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
- ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
+ private AccessNode setBase(final Node base) {
+ if (this.base == base) {
+ return this;
}
- property = property.setType(type);
- getSymbol().setTypeOverride(type); //always a temp so this is fine.
- hasCallSiteType = true;
- return this;
+ return new AccessNode(this, base, property, isFunction(), hasCallSiteType());
+ }
+
+
+ private AccessNode setProperty(final IdentNode property) {
+ if (this.property == property) {
+ return this;
+ }
+ return new AccessNode(this, base, property, isFunction(), hasCallSiteType());
}
@Override
- public boolean canHaveCallSiteType() {
- return true; //carried by the symbol and always the same nodetype==symboltype
+ public AccessNode setType(final Type type) {
+ logTypeChange(type);
+ getSymbol().setTypeOverride(type); //always a temp so this is fine.
+ return new AccessNode(this, base, property.setType(type), isFunction(), hasCallSiteType());
}
+
+ @Override
+ public BaseNode setIsFunction() {
+ if (isFunction()) {
+ return this;
+ }
+ return new AccessNode(this, base, property, true, hasCallSiteType());
+ }
+
}
--- a/nashorn/src/jdk/nashorn/internal/ir/BaseNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/BaseNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,6 +25,10 @@
package jdk.nashorn.internal.ir;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
+import jdk.nashorn.internal.codegen.ObjectClassGenerator;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.runtime.Source;
/**
@@ -33,12 +37,15 @@
* @see AccessNode
* @see IndexNode
*/
-public abstract class BaseNode extends Node implements FunctionCall {
+@Immutable
+public abstract class BaseNode extends Node implements FunctionCall, TypeOverride<BaseNode> {
/** Base Node. */
- protected Node base;
+ protected final Node base;
- private boolean function;
+ private final boolean isFunction;
+
+ private final boolean hasCallSiteType;
/**
* Constructor
@@ -47,37 +54,28 @@
* @param token token
* @param finish finish
* @param base base node
+ * @param isFunction is this a function
+ * @param hasCallSiteType does this access have a callsite type
*/
- public BaseNode(final Source source, final long token, final int finish, final Node base) {
- super(source, token, finish);
- this.base = base;
- setStart(base.getStart());
+ public BaseNode(final Source source, final long token, final int finish, final Node base, final boolean isFunction, final boolean hasCallSiteType) {
+ super(source, token, base.getStart(), finish);
+ this.base = base;
+ this.isFunction = isFunction;
+ this.hasCallSiteType = hasCallSiteType;
}
/**
- * Copy constructor
- *
- * @param baseNode the base node
- * @param cs a copy state
+ * Copy constructor for immutable nodes
+ * @param baseNode node to inherit from
+ * @param base base
+ * @param isFunction is this a function
+ * @param hasCallSiteType does this access have a callsite type
*/
- protected BaseNode(final BaseNode baseNode, final CopyState cs) {
+ protected BaseNode(final BaseNode baseNode, final Node base, final boolean isFunction, final boolean hasCallSiteType) {
super(baseNode);
- this.base = cs.existingOrCopy(baseNode.getBase());
- setStart(base.getStart());
- }
-
- @Override
- public boolean equals(final Object other) {
- if (!super.equals(other)) {
- return false;
- }
- final BaseNode baseNode = (BaseNode)other;
- return base.equals(baseNode.getBase());
- }
-
- @Override
- public int hashCode() {
- return base.hashCode();
+ this.base = base;
+ this.isFunction = isFunction;
+ this.hasCallSiteType = hasCallSiteType;
}
/**
@@ -88,25 +86,37 @@
return base;
}
- /**
- * Reset the base node for this access
- * @param base new base node
- */
- public void setBase(final Node base) {
- this.base = base;
- }
-
@Override
public boolean isFunction() {
- return function;
+ return isFunction;
}
/**
* Mark this node as being the callee operand of a {@link CallNode}.
* @return a base node identical to this one in all aspects except with its function flag set.
*/
- public BaseNode setIsFunction() {
- function = true;
- return this;
+ public abstract BaseNode setIsFunction();
+
+ @Override
+ public boolean canHaveCallSiteType() {
+ return true; //carried by the symbol and always the same nodetype==symboltype
+ }
+
+ /**
+ * Does the access have a call site type override?
+ * @return true if overridden
+ */
+ protected boolean hasCallSiteType() {
+ return hasCallSiteType;
+ }
+
+ /**
+ * Debug type change
+ * @param type new type
+ */
+ protected final void logTypeChange(final Type type) {
+ if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
+ ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType());
+ }
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java Thu May 16 11:47:51 2013 +0100
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Source;
@@ -33,9 +34,12 @@
/**
* BinaryNode nodes represent two operand operations.
*/
-public class BinaryNode extends UnaryNode {
+@Immutable
+public final class BinaryNode extends Node implements Assignment<Node> {
/** Left hand side argument. */
- private Node lhs;
+ private final Node lhs;
+
+ private final Node rhs;
/**
* Constructor
@@ -46,28 +50,15 @@
* @param rhs right hand side
*/
public BinaryNode(final Source source, final long token, final Node lhs, final Node rhs) {
- super(source, token, rhs);
-
- start = lhs.getStart();
- finish = rhs.getFinish();
-
- this.lhs = lhs;
+ super(source, token, lhs.getStart(), rhs.getFinish());
+ this.lhs = lhs;
+ this.rhs = rhs;
}
- /**
- * Copy constructor
- *
- * @param binaryNode the binary node
- * @param cs copy state
- */
- protected BinaryNode(final BinaryNode binaryNode, final CopyState cs) {
- super(binaryNode, cs);
- lhs = cs.existingOrCopy(binaryNode.lhs);
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new BinaryNode(this, cs);
+ private BinaryNode(final BinaryNode binaryNode, final Node lhs, final Node rhs) {
+ super(binaryNode);
+ this.lhs = lhs;
+ this.rhs = rhs;
}
/**
@@ -149,28 +140,14 @@
return rhs();
}
- @Override
- public boolean equals(final Object other) {
- if (!super.equals(other)) {
- return false;
- }
- return lhs.equals(((BinaryNode)other).lhs());
- }
-
- @Override
- public int hashCode() {
- return super.hashCode() ^ lhs().hashCode();
- }
-
/**
* Assist in IR navigation.
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterBinaryNode(this) != null) {
- // TODO: good cause for a separate visitMembers: we could delegate to UnaryNode.visitMembers
- return visitor.leaveBinaryNode((BinaryNode)setLHS(lhs.accept(visitor)).setRHS(rhs().accept(visitor)));
+ if (visitor.enterBinaryNode(this)) {
+ return visitor.leaveBinaryNode(setLHS(lhs.accept(visitor)).setRHS(rhs.accept(visitor)));
}
return this;
@@ -231,14 +208,35 @@
}
/**
+ * Get the right hand side expression for this node
+ * @return the left hand side expression
+ */
+ public Node rhs() {
+ return rhs;
+ }
+
+ /**
* Set the left hand side expression for this node
* @param lhs new left hand side expression
* @return a node equivalent to this one except for the requested change.
*/
public BinaryNode setLHS(final Node lhs) {
- if(this.lhs == lhs) return this;
- final BinaryNode n = (BinaryNode)clone();
- n.lhs = lhs;
- return n;
+ if (this.lhs == lhs) {
+ return this;
+ }
+ return new BinaryNode(this, lhs, rhs);
}
+
+ /**
+ * Set the right hand side expression for this node
+ * @param rhs new left hand side expression
+ * @return a node equivalent to this one except for the requested change.
+ */
+ public BinaryNode setRHS(final Node rhs) {
+ if (this.rhs == rhs) {
+ return this;
+ }
+ return new BinaryNode(this, lhs, rhs);
+ }
+
}
--- a/nashorn/src/jdk/nashorn/internal/ir/Block.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/Block.java Thu May 16 11:47:51 2013 +0100
@@ -27,14 +27,15 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
-import java.util.ListIterator;
-import jdk.nashorn.internal.codegen.Frame;
+import java.util.Map;
import jdk.nashorn.internal.codegen.Label;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
@@ -42,97 +43,79 @@
* IR representation for a list of statements and functions. All provides the
* basis for script body.
*/
-public class Block extends Node {
+@Immutable
+public class Block extends BreakableNode implements Flags<Block> {
/** List of statements */
- protected List<Node> statements;
+ protected final List<Node> statements;
- /** Symbol table. */
- protected final HashMap<String, Symbol> symbols;
-
- /** Variable frame. */
- protected Frame frame;
+ /** Symbol table - keys must be returned in the order they were put in. */
+ protected final Map<String, Symbol> symbols;
/** Entry label. */
protected final Label entryLabel;
- /** Break label. */
- protected final Label breakLabel;
+ /** Does the block/function need a new scope? */
+ protected final int flags;
+
+ /** Flag indicating that this block needs scope */
+ public static final int NEEDS_SCOPE = 1 << 0;
- /** Does the block/function need a new scope? */
- protected boolean needsScope;
+ /**
+ * Flag indicating whether this block needs
+ * self symbol assignment at the start. This is used only for
+ * blocks that are the bodies of function nodes who refer to themselves
+ * by name. It causes codegen to insert a var [fn_name] = __callee__
+ * at the start of the body
+ */
+ public static final int NEEDS_SELF_SYMBOL = 1 << 1;
+
+ /**
+ * Is this block tagged as terminal based on its contents
+ * (usually the last statement)
+ */
+ public static final int IS_TERMINAL = 1 << 2;
/**
* Constructor
*
- * @param source source code
- * @param token token
- * @param finish finish
+ * @param source source code
+ * @param token token
+ * @param finish finish
+ * @param statements statements
*/
- public Block(final Source source, final long token, final int finish) {
- super(source, token, finish);
+ public Block(final Source source, final long token, final int finish, final Node... statements) {
+ super(source, token, finish, new Label("block_break"));
- this.statements = new ArrayList<>();
- this.symbols = new HashMap<>();
+ this.statements = Arrays.asList(statements);
+ this.symbols = new LinkedHashMap<>();
this.entryLabel = new Label("block_entry");
- this.breakLabel = new Label("block_break");
+ this.flags = 0;
}
/**
- * Internal copy constructor
+ * Constructor
*
- * @param block the source block
- * @param cs the copy state
+ * @param source source code
+ * @param token token
+ * @param finish finish
+ * @param statements statements
*/
- protected Block(final Block block, final CopyState cs) {
- super(block);
+ public Block(final Source source, final long token, final int finish, final List<Node> statements) {
+ this(source, token, finish, statements.toArray(new Node[statements.size()]));
+ }
- this.statements = new ArrayList<>();
- for (final Node statement : block.getStatements()) {
- statements.add(cs.existingOrCopy(statement));
- }
- this.symbols = new HashMap<>();
- this.frame = block.frame == null ? null : block.frame.copy();
+ private Block(final Block block, final int finish, final List<Node> statements, final int flags) {
+ super(block);
+ this.statements = statements;
+ this.flags = flags;
+ this.symbols = block.symbols; //todo - symbols have no dependencies on any IR node and can as far as we understand it be shallow copied now
this.entryLabel = new Label(block.entryLabel);
- this.breakLabel = new Label(block.breakLabel);
-
- assert block.symbols.isEmpty() : "must not clone with symbols";
+ this.finish = finish;
}
@Override
- protected Node copy(final CopyState cs) {
- return new Block(this, cs);
- }
-
- /**
- * Add a new statement to the statement list.
- *
- * @param statement Statement node to add.
- */
- public void addStatement(final Node statement) {
- if (statement != null) {
- statements.add(statement);
- if (getFinish() < statement.getFinish()) {
- setFinish(statement.getFinish());
- }
- }
- }
-
- /**
- * Prepend statements to the statement list
- *
- * @param prepended statement to add
- */
- public void prependStatements(final List<Node> prepended) {
- statements.addAll(0, prepended);
- }
-
- /**
- * Add a list of statements to the statement list.
- *
- * @param statementList Statement nodes to add.
- */
- public void addStatements(final List<Node> statementList) {
- statements.addAll(statementList);
+ public Node ensureUniqueLabels(final LexicalContext lc) {
+ return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags));
}
/**
@@ -142,19 +125,9 @@
* @return new or same node
*/
@Override
- public Node accept(final NodeVisitor visitor) {
- final Block saveBlock = visitor.getCurrentBlock();
- visitor.setCurrentBlock(this);
-
- try {
- // Ignore parent to avoid recursion.
-
- if (visitor.enterBlock(this) != null) {
- visitStatements(visitor);
- return visitor.leaveBlock(this);
- }
- } finally {
- visitor.setCurrentBlock(saveBlock);
+ public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterBlock(this)) {
+ return visitor.leaveBlock(setStatements(lc, Node.accept(visitor, Node.class, statements)));
}
return this;
@@ -222,11 +195,18 @@
}
/**
- * Get the break label for this block
- * @return the break label
+ * Tag block as terminal or non terminal
+ * @param lc lexical context
+ * @param isTerminal is block terminal
+ * @return same block, or new if flag changed
*/
- public Label getBreakLabel() {
- return breakLabel;
+ public Block setIsTerminal(final LexicalContext lc, final boolean isTerminal) {
+ return isTerminal ? setFlag(lc, IS_TERMINAL) : clearFlag(lc, IS_TERMINAL);
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return getFlag(IS_TERMINAL);
}
/**
@@ -238,23 +218,6 @@
}
/**
- * Get the frame for this block
- * @return the frame
- */
- public Frame getFrame() {
- return frame;
- }
-
- /**
- * Reset the frame for this block
- *
- * @param frame the new frame
- */
- public void setFrame(final Frame frame) {
- this.frame = frame;
- }
-
- /**
* Get the list of statements in this block
*
* @return a list of statements
@@ -264,21 +227,21 @@
}
/**
- * Applies the specified visitor to all statements in the block.
- * @param visitor the visitor.
- */
- public void visitStatements(NodeVisitor visitor) {
- for (ListIterator<Node> stmts = statements.listIterator(); stmts.hasNext();) {
- stmts.set(stmts.next().accept(visitor));
- }
- }
- /**
* Reset the statement list for this block
*
- * @param statements new statement list
+ * @param lc lexical context
+ * @param statements new statement list
+ * @return new block if statements changed, identity of statements == block.statements
*/
- public void setStatements(final List<Node> statements) {
- this.statements = statements;
+ public Block setStatements(final LexicalContext lc, final List<Node> statements) {
+ if (this.statements == statements) {
+ return this;
+ }
+ int lastFinish = 0;
+ if (!statements.isEmpty()) {
+ lastFinish = statements.get(statements.size() - 1).getFinish();
+ }
+ return Node.replaceInLexicalContext(lc, this, new Block(this, Math.max(finish, lastFinish), statements, flags));
}
/**
@@ -297,39 +260,65 @@
* @return true if this function needs a scope
*/
public boolean needsScope() {
- return needsScope;
+ return (flags & NEEDS_SCOPE) == NEEDS_SCOPE;
+ }
+
+ @Override
+ public Block setFlags(final LexicalContext lc, int flags) {
+ if (this.flags == flags) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags));
+ }
+
+ @Override
+ public Block clearFlag(final LexicalContext lc, int flag) {
+ return setFlags(lc, flags & ~flag);
+ }
+
+ @Override
+ public Block setFlag(final LexicalContext lc, int flag) {
+ return setFlags(lc, flags | flag);
+ }
+
+ @Override
+ public boolean getFlag(final int flag) {
+ return (flags & flag) == flag;
}
/**
* Set the needs scope flag.
+ * @param lc lexicalContext
+ * @return new block if state changed, otherwise this
*/
- public void setNeedsScope() {
- needsScope = true;
+ public Block setNeedsScope(final LexicalContext lc) {
+ if (needsScope()) {
+ return this;
+ }
+
+ return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE));
}
/**
- * Marks this block as using a specified scoped symbol. The block and its parent blocks up to but not
- * including the block defining the symbol will be marked as needing parent scope. The block defining the symbol
- * will be marked as one that needs to have its own scope.
- * @param symbol the symbol being used.
- * @param ancestors the iterator over block's containing lexical context
+ * Computationally determine the next slot for this block,
+ * indexed from 0. Use this as a relative base when computing
+ * frames
+ * @return next slot
*/
- public void setUsesScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) {
- if(symbol.getBlock() == this) {
- setNeedsScope();
- } else {
- setUsesParentScopeSymbol(symbol, ancestors);
+ public int nextSlot() {
+ final Iterator<Symbol> iter = symbolIterator();
+ int next = 0;
+ while (iter.hasNext()) {
+ final Symbol symbol = iter.next();
+ if (symbol.hasSlot()) {
+ next += symbol.slotCount();
}
+ }
+ return next;
}
- /**
- * Invoked when this block uses a scope symbol defined in one of its ancestors.
- * @param symbol the scope symbol being used
- * @param ancestors iterator over ancestor blocks
- */
- void setUsesParentScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) {
- if(ancestors.hasNext()) {
- ancestors.next().setUsesScopeSymbol(symbol, ancestors);
- }
+ @Override
+ protected boolean isBreakableWithoutLabel() {
+ return false;
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/ir/BlockLexicalContext.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2010, 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 jdk.nashorn.internal.ir;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * This is a subclass of lexical context used for filling
+ * blocks (and function nodes) with statements. When popping
+ * a block from the lexical context, any statements that have
+ * been generated in it are commited to the block. This saves
+ * unnecessary object mutations and lexical context replacement
+ */
+public class BlockLexicalContext extends LexicalContext {
+ /** statement stack, each block on the lexical context maintains one of these, which is
+ * committed to the block on pop */
+ private Deque<List<Node>> sstack = new ArrayDeque<>();
+
+ /** Last non debug statement emitted in this context */
+ protected Node lastStatement;
+
+ @Override
+ public <T extends LexicalContextNode> T push(final T node) {
+ T pushed = super.push(node);
+ if (node instanceof Block) {
+ sstack.push(new ArrayList<Node>());
+ }
+ return pushed;
+ }
+
+ /**
+ * Get the statement list from the stack, possibly filtered
+ * @return statement list
+ */
+ protected List<Node> popStatements() {
+ return sstack.pop();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T extends LexicalContextNode> T pop(final T node) {
+ T expected = node;
+ if (node instanceof Block) {
+ final List<Node> newStatements = popStatements();
+ expected = (T)((Block)node).setStatements(this, newStatements);
+ if (!sstack.isEmpty()) {
+ lastStatement = lastStatement(sstack.peek());
+ }
+ }
+ return super.pop(expected);
+ }
+
+ /**
+ * Append a statement to the block being generated
+ * @param statement statement to add
+ */
+ public void appendStatement(final Node statement) {
+ assert statement != null;
+ sstack.peek().add(statement);
+ if (!statement.isDebug()) {
+ lastStatement = statement;
+ }
+ }
+
+ /**
+ * Prepend a statement to the block being generated
+ * @param statement statement to prepend
+ * @return the prepended statement
+ */
+ public Node prependStatement(final Node statement) {
+ assert statement != null;
+ sstack.peek().add(0, statement);
+ return statement;
+ }
+
+ /**
+ * Get the last (non debug) statement that was emitted into a block
+ * @return the last statement emitted
+ */
+ public Node getLastStatement() {
+ return lastStatement;
+ }
+
+ private static Node lastStatement(final List<Node> statements) {
+ for (final ListIterator<Node> iter = statements.listIterator(statements.size()); iter.hasPrevious(); ) {
+ final Node node = iter.previous();
+ if (!node.isDebug()) {
+ return node;
+ }
+ }
+ return null;
+ }
+}
--- a/nashorn/src/jdk/nashorn/internal/ir/BreakNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/BreakNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,37 +25,34 @@
package jdk.nashorn.internal.ir;
-import jdk.nashorn.internal.codegen.Label;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for {@code break} statements.
*/
-public class BreakNode extends LabeledNode {
+@Immutable
+public final class BreakNode extends Node {
- /**
+ private final IdentNode label;
+
+ /**
* Constructor
*
- * @param source source code
- * @param token token
- * @param finish finish
- * @param labelNode break label
- * @param targetNode node to break to
- * @param tryChain surrounding try chain
+ * @param source source code
+ * @param token token
+ * @param finish finish
+ * @param label label for break or null if none
*/
- public BreakNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) {
- super(source, token, finish, labelNode, targetNode, tryChain);
- setHasGoto();
- }
-
- private BreakNode(final BreakNode breakNode, final CopyState cs) {
- super(breakNode, cs);
+ public BreakNode(final Source source, final long token, final int finish, final IdentNode label) {
+ super(source, token, finish);
+ this.label = label;
}
@Override
- protected Node copy(final CopyState cs) {
- return new BreakNode(this, cs);
+ public boolean hasGoto() {
+ return true;
}
/**
@@ -64,7 +61,7 @@
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterBreakNode(this) != null) {
+ if (visitor.enterBreakNode(this)) {
return visitor.leaveBreakNode(this);
}
@@ -72,26 +69,20 @@
}
/**
- * Return the target label of this break node.
- * @return the target label.
+ * Get the label for this break node
+ * @return label, or null if none
*/
- public Label getTargetLabel() {
- if (targetNode instanceof BreakableNode) {
- return ((BreakableNode)targetNode).getBreakLabel();
- } else if (targetNode instanceof Block) {
- return ((Block)targetNode).getBreakLabel();
- }
-
- throw new AssertionError("Invalid break target " + targetNode.getClass());
+ public IdentNode getLabel() {
+ return label;
}
@Override
public void toString(final StringBuilder sb) {
sb.append("break");
- if (labelNode != null) {
+ if (label != null) {
sb.append(' ');
- labelNode.getLabel().toString(sb);
+ label.toString(sb);
}
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/BreakableNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/BreakableNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,27 +25,34 @@
package jdk.nashorn.internal.ir;
+import java.util.Arrays;
+import java.util.List;
+
import jdk.nashorn.internal.codegen.Label;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.runtime.Source;
/**
* This class represents a node from which control flow can execute
* a {@code break} statement
*/
-public abstract class BreakableNode extends Node {
+@Immutable
+public abstract class BreakableNode extends LexicalContextNode {
/** break label. */
- protected Label breakLabel;
+ protected final Label breakLabel;
/**
* Constructor
*
- * @param source source code
- * @param token token
- * @param finish finish
+ * @param source source code
+ * @param token token
+ * @param finish finish
+ * @param breakLabel break label
*/
- public BreakableNode(final Source source, final long token, final int finish) {
+ protected BreakableNode(final Source source, final long token, final int finish, final Label breakLabel) {
super(source, token, finish);
+ this.breakLabel = breakLabel;
}
/**
@@ -55,6 +62,19 @@
*/
protected BreakableNode(final BreakableNode breakableNode) {
super(breakableNode);
+ this.breakLabel = new Label(breakableNode.getBreakLabel());
+ }
+
+ @Override
+ public abstract Node ensureUniqueLabels(final LexicalContext lc);
+
+ /**
+ * Check whether this can be broken out from without using a label,
+ * e.g. everything but Blocks, basically
+ * @return true if breakable without label
+ */
+ protected boolean isBreakableWithoutLabel() {
+ return true;
}
/**
@@ -65,4 +85,14 @@
return breakLabel;
}
+ /**
+ * Return the labels associated with this node. Breakable nodes that
+ * aren't LoopNodes only have a break label -> the location immediately
+ * afterwards the node in code
+ * @return list of labels representing locations around this node
+ */
+ public List<Label> getLabels() {
+ return Arrays.asList(breakLabel);
+ }
+
}
--- a/nashorn/src/jdk/nashorn/internal/ir/CallNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/CallNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,49 +25,48 @@
package jdk.nashorn.internal.ir;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for a function call.
- *
*/
-public class CallNode extends Node implements TypeOverride<CallNode> {
+@Immutable
+public final class CallNode extends LexicalContextNode implements TypeOverride<CallNode> {
- private Type type;
+ private final Type type;
/** Function identifier or function body. */
- private Node function;
+ private final Node function;
/** Call arguments. */
- private List<Node> args;
+ private final List<Node> args;
- /** flag - is new expression */
- private boolean isNew;
+ /** Is this a "new" operation */
+ public static final int IS_NEW = 0x1;
- /** flag - is in with block */
- private boolean inWithBlock;
+ private final int flags;
/**
* Arguments to be passed to builtin {@code eval} function
*/
public static class EvalArgs {
/** evaluated code */
- private Node code;
+ private final Node code;
/** 'this' passed to evaluated code */
- private IdentNode evalThis;
+ private final IdentNode evalThis;
/** location string for the eval call */
- final private String location;
+ private final String location;
/** is this call from a strict context? */
- final private boolean strictMode;
+ private final boolean strictMode;
/**
* Constructor
@@ -92,12 +91,11 @@
return code;
}
- /**
- * Set the code that is to be eval.ed by this eval function
- * @param code the code as an AST node
- */
- public void setCode(final Node code) {
- this.code = code;
+ private EvalArgs setCode(final Node code) {
+ if (this.code == code) {
+ return this;
+ }
+ return new EvalArgs(code, evalThis, location, strictMode);
}
/**
@@ -108,12 +106,11 @@
return this.evalThis;
}
- /**
- * Set the {@code this} symbol used to invoke this eval call
- * @param evalThis the {@code this} symbol
- */
- public void setThis(final IdentNode evalThis) {
- this.evalThis = evalThis;
+ private EvalArgs setThis(final IdentNode evalThis) {
+ if (this.evalThis == evalThis) {
+ return this;
+ }
+ return new EvalArgs(code, evalThis, location, strictMode);
}
/**
@@ -135,7 +132,7 @@
/** arguments for 'eval' call. Non-null only if this call node is 'eval' */
@Ignore
- private EvalArgs evalArgs;
+ private final EvalArgs evalArgs;
/**
* Constructors
@@ -149,28 +146,22 @@
public CallNode(final Source source, final long token, final int finish, final Node function, final List<Node> args) {
super(source, token, finish);
- setStart(function.getStart());
-
- this.function = function;
- this.args = args;
+ this.function = function;
+ this.args = args;
+ this.flags = 0;
+ this.type = null;
+ this.evalArgs = null;
}
- private CallNode(final CallNode callNode, final CopyState cs) {
+ private CallNode(final CallNode callNode, final Node function, final List<Node> args, final int flags, final Type type, final EvalArgs evalArgs) {
super(callNode);
-
- final List<Node> newArgs = new ArrayList<>();
-
- for (final Node arg : callNode.args) {
- newArgs.add(cs.existingOrCopy(arg));
- }
-
- this.function = cs.existingOrCopy(callNode.function); //TODO existing or same?
- this.args = newArgs;
- this.isNew = callNode.isNew;
- this.inWithBlock = callNode.inWithBlock;
+ this.function = function;
+ this.args = args;
+ this.flags = flags;
+ this.type = type;
+ this.evalArgs = evalArgs;
}
-
@Override
public Type getType() {
if (hasCallSiteType()) {
@@ -181,8 +172,10 @@
@Override
public CallNode setType(final Type type) {
- this.type = type;
- return this;
+ if (this.type == type) {
+ return this;
+ }
+ return new CallNode(this, function, args, flags, type, evalArgs);
}
private boolean hasCallSiteType() {
@@ -194,11 +187,6 @@
return true;
}
- @Override
- protected Node copy(final CopyState cs) {
- return new CallNode(this, cs);
- }
-
/**
* Assist in IR navigation.
*
@@ -207,15 +195,22 @@
* @return node or replacement
*/
@Override
- public Node accept(final NodeVisitor visitor) {
- if (visitor.enterCallNode(this) != null) {
- function = function.accept(visitor);
-
- for (int i = 0, count = args.size(); i < count; i++) {
- args.set(i, args.get(i).accept(visitor));
+ public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterCallNode(this)) {
+ final CallNode newCallNode = (CallNode)visitor.leaveCallNode(
+ setFunction(function.accept(visitor)).
+ setArgs(Node.accept(visitor, Node.class, args)).
+ setFlags(flags).
+ setType(type).
+ setEvalArgs(evalArgs == null ?
+ null :
+ evalArgs.setCode(evalArgs.getCode().accept(visitor)).
+ setThis((IdentNode)evalArgs.getThis().accept(visitor))));
+ // Theoretically, we'd need to instead pass lc to every setter and do a replacement on each. In practice,
+ // setType from TypeOverride can't accept a lc, and we don't necessarily want to go there now.
+ if(this != newCallNode) {
+ return Node.replaceInLexicalContext(lc, this, newCallNode);
}
-
- return visitor.leaveCallNode(this);
}
return this;
@@ -226,7 +221,7 @@
if (hasCallSiteType()) {
sb.append('{');
final String desc = getType().getDescriptor();
- sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
+ sb.append(desc.charAt(desc.length() - 1) == ';' ? 'O' : getType().getDescriptor());
sb.append('}');
}
@@ -261,8 +256,11 @@
* Reset the arguments for the call
* @param args new arguments list
*/
- public void setArgs(final List<Node> args) {
- this.args = args;
+ private CallNode setArgs(final List<Node> args) {
+ if (this.args == args) {
+ return this;
+ }
+ return new CallNode(this, function, args, flags, type, evalArgs);
}
/**
@@ -278,9 +276,13 @@
* {@code eval}
*
* @param evalArgs eval args
+ * @return same node or new one on state change
*/
- public void setEvalArgs(final EvalArgs evalArgs) {
- this.evalArgs = evalArgs;
+ public CallNode setEvalArgs(final EvalArgs evalArgs) {
+ if (this.evalArgs == evalArgs) {
+ return this;
+ }
+ return new CallNode(this, function, args, flags, type, evalArgs);
}
/**
@@ -301,10 +303,14 @@
/**
* Reset the function expression that this call invokes
- * @param node the function
+ * @param function the function
+ * @return same node or new one on state change
*/
- public void setFunction(final Node node) {
- function = node;
+ public CallNode setFunction(final Node function) {
+ if (this.function == function) {
+ return this;
+ }
+ return new CallNode(this, function, args, flags, type, evalArgs);
}
/**
@@ -312,28 +318,21 @@
* @return true if this a new operation
*/
public boolean isNew() {
- return isNew;
+ return (flags & IS_NEW) == IS_NEW;
}
/**
* Flag this call as a new operation
+ * @return same node or new one on state change
*/
- public void setIsNew() {
- this.isNew = true;
+ public CallNode setIsNew() {
+ return setFlags(IS_NEW);
}
- /**
- * Check if this call is inside a {@code with} block
- * @return true if the call is inside a {@code with} block
- */
- public boolean inWithBlock() {
- return inWithBlock;
- }
-
- /**
- * Flag this call to be inside a {@code with} block
- */
- public void setInWithBlock() {
- this.inWithBlock = true;
+ private CallNode setFlags(final int flags) {
+ if (this.flags == flags) {
+ return this;
+ }
+ return new CallNode(this, function, args, flags, type, evalArgs);
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/CaseNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/CaseNode.java Thu May 16 11:47:51 2013 +0100
@@ -26,19 +26,21 @@
package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.codegen.Label;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of CASE clause.
- *
+ * Case nodes are not BreakableNodes, but the SwitchNode is
*/
-public class CaseNode extends BreakableNode {
+@Immutable
+public final class CaseNode extends Node {
/** Test expression. */
- private Node test;
+ private final Node test;
/** Statements. */
- private Block body;
+ private final Block body;
/** Case entry label. */
private final Label entry;
@@ -60,17 +62,17 @@
this.entry = new Label("entry");
}
- private CaseNode(final CaseNode caseNode, final CopyState cs) {
+ CaseNode(final CaseNode caseNode, final Node test, final Block body) {
super(caseNode);
- this.test = cs.existingOrCopy(caseNode.test);
- this.body = (Block)cs.existingOrCopy(caseNode.body);
+ this.test = test;
+ this.body = body;
this.entry = new Label(caseNode.entry);
}
@Override
- protected Node copy(final CopyState cs) {
- return new CaseNode(this, cs);
+ public boolean isTerminal() {
+ return body.isTerminal();
}
/**
@@ -79,15 +81,11 @@
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterCaseNode(this) != null) {
- if (test != null) {
- test = test.accept(visitor);
- }
- if (body != null) {
- body = (Block)body.accept(visitor);
- }
+ if (visitor.enterCaseNode(this)) {
+ final Node newTest = test == null ? null : test.accept(visitor);
+ final Block newBody = body == null ? null : (Block)body.accept(visitor);
- return visitor.leaveCaseNode(this);
+ return visitor.leaveCaseNode(setTest(newTest).setBody(newBody));
}
return this;
@@ -131,8 +129,19 @@
/**
* Reset the test expression for this case node
* @param test new test expression
+ * @return new or same CaseNode
*/
- public void setTest(final Node test) {
- this.test = test;
+ public CaseNode setTest(final Node test) {
+ if (this.test == test) {
+ return this;
+ }
+ return new CaseNode(this, test, body);
+ }
+
+ private CaseNode setBody(final Block body) {
+ if (this.body == body) {
+ return this;
+ }
+ return new CaseNode(this, test, body);
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/CatchNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/CatchNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,26 +25,23 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of a catch clause.
- *
*/
-public class CatchNode extends Node {
+@Immutable
+public final class CatchNode extends Node {
/** Exception identifier. */
- private IdentNode exception;
+ private final IdentNode exception;
/** Exception condition. */
- private Node exceptionCondition;
+ private final Node exceptionCondition;
/** Catch body. */
- private Block body;
-
- /** Is rethrow - e.g. synthetic catch block for e.g. finallies, the parser case where
- * there has to be at least on catch for syntactic validity */
- private boolean isSyntheticRethrow;
+ private final Block body;
/**
* Constructors
@@ -64,18 +61,12 @@
this.body = body;
}
- private CatchNode(final CatchNode catchNode, final CopyState cs) {
+ private CatchNode(final CatchNode catchNode, final IdentNode exception, final Node exceptionCondition, final Block body) {
super(catchNode);
- this.exception = (IdentNode)cs.existingOrCopy(catchNode.exception);
- this.exceptionCondition = cs.existingOrCopy(catchNode.exceptionCondition);
- this.body = (Block)cs.existingOrCopy(catchNode.body);
- this.isSyntheticRethrow = catchNode.isSyntheticRethrow;
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new CatchNode(this, cs);
+ this.exception = exception;
+ this.exceptionCondition = exceptionCondition;
+ this.body = body;
}
/**
@@ -84,21 +75,22 @@
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterCatchNode(this) != null) {
- exception = (IdentNode)exception.accept(visitor);
-
- if (exceptionCondition != null) {
- exceptionCondition = exceptionCondition.accept(visitor);
- }
-
- body = (Block)body.accept(visitor);
- return visitor.leaveCatchNode(this);
+ if (visitor.enterCatchNode(this)) {
+ return visitor.leaveCatchNode(
+ setException((IdentNode)exception.accept(visitor)).
+ setExceptionCondition(exceptionCondition == null ? null : exceptionCondition.accept(visitor)).
+ setBody((Block)body.accept(visitor)));
}
return this;
}
@Override
+ public boolean isTerminal() {
+ return body.isTerminal();
+ }
+
+ @Override
public void toString(final StringBuilder sb) {
sb.append(" catch (");
exception.toString(sb);
@@ -111,23 +103,6 @@
}
/**
- * Check if this catch is a synthetic rethrow
- * @return true if this is a synthetic rethrow
- */
- public boolean isSyntheticRethrow() {
- return isSyntheticRethrow;
- }
-
- /**
- * Flag this as deliberatly generated catch all that rethrows the
- * caught exception. This is used for example for generating finally
- * expressions
- */
- public void setIsSyntheticRethrow() {
- this.isSyntheticRethrow = true;
- }
-
- /**
* Get the identifier representing the exception thrown
* @return the exception identifier
*/
@@ -146,9 +121,13 @@
/**
* Reset the exception condition for this catch block
* @param exceptionCondition the new exception condition
+ * @return new or same CatchNode
*/
- public void setExceptionCondition(final Node exceptionCondition) {
- this.exceptionCondition = exceptionCondition;
+ public CatchNode setExceptionCondition(final Node exceptionCondition) {
+ if (this.exceptionCondition == exceptionCondition) {
+ return this;
+ }
+ return new CatchNode(this, exception, exceptionCondition, body);
}
/**
@@ -158,4 +137,18 @@
public Block getBody() {
return body;
}
+
+ private CatchNode setException(final IdentNode exception) {
+ if (this.exception == exception) {
+ return this;
+ }
+ return new CatchNode(this, exception, exceptionCondition, body);
+ }
+
+ private CatchNode setBody(final Block body) {
+ if (this.body == body) {
+ return this;
+ }
+ return new CatchNode(this, exception, exceptionCondition, body);
+ }
}
--- a/nashorn/src/jdk/nashorn/internal/ir/ContinueNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/ContinueNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,43 +25,39 @@
package jdk.nashorn.internal.ir;
-import jdk.nashorn.internal.codegen.Label;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for CONTINUE statements.
- *
*/
-public class ContinueNode extends LabeledNode {
+@Immutable
+public class ContinueNode extends Node {
+
+ private IdentNode label;
/**
* Constructor
*
- * @param source the source
- * @param token token
- * @param finish finish
- * @param labelNode the continue label
- * @param targetNode node to continue to
- * @param tryChain surrounding try chain
+ * @param source source code
+ * @param token token
+ * @param finish finish
+ * @param label label for break or null if none
*/
- public ContinueNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) {
- super(source, token, finish, labelNode, targetNode, tryChain);
- setHasGoto();
- }
-
- private ContinueNode(final ContinueNode continueNode, final CopyState cs) {
- super(continueNode, cs);
+ public ContinueNode(final Source source, final long token, final int finish, final IdentNode label) {
+ super(source, token, finish);
+ this.label = label;
}
@Override
- protected Node copy(final CopyState cs) {
- return new ContinueNode(this, cs);
+ public boolean hasGoto() {
+ return true;
}
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterContinueNode(this) != null) {
+ if (visitor.enterContinueNode(this)) {
return visitor.leaveContinueNode(this);
}
@@ -69,21 +65,20 @@
}
/**
- * Return the target label of this continue node.
- * @return the target label.
+ * Get the label for this break node
+ * @return label, or null if none
*/
- public Label getTargetLabel() {
- assert targetNode instanceof WhileNode : "continue target must be a while node";
- return ((WhileNode)targetNode).getContinueLabel();
+ public IdentNode getLabel() {
+ return label;
}
@Override
public void toString(final StringBuilder sb) {
sb.append("continue");
- if (labelNode != null) {
+ if (label != null) {
sb.append(' ');
- labelNode.getLabel().toString(sb);
+ label.toString(sb);
}
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/DoWhileNode.java Wed May 08 11:22:25 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2010, 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 jdk.nashorn.internal.ir;
-
-import jdk.nashorn.internal.ir.visitor.NodeVisitor;
-import jdk.nashorn.internal.runtime.Source;
-
-/**
- * Loop representing do while loops. This is mostly split from WhileNode
- * because of the different order of the Phi Traversals
- *
- */
-public class DoWhileNode extends WhileNode {
-
- /**
- * Constructor
- *
- * @param source the source
- * @param token token
- * @param finish finish
- */
- public DoWhileNode(final Source source, final long token, final int finish) {
- super(source, token, finish);
- }
-
- /**
- * Copy constructor
- *
- * @param doWhileNode source node
- * @param cs copy state
- */
- protected DoWhileNode(final DoWhileNode doWhileNode, final CopyState cs) {
- super(doWhileNode, cs);
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new DoWhileNode(this, cs);
- }
-
- @Override
- public Node accept(final NodeVisitor visitor) {
- if (visitor.enterDoWhileNode(this) != null) {
- body = (Block)body.accept(visitor);
- test = test.accept(visitor);
-
- return visitor.leaveDoWhileNode(this);
- }
-
- return this;
- }
-
- @Override
- public void toString(final StringBuilder sb) {
- sb.append("while (");
- test.toString(sb);
- sb.append(')');
- }
-}
--- a/nashorn/src/jdk/nashorn/internal/ir/EmptyNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/EmptyNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,14 +25,15 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for an empty statement.
- *
*/
-public class EmptyNode extends Node {
+@Immutable
+public final class EmptyNode extends Node {
/**
* Constructor
@@ -57,7 +58,7 @@
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterEmptyNode(this) != null) {
+ if (visitor.enterEmptyNode(this)) {
return visitor.leaveEmptyNode(this);
}
return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/ExecuteNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/ExecuteNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,6 +25,7 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
@@ -33,9 +34,10 @@
* node means "this code will be executed" and evaluating it results in
* statements being added to the IR
*/
-public class ExecuteNode extends Node {
+@Immutable
+public final class ExecuteNode extends Node {
/** Expression to execute. */
- private Node expression;
+ private final Node expression;
/**
* Constructor
@@ -50,6 +52,11 @@
this.expression = expression;
}
+ private ExecuteNode(final ExecuteNode executeNode, final Node expression) {
+ super(executeNode);
+ this.expression = expression;
+ }
+
/**
* Constructor
*
@@ -60,34 +67,15 @@
this.expression = expression;
}
- private ExecuteNode(final ExecuteNode executeNode, final CopyState cs) {
- super(executeNode);
- this.expression = cs.existingOrCopy(executeNode.expression);
- }
-
@Override
- protected Node copy(final CopyState cs) {
- return new ExecuteNode(this, cs);
- }
-
- @Override
- public boolean equals(final Object other) {
- if (!super.equals(other)) {
- return false;
- }
- return expression.equals(((ExecuteNode)other).getExpression());
- }
-
- @Override
- public int hashCode() {
- return super.hashCode() ^ expression.hashCode();
+ public boolean isTerminal() {
+ return expression.isTerminal();
}
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterExecuteNode(this) != null) {
- setExpression(expression.accept(visitor));
- return visitor.leaveExecuteNode(this);
+ if (visitor.enterExecuteNode(this)) {
+ return visitor.leaveExecuteNode(setExpression(expression.accept(visitor)));
}
return this;
@@ -109,8 +97,12 @@
/**
* Reset the expression to be executed
* @param expression the expression
+ * @return new or same execute node
*/
- public void setExpression(final Node expression) {
- this.expression = expression;
+ public ExecuteNode setExpression(final Node expression) {
+ if (this.expression == expression) {
+ return this;
+ }
+ return new ExecuteNode(this, expression);
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/ir/Flags.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2010, 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 jdk.nashorn.internal.ir;
+
+/**
+ * Interface implemented by all nodes that have flags in
+ * a lexical context. This is needed as we sometimes have to save
+ * the setting of flags in the lexical context until a block
+ * is completely finished and its final version (after multiple
+ * copy on writes) is placed in the lexical context
+ *
+ * @param <T> lexical context node that can have flags set during code generation
+ */
+public interface Flags<T extends LexicalContextNode> {
+
+ /**
+ * Check if a flag is set in a lexical context node
+ * @param flag flag to check
+ * @return flags
+ */
+ public boolean getFlag(int flag);
+
+ /**
+ * Clear a flag of a LexicalContextNode
+ * @param lc lexical context
+ * @param flag flag to clear
+ * @return the new LexicalContext node if flags were changed, same otherwise
+ */
+ public T clearFlag(final LexicalContext lc, int flag);
+
+ /**
+ * Set a flag of a LexicalContextNode
+ * @param lc lexical context
+ * @param flag flag to set
+ * @return the new LexicalContext node if flags were changed, same otherwise
+ */
+ public T setFlag(final LexicalContext lc, int flag);
+
+ /**
+ * Set all flags of a LexicalContextNode, overwriting previous flags
+ * @param lc lexical context
+ * @param flags new flags value
+ * @return the new LexicalContext node if flags were changed, same otherwise
+ */
+ public T setFlags(final LexicalContext lc, int flags);
+}
--- a/nashorn/src/jdk/nashorn/internal/ir/ForNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/ForNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,73 +25,75 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representing a FOR statement.
- *
*/
-public class ForNode extends WhileNode {
+@Immutable
+public final class ForNode extends LoopNode {
/** Initialize expression. */
- private Node init;
+ private final Node init;
/** Test expression. */
- private Node modify;
+ private final Node modify;
/** Iterator symbol. */
private Symbol iterator;
- /** is for in */
- private boolean isForIn;
+ /** Is this a normal for loop? */
+ public static final int IS_FOR = 1 << 0;
- /** is for each */
- private boolean isForEach;
+ /** Is this a normal for in loop? */
+ public static final int IS_FOR_IN = 1 << 1;
+
+ /** Is this a normal for each in loop? */
+ public static final int IS_FOR_EACH = 1 << 2;
+
+ private final int flags;
/**
* Constructor
*
- * @param source the source
- * @param token token
- * @param finish finish
+ * @param source the source
+ * @param token token
+ * @param finish finish
+ * @param init init
+ * @param test test
+ * @param body body
+ * @param modify modify
+ * @param flags flags
*/
- public ForNode(final Source source, final long token, final int finish) {
- super(source, token, finish);
+ public ForNode(final Source source, final long token, final int finish, final Node init, final Node test, final Block body, final Node modify, final int flags) {
+ super(source, token, finish, test, body, false);
+ this.init = init;
+ this.modify = modify;
+ this.flags = flags;
}
- private ForNode(final ForNode forNode, final CopyState cs) {
- super(forNode, cs);
-
- this.init = cs.existingOrCopy(forNode.init);
- this.modify = cs.existingOrCopy(forNode.modify);
- this.iterator = forNode.iterator;
- this.isForIn = forNode.isForIn;
- this.isForEach = forNode.isForEach;
+ private ForNode(final ForNode forNode, final Node init, final Node test, final Block body, final Node modify, final int flags, final boolean controlFlowEscapes) {
+ super(forNode, test, body, controlFlowEscapes);
+ this.init = init;
+ this.modify = modify;
+ this.flags = flags;
+ this.iterator = forNode.iterator; //TODO is this acceptable? symbols are never cloned, just copied as references
}
@Override
- protected Node copy(final CopyState cs) {
- return new ForNode(this, cs);
+ public Node ensureUniqueLabels(LexicalContext lc) {
+ return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
}
@Override
- public Node accept(final NodeVisitor visitor) {
- if (visitor.enterForNode(this) != null) {
- if (init != null) {
- init = init.accept(visitor);
- }
-
- if (test != null) {
- test = test.accept(visitor);
- }
-
- if (modify != null) {
- modify = modify.accept(visitor);
- }
-
- body = (Block)body.accept(visitor);
-
- return visitor.leaveForNode(this);
+ protected Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterForNode(this)) {
+ return visitor.leaveForNode(
+ setInit(lc, init == null ? null : init.accept(visitor)).
+ setTest(lc, test == null ? null : test.accept(visitor)).
+ setModify(lc, modify == null ? null : modify.accept(visitor)).
+ setBody(lc, (Block)body.accept(visitor)));
}
return this;
@@ -122,6 +124,19 @@
sb.append(')');
}
+ @Override
+ public boolean hasGoto() {
+ return !isForIn() && test == null;
+ }
+
+ @Override
+ public boolean mustEnter() {
+ if (isForIn()) {
+ return false; //may be an empty set to iterate over, then we skip the loop
+ }
+ return test == null;
+ }
+
/**
* Get the initialization expression for this for loop
* @return the initialization expression
@@ -132,10 +147,15 @@
/**
* Reset the initialization expression for this for loop
+ * @param lc lexical context
* @param init new initialization expression
+ * @return new for node if changed or existing if not
*/
- public void setInit(final Node init) {
- this.init = init;
+ public ForNode setInit(final LexicalContext lc, final Node init) {
+ if (this.init == init) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
}
/**
@@ -143,14 +163,16 @@
* @return true if this is a for in constructor
*/
public boolean isForIn() {
- return isForIn;
+ return (flags & IS_FOR_IN) != 0;
}
/**
* Flag this to be a for in construct
+ * @param lc lexical context
+ * @return new for node if changed or existing if not
*/
- public void setIsForIn() {
- this.isForIn = true;
+ public ForNode setIsForIn(final LexicalContext lc) {
+ return setFlags(lc, flags | IS_FOR_IN);
}
/**
@@ -159,14 +181,16 @@
* @return true if this is a for each construct
*/
public boolean isForEach() {
- return isForEach;
+ return (flags & IS_FOR_EACH) != 0;
}
/**
* Flag this to be a for each construct
+ * @param lc lexical context
+ * @return new for node if changed or existing if not
*/
- public void setIsForEach() {
- this.isForEach = true;
+ public ForNode setIsForEach(final LexicalContext lc) {
+ return setFlags(lc, flags | IS_FOR_EACH);
}
/**
@@ -195,10 +219,15 @@
/**
* Reset the modification expression for this ForNode
+ * @param lc lexical context
* @param modify new modification expression
+ * @return new for node if changed or existing if not
*/
- public void setModify(final Node modify) {
- this.modify = modify;
+ public ForNode setModify(final LexicalContext lc, final Node modify) {
+ if (this.modify == modify) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
}
@Override
@@ -207,7 +236,39 @@
}
@Override
- public void setTest(final Node test) {
- this.test = test;
+ public ForNode setTest(final LexicalContext lc, final Node test) {
+ if (this.test == test) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
+ }
+
+ @Override
+ public Block getBody() {
+ return body;
}
+
+ @Override
+ public ForNode setBody(final LexicalContext lc, final Block body) {
+ if (this.body == body) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
+ }
+
+ @Override
+ public ForNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes) {
+ if (this.controlFlowEscapes == controlFlowEscapes) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
+ }
+
+ private ForNode setFlags(final LexicalContext lc, final int flags) {
+ if (this.flags == flags) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
+ }
+
}
--- a/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Thu May 16 11:47:51 2013 +0100
@@ -30,23 +30,19 @@
import static jdk.nashorn.internal.ir.Symbol.IS_CONSTANT;
import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Iterator;
+import java.util.HashSet;
import java.util.List;
-import java.util.Map;
-import java.util.Stack;
+import java.util.Set;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
-import jdk.nashorn.internal.codegen.Frame;
-import jdk.nashorn.internal.codegen.MethodEmitter;
+import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.Namespace;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
-import jdk.nashorn.internal.parser.Parser;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.UserAccessorProperty;
@@ -55,9 +51,11 @@
/**
* IR representation for function (or script.)
*/
-public class FunctionNode extends Block {
+@Immutable
+public final class FunctionNode extends LexicalContextNode implements Flags<FunctionNode> {
- private static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class);
+ /** Type used for all FunctionNodes */
+ public static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class);
/** Function kinds */
public enum Kind {
@@ -90,134 +88,106 @@
/** method has had its types finalized */
FINALIZED,
/** method has been emitted to bytecode */
- EMITTED,
- /** code installed in a class loader */
- INSTALLED
+ EMITTED
}
/** External function identifier. */
@Ignore
- private IdentNode ident;
+ private final IdentNode ident;
+
+ /** The body of the function node */
+ private final Block body;
/** Internal function name. */
- private String name;
+ private final String name;
/** Compilation unit. */
- private CompileUnit compileUnit;
-
- /** Method emitter for current method. */
- private MethodEmitter method;
+ private final CompileUnit compileUnit;
/** Function kind. */
- private Kind kind;
+ private final Kind kind;
/** List of parameters. */
- private List<IdentNode> parameters;
+ private final List<IdentNode> parameters;
/** First token of function. **/
- private long firstToken;
+ private final long firstToken;
/** Last token of function. **/
- private long lastToken;
+ private final long lastToken;
- /** Variable frames. */
- private Frame frames;
+ /** Declared symbols in this function node */
+ @Ignore
+ private final Set<Symbol> declaredSymbols;
/** Method's namespace. */
private final Namespace namespace;
- /** Node representing current this. */
- @Ignore
- private IdentNode thisNode;
-
- /** Node representing current scope. */
- @Ignore
- private IdentNode scopeNode;
-
- /** Node representing return value. */
- @Ignore
- private IdentNode resultNode;
-
- /** Node representing current arguments. */
- @Ignore
- private IdentNode argumentsNode;
-
- /** Node representing callee */
- @Ignore
- private IdentNode calleeNode;
-
- /** Node representing varargs */
- @Ignore
- private IdentNode varArgsNode;
-
- /** Pending label list. */
- private final Stack<LabelNode> labelStack;
-
- /** Pending control list. */
- private final Stack<Node> controlStack;
-
- /** VarNode for this function statement */
- @Ignore //this is explicit code anyway and should not be traversed after lower
- private VarNode funcVarNode;
-
- /** Line number for function declaration */
- @Ignore
- private LineNumberNode funcVarLineNumberNode;
-
- /** Initializer var func = __callee__, where applicable */
- @Ignore
- private Node selfSymbolInit;
-
/** Current compilation state */
@Ignore
private final EnumSet<CompilationState> compilationState;
- /** Type hints, e.g based on parameters at call site */
- private final Map<IdentNode, Type> specializedTypes;
-
/** Function flags. */
- private int flags;
+ private final int flags;
/** Is anonymous function flag. */
- private static final int IS_ANONYMOUS = 1 << 0;
+ public static final int IS_ANONYMOUS = 1 << 0;
+
/** Is the function created in a function declaration (as opposed to a function expression) */
- private static final int IS_DECLARED = 1 << 1;
+ public static final int IS_DECLARED = 1 << 1;
+
/** is this a strict mode function? */
- private static final int IS_STRICT_MODE = 1 << 2;
+ public static final int IS_STRICT = 1 << 2;
+
/** Does the function use the "arguments" identifier ? */
- private static final int USES_ARGUMENTS = 1 << 3;
- /** Are we lowered ? */
- private static final int IS_LOWERED = 1 << 4;
+ public static final int USES_ARGUMENTS = 1 << 3;
+
/** Has this node been split because it was too large? */
- private static final int IS_SPLIT = 1 << 5;
- /** Does the function call eval? */
- private static final int HAS_EVAL = 1 << 6;
- /** Does the function contain a with block ? */
- private static final int HAS_WITH = 1 << 7;
- /** Does a descendant function contain a with or eval? */
- private static final int HAS_DESCENDANT_WITH_OR_EVAL = 1 << 8;
- /** Does the function define "arguments" identifier as a parameter of nested function name? */
- private static final int DEFINES_ARGUMENTS = 1 << 9;
- /** Does the function need a self symbol? */
- private static final int NEEDS_SELF_SYMBOL = 1 << 10;
+ public static final int IS_SPLIT = 1 << 4;
+
+ /** Does the function call eval? If it does, then all variables in this function might be get/set by it and it can
+ * introduce new variables into this function's scope too.*/
+ public static final int HAS_EVAL = 1 << 5;
+
+ /** Does a nested function contain eval? If it does, then all variables in this function might be get/set by it. */
+ public static final int HAS_NESTED_EVAL = 1 << 6;
+
+ /**
+ * Flag this function as one that defines the identifier "arguments" as a function parameter or nested function
+ * name. This precludes it from needing to have an Arguments object defined as "arguments" local variable. Note that
+ * defining a local variable named "arguments" still requires construction of the Arguments object (see
+ * ECMAScript 5.1 Chapter 10.5).
+ * @see #needsArguments()
+ */
+ public static final int DEFINES_ARGUMENTS = 1 << 8;
+
/** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */
- private static final int USES_ANCESTOR_SCOPE = 1 << 11;
+ public static final int USES_ANCESTOR_SCOPE = 1 << 9;
+
/** Is this function lazily compiled? */
- private static final int IS_LAZY = 1 << 12;
+ public static final int IS_LAZY = 1 << 10;
+
/** Does this function have lazy, yet uncompiled children */
- private static final int HAS_LAZY_CHILDREN = 1 << 13;
+ public static final int HAS_LAZY_CHILDREN = 1 << 11;
+
/** Does this function have lazy, yet uncompiled children */
- private static final int IS_PROGRAM = 1 << 14;
+ public static final int IS_PROGRAM = 1 << 12;
+
+ /** Does this function have nested declarations? */
+ public static final int HAS_FUNCTION_DECLARATIONS = 1 << 13;
- /** Does this function or any nested functions contain a with or an eval? */
- private static final int HAS_DEEP_WITH_OR_EVAL = HAS_EVAL | HAS_WITH | HAS_DESCENDANT_WITH_OR_EVAL;
+ /** Does this function or any nested functions contain an eval? */
+ private static final int HAS_DEEP_EVAL = HAS_EVAL | HAS_NESTED_EVAL;
+
/** Does this function need to store all its variables in scope? */
- private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_WITH_OR_EVAL | IS_SPLIT | HAS_LAZY_CHILDREN;
+ private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_EVAL | IS_SPLIT | HAS_LAZY_CHILDREN;
+
/** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */
private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL;
- /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep with or eval.
+
+ /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval.
* We also pessimistically need a parent scope if we have lazy children that have not yet been compiled */
- private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_WITH_OR_EVAL | HAS_LAZY_CHILDREN;
+ private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL | HAS_LAZY_CHILDREN;
/** What is the return type of this function? */
private Type returnType = Type.UNKNOWN;
@@ -225,107 +195,77 @@
/**
* Constructor
*
- * @param source the source
- * @param token token
- * @param finish finish
- * @param namespace the namespace
- * @param ident the identifier
- * @param name the name of the function
+ * @param source the source
+ * @param token token
+ * @param finish finish
+ * @param firstToken first token of the funtion node (including the function declaration)
+ * @param namespace the namespace
+ * @param ident the identifier
+ * @param name the name of the function
+ * @param parameters parameter list
+ * @param kind kind of function as in {@link FunctionNode.Kind}
+ * @param flags initial flags
*/
- public FunctionNode(final Source source, final long token, final int finish, final Namespace namespace, final IdentNode ident, final String name) {
+ public FunctionNode(
+ final Source source,
+ final long token,
+ final int finish,
+ final long firstToken,
+ final Namespace namespace,
+ final IdentNode ident,
+ final String name,
+ final List<IdentNode> parameters,
+ final FunctionNode.Kind kind,
+ final int flags) {
super(source, token, finish);
this.ident = ident;
this.name = name;
- this.kind = Kind.NORMAL;
- this.parameters = new ArrayList<>();
- this.firstToken = token;
+ this.kind = kind;
+ this.parameters = parameters;
+ this.firstToken = firstToken;
this.lastToken = token;
this.namespace = namespace;
- this.labelStack = new Stack<>();
- this.controlStack = new Stack<>();
this.compilationState = EnumSet.of(CompilationState.INITIALIZED);
- this.specializedTypes = new HashMap<>();
+ this.declaredSymbols = new HashSet<>();
+ this.flags = flags;
+ this.compileUnit = null;
+ this.body = null;
}
- private FunctionNode(final FunctionNode functionNode, final CopyState cs) {
- super(functionNode, cs);
-
- this.ident = (IdentNode)cs.existingOrCopy(functionNode.ident);
- this.name = functionNode.name;
- this.kind = functionNode.kind;
-
- this.parameters = new ArrayList<>();
- for (final IdentNode param : functionNode.getParameters()) {
- this.parameters.add((IdentNode)cs.existingOrCopy(param));
- }
+ private FunctionNode(final FunctionNode functionNode, final long lastToken, final int flags, final Type returnType, final CompileUnit compileUnit, final EnumSet<CompilationState> compilationState, final Block body) {
+ super(functionNode);
+ this.flags = flags;
+ this.returnType = returnType;
+ this.compileUnit = compileUnit;
+ this.lastToken = lastToken;
+ this.compilationState = compilationState;
+ this.body = body;
- this.firstToken = functionNode.firstToken;
- this.lastToken = functionNode.lastToken;
- this.namespace = functionNode.getNamespace();
- this.thisNode = (IdentNode)cs.existingOrCopy(functionNode.thisNode);
- this.scopeNode = (IdentNode)cs.existingOrCopy(functionNode.scopeNode);
- this.resultNode = (IdentNode)cs.existingOrCopy(functionNode.resultNode);
- this.argumentsNode = (IdentNode)cs.existingOrCopy(functionNode.argumentsNode);
- this.varArgsNode = (IdentNode)cs.existingOrCopy(functionNode.varArgsNode);
- this.calleeNode = (IdentNode)cs.existingOrCopy(functionNode.calleeNode);
- this.labelStack = new Stack<>();
- this.controlStack = new Stack<>();
-
- this.flags = functionNode.flags;
-
- this.funcVarNode = (VarNode)cs.existingOrCopy(functionNode.funcVarNode);
- /** VarNode for this function statement */
-
- this.compilationState = EnumSet.copyOf(functionNode.compilationState);
- this.specializedTypes = new HashMap<>();
+ // the fields below never change - they are final and assigned in constructor
+ this.name = functionNode.name;
+ this.ident = functionNode.ident;
+ this.namespace = functionNode.namespace;
+ this.declaredSymbols = functionNode.declaredSymbols;
+ this.kind = functionNode.kind;
+ this.parameters = functionNode.parameters;
+ this.firstToken = functionNode.firstToken;
}
@Override
- protected Node copy(final CopyState cs) {
- // deep clone all parent blocks
- return new FunctionNode(this, cs);
- }
-
- @Override
- public Node accept(final NodeVisitor visitor) {
- final FunctionNode saveFunctionNode = visitor.getCurrentFunctionNode();
- final Block saveBlock = visitor.getCurrentBlock();
- final MethodEmitter saveMethodEmitter = visitor.getCurrentMethodEmitter();
- final CompileUnit saveCompileUnit = visitor.getCurrentCompileUnit();
-
- visitor.setCurrentFunctionNode(this);
- visitor.setCurrentBlock(this);
-
- try {
- if (visitor.enterFunctionNode(this) != null) {
- if (ident != null) {
- ident = (IdentNode)ident.accept(visitor);
- }
-
- for (int i = 0, count = parameters.size(); i < count; i++) {
- parameters.set(i, (IdentNode)parameters.get(i).accept(visitor));
- }
-
- for (int i = 0, count = statements.size(); i < count; i++) {
- statements.set(i, statements.get(i).accept(visitor));
- }
-
- return visitor.leaveFunctionNode(this);
- }
- } finally {
- visitor.setCurrentBlock(saveBlock);
- visitor.setCurrentFunctionNode(saveFunctionNode);
- visitor.setCurrentCompileUnit(saveCompileUnit);
- visitor.setCurrentMethodEmitter(saveMethodEmitter);
+ public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterFunctionNode(this)) {
+ return visitor.leaveFunctionNode(setBody(lc, (Block)body.accept(visitor)));
}
-
return this;
}
- @Override
- public boolean needsScope() {
- return super.needsScope() || isProgram();
+ /**
+ * Get the compilation state of this function
+ * @return the compilation state
+ */
+ public EnumSet<CompilationState> getState() {
+ return compilationState;
}
/**
@@ -357,62 +297,17 @@
* FunctionNode has been lowered, the compiler will add
* {@code CompilationState#LOWERED} to the state vector
*
+ * @param lc lexical context
* @param state {@link CompilationState} to add
- */
- public void setState(final CompilationState state) {
- compilationState.add(state);
- }
-
- /*
- * Frame management.
- */
-
- /**
- * Push a new block frame.
- *
- * @return the new frame
- */
- public final Frame pushFrame() {
- frames = new Frame(frames);
- return frames;
- }
-
- /**
- * Pop a block frame.
+ * @return function node or a new one if state was changed
*/
- public final void popFrame() {
- frames = frames.getPrevious();
- }
-
- /**
- * Create a temporary variable to the current frame.
- *
- * @param currentFrame Frame to add to - defaults to current function frame
- * @param type Strong type of symbol.
- * @param node Primary node to use symbol.
- *
- * @return Symbol used.
- */
- public Symbol newTemporary(final Frame currentFrame, final Type type, final Node node) {
- assert currentFrame != null;
- Symbol symbol = node.getSymbol();
-
- // If no symbol already present.
- if (symbol == null) {
- final String uname = uniqueName(TEMP_PREFIX.tag());
- symbol = new Symbol(uname, IS_TEMP, type);
- symbol.setNode(node);
+ public FunctionNode setState(final LexicalContext lc, final CompilationState state) {
+ if (this.compilationState.equals(state)) {
+ return this;
}
-
- // Assign a slot if it doesn't have one.
- if (!symbol.hasSlot()) {
- currentFrame.addSymbol(symbol);
- }
-
- // Set symbol to node.
- node.setSymbol(symbol);
-
- return symbol;
+ final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
+ newState.add(state);
+ return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, newState, body));
}
/**
@@ -425,18 +320,6 @@
}
/**
- * Add a new temporary variable to the current frame
- *
- * @param type Strong type of symbol
- * @param node Primary node to use symbol
- *
- * @return symbol used
- */
- public Symbol newTemporary(final Type type, final Node node) {
- return newTemporary(frames, type, node);
- }
-
- /**
* Create a virtual symbol for a literal.
*
* @param literalNode Primary node to use symbol.
@@ -444,9 +327,8 @@
* @return Symbol used.
*/
public Symbol newLiteral(final LiteralNode<?> literalNode) {
- final String uname = uniqueName(LITERAL_PREFIX.tag());
+ final String uname = uniqueName(LITERAL_PREFIX.symbolName());
final Symbol symbol = new Symbol(uname, IS_CONSTANT, literalNode.getType());
- symbol.setNode(literalNode);
literalNode.setSymbol(symbol);
return symbol;
@@ -482,29 +364,35 @@
sb.append(')');
}
+ @Override
+ public boolean getFlag(final int flag) {
+ return (flags & flag) != 0;
+ }
+
+ @Override
+ public FunctionNode setFlags(final LexicalContext lc, int flags) {
+ if (this.flags == flags) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
+ }
+
+ @Override
+ public FunctionNode clearFlag(final LexicalContext lc, final int flag) {
+ return setFlags(lc, flags & ~flag);
+ }
+
+ @Override
+ public FunctionNode setFlag(final LexicalContext lc, final int flag) {
+ return setFlags(lc, flags | flag);
+ }
+
/**
* Returns true if the function is the top-level program.
* @return True if this function node represents the top-level program.
*/
public boolean isProgram() {
- return (flags & IS_PROGRAM) != 0;
- }
-
- /**
- * Marks the function as representing the top-level program.
- */
- public void setProgram() {
- flags |= IS_PROGRAM;
- }
-
- /**
- * Get the control stack. Used when parsing to establish nesting depths of
- * different control structures
- *
- * @return the control stack
- */
- public Stack<Node> getControlStack() {
- return controlStack;
+ return getFlag(IS_PROGRAM);
}
/**
@@ -512,54 +400,7 @@
* @return true if lazy
*/
public boolean isLazy() {
- return (flags & IS_LAZY) != 0;
- }
-
- /**
- * Set if this function should be lazily generated
- * @param isLazy is lazy
- */
- public void setIsLazy(final boolean isLazy) {
- this.flags = isLazy ? flags | IS_LAZY : flags & ~IS_LAZY;
- }
-
- /**
- * Check if the {@code with} keyword is used in this function
- *
- * @return true if {@code with} is used
- */
- public boolean hasWith() {
- return (flags & HAS_WITH) != 0;
- }
-
- /**
- * Flag this function as using the {@code with} keyword
- * @param ancestors the iterator over functions in this functions's containing lexical context
- */
- public void setHasWith(final Iterator<FunctionNode> ancestors) {
- if(!hasWith()) {
- this.flags |= HAS_WITH;
- // with requires scope in parents.
- // TODO: refine this. with should not force all variables in parents to be in scope, only those that are
- // actually referenced as identifiers by name
- markParentForWithOrEval(ancestors);
- }
- }
-
- private void markParentForWithOrEval(final Iterator<FunctionNode> ancestors) {
- // If this is invoked, then either us or a descendant uses with or eval, meaning we must have our own scope.
- setNeedsScope();
-
- if(ancestors.hasNext()) {
- ancestors.next().setDescendantHasWithOrEval(ancestors);
- }
- }
-
- private void setDescendantHasWithOrEval(final Iterator<FunctionNode> ancestors) {
- if((flags & HAS_DESCENDANT_WITH_OR_EVAL) == 0) {
- flags |= HAS_DESCENDANT_WITH_OR_EVAL;
- markParentForWithOrEval(ancestors);
- }
+ return getFlag(IS_LAZY);
}
/**
@@ -568,30 +409,7 @@
* @return true if {@code eval} is used
*/
public boolean hasEval() {
- return (flags & HAS_EVAL) != 0;
- }
-
- /**
- * Flag this function as calling the {@code eval} function
- * @param ancestors the iterator over functions in this functions's containing lexical context
- */
- public void setHasEval(final Iterator<FunctionNode> ancestors) {
- if(!hasEval()) {
- this.flags |= HAS_EVAL;
- markParentForWithOrEval(ancestors);
- }
- }
-
- /**
- * Test whether this function or any of its nested functions contains a <tt>with</tt> statement
- * or an <tt>eval</tt> call.
- *
- * @see #hasWith()
- * @see #hasEval()
- * @return true if this or a nested function contains with or eval
- */
- public boolean hasDeepWithOrEval() {
- return (flags & HAS_DEEP_WITH_OR_EVAL) != 0;
+ return getFlag(HAS_EVAL);
}
/**
@@ -603,90 +421,11 @@
}
/**
- * Set the first token for this function
- * @param firstToken the first token
- */
- public void setFirstToken(final long firstToken) {
- this.firstToken = firstToken;
- }
-
- /**
- * Returns a list of functions declared by this function. Only includes declared functions, and does not include any
- * function expressions that might occur in its body.
- * @return a list of functions declared by this function.
+ * Check whether this function has nested function declarations
+ * @return true if nested function declarations exist
*/
- public List<FunctionNode> getDeclaredFunctions() {
- // Note that the function does not have a dedicated list of declared functions, but rather relies on the
- // invariant that all function declarations are at the beginning of the statement list as VarNode with a
- // FunctionNode marked as statement with its variable initializer. Every VarNode is also preceded by a
- // LineNumberNode. This invariant is established by the parser and has to be preserved in visitors.
- final List<FunctionNode> fns = new ArrayList<>();
- for (final Node stmt : statements) {
- if(stmt instanceof LineNumberNode) {
- continue;
- } else if(stmt instanceof VarNode) {
- final Node init = ((VarNode)stmt).getInit();
- if(init instanceof FunctionNode) {
- final FunctionNode fn = (FunctionNode)init;
- if(fn.isDeclared()) {
- fns.add(fn);
- continue;
- }
- }
- }
- // Node is neither a LineNumberNode, nor a function declaration VarNode. Since all function declarations are
- // at the start of the function, we've reached the end of function declarations.
- break;
- }
- return fns;
- }
-
- /**
- * Get the label stack. This is used by the parser to establish
- * label nesting depth
- *
- * @return the label stack
- */
- public Stack<LabelNode> getLabelStack() {
- return labelStack;
- }
-
- /**
- * If this function needs to use var args, return the identifier to the node used
- * for the var args structure
- *
- * @return IdentNode representing the var args structure
- */
- public IdentNode getVarArgsNode() {
- return varArgsNode;
- }
-
- /**
- * Set the identifier to the node used for the var args structure
- *
- * @param varArgsNode IdentNode representing the var args
- */
- public void setVarArgsNode(final IdentNode varArgsNode) {
- this.varArgsNode = varArgsNode;
- }
-
- /**
- * If this function uses the {@code callee} variable, return the node used
- * as this variable
- *
- * @return an IdentNode representing the {@code callee} variable
- */
- public IdentNode getCalleeNode() {
- return calleeNode;
- }
-
- /**
- * If this function uses the {@code callee} variable, set the node representing the
- * callee
- * @param calleeNode an IdentNode representing the callee
- */
- public void setCalleeNode(final IdentNode calleeNode) {
- this.calleeNode = calleeNode;
+ public boolean hasDeclaredFunctions() {
+ return getFlag(HAS_FUNCTION_DECLARATIONS);
}
/**
@@ -697,26 +436,7 @@
* @return true if the function's generated Java method needs a {@code callee} parameter.
*/
public boolean needsCallee() {
- return needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrictMode());
- }
-
- /**
- * If this is a function where {@code arguments} is used, return the node used as the {@code arguments}
- * variable
- * @return an IdentNode representing {@code arguments}
- */
- public IdentNode getArgumentsNode() {
- return argumentsNode;
- }
-
- /**
- * If this is a Function where {@code arguments} is used, an identifier to the node representing
- * the {@code arguments} value has to be supplied by the compiler
- *
- * @param argumentsNode IdentNode that represents {@code arguments}
- */
- public void setArgumentsNode(final IdentNode argumentsNode) {
- this.argumentsNode = argumentsNode;
+ return needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrict());
}
/**
@@ -728,11 +448,42 @@
}
/**
- * Reset the identifier for this function
- * @param ident IdentNode for new identifier
+ * Return a set of symbols declared in this function node. This
+ * is only relevant after Attr, otherwise it will be an empty
+ * set as no symbols have been introduced
+ * @return set of declared symbols in function
+ */
+ public Set<Symbol> getDeclaredSymbols() {
+ return Collections.unmodifiableSet(declaredSymbols);
+ }
+
+ /**
+ * Add a declared symbol to this function node
+ * @param symbol symbol that is declared
*/
- public void setIdent(final IdentNode ident) {
- this.ident = ident;
+ public void addDeclaredSymbol(final Symbol symbol) {
+ declaredSymbols.add(symbol);
+ }
+
+ /**
+ * Get the function body
+ * @return the function body
+ */
+ public Block getBody() {
+ return body;
+ }
+
+ /**
+ * Reset the function body
+ * @param lc lexical context
+ * @param body new body
+ * @return new function node if body changed, same if not
+ */
+ public FunctionNode setBody(final LexicalContext lc, final Block body) {
+ if(this.body == body) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
}
/**
@@ -748,17 +499,6 @@
}
/**
- * Flag this function as one that defines the identifier "arguments" as a function parameter or nested function
- * name. This precludes it from needing to have an Arguments object defined as "arguments" local variable. Note that
- * defining a local variable named "arguments" still requires construction of the Arguments object (see
- * ECMAScript 5.1 Chapter 10.5).
- * @see #needsArguments()
- */
- public void setDefinesArguments() {
- this.flags |= DEFINES_ARGUMENTS;
- }
-
- /**
* Returns true if this function needs to have an Arguments object defined as a local variable named "arguments".
* Functions that use "arguments" as identifier and don't define it as a name of a parameter or a nested function
* (see ECMAScript 5.1 Chapter 10.5), as well as any function that uses eval or with, or has a nested function that
@@ -770,15 +510,7 @@
public boolean needsArguments() {
// uses "arguments" or calls eval, but it does not redefine "arguments", and finally, it's not a script, since
// for top-level script, "arguments" is picked up from Context by Global.init() instead.
- return (flags & MAYBE_NEEDS_ARGUMENTS) != 0 && (flags & DEFINES_ARGUMENTS) == 0 && !isProgram();
- }
-
- /**
- * Flags this function as one that uses the "arguments" identifier.
- * @see #needsArguments()
- */
- public void setUsesArguments() {
- flags |= USES_ARGUMENTS;
+ return getFlag(MAYBE_NEEDS_ARGUMENTS) && !getFlag(DEFINES_ARGUMENTS) && !isProgram();
}
/**
@@ -789,7 +521,7 @@
* @return true if the function needs parent scope.
*/
public boolean needsParentScope() {
- return (flags & NEEDS_PARENT_SCOPE) != 0 || isProgram();
+ return getFlag(NEEDS_PARENT_SCOPE) || isProgram();
}
/**
@@ -802,15 +534,6 @@
}
/**
- * Set the kind of this function
- * @see FunctionNode.Kind
- * @param kind the kind
- */
- public void setKind(final Kind kind) {
- this.kind = kind;
- }
-
- /**
* Return the last token for this function's code
* @return last token
*/
@@ -820,10 +543,15 @@
/**
* Set the last token for this function's code
+ * @param lc lexical context
* @param lastToken the last token
+ * @return function node or a new one if state was changed
*/
- public void setLastToken(final long lastToken) {
- this.lastToken = lastToken;
+ public FunctionNode setLastToken(final LexicalContext lc, final long lastToken) {
+ if (this.lastToken == lastToken) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
}
/**
@@ -835,21 +563,13 @@
}
/**
- * Set the name of this function
- * @param name the name
- */
- public void setName(final String name) {
- this.name = name;
- }
-
- /**
* Check if this function should have all its variables in its own scope. Scripts, split sub-functions, and
* functions having with and/or eval blocks are such.
*
* @return true if all variables should be in scope
*/
public boolean allVarsInScope() {
- return isProgram() || (flags & HAS_ALL_VARS_IN_SCOPE) != 0;
+ return isProgram() || getFlag(HAS_ALL_VARS_IN_SCOPE);
}
/**
@@ -858,15 +578,7 @@
* @return true if this function is split from a larger one
*/
public boolean isSplit() {
- return (flags & IS_SPLIT) != 0;
- }
-
- /**
- * Flag this function node as being a sub-function generated by the splitter
- */
- public void setIsSplit() {
- this.flags |= IS_SPLIT;
- setNeedsScope();
+ return getFlag(IS_SPLIT);
}
/**
@@ -875,15 +587,7 @@
* @return true if there are lazy child functions
*/
public boolean hasLazyChildren() {
- return (flags & HAS_LAZY_CHILDREN) != 0;
- }
-
- /**
- * Flag this function node as having yet-to-be-generated child functions
- */
- public void setHasLazyChildren() {
- this.flags |= HAS_LAZY_CHILDREN;
- setNeedsScope();
+ return getFlag(HAS_LAZY_CHILDREN);
}
/**
@@ -895,66 +599,13 @@
}
/**
- * Set the paremeters to this function
- * @param parameters a list of IdentNodes representing parameters in left to right order
- */
- public void setParameters(final List<IdentNode> parameters) {
- this.parameters = parameters;
- }
-
- /**
* Get a specialized type for an identity, if one exists
* @param node node to check specialized type for
* @return null if no specialization exists, otherwise type
*/
+ @SuppressWarnings("static-method")
public Type getSpecializedType(final IdentNode node) {
- return specializedTypes.get(node);
- }
-
- /**
- * Set parameter type hints for specialization.
- * @param types types array of length equal to parameter list size
- */
- public void setParameterTypes(final Class<?>[] types) {
- assert types.length == parameters.size() : "Type vector length doesn't correspond to parameter types";
- //diff - skip the callee and this etc, they are not explicit params in the parse tree
- for (int i = 0; i < types.length ; i++) {
- specializedTypes.put(parameters.get(i), Type.typeFor(types[i]));
- }
- }
-
- /**
- * Get the identifier for the variable in which the function return value
- * should be stored
- * @return an IdentNode representing the return value
- */
- public IdentNode getResultNode() {
- return resultNode;
- }
-
- /**
- * Set the identifier representing the variable in which the function return
- * value should be stored
- * @param resultNode an IdentNode representing the return value
- */
- public void setResultNode(final IdentNode resultNode) {
- this.resultNode = resultNode;
- }
-
- /**
- * Get the identifier representing this function's scope
- * @return an IdentNode representing this function's scope
- */
- public IdentNode getScopeNode() {
- return scopeNode;
- }
-
- /**
- * Set the identifier representing this function's scope
- * @param scopeNode an IdentNode representing this function's scope
- */
- public void setScopeNode(final IdentNode scopeNode) {
- this.scopeNode = scopeNode;
+ return null; //TODO implement specialized types later
}
/**
@@ -962,15 +613,7 @@
* @return true if function is declared.
*/
public boolean isDeclared() {
- return (flags & IS_DECLARED) != 0;
- }
-
- /**
- * Flag this function as being created as a function declaration (as opposed to a function expression).
- * @see Parser
- */
- public void setIsDeclared() {
- this.flags |= IS_DECLARED;
+ return getFlag(IS_DECLARED);
}
/**
@@ -978,15 +621,7 @@
* @return true if function is anonymous
*/
public boolean isAnonymous() {
- return (flags & IS_ANONYMOUS) != 0;
- }
-
- /**
- * Flag this function as an anonymous function.
- * @see Parser
- */
- public void setIsAnonymous() {
- this.flags |= IS_ANONYMOUS;
+ return getFlag(IS_ANONYMOUS);
}
/**
@@ -995,109 +630,7 @@
* @return true if function needs a symbol for self
*/
public boolean needsSelfSymbol() {
- return (flags & NEEDS_SELF_SYMBOL) != 0;
- }
-
- /**
- * Get the initializer statement for the __callee__ variable, where applicable
- * for self references
- * @return initialization
- */
- public Node getSelfSymbolInit() {
- return this.selfSymbolInit;
- }
-
- /**
- * Flag the function as needing a self symbol. This is needed only for
- * self referring functions
- * @param selfSymbolInit initialization expression for self symbol
- */
- public void setNeedsSelfSymbol(final Node selfSymbolInit) {
- this.flags |= NEEDS_SELF_SYMBOL;
- this.selfSymbolInit = selfSymbolInit;
- }
-
- /**
- * Marks this function as using any of its ancestors' scopes.
- */
- public void setUsesAncestorScope() {
- this.flags |= USES_ANCESTOR_SCOPE;
- }
-
- @Override
- void setUsesParentScopeSymbol(Symbol symbol, Iterator<Block> ancestors) {
- setUsesAncestorScope();
- super.setUsesParentScopeSymbol(symbol, ancestors);
- }
-
- /**
- * Return the node representing {@code this} in this function
- * @return IdentNode representing {@code this}
- */
- public IdentNode getThisNode() {
- return thisNode;
- }
-
- /**
- * Set the node representing {@code this} in this function
- * @param thisNode identifier representing {@code this}
- */
- public void setThisNode(final IdentNode thisNode) {
- this.thisNode = thisNode;
- }
-
- /**
- * Every function declared as {@code function x()} is internally hoisted
- * and represented as {@code var x = function() ... }. This getter returns
- * the VarNode representing this virtual assignment
- *
- * @return the var node emitted for setting this function symbol
- */
- public VarNode getFunctionVarNode() {
- return funcVarNode;
- }
-
- /**
- * Set the virtual VarNode assignment for this function.
- * @see FunctionNode#getFunctionVarNode()
- *
- * @param varNode the virtual var node assignment
- */
- public void setFunctionVarNode(final VarNode varNode) {
- funcVarNode = varNode;
- }
-
- /**
- * The line number information where the function was declared must be propagated
- * to the virtual {@code var x = function() ... } assignment described in
- * {@link FunctionNode#getFunctionVarNode()}
- * This maintains the line number of the declaration
- *
- * @return a line number node representing the line this function was declared
- */
- public LineNumberNode getFunctionVarLineNumberNode() {
- return funcVarLineNumberNode;
- }
-
- /**
- * Set the virtual VarNode assignment for this function, along with
- * a line number node for tracking the original start line of the function
- * declaration
- *
- * @param varNode the virtual var node assignment
- * @param lineNumber the line number node for the function declaration
- */
- public void setFunctionVarNode(final VarNode varNode, final LineNumberNode lineNumber) {
- funcVarNode = varNode;
- funcVarLineNumberNode = lineNumber;
- }
-
- /**
- * Get the namespace this function uses for its symbols
- * @return the namespace
- */
- public Namespace getNamespace() {
- return namespace;
+ return body.getFlag(Block.NEEDS_SELF_SYMBOL);
}
@Override
@@ -1118,47 +651,38 @@
/**
* Set the function return type
- *
+ * @param lc lexical context
* @param returnType new return type
+ * @return function node or a new one if state was changed
*/
- public void setReturnType(final Type returnType) {
+ public FunctionNode setReturnType(final LexicalContext lc, final Type returnType) {
//we never bother with object types narrower than objects, that will lead to byte code verification errors
//as for instance even if we know we are returning a string from a method, the code generator will always
//treat it as an object, at least for now
- this.returnType = Type.widest(this.returnType, returnType.isObject() ? Type.OBJECT : returnType);
- }
-
- /**
- * Set strict mode on or off for this function
- *
- * @param isStrictMode true if strict mode should be enabled
- */
- public void setStrictMode(final boolean isStrictMode) {
- flags = isStrictMode ? flags | IS_STRICT_MODE : flags & ~IS_STRICT_MODE;
+ if (this.returnType == returnType) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ flags,
+ Type.widest(this.returnType, returnType.isObject() ?
+ Type.OBJECT :
+ returnType),
+ compileUnit,
+ compilationState,
+ body));
}
/**
* Check if the function is generated in strict mode
* @return true if strict mode enabled for function
*/
- public boolean isStrictMode() {
- return (flags & IS_STRICT_MODE) != 0;
- }
-
- /**
- * Set the lowered state
- */
- public void setIsLowered() {
- flags |= IS_LOWERED;
- }
-
- /**
- * Get the lowered state
- *
- * @return true if function is lowered
- */
- public boolean isLowered() {
- return (flags & IS_LOWERED) != 0;
+ public boolean isStrict() {
+ return getFlag(IS_STRICT);
}
/**
@@ -1173,25 +697,47 @@
/**
* Reset the compile unit used to compile this function
* @see Compiler
+ * @param lc lexical context
* @param compileUnit the compile unit
+ * @return function node or a new one if state was changed
*/
- public void setCompileUnit(final CompileUnit compileUnit) {
- this.compileUnit = compileUnit;
+ public FunctionNode setCompileUnit(final LexicalContext lc, final CompileUnit compileUnit) {
+ if (this.compileUnit == compileUnit) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
}
/**
- * Return the method emitter used to write bytecode for this function
- * @return the method emitter
+ * Create a temporary variable to the current frame.
+ *
+ * @param block that needs the temporary
+ * @param type Strong type of symbol.
+ * @param node Primary node to use symbol.
+ *
+ * @return Symbol used.
*/
- public MethodEmitter getMethodEmitter() {
- return method;
+ public Symbol ensureSymbol(final Block block, final Type type, final Node node) {
+ Symbol symbol = node.getSymbol();
+
+ // If no symbol already present.
+ if (symbol == null) {
+ final String uname = uniqueName(TEMP_PREFIX.symbolName());
+ symbol = new Symbol(uname, IS_TEMP, type);
+ block.putSymbol(uname, symbol);
+ node.setSymbol(symbol);
+ }
+
+ return symbol;
}
/**
- * Set the method emitter that is to be used to write bytecode for this function
- * @param method a method emitter
+ * Get the symbol for a compiler constant, or null if not available (yet)
+ * @param cc compiler constant
+ * @return symbol for compiler constant, or null if not defined yet (for example in Lower)
*/
- public void setMethodEmitter(final MethodEmitter method) {
- this.method = method;
+ public Symbol compilerConstant(final CompilerConstants cc) {
+ return body.getExistingSymbol(cc.symbolName());
}
+
}
--- a/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java Thu May 16 11:47:51 2013 +0100
@@ -32,13 +32,15 @@
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for an identifier.
*/
-public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNode>, FunctionCall {
+@Immutable
+public final class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNode>, FunctionCall {
private static final int PROPERTY_NAME = 1 << 0;
private static final int INITIALIZED_HERE = 1 << 1;
private static final int FUNCTION = 1 << 2;
@@ -47,9 +49,9 @@
private final String name;
/** Type for a callsite, e.g. X in a get()X or a set(X)V */
- private Type callSiteType;
+ private final Type callSiteType;
- private byte flags;
+ private final int flags;
/**
* Constructor
@@ -62,6 +64,15 @@
public IdentNode(final Source source, final long token, final int finish, final String name) {
super(source, token, finish);
this.name = name;
+ this.callSiteType = null;
+ this.flags = 0;
+ }
+
+ private IdentNode(final IdentNode identNode, final String name, final Type callSiteType, final int flags) {
+ super(identNode);
+ this.name = name;
+ this.callSiteType = callSiteType;
+ this.flags = flags;
}
/**
@@ -71,8 +82,9 @@
*/
public IdentNode(final IdentNode identNode) {
super(identNode);
- this.name = identNode.getName();
- this.flags = identNode.flags;
+ this.name = identNode.getName();
+ this.callSiteType = null;
+ this.flags = identNode.flags;
}
@Override
@@ -92,40 +104,15 @@
@Override
public IdentNode setType(final Type type) {
- if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
- ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
- }
// do NOT, repeat NOT touch the symbol here. it might be a local variable or whatever. This is the override if it isn't
- if(this.callSiteType == type) {
+ if (this.callSiteType == type) {
return this;
}
- final IdentNode n = (IdentNode)clone();
- n.callSiteType = type;
- return n;
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new IdentNode(this);
- }
+ if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
+ ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType());
+ }
- /**
- * Test to see if two IdentNode are the same.
- *
- * @param other Other ident.
- * @return true if the idents are the same.
- */
- @Override
- public boolean equals(final Object other) {
- if (other instanceof IdentNode) {
- return name.equals(((IdentNode)other).name);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return name.hashCode();
+ return new IdentNode(this, name, type, flags);
}
/**
@@ -135,7 +122,7 @@
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterIdentNode(this) != null) {
+ if (visitor.enterIdentNode(this)) {
return visitor.leaveIdentNode(this);
}
@@ -147,7 +134,7 @@
if (hasCallSiteType()) {
sb.append('{');
final String desc = getType().getDescriptor();
- sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
+ sb.append(desc.charAt(desc.length() - 1) == ';' ? 'O' : getType().getDescriptor());
sb.append('}');
}
@@ -191,10 +178,10 @@
* @return a node equivalent to this one except for the requested change.
*/
public IdentNode setIsPropertyName() {
- if(isPropertyName()) return this;
- final IdentNode n = (IdentNode)clone();
- n.flags |= PROPERTY_NAME;
- return n;
+ if (isPropertyName()) {
+ return this;
+ }
+ return new IdentNode(this, name, callSiteType, flags | PROPERTY_NAME);
}
/**
@@ -210,10 +197,10 @@
* @return a node equivalent to this one except for the requested change.
*/
public IdentNode setIsInitializedHere() {
- if(isInitializedHere()) return this;
- final IdentNode n = (IdentNode)clone();
- n.flags |= INITIALIZED_HERE;
- return n;
+ if (isInitializedHere()) {
+ return this;
+ }
+ return new IdentNode(this, name, callSiteType, flags | INITIALIZED_HERE);
}
/**
@@ -223,7 +210,7 @@
* @return true if this IdentNode is special
*/
public boolean isSpecialIdentity() {
- return name.equals(__DIR__.tag()) || name.equals(__FILE__.tag()) || name.equals(__LINE__.tag());
+ return name.equals(__DIR__.symbolName()) || name.equals(__FILE__.symbolName()) || name.equals(__LINE__.symbolName());
}
@Override
@@ -236,9 +223,9 @@
* @return an ident node identical to this one in all aspects except with its function flag set.
*/
public IdentNode setIsFunction() {
- if(isFunction()) return this;
- final IdentNode n = (IdentNode)clone();
- n.flags |= FUNCTION;
- return n;
+ if (isFunction()) {
+ return this;
+ }
+ return new IdentNode(this, name, callSiteType, flags | FUNCTION);
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/IfNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/IfNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,22 +25,23 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for an IF statement.
- *
*/
-public class IfNode extends Node {
+@Immutable
+public final class IfNode extends Node {
/** Test expression. */
- private Node test;
+ private final Node test;
/** Pass statements. */
- private Block pass;
+ private final Block pass;
/** Fail statements. */
- private Block fail;
+ private final Block fail;
/**
* Constructor
@@ -54,37 +55,30 @@
*/
public IfNode(final Source source, final long token, final int finish, final Node test, final Block pass, final Block fail) {
super(source, token, finish);
+ this.test = test;
+ this.pass = pass;
+ this.fail = fail;
+ }
+ private IfNode(final IfNode ifNode, final Node test, final Block pass, final Block fail) {
+ super(ifNode);
this.test = test;
this.pass = pass;
this.fail = fail;
}
- private IfNode(final IfNode ifNode, final CopyState cs) {
- super(ifNode);
-
- this.test = cs.existingOrCopy(ifNode.test);
- this.pass = (Block)cs.existingOrCopy(ifNode.pass);
- this.fail = (Block)cs.existingOrCopy(ifNode.fail);
- }
-
@Override
- protected Node copy(final CopyState cs) {
- return new IfNode(this, cs);
+ public boolean isTerminal() {
+ return pass.isTerminal() && fail != null && fail.isTerminal();
}
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterIfNode(this) != null) {
- test = test.accept(visitor);
-
- pass = (Block)pass.accept(visitor);
-
- if (fail != null) {
- fail = (Block)fail.accept(visitor);
- }
-
- return visitor.leaveIfNode(this);
+ if (visitor.enterIfNode(this)) {
+ return visitor.leaveIfNode(
+ setTest(test.accept(visitor)).
+ setPass((Block)pass.accept(visitor)).
+ setFail(fail == null ? null : (Block)fail.accept(visitor)));
}
return this;
@@ -105,6 +99,13 @@
return fail;
}
+ private IfNode setFail(final Block fail) {
+ if (this.fail == fail) {
+ return this;
+ }
+ return new IfNode(this, test, pass, fail);
+ }
+
/**
* Get the then block for this IfNode
* @return the then block
@@ -113,6 +114,13 @@
return pass;
}
+ private IfNode setPass(final Block pass) {
+ if (this.pass == pass) {
+ return this;
+ }
+ return new IfNode(this, test, pass, fail);
+ }
+
/**
* Get the test expression for this IfNode
* @return the test expression
@@ -124,8 +132,12 @@
/**
* Reset the test expression for this IfNode
* @param test a new test expression
+ * @return new or same IfNode
*/
- public void setTest(final Node test) {
- this.test = test;
+ public IfNode setTest(final Node test) {
+ if (this.test == test) {
+ return this;
+ }
+ return new IfNode(this, test, pass, fail);
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/IndexNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/IndexNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,22 +25,18 @@
package jdk.nashorn.internal.ir;
-import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
-
-import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of an indexed access (brackets operator.)
- *
*/
-public class IndexNode extends BaseNode implements TypeOverride<IndexNode> {
- /** Property ident. */
- private Node index;
-
- private boolean hasCallSiteType;
+@Immutable
+public final class IndexNode extends BaseNode {
+ /** Property index. */
+ private final Node index;
/**
* Constructors
@@ -52,50 +48,27 @@
* @param index index for access
*/
public IndexNode(final Source source, final long token, final int finish, final Node base, final Node index) {
- super(source, token, finish, base);
-
+ super(source, token, finish, base, false, false);
this.index = index;
}
- /**
- * Copy constructor
- *
- * @param indexNode source node
- */
- public IndexNode(final IndexNode indexNode) {
- this(indexNode, new CopyState());
- }
-
- private IndexNode(final IndexNode indexNode, final CopyState cs) {
- super(indexNode, cs);
-
- index = cs.existingOrCopy(indexNode.index);
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new IndexNode(this, cs);
- }
-
- @Override
- public boolean equals(final Object other) {
- if (!super.equals(other)) {
- return false;
- }
- return index.equals(((IndexNode)other).getIndex());
- }
-
- @Override
- public int hashCode() {
- return super.hashCode() ^ getIndex().hashCode();
+ private IndexNode(final IndexNode indexNode, final Node base, final Node index, final boolean isFunction, final boolean hasCallSiteType) {
+ super(indexNode, base, isFunction, hasCallSiteType);
+ this.index = index;
}
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterIndexNode(this) != null) {
- base = base.accept(visitor);
- index = index.accept(visitor);
- return visitor.leaveIndexNode(this);
+ if (visitor.enterIndexNode(this)) {
+ final Node newBase = base.accept(visitor);
+ final Node newIndex = index.accept(visitor);
+ final IndexNode newNode;
+ if (newBase != base || newIndex != index) {
+ newNode = new IndexNode(this, newBase, newIndex, isFunction(), hasCallSiteType());
+ } else {
+ newNode = this;
+ }
+ return visitor.leaveIndexNode(newNode);
}
return this;
@@ -105,7 +78,7 @@
public void toString(final StringBuilder sb) {
final boolean needsParen = tokenType().needsParens(base.tokenType(), true);
- if (hasCallSiteType) {
+ if (hasCallSiteType()) {
sb.append('{');
final String desc = getType().getDescriptor();
sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
@@ -135,27 +108,19 @@
return index;
}
- /**
- * Reset the index expression for this IndexNode
- * @param index a new index expression
- */
- public void setIndex(final Node index) {
- this.index = index;
+ @Override
+ public BaseNode setIsFunction() {
+ if (isFunction()) {
+ return this;
+ }
+ return new IndexNode(this, base, index, true, hasCallSiteType());
}
@Override
public IndexNode setType(final Type type) {
- if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
- ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
- }
- hasCallSiteType = true;
- getSymbol().setTypeOverride(type);
- return this;
- }
-
- @Override
- public boolean canHaveCallSiteType() {
- return true; //carried by the symbol and always the same nodetype==symboltype
+ logTypeChange(type);
+ getSymbol().setTypeOverride(type); //always a temp so this is fine.
+ return new IndexNode(this, base, index, isFunction(), true);
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/LabelNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/LabelNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,29 +25,20 @@
package jdk.nashorn.internal.ir;
-import jdk.nashorn.internal.ir.annotations.Ignore;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for a labeled statement.
- *
*/
-
-public class LabelNode extends Node {
+@Immutable
+public final class LabelNode extends LexicalContextNode {
/** Label ident. */
- private IdentNode label;
+ private final IdentNode label;
/** Statements. */
- private Block body;
-
- /** Node to break from. */
- @Ignore
- private Node breakNode;
-
- /** Node to continue. */
- @Ignore
- private Node continueNode;
+ private final Block body;
/**
* Constructor
@@ -65,26 +56,23 @@
this.body = body;
}
- private LabelNode(final LabelNode labelNode, final CopyState cs) {
+ private LabelNode(final LabelNode labelNode, final IdentNode label, final Block body) {
super(labelNode);
-
- this.label = (IdentNode)cs.existingOrCopy(labelNode.label);
- this.body = (Block)cs.existingOrCopy(labelNode.body);
- this.breakNode = cs.existingOrSame(labelNode.breakNode);
- this.continueNode = cs.existingOrSame(labelNode.continueNode);
+ this.label = label;
+ this.body = body;
}
@Override
- protected Node copy(final CopyState cs) {
- return new LabelNode(this, cs);
+ public boolean isTerminal() {
+ return body.isTerminal();
}
@Override
- public Node accept(final NodeVisitor visitor) {
- if (visitor.enterLabelNode(this) != null) {
- label = (IdentNode)label.accept(visitor);
- body = (Block)body.accept(visitor);
- return visitor.leaveLabelNode(this);
+ public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterLabelNode(this)) {
+ return visitor.leaveLabelNode(
+ setLabel(visitor.getLexicalContext(), (IdentNode)label.accept(visitor)).
+ setBody(visitor.getLexicalContext(), (Block)body.accept(visitor)));
}
return this;
@@ -106,44 +94,15 @@
/**
* Reset the body of the node
+ * @param lc lexical context
* @param body new body
- */
- public void setBody(final Block body) {
- this.body = body;
- }
-
- /**
- * Get the break node for this node
- * @return the break node
- */
- public Node getBreakNode() {
- return breakNode;
- }
-
- /**
- * Reset the break node for this node
- * @param breakNode the break node
+ * @return new for node if changed or existing if not
*/
- public void setBreakNode(final Node breakNode) {
- assert breakNode instanceof BreakableNode || breakNode instanceof Block : "Invalid break node: " + breakNode;
- this.breakNode = breakNode;
- }
-
- /**
- * Get the continue node for this node
- * @return the continue node
- */
- public Node getContinueNode() {
- return continueNode;
- }
-
- /**
- * Reset the continue node for this node
- * @param continueNode the continue node
- */
- public void setContinueNode(final Node continueNode) {
- assert continueNode instanceof WhileNode : "invalid continue node: " + continueNode;
- this.continueNode = continueNode;
+ public LabelNode setBody(final LexicalContext lc, final Block body) {
+ if (this.body == body) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new LabelNode(this, label, body));
}
/**
@@ -154,4 +113,11 @@
return label;
}
+ private LabelNode setLabel(final LexicalContext lc, final IdentNode label) {
+ if (this.label == label) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new LabelNode(this, label, body));
+ }
+
}
--- a/nashorn/src/jdk/nashorn/internal/ir/LabeledNode.java Wed May 08 11:22:25 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,123 +0,0 @@
-/*
- * Copyright (c) 2010, 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 jdk.nashorn.internal.ir;
-
-import jdk.nashorn.internal.ir.annotations.Ignore;
-import jdk.nashorn.internal.runtime.Source;
-
-/**
- * IR base class for break and continue.
- *
- */
-public abstract class LabeledNode extends Node {
- /** Optional label. */
- @Ignore
- protected final LabelNode labelNode;
-
- /** Target control node. */
- @Ignore
- protected final Node targetNode;
-
- /** Try chain. */
- @Ignore
- protected final TryNode tryChain;
-
- /** scope nesting level */
- protected int scopeNestingLevel;
-
- /**
- * Constructor
- *
- * @param source the source
- * @param token token
- * @param finish finish
- * @param labelNode the label node
- * @param targetNode the place to break to
- * @param tryChain the try chain
- */
- public LabeledNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) {
- super(source, token, finish);
-
- this.labelNode = labelNode;
- this.targetNode = targetNode;
- this.tryChain = tryChain;
- }
-
- /**
- * Copy constructor
- *
- * @param labeledNode source node
- * @param cs copy state
- */
- protected LabeledNode(final LabeledNode labeledNode, final CopyState cs) {
- super(labeledNode);
-
- this.labelNode = (LabelNode)cs.existingOrCopy(labeledNode.labelNode);
- this.targetNode = cs.existingOrSame(labeledNode.targetNode);
- this.tryChain = (TryNode)cs.existingOrSame(labeledNode.tryChain);
- this.scopeNestingLevel = labeledNode.scopeNestingLevel;
- }
-
- /**
- * Get the label
- * @return the label
- */
- public LabelNode getLabel() {
- return labelNode;
- }
-
- /**
- * Get the target node
- * @return the target node
- */
- public Node getTargetNode() {
- return targetNode;
- }
-
- /**
- * Get the surrounding try chain
- * @return the try chain
- */
- public TryNode getTryChain() {
- return tryChain;
- }
-
- /**
- * Get the scope nesting level
- * @return nesting level
- */
- public int getScopeNestingLevel() {
- return scopeNestingLevel;
- }
-
- /**
- * Set scope nesting level
- * @param level the new level
- */
- public void setScopeNestingLevel(final int level) {
- scopeNestingLevel = level;
- }
-}
--- a/nashorn/src/jdk/nashorn/internal/ir/LexicalContext.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/LexicalContext.java Thu May 16 11:47:51 2013 +0100
@@ -1,40 +1,224 @@
+/*
+ * Copyright (c) 2010, 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 jdk.nashorn.internal.ir;
-import java.util.ArrayDeque;
-import java.util.Deque;
+import java.io.File;
import java.util.Iterator;
import java.util.NoSuchElementException;
+import jdk.nashorn.internal.codegen.Label;
+import jdk.nashorn.internal.runtime.Debug;
+import jdk.nashorn.internal.runtime.Source;
/**
* A class that tracks the current lexical context of node visitation as a stack of {@link Block} nodes. Has special
* methods to retrieve useful subsets of the context.
+ *
+ * This is implemented with a primitive array and a stack pointer, because it really makes a difference
+ * performance wise. None of the collection classes were optimal
*/
-public class LexicalContext implements Cloneable {
- private final Deque<Block> lexicalContext;
+public class LexicalContext {
+ private LexicalContextNode[] stack;
+
+ private int[] flags;
+ private int sp;
/**
* Creates a new empty lexical context.
*/
public LexicalContext() {
- lexicalContext = new ArrayDeque<>();
+ stack = new LexicalContextNode[16];
+ flags = new int[16];
+ }
+
+ /**
+ * Set the flags for a lexical context node on the stack. Does not
+ * replace the flags, but rather adds to them
+ *
+ * @param node node
+ * @param flag new flag to set
+ */
+ public void setFlag(final LexicalContextNode node, final int flag) {
+ if (flag != 0) {
+ for (int i = sp - 1; i >= 0; i--) {
+ if (stack[i] == node) {
+ flags[i] |= flag;
+ //System.err.println("Setting flag " + node + " " + flag);
+ return;
+ }
+ }
+ }
+ assert false;
}
/**
+ * Get the flags for a lexical context node on the stack
+ * @param node node
+ * @return the flags for the node
+ */
+ public int getFlags(final LexicalContextNode node) {
+ for (int i = sp - 1; i >= 0; i--) {
+ if (stack[i] == node) {
+ return flags[i];
+ }
+ }
+ throw new AssertionError("flag node not on context stack");
+ }
+
+ /**
+ * Get the function body of a function node on the lexical context
+ * stack. This will trigger an assertion if node isn't present
+ * @param functionNode function node
+ * @return body of function node
+ */
+ public Block getFunctionBody(final FunctionNode functionNode) {
+ for (int i = sp - 1; i >= 0 ; i--) {
+ if (stack[i] == functionNode) {
+ return (Block)stack[i + 1];
+ }
+ }
+ throw new AssertionError(functionNode.getName() + " not on context stack");
+ }
+
+ /**
+ * Return all nodes in the LexicalContext
+ * @return all nodes
+ */
+ public Iterator<LexicalContextNode> getAllNodes() {
+ return new NodeIterator<>(LexicalContextNode.class);
+ }
+
+ /**
+ * Returns the outermost function in this context. It is either the program, or a lazily compiled function.
+ * @return the outermost function in this context.
+ */
+ public FunctionNode getOutermostFunction() {
+ return (FunctionNode)stack[0];
+ }
+
+
+
+ /**
* Pushes a new block on top of the context, making it the innermost open block.
- * @param block the new block
+ * @param node the new node
+ * @return the node that was pushed
*/
- public void push(Block block) {
- //new Exception(block.toString()).printStackTrace();
- lexicalContext.push(block);
+ public <T extends LexicalContextNode> T push(final T node) {
+ if (sp == stack.length) {
+ final LexicalContextNode[] newStack = new LexicalContextNode[sp * 2];
+ System.arraycopy(stack, 0, newStack, 0, sp);
+ stack = newStack;
+
+ final int[] newFlags = new int[sp * 2];
+ System.arraycopy(flags, 0, newFlags, 0, sp);
+ flags = newFlags;
+
+ }
+ stack[sp] = node;
+ flags[sp] = 0;
+
+ sp++;
+
+ return node;
+ }
+
+ /**
+ * Is the context empty?
+ * @return true if empty
+ */
+ public boolean isEmpty() {
+ return sp == 0;
+ }
+
+ /**
+ * The depth of the lexical context
+ * @return depth
+ */
+ public int size() {
+ return sp;
}
/**
- * Pops the innermost block off the context.
- * @param the block expected to be popped, used to detect unbalanced pushes/pops
+ * Pops the innermost block off the context and all nodes that has been contributed
+ * since it was put there
+ *
+ * @param node the node expected to be popped, used to detect unbalanced pushes/pops
+ * @return the node that was popped
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends LexicalContextNode> T pop(final T node) {
+ --sp;
+ final LexicalContextNode popped = stack[sp];
+ stack[sp] = null;
+ if (popped instanceof Flags) {
+ return (T)((Flags<?>)popped).setFlag(this, flags[sp]);
+ }
+
+ return (T)popped;
+ }
+
+
+ /**
+ * Return the top element in the context
+ * @return the node that was pushed last
*/
- public void pop(Block block) {
- final Block popped = lexicalContext.pop();
- assert popped == block;
+ public LexicalContextNode peek() {
+ return stack[sp - 1];
+ }
+
+ /**
+ * Check if a node is in the lexical context
+ * @param node node to check for
+ * @return true if in the context
+ */
+ public boolean contains(final LexicalContextNode node) {
+ for (int i = 0; i < sp; i++) {
+ if (stack[i] == node) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Replace a node on the lexical context with a new one. Normally
+ * you should try to engineer IR traversals so this isn't needed
+ *
+ * @param oldNode old node
+ * @param newNode new node
+ * @return the new node
+ */
+ public LexicalContextNode replace(final LexicalContextNode oldNode, final LexicalContextNode newNode) {
+ //System.err.println("REPLACE old=" + Debug.id(oldNode) + " new=" + Debug.id(newNode));
+ for (int i = sp - 1; i >= 0; i--) {
+ if (stack[i] == oldNode) {
+ assert i == (sp - 1) : "violation of contract - we always expect to find the replacement node on top of the lexical context stack: " + newNode + " has " + stack[i + 1].getClass() + " above it";
+ stack[i] = newNode;
+ break;
+ }
+ }
+ return newNode;
}
/**
@@ -42,7 +226,7 @@
* @return an iterator over all blocks in the context.
*/
public Iterator<Block> getBlocks() {
- return lexicalContext.iterator();
+ return new NodeIterator<>(Block.class);
}
/**
@@ -50,47 +234,17 @@
* @return an iterator over all functions in the context.
*/
public Iterator<FunctionNode> getFunctions() {
- return new FunctionIterator(getBlocks());
+ return new NodeIterator<>(FunctionNode.class);
}
- private static final class FunctionIterator implements Iterator<FunctionNode> {
- private final Iterator<Block> it;
- private FunctionNode next;
-
- FunctionIterator(Iterator<Block> it) {
- this.it = it;
- next = findNext();
- }
-
- @Override
- public boolean hasNext() {
- return next != null;
- }
-
- @Override
- public FunctionNode next() {
- if(next == null) {
- throw new NoSuchElementException();
- }
- FunctionNode lnext = next;
- next = findNext();
- return lnext;
- }
-
- private FunctionNode findNext() {
- while(it.hasNext()) {
- final Block block = it.next();
- if(block instanceof FunctionNode) {
- return ((FunctionNode)block);
- }
- }
- return null;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
+ /**
+ * Get the parent block for the current lexical context block
+ * @return parent block
+ */
+ public Block getParentBlock() {
+ final Iterator<Block> iter = new NodeIterator<>(Block.class, getCurrentFunction());
+ iter.next();
+ return iter.hasNext() ? iter.next() : null;
}
/**
@@ -98,12 +252,12 @@
* @param block the block whose ancestors are returned
* @return an iterator over all ancestors block of the given block.
*/
- public Iterator<Block> getAncestorBlocks(Block block) {
- final Iterator<Block> it = getBlocks();
- while(it.hasNext()) {
- final Block b = it.next();
- if(block == b) {
- return it;
+ public Iterator<Block> getAncestorBlocks(final Block block) {
+ final Iterator<Block> iter = getBlocks();
+ while (iter.hasNext()) {
+ final Block b = iter.next();
+ if (block == b) {
+ return iter;
}
}
throw new AssertionError("Block is not on the current lexical context stack");
@@ -115,17 +269,17 @@
* @return an iterator over a block and all its ancestors.
*/
public Iterator<Block> getBlocks(final Block block) {
- final Iterator<Block> it = getAncestorBlocks(block);
+ final Iterator<Block> iter = getAncestorBlocks(block);
return new Iterator<Block>() {
boolean blockReturned = false;
@Override
public boolean hasNext() {
- return it.hasNext() || !blockReturned;
+ return iter.hasNext() || !blockReturned;
}
@Override
public Block next() {
- if(blockReturned) {
- return it.next();
+ if (blockReturned) {
+ return iter.next();
}
blockReturned = true;
return block;
@@ -138,45 +292,25 @@
}
/**
- * Returns the closest function node to the block. If the block is itself a function, it is returned.
- * @param block the block
- * @return the function closest to the block.
- * @see #getParentFunction(Block)
- */
- public FunctionNode getFunction(Block block) {
- if(block instanceof FunctionNode) {
- return (FunctionNode)block;
- }
- return getParentFunction(block);
- }
-
- /**
- * Returns the closest function node to the block and all its ancestor functions. If the block is itself a function,
- * it is returned too.
- * @param block the block
- * @return the closest function node to the block and all its ancestor functions.
+ * Get the function for this block. If the block is itself a function
+ * this returns identity
+ * @param block block for which to get function
+ * @return function for block
*/
- public Iterator<FunctionNode> getFunctions(final Block block) {
- return new FunctionIterator(getBlocks(block));
- }
-
- /**
- * Returns the containing function of the block. If the block is itself a function, its parent function is returned.
- * @param block the block
- * @return the containing function of the block.
- * @see #getFunction(Block)
- */
- public FunctionNode getParentFunction(Block block) {
- return getFirstFunction(getAncestorBlocks(block));
- }
-
- private static FunctionNode getFirstFunction(Iterator<Block> it) {
- while(it.hasNext()) {
- final Block ancestor = it.next();
- if(ancestor instanceof FunctionNode) {
- return (FunctionNode)ancestor;
+ public FunctionNode getFunction(final Block block) {
+ final Iterator<LexicalContextNode> iter = new NodeIterator<>(LexicalContextNode.class);
+ while (iter.hasNext()) {
+ final LexicalContextNode next = iter.next();
+ if (next == block) {
+ while (iter.hasNext()) {
+ final LexicalContextNode next2 = iter.next();
+ if (next2 instanceof FunctionNode) {
+ return (FunctionNode)next2;
+ }
+ }
}
}
+ assert false;
return null;
}
@@ -185,7 +319,7 @@
* @return the innermost block in the context.
*/
public Block getCurrentBlock() {
- return lexicalContext.element();
+ return getBlocks().next();
}
/**
@@ -193,6 +327,284 @@
* @return the innermost function in the context.
*/
public FunctionNode getCurrentFunction() {
- return getFirstFunction(getBlocks());
+ if (isEmpty()) {
+ return null;
+ }
+ return new NodeIterator<>(FunctionNode.class).next();
+ }
+
+ /**
+ * Get the block in which a symbol is defined
+ * @param symbol symbol
+ * @return block in which the symbol is defined, assert if no such block in context
+ */
+ public Block getDefiningBlock(final Symbol symbol) {
+ if (symbol.isTemp()) {
+ return null;
+ }
+ final String name = symbol.getName();
+ for (final Iterator<Block> it = getBlocks(); it.hasNext();) {
+ final Block next = it.next();
+ if (next.getExistingSymbol(name) == symbol) {
+ return next;
+ }
+ }
+ throw new AssertionError("Couldn't find symbol " + name + " in the context");
+ }
+
+ /**
+ * Get the function in which a symbol is defined
+ * @param symbol symbol
+ * @return function node in which this symbol is defined, assert if no such symbol exists in context
+ */
+ public FunctionNode getDefiningFunction(Symbol symbol) {
+ if (symbol.isTemp()) {
+ return null;
+ }
+ final String name = symbol.getName();
+ for (final Iterator<LexicalContextNode> iter = new NodeIterator<>(LexicalContextNode.class); iter.hasNext();) {
+ final LexicalContextNode next = iter.next();
+ if (next instanceof Block && ((Block)next).getExistingSymbol(name) == symbol) {
+ while (iter.hasNext()) {
+ final LexicalContextNode next2 = iter.next();
+ if (next2 instanceof FunctionNode) {
+ return ((FunctionNode)next2);
+ }
+ }
+ throw new AssertionError("Defining block for symbol " + name + " has no function in the context");
+ }
+ }
+ throw new AssertionError("Couldn't find symbol " + name + " in the context");
+ }
+
+ /**
+ * Is the topmost lexical context element a function body?
+ * @return true if function body
+ */
+ public boolean isFunctionBody() {
+ return getParentBlock() == null;
+ }
+
+ /**
+ * Returns true if the expression defining the function is a callee of a CallNode that should be the second
+ * element on the stack, e.g. <code>(function(){})()</code>. That is, if the stack ends with
+ * {@code [..., CallNode, FunctionNode]} then {@code callNode.getFunction()} should be equal to
+ * {@code functionNode}, and the top of the stack should itself be a variant of {@code functionNode}.
+ * @param functionNode the function node being tested
+ * @return true if the expression defining the current function is a callee of a call expression.
+ */
+ public boolean isFunctionDefinedInCurrentCall(FunctionNode functionNode) {
+ final LexicalContextNode parent = stack[sp - 2];
+ if(parent instanceof CallNode && ((CallNode)parent).getFunction() == functionNode) {
+ assert functionNode.getSource() == peek().getSource();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Get the parent function for a function in the lexical context
+ * @param functionNode function for which to get parent
+ * @return parent function of functionNode or null if none (e.g. if functionNode is the program)
+ */
+ public FunctionNode getParentFunction(final FunctionNode functionNode) {
+ final Iterator<FunctionNode> iter = new NodeIterator<>(FunctionNode.class);
+ while (iter.hasNext()) {
+ final FunctionNode next = iter.next();
+ if (next == functionNode) {
+ return iter.hasNext() ? iter.next() : null;
+ }
+ }
+ assert false;
+ return null;
+ }
+
+ /**
+ * Count the number of with scopes until a given node
+ * @param until node to stop counting at, or null if all nodes should be counted
+ * @return number of with scopes encountered in the context
+ */
+ public int getScopeNestingLevelTo(final LexicalContextNode until) {
+ //count the number of with nodes until "until" is hit
+ int n = 0;
+ for (final Iterator<WithNode> iter = new NodeIterator<>(WithNode.class, until); iter.hasNext(); iter.next()) {
+ n++;
+ }
+ return n;
+ }
+
+ private BreakableNode getBreakable() {
+ for (final NodeIterator<BreakableNode> iter = new NodeIterator<>(BreakableNode.class, getCurrentFunction()); iter.hasNext(); ) {
+ final BreakableNode next = iter.next();
+ if (next.isBreakableWithoutLabel()) {
+ return next;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Find the breakable node corresponding to this label.
+ * @param label label to search for, if null the closest breakable node will be returned unconditionally, e.g. a while loop with no label
+ * @return closest breakable node
+ */
+ public BreakableNode getBreakable(final IdentNode label) {
+ if (label != null) {
+ final LabelNode foundLabel = findLabel(label.getName());
+ if (foundLabel != null) {
+ // iterate to the nearest breakable to the foundLabel
+ BreakableNode breakable = null;
+ for (final NodeIterator<BreakableNode> iter = new NodeIterator<>(BreakableNode.class, foundLabel); iter.hasNext(); ) {
+ breakable = iter.next();
+ }
+ return breakable;
+ }
+ return null;
+ }
+ return getBreakable();
+ }
+
+ private LoopNode getContinueTo() {
+ final Iterator<LoopNode> iter = new NodeIterator<>(LoopNode.class, getCurrentFunction());
+ return iter.hasNext() ? iter.next() : null;
+ }
+
+ /**
+ * Find the continue target node corresponding to this label.
+ * @param label label to search for, if null the closest loop node will be returned unconditionally, e.g. a while loop with no label
+ * @return closest continue target node
+ */
+ public LoopNode getContinueTo(final IdentNode label) {
+ if (label != null) {
+ final LabelNode foundLabel = findLabel(label.getName());
+ if (foundLabel != null) {
+ // iterate to the nearest loop to the foundLabel
+ LoopNode loop = null;
+ for (final NodeIterator<LoopNode> iter = new NodeIterator<>(LoopNode.class, foundLabel); iter.hasNext(); ) {
+ loop = iter.next();
+ }
+ return loop;
+ }
+ return null;
+ }
+ return getContinueTo();
+ }
+
+ /**
+ * Check the lexical context for a given label node by name
+ * @param name name of the label
+ * @return LabelNode if found, null otherwise
+ */
+ public LabelNode findLabel(final String name) {
+ for (final Iterator<LabelNode> iter = new NodeIterator<>(LabelNode.class, getCurrentFunction()); iter.hasNext(); ) {
+ final LabelNode next = iter.next();
+ if (next.getLabel().getName().equals(name)) {
+ return next;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Checks whether a given label is a jump destination that lies outside a given
+ * split node
+ * @param splitNode the split node
+ * @param label the label
+ * @return true if label resides outside the split node
+ */
+ public boolean isExternalTarget(final SplitNode splitNode, final Label label) {
+ boolean targetFound = false;
+ for (int i = sp - 1; i >= 0; i--) {
+ final LexicalContextNode next = stack[i];
+ if (next == splitNode) {
+ return !targetFound;
+ }
+
+ if (next instanceof BreakableNode) {
+ for (final Label l : ((BreakableNode)next).getLabels()) {
+ if (l == label) {
+ targetFound = true;
+ break;
+ }
+ }
+ }
+ }
+ assert false : label + " was expected in lexical context " + LexicalContext.this + " but wasn't";
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer();
+ sb.append("[ ");
+ for (int i = 0; i < sp; i++) {
+ final Node node = stack[i];
+ sb.append(node.getClass().getSimpleName());
+ sb.append('@');
+ sb.append(Debug.id(node));
+ sb.append(':');
+ final Source source = node.getSource();
+ String src = source.toString();
+ if (src.indexOf(File.pathSeparator) != -1) {
+ src = src.substring(src.lastIndexOf(File.pathSeparator));
+ }
+ src += ' ';
+ src += source.getLine(node.getStart());
+ sb.append(' ');
+ }
+ sb.append(" ==> ]");
+ return sb.toString();
+ }
+
+ private class NodeIterator <T extends LexicalContextNode> implements Iterator<T> {
+ private int index;
+ private T next;
+ private final Class<T> clazz;
+ private LexicalContextNode until;
+
+ NodeIterator(final Class<T> clazz) {
+ this(clazz, null);
+ }
+
+ NodeIterator(final Class<T> clazz, final LexicalContextNode until) {
+ this.index = sp - 1;
+ this.clazz = clazz;
+ this.until = until;
+ this.next = findNext();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ @Override
+ public T next() {
+ if (next == null) {
+ throw new NoSuchElementException();
+ }
+ T lnext = next;
+ next = findNext();
+ return lnext;
+ }
+
+ private T findNext() {
+ for (int i = index; i >= 0; i--) {
+ final Node node = stack[i];
+ if (node == until) {
+ return null;
+ }
+ if (clazz.isAssignableFrom(node.getClass())) {
+ index = i - 1;
+ return clazz.cast(node);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/ir/LexicalContextNode.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2010, 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 jdk.nashorn.internal.ir;
+
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.runtime.Source;
+
+/**
+ * Superclass for nodes that can be part of the lexical context
+ * @see LexicalContext
+ */
+public abstract class LexicalContextNode extends Node {
+ /**
+ * Constructor
+ *
+ * @param source source
+ * @param token token
+ * @param finish finish
+ */
+ protected LexicalContextNode(final Source source, final long token, final int finish) {
+ super(source, token, finish);
+ }
+
+ /**
+ * Copy constructor
+ *
+ * @param node source node
+ */
+ protected LexicalContextNode(final LexicalContextNode node) {
+ super(node);
+ }
+
+ /**
+ * Accept function for the node given a lexical context. It must be prepared
+ * to replace itself if present in the lexical context
+ *
+ * @param lc lexical context
+ * @param visitor node visitor
+ *
+ * @return new node or same node depending on state change
+ */
+ protected abstract Node accept(final LexicalContext lc, final NodeVisitor visitor);
+
+ @Override
+ public Node accept(final NodeVisitor visitor) {
+ final LexicalContext lc = visitor.getLexicalContext();
+ lc.push(this);
+ final LexicalContextNode newNode = (LexicalContextNode)accept(lc, visitor);
+ return lc.pop(newNode);
+ }
+}
--- a/nashorn/src/jdk/nashorn/internal/ir/LineNumberNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/LineNumberNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,6 +25,7 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.runtime.Source;
@@ -32,8 +33,8 @@
/**
* IR Node representing a line number
*/
-
-public class LineNumberNode extends Node {
+@Immutable
+public final class LineNumberNode extends Node {
/** Line number */
private final int lineNumber;
@@ -46,24 +47,17 @@
*/
public LineNumberNode(final Source source, final long token, final int lineNumber) {
super(source, token, Token.descPosition(token));
-
this.lineNumber = lineNumber;
}
- private LineNumberNode(final LineNumberNode lineNumberNode) {
+ private LineNumberNode(final LineNumberNode lineNumberNode) {
super(lineNumberNode);
-
this.lineNumber = lineNumberNode.getLineNumber();
}
@Override
- protected Node copy(final CopyState cs) {
- return new LineNumberNode(this);
- }
-
- @Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterLineNumberNode(this) != null) {
+ if (visitor.enterLineNumberNode(this)) {
return visitor.leaveLineNumberNode(this);
}
--- a/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java Thu May 16 11:47:51 2013 +0100
@@ -30,6 +30,7 @@
import java.util.List;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Lexer.LexerToken;
import jdk.nashorn.internal.parser.Token;
@@ -44,6 +45,7 @@
*
* @param <T> the literal type
*/
+@Immutable
public abstract class LiteralNode<T> extends Node implements PropertyKey {
/** Literal value */
protected final T value;
@@ -93,23 +95,6 @@
return value == null;
}
- @Override
- public int hashCode() {
- return value == null ? 0 : value.hashCode();
- }
-
- @Override
- public boolean equals(final Object other) {
- if (!(other instanceof LiteralNode<?>)) {
- return false;
- }
- final LiteralNode<?> otherNode = (LiteralNode<?>)other;
- if (otherNode.isNull()) {
- return isNull();
- }
- return ((LiteralNode<?>)other).getValue().equals(value);
- }
-
/**
* Check if the literal value is boolean true
* @return true if literal value is boolean true
@@ -226,7 +211,7 @@
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterLiteralNode(this) != null) {
+ if (visitor.enterLiteralNode(this)) {
return visitor.leaveLiteralNode(this);
}
@@ -274,7 +259,8 @@
return new NodeLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish());
}
- private static class BooleanLiteralNode extends LiteralNode<Boolean> {
+ @Immutable
+ private static final class BooleanLiteralNode extends LiteralNode<Boolean> {
private BooleanLiteralNode(final Source source, final long token, final int finish, final boolean value) {
super(source, Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value);
@@ -285,11 +271,6 @@
}
@Override
- protected Node copy(final CopyState cs) {
- return new BooleanLiteralNode(this);
- }
-
- @Override
public boolean isTrue() {
return value;
}
@@ -331,7 +312,8 @@
return new BooleanLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
}
- private static class NumberLiteralNode extends LiteralNode<Number> {
+ @Immutable
+ private static final class NumberLiteralNode extends LiteralNode<Number> {
private final Type type = numberGetType(value);
@@ -358,11 +340,6 @@
}
@Override
- protected Node copy(final CopyState cs) {
- return new NumberLiteralNode(this);
- }
-
- @Override
public Type getType() {
return type;
}
@@ -407,11 +384,6 @@
private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) {
super(literalNode);
}
-
- @Override
- protected Node copy(final CopyState cs) {
- return new UndefinedLiteralNode(this);
- }
}
/**
@@ -440,6 +412,7 @@
return new UndefinedLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish());
}
+ @Immutable
private static class StringLiteralNode extends LiteralNode<String> {
private StringLiteralNode(final Source source, final long token, final int finish, final String value) {
super(source, Token.recast(token, TokenType.STRING), finish, value);
@@ -450,11 +423,6 @@
}
@Override
- protected Node copy(final CopyState cs) {
- return new StringLiteralNode(this);
- }
-
- @Override
public void toString(final StringBuilder sb) {
sb.append('\"');
sb.append(value);
@@ -488,6 +456,7 @@
return new StringLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
}
+ @Immutable
private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> {
private LexerTokenLiteralNode(final Source source, final long token, final int finish, final LexerToken value) {
super(source, Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here?
@@ -498,11 +467,6 @@
}
@Override
- protected Node copy(final CopyState cs) {
- return new LexerTokenLiteralNode(this);
- }
-
- @Override
public Type getType() {
return Type.OBJECT;
}
@@ -539,7 +503,7 @@
return new LexerTokenLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
}
- private static class NodeLiteralNode extends LiteralNode<Node> {
+ private static final class NodeLiteralNode extends LiteralNode<Node> {
private NodeLiteralNode(final Source source, final long token, final int finish) {
this(source, token, finish, null);
@@ -558,13 +522,8 @@
}
@Override
- protected Node copy(final CopyState cs) {
- return new NodeLiteralNode(this);
- }
-
- @Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterLiteralNode(this) != null) {
+ if (visitor.enterLiteralNode(this)) {
if (value != null) {
final Node newValue = value.accept(visitor);
if(value != newValue) {
@@ -617,7 +576,7 @@
/**
* Array literal node class.
*/
- public static class ArrayLiteralNode extends LiteralNode<Node[]> {
+ public static final class ArrayLiteralNode extends LiteralNode<Node[]> {
private static class PostsetMarker {
//empty
}
@@ -705,11 +664,6 @@
this.elementType = node.elementType;
}
- @Override
- protected Node copy(final CopyState cs) {
- return new ArrayLiteralNode(this);
- }
-
/**
* Compute things like widest element type needed. Internal use from compiler only
*/
@@ -894,7 +848,7 @@
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterLiteralNode(this) != null) {
+ if (visitor.enterLiteralNode(this)) {
for (int i = 0; i < value.length; i++) {
final Node element = value[i];
if (element != null) {
--- a/nashorn/src/jdk/nashorn/internal/ir/Location.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/Location.java Thu May 16 11:47:51 2013 +0100
@@ -25,16 +25,13 @@
package jdk.nashorn.internal.ir;
-import java.util.Objects;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Source;
/**
* Used to locate an entity back to it's source file.
- *
*/
-
public class Location implements Cloneable {
/** Source of entity. */
private final Source source;
@@ -73,22 +70,13 @@
}
@Override
- public boolean equals(final Object other) {
- if (other == null) {
- return false;
- }
-
- if (other.getClass() != this.getClass()) {
- return false;
- }
-
- final Location loc = (Location)other;
- return token == loc.token && Objects.equals(source, loc.source);
+ public final boolean equals(final Object other) {
+ return super.equals(other);
}
@Override
- public int hashCode() {
- return Token.hashCode(token) ^ Objects.hashCode(source);
+ public final int hashCode() {
+ return super.hashCode();
}
/**
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/ir/LoopNode.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2010, 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 jdk.nashorn.internal.ir;
+
+import java.util.Arrays;
+import java.util.List;
+
+import jdk.nashorn.internal.codegen.Label;
+import jdk.nashorn.internal.runtime.Source;
+
+/**
+ * A loop node, for example a while node, do while node or for node
+ */
+public abstract class LoopNode extends BreakableNode {
+ /** loop continue label. */
+ protected final Label continueLabel;
+
+ /** Loop test node, null if infinite */
+ protected final Node test;
+
+ /** Loop body */
+ protected final Block body;
+
+ /** Can control flow escape from loop, e.g. through breaks or continues to outer loops? */
+ protected final boolean controlFlowEscapes;
+
+ /**
+ * Constructor
+ *
+ * @param source source
+ * @param token token
+ * @param finish finish
+ * @param test test, or null if infinite loop
+ * @param body loop body
+ * @param controlFlowEscapes controlFlowEscapes
+ */
+ protected LoopNode(final Source source, final long token, final int finish, final Node test, final Block body, final boolean controlFlowEscapes) {
+ super(source, token, finish, new Label("while_break"));
+ this.continueLabel = new Label("while_continue");
+ this.test = test;
+ this.body = body;
+ this.controlFlowEscapes = controlFlowEscapes;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param loopNode loop node
+ * @param test new test
+ * @param body new body
+ * @param controlFlowEscapes controlFlowEscapes
+ */
+ protected LoopNode(final LoopNode loopNode, final Node test, final Block body, final boolean controlFlowEscapes) {
+ super(loopNode);
+ this.continueLabel = new Label(loopNode.continueLabel);
+ this.test = test;
+ this.body = body;
+ this.controlFlowEscapes = controlFlowEscapes;
+ }
+
+ @Override
+ public abstract Node ensureUniqueLabels(final LexicalContext lc);
+
+ /**
+ * Does the control flow escape from this loop, i.e. through breaks or
+ * continues to outer loops?
+ * @return true if control flow escapes
+ */
+ public boolean controlFlowEscapes() {
+ return controlFlowEscapes;
+ }
+
+
+ @Override
+ public boolean isTerminal() {
+ if (!mustEnter()) {
+ return false;
+ }
+ //must enter but control flow may escape - then not terminal
+ if (controlFlowEscapes) {
+ return false;
+ }
+ //must enter, but body ends with return - then terminal
+ if (body.isTerminal()) {
+ return true;
+ }
+ //no breaks or returns, it is still terminal if we can never exit
+ return test == null;
+ }
+
+ /**
+ * Conservative check: does this loop have to be entered?
+ * @return true if body will execute at least once
+ */
+ public abstract boolean mustEnter();
+
+ /**
+ * Get the continue label for this while node, i.e. location to go to on continue
+ * @return continue label
+ */
+ public Label getContinueLabel() {
+ return continueLabel;
+ }
+
+ @Override
+ public List<Label> getLabels() {
+ return Arrays.asList(breakLabel, continueLabel);
+ }
+
+ @Override
+ public boolean isLoop() {
+ return true;
+ }
+
+ /**
+ * Get the body for this for node
+ * @return the body
+ */
+ public abstract Block getBody();
+
+ /**
+ * @param lc lexical context
+ * @param body new body
+ * @return new for node if changed or existing if not
+ */
+ public abstract LoopNode setBody(final LexicalContext lc, final Block body);
+
+ /**
+ * Get the test for this for node
+ * @return the test
+ */
+ public abstract Node getTest();
+
+ /**
+ * Set the test for this for node
+ *
+ * @param lc lexical context
+ * @param test new test
+ * @return same or new node depending on if test was changed
+ */
+ public abstract LoopNode setTest(final LexicalContext lc, final Node test);
+
+ /**
+ * Set the control flow escapes flag for this node.
+ * TODO - integrate this with Lowering in a better way
+ *
+ * @param lc lexical context
+ * @param controlFlowEscapes control flow escapes value
+ * @return new loop node if changed otherwise the same
+ */
+ public abstract LoopNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes);
+
+}
--- a/nashorn/src/jdk/nashorn/internal/ir/Node.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/Node.java Thu May 16 11:47:51 2013 +0100
@@ -25,7 +25,9 @@
package jdk.nashorn.internal.ir;
-import java.util.IdentityHashMap;
+import java.util.ArrayList;
+import java.util.List;
+
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
@@ -33,30 +35,17 @@
/**
* Nodes are used to compose Abstract Syntax Trees.
- *
*/
public abstract class Node extends Location {
/** Node symbol. */
private Symbol symbol;
/** Start of source range. */
- protected int start;
+ protected final int start;
/** End of source range. */
protected int finish;
- /** Has this node been resolved - i.e. emitted code already */
- private boolean isResolved;
-
- /** Is this node terminal */
- private boolean isTerminal;
-
- /** Is this a goto node */
- private boolean hasGoto;
-
- /** Is this a discard */
- private boolean shouldDiscard;
-
/**
* Constructor
*
@@ -72,6 +61,21 @@
}
/**
+ * Constructor
+ *
+ * @param source source
+ * @param token token
+ * @param start start
+ * @param finish finish
+ */
+ protected Node(final Source source, final long token, final int start, final int finish) {
+ super(source, token);
+
+ this.start = start;
+ this.finish = finish;
+ }
+
+ /**
* Copy constructor
*
* @param node source node
@@ -79,13 +83,9 @@
protected Node(final Node node) {
super(node);
- this.symbol = node.symbol;
- this.isResolved = node.isResolved;
- this.isTerminal = node.isTerminal;
- this.hasGoto = node.hasGoto;
- this.shouldDiscard = node.shouldDiscard;
- this.start = node.start;
- this.finish = node.finish;
+ this.symbol = node.symbol;
+ this.start = node.start;
+ this.finish = node.finish;
}
/**
@@ -156,28 +156,6 @@
}
/**
- * Test to see if code been generated for this node. Set isResolved if not.
- *
- * @return True if node has already been resolved.
- */
- public boolean testResolved() {
- if (isResolved()) {
- return true;
- }
-
- setIsResolved(true);
-
- return false;
- }
-
- /**
- * Reset the resolved flag.
- */
- public void resetResolved() {
- setIsResolved(false);
- }
-
- /**
* Is this a debug info node like LineNumberNode etc?
*
* @return true if this is a debug node
@@ -187,72 +165,13 @@
}
/**
- * Helper class used for node cloning
+ * For reference copies - ensure that labels in the copy node are unique
+ * using an appropriate copy constructor
+ * @param lc lexical context
+ * @return new node or same of no labels
*/
- public static final class CopyState {
- private final IdentityHashMap<Node, Node> cloneMap = new IdentityHashMap<>();
-
- /**
- * Find existing or create new copy of the node.
- *
- * @param node Node to copy.
- *
- * @return New object.
- */
- public Node existingOrCopy(final Node node) {
- if (node != null) {
- Node copy = cloneMap.get(node);
-
- if (copy == null) {
- copy = node.copy(this);
- cloneMap.put(node, copy);
- }
-
- return copy;
- }
-
- return node;
- }
-
- /**
- * Find existing or use old copy of the node.
- *
- * @param node Node to copy.
- *
- * @return new object.
- */
- public Node existingOrSame(final Node node) {
- if (node != null) {
- Node copy = cloneMap.get(node);
-
- if (copy == null) {
- copy = node;
- }
-
- return copy;
- }
-
- return node;
- }
- }
-
- /**
- * Deep copy the node.
- *
- * @return Deep copy of the Node.
- */
- public final Node copy() {
- return copy(new CopyState());
- }
-
- /**
- * Deep copy the node.
- *
- * @param cs CopyState passed around to re-use certain nodes.
- * @return Deep copy of the Node.
- */
- protected Node copy(final CopyState cs) {
- return cs.existingOrCopy(this);
+ public Node ensureUniqueLabels(final LexicalContext lc) {
+ return this;
}
/**
@@ -283,35 +202,7 @@
* @return true if terminal
*/
public boolean hasTerminalFlags() {
- return isTerminal || hasGoto;
- }
-
- /**
- * Copy the terminal flags state of a node to another node
- *
- * @param other source node
- */
- public void copyTerminalFlags(final Node other) {
- isTerminal = other.isTerminal;
- hasGoto = other.hasGoto;
- }
-
- /**
- * Check if the return value of this expression should be discarded
- * @return true if return value is discarded
- */
- public boolean shouldDiscard() {
- return shouldDiscard;
- }
-
- /**
- * Setter that determines whether this node's return value should be discarded
- * or not
- *
- * @param shouldDiscard true if return value is discarded, false otherwise
- */
- public void setDiscard(final boolean shouldDiscard) {
- this.shouldDiscard = shouldDiscard;
+ return isTerminal() || hasGoto();
}
/**
@@ -336,29 +227,7 @@
* @return true if node has goto semantics
*/
public boolean hasGoto() {
- return hasGoto;
- }
-
- /**
- * Flag this node as having goto semantics as described in {@link Node#hasGoto()}
- */
- public void setHasGoto() {
- this.hasGoto = true;
- }
-
- /**
- * Check whether this node is resolved, i.e. code has been generated for it
- * @return true if node is resolved
- */
- public boolean isResolved() {
- return isResolved;
- }
-
- /**
- * Flag this node as resolved or not, i.e. code has been generated for it
- */
- private void setIsResolved(boolean isResolved) {
- this.isResolved = isResolved;
+ return false;
}
/**
@@ -370,14 +239,6 @@
}
/**
- * Set start position for node
- * @param start start position
- */
- public void setStart(final int start) {
- this.start = start;
- }
-
- /**
* Return the Symbol the compiler has assigned to this Node. The symbol
* is the place where it's expression value is stored after evaluation
*
@@ -404,17 +265,29 @@
* @return true if this node is terminal
*/
public boolean isTerminal() {
- return isTerminal;
+ return false;
}
- /**
- * Set this to be a terminal node, i.e. it terminates control flow as described
- * in {@link Node#isTerminal()}
- *
- * @param isTerminal true if this is a terminal node, false otherwise
- */
- public void setIsTerminal(final boolean isTerminal) {
- this.isTerminal = isTerminal;
+ //on change, we have to replace the entire list, that's we can't simple do ListIterator.set
+ static <T extends Node> List<T> accept(final NodeVisitor visitor, final Class<T> clazz, final List<T> list) {
+ boolean changed = false;
+ final List<T> newList = new ArrayList<>();
+
+ for (final Node node : list) {
+ final T newNode = clazz.cast(node.accept(visitor));
+ if (newNode != node) {
+ changed = true;
+ }
+ newList.add(newNode);
+ }
+
+ return changed ? newList : list;
}
+ static <T extends LexicalContextNode> T replaceInLexicalContext(final LexicalContext lc, final T oldNode, final T newNode) {
+ if (lc != null) {
+ lc.replace(oldNode, newNode);
+ }
+ return newNode;
+ }
}
--- a/nashorn/src/jdk/nashorn/internal/ir/ObjectNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/ObjectNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,16 +25,18 @@
package jdk.nashorn.internal.ir;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of an object literal.
*/
-public class ObjectNode extends Node {
+@Immutable
+public final class ObjectNode extends Node {
/** Literal elements. */
private final List<Node> elements;
@@ -49,35 +51,18 @@
*/
public ObjectNode(final Source source, final long token, final int finish, final List<Node> elements) {
super(source, token, finish);
-
this.elements = elements;
}
- private ObjectNode(final ObjectNode objectNode, final CopyState cs) {
+ private ObjectNode(final ObjectNode objectNode, final List<Node> elements) {
super(objectNode);
-
- final List<Node> newElements = new ArrayList<>();
-
- for (final Node element : objectNode.elements) {
- newElements.add(cs.existingOrCopy(element));
- }
-
- this.elements = newElements;
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new ObjectNode(this, cs);
+ this.elements = elements;
}
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterObjectNode(this) != null) {
- for (int i = 0, count = elements.size(); i < count; i++) {
- elements.set(i, elements.get(i).accept(visitor));
- }
-
- return visitor.leaveObjectNode(this);
+ if (visitor.enterObjectNode(this)) {
+ return visitor.leaveObjectNode(setElements(Node.accept(visitor, Node.class, elements)));
}
return this;
@@ -112,4 +97,11 @@
public List<Node> getElements() {
return Collections.unmodifiableList(elements);
}
+
+ private ObjectNode setElements(final List<Node> elements) {
+ if (this.elements == elements) {
+ return this;
+ }
+ return new ObjectNode(this, elements);
+ }
}
--- a/nashorn/src/jdk/nashorn/internal/ir/PropertyNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/PropertyNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,28 +25,27 @@
package jdk.nashorn.internal.ir;
-import jdk.nashorn.internal.ir.annotations.Reference;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of an object literal property.
*/
-public class PropertyNode extends Node {
+@Immutable
+public final class PropertyNode extends Node {
/** Property key. */
- private PropertyKey key;
+ private final PropertyKey key;
/** Property value. */
- private Node value;
+ private final Node value;
/** Property getter. */
- @Reference
- private Node getter;
+ private final FunctionNode getter;
/** Property getter. */
- @Reference
- private Node setter;
+ private final FunctionNode setter;
/**
* Constructor
@@ -56,26 +55,23 @@
* @param finish finish
* @param key the key of this property
* @param value the value of this property
+ * @param getter getter function body
+ * @param setter setter function body
*/
- public PropertyNode(final Source source, final long token, final int finish, final PropertyKey key, final Node value) {
+ public PropertyNode(final Source source, final long token, final int finish, final PropertyKey key, final Node value, final FunctionNode getter, final FunctionNode setter) {
super(source, token, finish);
-
this.key = key;
this.value = value;
+ this.getter = getter;
+ this.setter = setter;
}
- private PropertyNode(final PropertyNode propertyNode, final CopyState cs) {
+ private PropertyNode(final PropertyNode propertyNode, final PropertyKey key, final Node value, final FunctionNode getter, final FunctionNode setter) {
super(propertyNode);
-
- this.key = (PropertyKey)cs.existingOrCopy((Node)propertyNode.key);
- this.value = cs.existingOrCopy(propertyNode.value);
- this.getter = cs.existingOrSame(propertyNode.getter);
- this.setter = cs.existingOrSame(propertyNode.setter);
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new PropertyNode(this, cs);
+ this.key = key;
+ this.value = value;
+ this.getter = getter;
+ this.setter = setter;
}
/**
@@ -88,22 +84,12 @@
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterPropertyNode(this) != null) {
- key = (PropertyKey)((Node)key).accept(visitor);
-
- if (value != null) {
- value = value.accept(visitor);
- }
-
- if (getter != null) {
- getter = getter.accept(visitor);
- }
-
- if (setter != null) {
- setter = setter.accept(visitor);
- }
-
- return visitor.leavePropertyNode(this);
+ if (visitor.enterPropertyNode(this)) {
+ return visitor.leavePropertyNode(
+ setKey((PropertyKey)((Node)key).accept(visitor)).
+ setValue(value == null ? null : value.accept(visitor)).
+ setGetter(getter == null ? null : (FunctionNode)getter.accept(visitor)).
+ setSetter(setter == null ? null : (FunctionNode)setter.accept(visitor)));
}
return this;
@@ -136,16 +122,20 @@
* Get the getter for this property
* @return getter or null if none exists
*/
- public Node getGetter() {
+ public FunctionNode getGetter() {
return getter;
}
/**
* Set the getter of this property, null if none
* @param getter getter
+ * @return same node or new node if state changed
*/
- public void setGetter(final Node getter) {
- this.getter = getter;
+ public PropertyNode setGetter(final FunctionNode getter) {
+ if (this.getter == getter) {
+ return this;
+ }
+ return new PropertyNode(this, key, value, getter, setter);
}
/**
@@ -156,20 +146,31 @@
return (Node)key;
}
+ private PropertyNode setKey(final PropertyKey key) {
+ if (this.key == key) {
+ return this;
+ }
+ return new PropertyNode(this, key, value, getter, setter);
+ }
+
/**
* Get the setter for this property
* @return setter or null if none exists
*/
- public Node getSetter() {
+ public FunctionNode getSetter() {
return setter;
}
/**
* Set the setter for this property, null if none
* @param setter setter
+ * @return same node or new node if state changed
*/
- public void setSetter(final Node setter) {
- this.setter = setter;
+ public PropertyNode setSetter(final FunctionNode setter) {
+ if (this.setter == setter) {
+ return this;
+ }
+ return new PropertyNode(this, key, value, getter, setter);
}
/**
@@ -183,8 +184,12 @@
/**
* Set the value of this property
* @param value new value
+ * @return same node or new node if state changed
*/
- public void setValue(final Node value) {
- this.value = value;
- }
+ public PropertyNode setValue(final Node value) {
+ if (this.value == value) {
+ return this;
+ }
+ return new PropertyNode(this, key, value, getter, setter);
+ }
}
--- a/nashorn/src/jdk/nashorn/internal/ir/ReturnNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/ReturnNode.java Thu May 16 11:47:51 2013 +0100
@@ -27,22 +27,17 @@
import static jdk.nashorn.internal.parser.TokenType.RETURN;
import static jdk.nashorn.internal.parser.TokenType.YIELD;
-
-import jdk.nashorn.internal.ir.annotations.Ignore;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for RETURN or YIELD statements.
- *
*/
+@Immutable
public class ReturnNode extends Node {
/** Optional expression. */
- private Node expression;
-
- /** Try chain. */
- @Ignore
- private final TryNode tryChain;
+ private final Node expression;
/**
* Constructor
@@ -51,27 +46,20 @@
* @param token token
* @param finish finish
* @param expression expression to return
- * @param tryChain surrounding try chain.
*/
- public ReturnNode(final Source source, final long token, final int finish, final Node expression, final TryNode tryChain) {
+ public ReturnNode(final Source source, final long token, final int finish, final Node expression) {
super(source, token, finish);
-
this.expression = expression;
- this.tryChain = tryChain;
-
- setIsTerminal(true);
}
- private ReturnNode(final ReturnNode returnNode, final CopyState cs) {
+ private ReturnNode(final ReturnNode returnNode, final Node expression) {
super(returnNode);
-
- this.expression = cs.existingOrCopy(returnNode.expression);
- this.tryChain = (TryNode)cs.existingOrSame(returnNode.tryChain);
+ this.expression = expression;
}
@Override
- protected Node copy(final CopyState cs) {
- return new ReturnNode(this, cs);
+ public boolean isTerminal() {
+ return true;
}
/**
@@ -100,11 +88,10 @@
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterReturnNode(this) != null) {
+ if (visitor.enterReturnNode(this)) {
if (expression != null) {
- expression = expression.accept(visitor);
+ return visitor.leaveReturnNode(setExpression(expression.accept(visitor)));
}
-
return visitor.leaveReturnNode(this);
}
@@ -121,25 +108,6 @@
}
}
- @Override
- public boolean equals(final Object other) {
- if (other instanceof ReturnNode) {
- final ReturnNode otherReturn = (ReturnNode)other;
- if (hasExpression() != otherReturn.hasExpression()) {
- return false;
- } else if (hasExpression()) {
- return otherReturn.getExpression().equals(getExpression());
- }
- return true;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return 0x4711_17 ^ (expression == null ? 0 : expression.hashCode());
- }
-
/**
* Get the expression this node returns
* @return return expression, or null if void return
@@ -151,16 +119,13 @@
/**
* Reset the expression this node returns
* @param expression new expression, or null if void return
+ * @return new or same return node
*/
- public void setExpression(final Node expression) {
- this.expression = expression;
+ public ReturnNode setExpression(final Node expression) {
+ if (this.expression == expression) {
+ return this;
+ }
+ return new ReturnNode(this, expression);
}
- /**
- * Get the surrounding try chain for this return node
- * @return try chain
- */
- public TryNode getTryChain() {
- return tryChain;
- }
}
--- a/nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java Thu May 16 11:47:51 2013 +0100
@@ -30,14 +30,15 @@
import java.util.Collections;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for a runtime call.
- *
*/
+@Immutable
public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
/**
@@ -271,10 +272,10 @@
private final List<Node> args;
/** Call site override - e.g. we know that a ScriptRuntime.ADD will return an int */
- private Type callSiteType;
+ private final Type callSiteType;
/** is final - i.e. may not be removed again, lower in the code pipeline */
- private boolean isFinal;
+ private final boolean isFinal;
/**
* Constructor
@@ -290,6 +291,17 @@
this.request = request;
this.args = args;
+ this.callSiteType = null;
+ this.isFinal = false;
+ }
+
+ private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final Type callSiteType, final boolean isFinal, final List<Node> args) {
+ super(runtimeNode);
+
+ this.request = request;
+ this.args = args;
+ this.callSiteType = callSiteType;
+ this.isFinal = isFinal;
}
/**
@@ -326,8 +338,10 @@
public RuntimeNode(final Node parent, final Request request, final List<Node> args) {
super(parent);
- this.request = request;
- this.args = args;
+ this.request = request;
+ this.args = args;
+ this.callSiteType = null;
+ this.isFinal = false;
}
/**
@@ -350,20 +364,6 @@
this(parent, request, parent.lhs(), parent.rhs());
}
- private RuntimeNode(final RuntimeNode runtimeNode, final CopyState cs) {
- super(runtimeNode);
-
- final List<Node> newArgs = new ArrayList<>();
-
- for (final Node arg : runtimeNode.args) {
- newArgs.add(cs.existingOrCopy(arg));
- }
-
- this.request = runtimeNode.request;
- this.args = newArgs;
- this.callSiteType = runtimeNode.callSiteType;
- }
-
/**
* Is this node final - i.e. it can never be replaced with other nodes again
* @return true if final
@@ -374,14 +374,14 @@
/**
* Flag this node as final - i.e it may never be replaced with other nodes again
+ * @param isFinal is the node final, i.e. can not be removed and replaced by a less generic one later in codegen
+ * @return same runtime node if already final, otherwise a new one
*/
- public void setIsFinal() {
- this.isFinal = true;
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new RuntimeNode(this, cs);
+ public RuntimeNode setIsFinal(final boolean isFinal) {
+ if (this.isFinal == isFinal) {
+ return this;
+ }
+ return new RuntimeNode(this, request, callSiteType, isFinal, args);
}
/**
@@ -394,8 +394,10 @@
@Override
public RuntimeNode setType(final Type type) {
- this.callSiteType = type;
- return this;
+ if (this.callSiteType == type) {
+ return this;
+ }
+ return new RuntimeNode(this, request, type, isFinal, args);
}
@Override
@@ -409,12 +411,12 @@
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterRuntimeNode(this) != null) {
- for (int i = 0, count = args.size(); i < count; i++) {
- args.set(i, args.get(i).accept(visitor));
+ if (visitor.enterRuntimeNode(this)) {
+ final List<Node> newArgs = new ArrayList<>();
+ for (final Node arg : args) {
+ newArgs.add(arg.accept(visitor));
}
-
- return visitor.leaveRuntimeNode(this);
+ return visitor.leaveRuntimeNode(setArgs(newArgs));
}
return this;
@@ -449,6 +451,13 @@
return Collections.unmodifiableList(args);
}
+ private RuntimeNode setArgs(final List<Node> args) {
+ if (this.args == args) {
+ return this;
+ }
+ return new RuntimeNode(this, request, callSiteType, isFinal, args);
+ }
+
/**
* Get the request that this runtime node implements
* @return the request
--- a/nashorn/src/jdk/nashorn/internal/ir/SplitNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/SplitNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,99 +25,65 @@
package jdk.nashorn.internal.ir;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import jdk.nashorn.internal.codegen.CompileUnit;
-import jdk.nashorn.internal.codegen.Label;
-import jdk.nashorn.internal.codegen.MethodEmitter;
-import jdk.nashorn.internal.ir.annotations.Ignore;
-import jdk.nashorn.internal.ir.annotations.Reference;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
-
/**
* Node indicating code is split across classes.
*/
-public class SplitNode extends Node {
+@Immutable
+public class SplitNode extends LexicalContextNode {
/** Split node method name. */
private final String name;
/** Compilation unit. */
- private CompileUnit compileUnit;
-
- /** Method emitter for current method. */
- private MethodEmitter method;
-
- /** Method emitter for caller method. */
- private MethodEmitter caller;
-
- /** Containing function. */
- @Reference
- private final FunctionNode functionNode;
-
- /** A list of target labels in parent methods this split node may encounter. */
- @Ignore
- private final List<Label> externalTargets;
-
- /** True if this split node or any of its children contain a return statement. */
- private boolean hasReturn;
+ private final CompileUnit compileUnit;
/** Body of split code. */
- @Ignore
- private Node body;
+ private final Node body;
/**
* Constructor
*
- * @param name name of split node
- * @param functionNode function node to split in
- * @param body body of split code
+ * @param name name of split node
+ * @param body body of split code
+ * @param compileUnit compile unit to use for the body
*/
- public SplitNode(final String name, final FunctionNode functionNode, final Node body) {
+ public SplitNode(final String name, final Node body, final CompileUnit compileUnit) {
super(body.getSource(), body.getToken(), body.getFinish());
-
- this.name = name;
- this.functionNode = functionNode;
- this.body = body;
- this.externalTargets = new ArrayList<>();
+ this.name = name;
+ this.body = body;
+ this.compileUnit = compileUnit;
}
- private SplitNode(final SplitNode splitNode, final CopyState cs) {
+ private SplitNode(final SplitNode splitNode, final Node body) {
super(splitNode);
+ this.name = splitNode.name;
+ this.body = body;
+ this.compileUnit = splitNode.compileUnit;
+ }
- this.name = splitNode.name;
- this.functionNode = (FunctionNode)cs.existingOrSame(splitNode.functionNode);
- this.body = cs.existingOrCopy(splitNode.body);
- this.externalTargets = new ArrayList<>();
+ /**
+ * Get the body for this split node - i.e. the actual code it encloses
+ * @return body for split node
+ */
+ public Node getBody() {
+ return body;
+ }
+
+ private SplitNode setBody(final LexicalContext lc, final Node body) {
+ if (this.body == body) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body));
}
@Override
- protected Node copy(final CopyState cs) {
- return new SplitNode(this, cs);
- }
-
- @Override
- public Node accept(final NodeVisitor visitor) {
- final CompileUnit saveCompileUnit = visitor.getCurrentCompileUnit();
- final MethodEmitter saveMethod = visitor.getCurrentMethodEmitter();
-
- setCaller(saveMethod);
-
- visitor.setCurrentCompileUnit(getCompileUnit());
- visitor.setCurrentMethodEmitter(getMethodEmitter());
-
- try {
- if (visitor.enterSplitNode(this) != null) {
- body = body.accept(visitor);
-
- return visitor.leaveSplitNode(this);
- }
- } finally {
- visitor.setCurrentCompileUnit(saveCompileUnit);
- visitor.setCurrentMethodEmitter(saveMethod);
+ public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterSplitNode(this)) {
+ return visitor.leaveSplitNode(setBody(lc, body.accept(visitor)));
}
-
return this;
}
@@ -130,22 +96,6 @@
}
/**
- * Get the method emitter of the caller for this split node
- * @return caller method emitter
- */
- public MethodEmitter getCaller() {
- return caller;
- }
-
- /**
- * Set the caller method emitter for this split node
- * @param caller method emitter
- */
- public void setCaller(final MethodEmitter caller) {
- this.caller = caller;
- }
-
- /**
* Get the name for this split node
* @return name
*/
@@ -161,67 +111,4 @@
return compileUnit;
}
- /**
- * Set the compile unit for this split node
- * @param compileUnit compile unit
- */
- public void setCompileUnit(final CompileUnit compileUnit) {
- this.compileUnit = compileUnit;
- }
-
- /**
- * Get the method emitter for this split node
- * @return method emitter
- */
- public MethodEmitter getMethodEmitter() {
- return method;
- }
-
- /**
- * Set the method emitter for this split node
- * @param method method emitter
- */
- public void setMethodEmitter(final MethodEmitter method) {
- this.method = method;
- }
-
- /**
- * Get the function node this SplitNode splits
- * @return function node reference
- */
- public FunctionNode getFunctionNode() {
- return functionNode;
- }
-
- /**
- * Get the external targets for this SplitNode
- * @return list of external targets
- */
- public List<Label> getExternalTargets() {
- return Collections.unmodifiableList(externalTargets);
- }
-
- /**
- * Add an external target for this SplitNode
- * @param targetLabel target label
- */
- public void addExternalTarget(final Label targetLabel) {
- externalTargets.add(targetLabel);
- }
-
- /**
- * Check whether this SplitNode returns a value
- * @return true if return
- */
- public boolean hasReturn() {
- return hasReturn;
- }
-
- /**
- * Set whether this SplitNode returns a value or not
- * @param hasReturn true if return exists, false otherwise
- */
- public void setHasReturn(final boolean hasReturn) {
- this.hasReturn = hasReturn;
- }
}
--- a/nashorn/src/jdk/nashorn/internal/ir/SwitchNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/SwitchNode.java Thu May 16 11:47:51 2013 +0100
@@ -28,73 +28,84 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+
import jdk.nashorn.internal.codegen.Label;
-import jdk.nashorn.internal.ir.annotations.Ignore;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of a SWITCH statement.
*/
-public class SwitchNode extends BreakableNode {
+@Immutable
+public final class SwitchNode extends BreakableNode {
/** Switch expression. */
- private Node expression;
+ private final Node expression;
+
+ /** Switch cases. */
+ private final List<CaseNode> cases;
+
+ /** Switch default index. */
+ private final int defaultCaseIndex;
/** Tag symbol. */
private Symbol tag;
- /** Switch cases. */
- private List<CaseNode> cases;
-
- /** Switch default. */
- @Ignore //points to one of the members in the list above, don't traverse twice
- private CaseNode defaultCase;
-
/**
* Constructor
*
- * @param source the source
- * @param token token
- * @param finish finish
+ * @param source the source
+ * @param token token
+ * @param finish finish
+ * @param expression switch expression
+ * @param cases cases
+ * @param defaultCase the default case node - null if none, otherwise has to be present in cases list
*/
- public SwitchNode(final Source source, final long token, final int finish) {
- super(source, token, finish);
- this.breakLabel = new Label("switch_break");
+ public SwitchNode(final Source source, final long token, final int finish, final Node expression, final List<CaseNode> cases, final CaseNode defaultCase) {
+ super(source, token, finish, new Label("switch_break"));
+ this.expression = expression;
+ this.cases = cases;
+ this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase);
}
- private SwitchNode(final SwitchNode switchNode, final CopyState cs) {
+ private SwitchNode(final SwitchNode switchNode, final Node expression, final List<CaseNode> cases, final int defaultCase) {
super(switchNode);
-
- final List<CaseNode> newCases = new ArrayList<>();
-
- for (final CaseNode caseNode : switchNode.getCases()) {
- newCases.add((CaseNode)cs.existingOrCopy(caseNode));
- }
-
- this.expression = cs.existingOrCopy(switchNode.getExpression());
- this.tag = switchNode.getTag();
- this.cases = newCases;
- this.defaultCase = (CaseNode)cs.existingOrCopy(switchNode.getDefaultCase());
- this.breakLabel = new Label(switchNode.getBreakLabel());
+ this.expression = expression;
+ this.cases = cases;
+ this.defaultCaseIndex = defaultCase;
+ this.tag = switchNode.getTag(); //TODO are symbols inhereted as references?
}
@Override
- protected Node copy(final CopyState cs) {
- return new SwitchNode(this, cs);
+ public Node ensureUniqueLabels(final LexicalContext lc) {
+ final List<CaseNode> newCases = new ArrayList<>();
+ for (final CaseNode caseNode : cases) {
+ newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody()));
+ }
+ return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex));
}
@Override
- public Node accept(final NodeVisitor visitor) {
- if (visitor.enterSwitchNode(this) != null) {
- expression = expression.accept(visitor);
+ public boolean isTerminal() {
+ //there must be a default case, and that including all other cases must terminate
+ if (!cases.isEmpty() && defaultCaseIndex != -1) {
+ for (final CaseNode caseNode : cases) {
+ if (!caseNode.isTerminal()) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
- for (int i = 0, count = cases.size(); i < count; i++) {
- cases.set(i, (CaseNode)cases.get(i).accept(visitor));
- }
+ }
- //the default case is in the cases list and should not be explicitly traversed!
-
- return visitor.leaveSwitchNode(this);
+ @Override
+ public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterSwitchNode(this)) {
+ return visitor.leaveSwitchNode(
+ setExpression(visitor.getLexicalContext(), expression.accept(visitor)).
+ setCases(visitor.getLexicalContext(), Node.accept(visitor, CaseNode.class, cases), defaultCaseIndex));
}
return this;
@@ -108,6 +119,14 @@
}
/**
+ * Return the case node that is default case
+ * @return default case or null if none
+ */
+ public CaseNode getDefaultCase() {
+ return defaultCaseIndex == -1 ? null : cases.get(defaultCaseIndex);
+ }
+
+ /**
* Get the cases in this switch
* @return a list of case nodes
*/
@@ -116,27 +135,33 @@
}
/**
- * Set or reset the list of cases in this switch
- * @param cases a list of cases, case nodes
+ * Replace case nodes with new list. the cases have to be the same
+ * and the default case index the same. This is typically used
+ * by NodeVisitors who perform operations on every case node
+ * @param lc lexical context
+ * @param cases list of cases
+ * @return new switcy node or same if no state was changed
*/
- public void setCases(final List<CaseNode> cases) {
- this.cases = cases;
+ public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases) {
+ return setCases(lc, cases, defaultCaseIndex);
+ }
+
+ private SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases, final int defaultCaseIndex) {
+ if (this.cases == cases) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex));
}
/**
- * Get the default case for this switch
- * @return default case node
+ * Set or reset the list of cases in this switch
+ * @param lc lexical context
+ * @param cases a list of cases, case nodes
+ * @param defaultCase a case in the list that is the default - must be in the list or class will assert
+ * @return new switch node or same if no state was changed
*/
- public CaseNode getDefaultCase() {
- return defaultCase;
- }
-
- /**
- * Set the default case for this switch
- * @param defaultCase default case node
- */
- public void setDefaultCase(final CaseNode defaultCase) {
- this.defaultCase = defaultCase;
+ public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases, final CaseNode defaultCase) {
+ return setCases(lc, cases, defaultCase == null ? -1 : cases.indexOf(defaultCase));
}
/**
@@ -149,10 +174,15 @@
/**
* Set or reset the expression to switch on
+ * @param lc lexical context
* @param expression switch expression
+ * @return new switch node or same if no state was changed
*/
- public void setExpression(final Node expression) {
- this.expression = expression;
+ public SwitchNode setExpression(final LexicalContext lc, final Node expression) {
+ if (this.expression == expression) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex));
}
/**
--- a/nashorn/src/jdk/nashorn/internal/ir/Symbol.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/Symbol.java Thu May 16 11:47:51 2013 +0100
@@ -29,8 +29,10 @@
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
+
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.options.Options;
/**
@@ -63,6 +65,8 @@
public static final int IS_LET = 1 << 8;
/** Is this an internal symbol, never represented explicitly in source code */
public static final int IS_INTERNAL = 1 << 9;
+ /** Is this a function self-reference symbol */
+ public static final int IS_FUNCTION_SELF = 1 << 10;
/** Null or name identifying symbol. */
private final String name;
@@ -70,12 +74,6 @@
/** Symbol flags. */
private int flags;
- /** Defining node. */
- private Node node;
-
- /** Definition block. */
- private final Block block;
-
/** Type of symbol. */
private Type type;
@@ -121,16 +119,12 @@
*
* @param name name of symbol
* @param flags symbol flags
- * @param node node this symbol is in
- * @param block block this symbol is in
* @param type type of this symbol
* @param slot bytecode slot for this symbol
*/
- protected Symbol(final String name, final int flags, final Node node, final Block block, final Type type, final int slot) {
+ protected Symbol(final String name, final int flags, final Type type, final int slot) {
this.name = name;
this.flags = flags;
- this.node = node;
- this.block = block;
this.type = type;
this.slot = slot;
this.fieldIndex = -1;
@@ -142,11 +136,9 @@
*
* @param name name of symbol
* @param flags symbol flags
- * @param node node this symbol is in
- * @param block block this symbol is in
*/
- public Symbol(final String name, final int flags, final Node node, final Block block) {
- this(name, flags, node, block, Type.UNKNOWN, -1);
+ public Symbol(final String name, final int flags) {
+ this(name, flags, Type.UNKNOWN, -1);
}
/**
@@ -157,7 +149,7 @@
* @param type type of this symbol
*/
public Symbol(final String name, final int flags, final Type type) {
- this(name, flags, null, null, type, -1);
+ this(name, flags, type, -1);
}
private static String align(final String string, final int max) {
@@ -269,20 +261,6 @@
return type.isCategory2() ? 2 : 1;
}
- @Override
- public boolean equals(final Object other) {
- if (!(other instanceof Symbol)) {
- return false;
- }
- final Symbol symbol = (Symbol) other;
- return name.equals(symbol.name) && block.equals(symbol.block);
- }
-
- @Override
- public int hashCode() {
- return name.hashCode() ^ block.hashCode();
- }
-
private static String type(final String desc) {
switch (desc.charAt(desc.length() - 1)) {
case ';':
@@ -371,14 +349,14 @@
/**
* Flag this symbol as scope as described in {@link Symbol#isScope()}
*/
- public void setIsScope() {
+ /**
+ * Flag this symbol as scope as described in {@link Symbol#isScope()}
+ */
+ public void setIsScope() {
if (!isScope()) {
trace("SET IS SCOPE");
}
flags |= IS_SCOPE;
- if(!isGlobal()) {
- getBlock().setNeedsScope();
- }
}
/**
@@ -478,11 +456,11 @@
}
/**
- * Get the block in which the symbol is defined
- * @return a block
+ * Flag this symbol as a function's self-referencing symbol.
+ * @return true if this symbol as a function's self-referencing symbol.
*/
- public Block getBlock() {
- return block;
+ public boolean isFunctionSelf() {
+ return (flags & IS_FUNCTION_SELF) == IS_FUNCTION_SELF;
}
/**
@@ -492,7 +470,7 @@
* @return field index
*/
public int getFieldIndex() {
- assert fieldIndex != -1 : "fieldIndex must be initialized";
+ assert fieldIndex != -1 : "fieldIndex must be initialized " + fieldIndex;
return fieldIndex;
}
@@ -503,7 +481,6 @@
* @param fieldIndex field index - a positive integer
*/
public void setFieldIndex(final int fieldIndex) {
- assert this.fieldIndex == -1 : "fieldIndex must be initialized only once";
this.fieldIndex = fieldIndex;
}
@@ -524,22 +501,6 @@
}
/**
- * Get the node this symbol stores the result for
- * @return node
- */
- public Node getNode() {
- return node;
- }
-
- /**
- * Set the node this symbol stores the result for
- * @param node node
- */
- public void setNode(final Node node) {
- this.node = node;
- }
-
- /**
* Get the name of this symbol
* @return symbol name
*/
@@ -616,18 +577,25 @@
}
/**
- * Check if this symbol is in the global scope, i.e. it is on the outermost level
- * in the script
- * @return true if this this is a global scope symbol
+ * From a lexical context, set this symbol as needing scope, which
+ * will set flags for the defining block that will be written when
+ * block is popped from the lexical context stack, used by codegen
+ * when flags need to be tagged, but block is in the
+ * middle of evaluation and cannot be modified.
+ *
+ * @param lc lexical context
+ * @param symbol symbol
*/
- public boolean isTopLevel() {
- return block instanceof FunctionNode && ((FunctionNode) block).isProgram();
+ public static void setSymbolIsScope(final LexicalContext lc, final Symbol symbol) {
+ symbol.setIsScope();
+ if (!symbol.isGlobal()) {
+ lc.setFlag(lc.getDefiningBlock(symbol), Block.NEEDS_SCOPE);
+ }
}
-
private void trace(final String desc) {
if (TRACE_SYMBOLS != null && (TRACE_SYMBOLS.isEmpty() || TRACE_SYMBOLS.contains(name))) {
- Context.err("SYMBOL: '" + name + "' " + desc);
+ Context.err(Debug.id(this) + " SYMBOL: '" + name + "' " + desc);
if (TRACE_SYMBOLS_STACKTRACE != null && (TRACE_SYMBOLS_STACKTRACE.isEmpty() || TRACE_SYMBOLS_STACKTRACE.contains(name))) {
new Throwable().printStackTrace(Context.getCurrentErr());
}
--- a/nashorn/src/jdk/nashorn/internal/ir/TernaryNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/TernaryNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,15 +25,21 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* TernaryNode nodes represent three operand operations (?:).
*/
-public class TernaryNode extends BinaryNode {
+@Immutable
+public final class TernaryNode extends Node {
+ private final Node lhs;
+
+ private final Node rhs;
+
/** Third argument. */
- private Node third;
+ private final Node third;
/**
* Constructor
@@ -45,43 +51,26 @@
* @param third third node
*/
public TernaryNode(final Source source, final long token, final Node lhs, final Node rhs, final Node third) {
- super(source, token, lhs, rhs);
-
- this.finish = third.getFinish();
+ super(source, token, third.getFinish());
+ this.lhs = lhs;
+ this.rhs = rhs;
this.third = third;
}
- private TernaryNode(final TernaryNode ternaryNode, final CopyState cs) {
- super(ternaryNode, cs);
-
- this.third = cs.existingOrCopy(ternaryNode.third);
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new TernaryNode(this, cs);
- }
-
- @Override
- public boolean equals(final Object other) {
- if (!super.equals(other)) {
- return false;
- }
- return third.equals(((TernaryNode)other).third());
- }
-
- @Override
- public int hashCode() {
- return super.hashCode() ^ third().hashCode();
+ private TernaryNode(final TernaryNode ternaryNode, final Node lhs, final Node rhs, final Node third) {
+ super(ternaryNode);
+ this.lhs = lhs;
+ this.rhs = rhs;
+ this.third = third;
}
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterTernaryNode(this) != null) {
+ if (visitor.enterTernaryNode(this)) {
final Node newLhs = lhs().accept(visitor);
final Node newRhs = rhs().accept(visitor);
final Node newThird = third.accept(visitor);
- return visitor.leaveTernaryNode((TernaryNode)setThird(newThird).setLHS(newLhs).setRHS(newRhs));
+ return visitor.leaveTernaryNode(setThird(newThird).setLHS(newLhs).setRHS(newRhs));
}
return this;
@@ -123,6 +112,22 @@
}
/**
+ * Get the lhs node for this ternary expression, i.e. "x" in x ? y : z
+ * @return a node
+ */
+ public Node lhs() {
+ return lhs;
+ }
+
+ /**
+ * Get the rhs node for this ternary expression, i.e. "y" in x ? y : z
+ * @return a node
+ */
+ public Node rhs() {
+ return rhs;
+ }
+
+ /**
* Get the "third" node for this ternary expression, i.e. "z" in x ? y : z
* @return a node
*/
@@ -131,14 +136,38 @@
}
/**
+ * Set the left hand side expression for this node
+ * @param lhs new left hand side expression
+ * @return a node equivalent to this one except for the requested change.
+ */
+ public TernaryNode setLHS(final Node lhs) {
+ if (this.lhs == lhs) {
+ return this;
+ }
+ return new TernaryNode(this, lhs, rhs, third);
+ }
+
+ /**
+ * Set the right hand side expression for this node
+ * @param rhs new left hand side expression
+ * @return a node equivalent to this one except for the requested change.
+ */
+ public TernaryNode setRHS(final Node rhs) {
+ if (this.rhs == rhs) {
+ return this;
+ }
+ return new TernaryNode(this, lhs, rhs, third);
+ }
+
+ /**
* Reset the "third" node for this ternary expression, i.e. "z" in x ? y : z
* @param third a node
* @return a node equivalent to this one except for the requested change.
*/
public TernaryNode setThird(final Node third) {
- if(this.third == third) return this;
- final TernaryNode n = (TernaryNode)clone();
- n.third = third;
- return n;
+ if (this.third == third) {
+ return this;
+ }
+ return new TernaryNode(this, lhs, rhs, third);
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/ThrowNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/ThrowNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,20 +25,17 @@
package jdk.nashorn.internal.ir;
-import jdk.nashorn.internal.ir.annotations.Ignore;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for THROW statements.
*/
-public class ThrowNode extends Node {
+@Immutable
+public final class ThrowNode extends Node {
/** Exception expression. */
- private Node expression;
-
- /** Try chain. */
- @Ignore
- private final TryNode tryChain;
+ private final Node expression;
/**
* Constructor
@@ -47,26 +44,21 @@
* @param token token
* @param finish finish
* @param expression expression to throw
- * @param tryChain surrounding try chain
*/
- public ThrowNode(final Source source, final long token, final int finish, final Node expression, final TryNode tryChain) {
+ public ThrowNode(final Source source, final long token, final int finish, final Node expression) {
super(source, token, finish);
this.expression = expression;
- this.tryChain = tryChain;
- setIsTerminal(true);
}
- private ThrowNode(final ThrowNode throwNode, final CopyState cs) {
- super(throwNode);
-
- this.expression = cs.existingOrCopy(throwNode.expression);
- this.tryChain = (TryNode)cs.existingOrSame(throwNode.tryChain);
+ private ThrowNode(final Node node, final Node expression) {
+ super(node);
+ this.expression = expression;
}
@Override
- protected Node copy(final CopyState cs) {
- return new ThrowNode(this, cs);
+ public boolean isTerminal() {
+ return true;
}
/**
@@ -75,9 +67,8 @@
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterThrowNode(this) != null) {
- setExpression(expression.accept(visitor));
- return visitor.leaveThrowNode(this);
+ if (visitor.enterThrowNode(this)) {
+ return visitor.leaveThrowNode(setExpression(expression.accept(visitor)));
}
return this;
@@ -103,16 +94,13 @@
/**
* Reset the expression being thrown by this node
* @param expression new expression
+ * @return new or same thrownode
*/
- public void setExpression(final Node expression) {
- this.expression = expression;
+ public ThrowNode setExpression(final Node expression) {
+ if (this.expression == expression) {
+ return this;
+ }
+ return new ThrowNode(this, expression);
}
- /**
- * Get surrounding tryChain for this node
- * @return try chain
- */
- public TryNode getTryChain() {
- return tryChain;
- }
}
--- a/nashorn/src/jdk/nashorn/internal/ir/TryNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/TryNode.java Thu May 16 11:47:51 2013 +0100
@@ -28,30 +28,28 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+
import jdk.nashorn.internal.codegen.Label;
-import jdk.nashorn.internal.ir.annotations.Ignore;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of a TRY statement.
*/
-public class TryNode extends Node {
- /** Try chain. */
- @Ignore //don't print, will be apparent from the AST
- private TryNode next;
-
+@Immutable
+public final class TryNode extends Node {
/** Try statements. */
- private Block body;
+ private final Block body;
/** List of catch clauses. */
- private List<Block> catchBlocks;
+ private final List<Block> catchBlocks;
/** Finally clause. */
- private Block finallyBody;
+ private final Block finallyBody;
/** Exit label. */
- private Label exit;
+ private final Label exit;
/** Exception symbol. */
private Symbol exception;
@@ -62,37 +60,46 @@
/**
* Constructor
*
- * @param source the source
- * @param token token
- * @param finish finish
- * @param next next try node in chain
+ * @param source the source
+ * @param token token
+ * @param finish finish
+ * @param body try node body
+ * @param catchBlocks list of catch blocks in order
+ * @param finallyBody body of finally block or null if none
*/
- public TryNode(final Source source, final long token, final int finish, final TryNode next) {
+ public TryNode(final Source source, final long token, final int finish, final Block body, final List<Block> catchBlocks, final Block finallyBody) {
super(source, token, finish);
-
- this.next = next;
+ this.body = body;
+ this.catchBlocks = catchBlocks;
+ this.finallyBody = finallyBody;
this.exit = new Label("exit");
}
- private TryNode(final TryNode tryNode, final CopyState cs) {
+ private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody) {
super(tryNode);
-
- final List<Block> newCatchBlocks = new ArrayList<>();
-
- for (final Block block : tryNode.catchBlocks) {
- newCatchBlocks.add((Block)cs.existingOrCopy(block));
- }
-
- this.next = (TryNode)cs.existingOrSame(tryNode.getNext());
- this.body = (Block)cs.existingOrCopy(tryNode.getBody());
- this.catchBlocks = newCatchBlocks;
- this.finallyBody = (Block)cs.existingOrCopy(tryNode.getFinallyBody());
- this.exit = new Label(tryNode.getExit());
+ this.body = body;
+ this.catchBlocks = catchBlocks;
+ this.finallyBody = finallyBody;
+ this.exit = new Label(tryNode.exit);
}
@Override
- protected Node copy(final CopyState cs) {
- return new TryNode(this, cs);
+ public Node ensureUniqueLabels(final LexicalContext lc) {
+ //try nodes are never in lex context
+ return new TryNode(this, body, catchBlocks, finallyBody);
+ }
+
+ @Override
+ public boolean isTerminal() {
+ if (body.isTerminal()) {
+ for (final Block catchBlock : getCatchBlocks()) {
+ if (!catchBlock.isTerminal()) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
}
/**
@@ -101,21 +108,16 @@
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterTryNode(this) != null) {
- // Need to do first for termination analysis.
- if (finallyBody != null) {
- finallyBody = (Block)finallyBody.accept(visitor);
- }
-
- body = (Block)body.accept(visitor);
-
- final List<Block> newCatchBlocks = new ArrayList<>(catchBlocks.size());
- for (final Block catchBlock : catchBlocks) {
- newCatchBlocks.add((Block)catchBlock.accept(visitor));
- }
- this.catchBlocks = newCatchBlocks;
-
- return visitor.leaveTryNode(this);
+ if (visitor.enterTryNode(this)) {
+ // Need to do finallybody first for termination analysis. TODO still necessary?
+ final Block newFinallyBody = finallyBody == null ? null : (Block)finallyBody.accept(visitor);
+ final Block newBody = (Block)body.accept(visitor);
+ return visitor.leaveTryNode(
+ setBody(newBody).
+ setFinallyBody(newFinallyBody).
+ setCatchBlocks(Node.accept(visitor, Block.class, catchBlocks)).
+ setException(exception).
+ setFinallyCatchAll(finallyCatchAll));
}
return this;
@@ -123,7 +125,7 @@
@Override
public void toString(final StringBuilder sb) {
- sb.append("try");
+ sb.append("try ");
}
/**
@@ -137,9 +139,13 @@
/**
* Reset the body of this try block
* @param body new body
+ * @return new TryNode or same if unchanged
*/
- public void setBody(final Block body) {
- this.body = body;
+ public TryNode setBody(final Block body) {
+ if (this.body == body) {
+ return this;
+ }
+ return new TryNode(this, body, catchBlocks, finallyBody);
}
/**
@@ -151,16 +157,7 @@
for (final Block catchBlock : catchBlocks) {
catches.add((CatchNode)catchBlock.getStatements().get(0));
}
- return catches;
- }
-
- /**
- * Returns true if the specified block is the body of this try block, or any of its catch blocks.
- * @param block the block
- * @return true if the specified block is the body of this try block, or any of its catch blocks.
- */
- public boolean isChildBlock(Block block) {
- return body == block || catchBlocks.contains(block);
+ return Collections.unmodifiableList(catches);
}
/**
@@ -174,9 +171,13 @@
/**
* Set the catch blocks of this try
* @param catchBlocks list of catch blocks
+ * @return new TryNode or same if unchanged
*/
- public void setCatchBlocks(final List<Block> catchBlocks) {
- this.catchBlocks = catchBlocks;
+ public TryNode setCatchBlocks(final List<Block> catchBlocks) {
+ if (this.catchBlocks == catchBlocks) {
+ return this;
+ }
+ return new TryNode(this, body, catchBlocks, finallyBody);
}
/**
@@ -190,9 +191,11 @@
/**
* Set the exception symbol for this try block
* @param exception a symbol for the compiler to store the exception in
+ * @return new TryNode or same if unchanged
*/
- public void setException(final Symbol exception) {
+ public TryNode setException(final Symbol exception) {
this.exception = exception;
+ return this;
}
/**
@@ -207,9 +210,13 @@
* If a finally block exists, the synthetic catchall needs another symbol to
* store its throwable
* @param finallyCatchAll a symbol for the finally catch all exception
+ * @return new TryNode or same if unchanged
+ *
+ * TODO can this still be stateful?
*/
- public void setFinallyCatchAll(final Symbol finallyCatchAll) {
+ public TryNode setFinallyCatchAll(final Symbol finallyCatchAll) {
this.finallyCatchAll = finallyCatchAll;
+ return this;
}
/**
@@ -221,14 +228,6 @@
}
/**
- * Set the exit label for this try block
- * @param exit label
- */
- public void setExit(final Label exit) {
- this.exit = exit;
- }
-
- /**
* Get the body of the finally clause for this try
* @return finally body, or null if no finally
*/
@@ -239,24 +238,12 @@
/**
* Set the finally body of this try
* @param finallyBody new finally body
- */
- public void setFinallyBody(final Block finallyBody) {
- this.finallyBody = finallyBody;
- }
-
- /**
- * Get next try node in try chain
- * @return next try node
+ * @return new TryNode or same if unchanged
*/
- public TryNode getNext() {
- return next;
- }
-
- /**
- * Set next try node in try chain
- * @param next next try node
- */
- public void setNext(final TryNode next) {
- this.next = next;
+ public TryNode setFinallyBody(final Block finallyBody) {
+ if (this.finallyBody == finallyBody) {
+ return this;
+ }
+ return new TryNode(this, body, catchBlocks, finallyBody);
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java Thu May 16 11:47:51 2013 +0100
@@ -31,6 +31,7 @@
import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
@@ -39,9 +40,10 @@
/**
* UnaryNode nodes represent single operand operations.
*/
-public class UnaryNode extends Node implements Assignment<Node> {
+@Immutable
+public final class UnaryNode extends Node implements Assignment<Node> {
/** Right hand side argument. */
- private Node rhs;
+ private final Node rhs;
/**
* Constructor
@@ -51,23 +53,26 @@
* @param rhs expression
*/
public UnaryNode(final Source source, final long token, final Node rhs) {
- super(source, token, Token.descPosition(token));
-
- this.start = Math.min(rhs.getStart(), Token.descPosition(token));
- this.finish = Math.max(Token.descPosition(token) + Token.descLength(token), rhs.getFinish());
- this.rhs = rhs;
+ this(source, token, Math.min(rhs.getStart(), Token.descPosition(token)), Math.max(Token.descPosition(token) + Token.descLength(token), rhs.getFinish()), rhs);
}
/**
- * Copy constructor
- *
- * @param unaryNode source node
- * @param cs copy state
+ * Constructor
+ * @param source the source
+ * @param token token
+ * @param start start
+ * @param finish finish
+ * @param rhs expression
*/
- protected UnaryNode(final UnaryNode unaryNode, final CopyState cs) {
+ public UnaryNode(final Source source, final long token, final int start, final int finish, final Node rhs) {
+ super(source, token, start, finish);
+ this.rhs = rhs;
+ }
+
+
+ private UnaryNode(final UnaryNode unaryNode, final Node rhs) {
super(unaryNode);
-
- this.rhs = cs.existingOrCopy(unaryNode.rhs);
+ this.rhs = rhs;
}
/**
@@ -113,31 +118,13 @@
return getAssignmentDest();
}
- @Override
- public boolean equals(final Object other) {
- if (!super.equals(other)) {
- return false;
- }
- return rhs.equals(((UnaryNode)other).rhs());
- }
-
- @Override
- public int hashCode() {
- return super.hashCode() ^ rhs().hashCode();
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new UnaryNode(this, cs);
- }
-
/**
* Assist in IR navigation.
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterUnaryNode(this) != null) {
+ if (visitor.enterUnaryNode(this)) {
return visitor.leaveUnaryNode(setRHS(rhs.accept(visitor)));
}
@@ -219,9 +206,9 @@
* @return a node equivalent to this one except for the requested change.
*/
public UnaryNode setRHS(final Node rhs) {
- if(this.rhs == rhs) return this;
- final UnaryNode n = (UnaryNode)clone();
- n.rhs = rhs;
- return n;
+ if (this.rhs == rhs) {
+ return this;
+ }
+ return new UnaryNode(this, rhs);
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/VarNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/VarNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,21 +25,31 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* Node represents a var/let declaration.
*/
-public class VarNode extends Node implements Assignment<IdentNode> {
+@Immutable
+public final class VarNode extends Node implements Assignment<IdentNode> {
/** Var name. */
- private IdentNode name;
+ private final IdentNode name;
/** Initialization expression. */
- private Node init;
+ private final Node init;
/** Is this a var statement (as opposed to a "var" in a for loop statement) */
- private final boolean isStatement;
+ private final int flags;
+
+ /** Flag that determines if this function node is a statement */
+ public static final int IS_STATEMENT = 1 << 0;
+
+ /** Flag that determines if this is the last function declaration in a function
+ * This is used to micro optimize the placement of return value assignments for
+ * a program node */
+ public static final int IS_LAST_FUNCTION_DECLARATION = 1 << 1;
/**
* Constructor
@@ -51,7 +61,14 @@
* @param init init node or null if just a declaration
*/
public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init) {
- this(source, token, finish, name, init, true);
+ this(source, token, finish, name, init, IS_STATEMENT);
+ }
+
+ private VarNode(final VarNode varNode, final IdentNode name, final Node init, final int flags) {
+ super(varNode);
+ this.name = init == null ? name : name.setIsInitializedHere();
+ this.init = init;
+ this.flags = flags;
}
/**
@@ -62,28 +79,14 @@
* @param finish finish
* @param name name of variable
* @param init init node or null if just a declaration
- * @param isStatement if this is a var statement (true), or a for-loop initializer (false)
+ * @param flags flags
*/
- public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init, boolean isStatement) {
+ public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init, final int flags) {
super(source, token, finish);
this.name = init == null ? name : name.setIsInitializedHere();
this.init = init;
- this.isStatement = isStatement;
- }
-
-
- private VarNode(final VarNode varNode, final CopyState cs) {
- super(varNode);
-
- this.name = (IdentNode)cs.existingOrCopy(varNode.name);
- this.init = cs.existingOrCopy(varNode.init);
- this.isStatement = varNode.isStatement;
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new VarNode(this, cs);
+ this.flags = flags;
}
@Override
@@ -115,45 +118,17 @@
}
/**
- * Test to see if two VarNodes are the same.
- * @param other Other VarNode.
- * @return True if the VarNodes are the same.
- */
- @Override
- public boolean equals(final Object other) {
- if (other instanceof VarNode) {
- final VarNode otherNode = (VarNode)other;
- final boolean nameMatches = name.equals(otherNode.name);
- if (hasInit() != otherNode.hasInit()) {
- return false;
- } else if (init == null) {
- return nameMatches;
- } else {
- return nameMatches && init.equals(otherNode.init);
- }
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return name.hashCode() ^ (init == null ? 0 : init.hashCode());
- }
-
- /**
* Assist in IR navigation.
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterVarNode(this) != null) {
+ if (visitor.enterVarNode(this)) {
final IdentNode newName = (IdentNode)name.accept(visitor);
- final Node newInit = init == null ? null : init.accept(visitor);
- final VarNode newThis;
- if(name != newName || init != newInit) {
- newThis = (VarNode)clone();
- newThis.init = newInit;
- newThis.name = newInit == null ? newName : newName.setIsInitializedHere();
+ final Node newInit = init == null ? null : init.accept(visitor);
+ final VarNode newThis;
+ if (name != newName || init != newInit) {
+ newThis = new VarNode(this, newName, newInit, flags);
} else {
newThis = this;
}
@@ -187,10 +162,10 @@
* @return a node equivalent to this one except for the requested change.
*/
public VarNode setInit(final Node init) {
- if(this.init == init) return this;
- final VarNode n = (VarNode)clone();
- n.init = init;
- return n;
+ if (this.init == init) {
+ return this;
+ }
+ return new VarNode(this, name, init, flags);
}
/**
@@ -204,12 +179,38 @@
/**
* Reset the identifier for this VarNode
* @param name new IdentNode representing the variable being set or declared
+ * @return a node equivalent to this one except for the requested change.
*/
- private VarNode setName(final IdentNode name) {
- if(this.name == name) return this;
- final VarNode n = (VarNode)clone();
- n.name = name;
- return n;
+ public VarNode setName(final IdentNode name) {
+ if (this.name == name) {
+ return this;
+ }
+ return new VarNode(this, name, init, flags);
+ }
+
+ private VarNode setFlags(final int flags) {
+ if (this.flags == flags) {
+ return this;
+ }
+ return new VarNode(this, name, init, flags);
+ }
+
+ /**
+ * Check if a flag is set for this var node
+ * @param flag flag
+ * @return true if flag is set
+ */
+ public boolean getFlag(final int flag) {
+ return (flags & flag) == flag;
+ }
+
+ /**
+ * Set a flag for this var node
+ * @param flag flag
+ * @return new node if flags changed, same otherwise
+ */
+ public VarNode setFlag(final int flag) {
+ return setFlags(flags | flag);
}
/**
@@ -217,7 +218,7 @@
* @return true if this is a var statement (as opposed to a var initializer in a for loop).
*/
public boolean isStatement() {
- return isStatement;
+ return (flags & IS_STATEMENT) != 0;
}
/**
--- a/nashorn/src/jdk/nashorn/internal/ir/WhileNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/WhileNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,7 +25,7 @@
package jdk.nashorn.internal.ir;
-import jdk.nashorn.internal.codegen.Label;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
@@ -33,130 +33,126 @@
* IR representation for a WHILE statement. This is the superclass of all
* loop nodes
*/
-public class WhileNode extends BreakableNode {
- /** Test expression. */
- protected Node test;
+@Immutable
+public final class WhileNode extends LoopNode {
- /** For body. */
- protected Block body;
-
- /** loop continue label. */
- protected Label continueLabel;
+ /** is this a do while node ? */
+ private final boolean isDoWhile;
/**
* Constructor
*
- * @param source the source
- * @param token token
- * @param finish finish
+ * @param source the source
+ * @param token token
+ * @param finish finish
+ * @param isDoWhile is this a do while loop?
*/
- public WhileNode(final Source source, final long token, final int finish) {
- super(source, token, finish);
-
- this.breakLabel = new Label("while_break");
- this.continueLabel = new Label("while_continue");
+ public WhileNode(final Source source, final long token, final int finish, final boolean isDoWhile) {
+ super(source, token, finish, null, null, false);
+ this.isDoWhile = isDoWhile;
}
/**
- * Copy constructor
+ * Internal copy constructor
*
- * @param whileNode source node
- * @param cs copy state
+ * @param whileNode while node
+ * @param test test
+ * @param body body
+ * @param controlFlowEscapes control flow escapes?
*/
- protected WhileNode(final WhileNode whileNode, final CopyState cs) {
- super(whileNode);
-
- this.test = cs.existingOrCopy(whileNode.test);
- this.body = (Block)cs.existingOrCopy(whileNode.body);
- this.breakLabel = new Label(whileNode.breakLabel);
- this.continueLabel = new Label(whileNode.continueLabel);
+ protected WhileNode(final WhileNode whileNode, final Node test, final Block body, final boolean controlFlowEscapes) {
+ super(whileNode, test, body, controlFlowEscapes);
+ this.isDoWhile = whileNode.isDoWhile;
}
@Override
- protected Node copy(final CopyState cs) {
- return new WhileNode(this, cs);
+ public Node ensureUniqueLabels(final LexicalContext lc) {
+ return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
+ }
+
+ @Override
+ public boolean hasGoto() {
+ return test == null;
}
@Override
- public boolean isLoop() {
- return true;
- }
+ protected Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterWhileNode(this)) {
+ if (isDoWhile()) {
+ return visitor.leaveWhileNode(
+ setTest(lc, test.accept(visitor)).
+ setBody(lc, (Block)body.accept(visitor)));
+ }
+ return visitor.leaveWhileNode(
+ setBody(lc, (Block)body.accept(visitor)).
+ setTest(lc, test.accept(visitor)));
- /**
- * Assist in IR navigation.
- * @param visitor IR navigating visitor.
- */
- @Override
- public Node accept(final NodeVisitor visitor) {
- if (visitor.enterWhileNode(this) != null) {
- test = test.accept(visitor);
- body = (Block)body.accept(visitor);
-
- return visitor.leaveWhileNode(this);
}
return this;
}
@Override
- public void toString(final StringBuilder sb) {
- sb.append("while (");
- test.toString(sb);
- sb.append(')');
+ public Node getTest() {
+ return test;
}
- /**
- * Get the loop body
- * @return body
- */
+ @Override
+ public WhileNode setTest(final LexicalContext lc, final Node test) {
+ if (this.test == test) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
+ }
+
+ @Override
public Block getBody() {
return body;
}
- /**
- * Reset the loop body
- * @param body new body
- */
- public void setBody(final Block body) {
- this.body = body;
+ @Override
+ public WhileNode setBody(final LexicalContext lc, final Block body) {
+ if (this.body == body) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
}
- /**
- * Set the break label (described in {@link WhileNode#getBreakLabel()} for this while node
- * @param breakLabel break label
- */
- public void setBreakLabel(final Label breakLabel) {
- this.breakLabel = breakLabel;
+ @Override
+ public WhileNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes) {
+ if (this.controlFlowEscapes == controlFlowEscapes) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
}
/**
- * Get the continue label for this while node, i.e. location to go to on continue
- * @return continue label
+ * Check if this is a do while loop or a normal while loop
+ * @return true if do while
*/
- public Label getContinueLabel() {
- return continueLabel;
- }
-
- /**
- * Set the continue label (described in {@link WhileNode#getContinueLabel()} for this while node
- * @param continueLabel continue label
- */
- public void setContinueLabel(final Label continueLabel) {
- this.continueLabel = continueLabel;
+ public boolean isDoWhile() {
+ return isDoWhile;
}
- /**
- * Get the test expression for this loop, that upon evaluation to true does another iteration
- * @return test expression
- */
- public Node getTest() {
- return test;
+ @Override
+ public void toString(final StringBuilder sb) {
+ if (isDoWhile()) {
+ sb.append("do {");
+ body.toString(sb);
+ sb.append("} while (");
+ test.toString(sb);
+ sb.append(')');
+ } else {
+ sb.append("while (");
+ test.toString(sb);
+ sb.append(')');
+ }
}
- /**
- * Set the test expression for this loop
- * @param test test expression, null if infinite loop
- */
- public void setTest(final Node test) {
- this.test = test;
+ @Override
+ public boolean mustEnter() {
+ if (isDoWhile()) {
+ return true;
+ }
+ return test == null;
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/WithNode.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/WithNode.java Thu May 16 11:47:51 2013 +0100
@@ -25,18 +25,20 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for {@code with} statements.
*/
-public class WithNode extends Node {
+@Immutable
+public final class WithNode extends LexicalContextNode {
/** This expression. */
- private Node expression;
+ private final Node expression;
/** Statements. */
- private Block body;
+ private final Block body;
/**
* Constructor
@@ -44,42 +46,39 @@
* @param source the source
* @param token token
* @param finish finish
- * @param expression expression in parenthesis
- * @param body with node body
*/
- public WithNode(final Source source, final long token, final int finish, final Node expression, final Block body) {
+ public WithNode(final Source source, final long token, final int finish) {
super(source, token, finish);
+ this.expression = null;
+ this.body = null;
+ }
+
+ private WithNode(final WithNode node, final Node expression, final Block body) {
+ super(node);
+
this.expression = expression;
this.body = body;
}
- private WithNode(final WithNode withNode, final CopyState cs) {
- super(withNode);
-
- this.expression = cs.existingOrCopy(withNode.expression);
- this.body = (Block)cs.existingOrCopy(withNode.body);
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new WithNode(this, cs);
- }
-
/**
* Assist in IR navigation.
*
* @param visitor IR navigating visitor.
*/
@Override
- public Node accept(final NodeVisitor visitor) {
- if (visitor.enterWithNode(this) != null) {
- expression = expression.accept(visitor);
- body = (Block)body.accept(visitor);
- return visitor.leaveWithNode(this);
+ public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterWithNode(this)) {
+ return visitor.leaveWithNode(
+ setExpression(lc, expression.accept(visitor)).
+ setBody(lc, (Block)body.accept(visitor)));
}
+ return this;
+ }
- return this;
+ @Override
+ public boolean isTerminal() {
+ return body.isTerminal();
}
@Override
@@ -99,10 +98,15 @@
/**
* Reset the body of this with node
+ * @param lc lexical context
* @param body new body
+ * @return new or same withnode
*/
- public void setBody(final Block body) {
- this.body = body;
+ public WithNode setBody(final LexicalContext lc, final Block body) {
+ if (this.body == body) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new WithNode(this, expression, body));
}
/**
@@ -115,10 +119,15 @@
/**
* Reset the expression of this with node
+ * @param lc lexical context
* @param expression new expression
+ * @return new or same withnode
*/
- public void setExpression(final Node expression) {
- this.expression = expression;
+ public WithNode setExpression(final LexicalContext lc, final Node expression) {
+ if (this.expression == expression) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new WithNode(this, expression, body));
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/ir/annotations/Immutable.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2010, 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 jdk.nashorn.internal.ir.annotations;
+
+/**
+ * Tag for nodes that are immutable. To be immutable all fields must be
+ * final and copy on write semantics must be in place
+ */
+public @interface Immutable {
+ //empty
+}
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/ASTWriter.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/ASTWriter.java Thu May 16 11:47:51 2013 +0100
@@ -33,7 +33,9 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+
import jdk.nashorn.internal.ir.BinaryNode;
+import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.annotations.Ignore;
@@ -113,6 +115,10 @@
type += "#" + node.getSymbol();
}
+ if (node instanceof Block && ((Block)node).needsScope()) {
+ type += " <scope>";
+ }
+
final List<Field> children = new LinkedList<>();
if (!isReference) {
@@ -121,10 +127,6 @@
String status = "";
- if (node.shouldDiscard()) {
- status += " Discard";
- }
-
if (node.isTerminal()) {
status += " Terminal";
}
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/JSONWriter.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/JSONWriter.java Thu May 16 11:47:51 2013 +0100
@@ -36,7 +36,6 @@
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;
@@ -88,7 +87,7 @@
final Parser parser = new Parser(env, new Source(name, code), new Context.ThrowErrorManager(), env._strict);
final JSONWriter jsonWriter = new JSONWriter(includeLoc);
try {
- final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.tag());
+ final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.symbolName());
functionNode.accept(jsonWriter);
return jsonWriter.getString();
} catch (final ParserException e) {
@@ -98,11 +97,16 @@
}
@Override
- protected Node enterDefault(final Node node) {
+ protected boolean enterDefault(final Node node) {
objectStart();
location(node);
- return node;
+ return true;
+ }
+
+ private boolean leave() {
+ objectEnd();
+ return false;
}
@Override
@@ -112,7 +116,7 @@
}
@Override
- public Node enterAccessNode(final AccessNode accessNode) {
+ public boolean enterAccessNode(final AccessNode accessNode) {
enterDefault(accessNode);
type("MemberExpression");
@@ -128,11 +132,11 @@
property("computed", false);
- return leaveDefault(accessNode);
+ return leave();
}
@Override
- public Node enterBlock(final Block block) {
+ public boolean enterBlock(final Block block) {
enterDefault(block);
type("BlockStatement");
@@ -140,21 +144,21 @@
array("body", block.getStatements());
- return leaveDefault(block);
+ return leave();
}
private static boolean isLogical(final TokenType tt) {
switch (tt) {
- case AND:
- case OR:
- return true;
- default:
- return false;
+ case AND:
+ case OR:
+ return true;
+ default:
+ return false;
}
}
@Override
- public Node enterBinaryNode(final BinaryNode binaryNode) {
+ public boolean enterBinaryNode(final BinaryNode binaryNode) {
enterDefault(binaryNode);
final String name;
@@ -179,29 +183,29 @@
property("right");
binaryNode.rhs().accept(this);
- return leaveDefault(binaryNode);
+ return leave();
}
@Override
- public Node enterBreakNode(final BreakNode breakNode) {
+ public boolean enterBreakNode(final BreakNode breakNode) {
enterDefault(breakNode);
type("BreakStatement");
comma();
- final LabelNode label = breakNode.getLabel();
+ final IdentNode label = breakNode.getLabel();
if (label != null) {
- property("label", label.getLabel().getName());
+ property("label", label.getName());
} else {
property("label");
nullValue();
}
- return leaveDefault(breakNode);
+ return leave();
}
@Override
- public Node enterCallNode(final CallNode callNode) {
+ public boolean enterCallNode(final CallNode callNode) {
enterDefault(callNode);
type("CallExpression");
@@ -213,11 +217,11 @@
array("arguments", callNode.getArgs());
- return leaveDefault(callNode);
+ return leave();
}
@Override
- public Node enterCaseNode(final CaseNode caseNode) {
+ public boolean enterCaseNode(final CaseNode caseNode) {
enterDefault(caseNode);
type("SwitchCase");
@@ -234,11 +238,11 @@
array("consequent", caseNode.getBody().getStatements());
- return leaveDefault(caseNode);
+ return leave();
}
@Override
- public Node enterCatchNode(final CatchNode catchNode) {
+ public boolean enterCatchNode(final CatchNode catchNode) {
enterDefault(catchNode);
type("CatchClause");
@@ -260,55 +264,38 @@
property("body");
catchNode.getBody().accept(this);
- return leaveDefault(catchNode);
+ return leave();
}
@Override
- public Node enterContinueNode(final ContinueNode continueNode) {
+ public boolean enterContinueNode(final ContinueNode continueNode) {
enterDefault(continueNode);
type("ContinueStatement");
comma();
- final LabelNode label = continueNode.getLabel();
+ final IdentNode label = continueNode.getLabel();
if (label != null) {
- property("label", label.getLabel().getName());
+ property("label", label.getName());
} else {
property("label");
nullValue();
}
- return leaveDefault(continueNode);
+ return leave();
}
@Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- enterDefault(doWhileNode);
-
- type("DoWhileStatement");
- comma();
-
- property("body");
- doWhileNode.getBody().accept(this);
- comma();
-
- property("test");
- doWhileNode.getTest().accept(this);
-
- return leaveDefault(doWhileNode);
- }
-
- @Override
- public Node enterEmptyNode(final EmptyNode emptyNode) {
+ public boolean enterEmptyNode(final EmptyNode emptyNode) {
enterDefault(emptyNode);
type("EmptyStatement");
- return leaveDefault(emptyNode);
+ return leave();
}
@Override
- public Node enterExecuteNode(final ExecuteNode executeNode) {
+ public boolean enterExecuteNode(final ExecuteNode executeNode) {
enterDefault(executeNode);
type("ExpressionStatement");
@@ -317,11 +304,11 @@
property("expression");
executeNode.getExpression().accept(this);
- return leaveDefault(executeNode);
+ return leave();
}
@Override
- public Node enterForNode(final ForNode forNode) {
+ public boolean enterForNode(final ForNode forNode) {
enterDefault(forNode);
if (forNode.isForIn() || (forNode.isForEach() && forNode.getInit() != null)) {
@@ -380,11 +367,11 @@
forNode.getBody().accept(this);
}
- return leaveDefault(forNode);
+ return leave();
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
enterDefault(functionNode);
final boolean program = functionNode.isProgram();
@@ -419,7 +406,7 @@
}
// body consists of nested functions and statements
- final List<Node> stats = functionNode.getStatements();
+ final List<Node> stats = functionNode.getBody().getStatements();
final int size = stats.size();
int idx = 0;
arrayStart("body");
@@ -435,11 +422,11 @@
}
arrayEnd();
- return leaveDefault(functionNode);
+ return leave();
}
@Override
- public Node enterIdentNode(final IdentNode identNode) {
+ public boolean enterIdentNode(final IdentNode identNode) {
enterDefault(identNode);
final String name = identNode.getName();
@@ -451,11 +438,11 @@
property("name", identNode.getName());
}
- return leaveDefault(identNode);
+ return leave();
}
@Override
- public Node enterIfNode(final IfNode ifNode) {
+ public boolean enterIfNode(final IfNode ifNode) {
enterDefault(ifNode);
type("IfStatement");
@@ -477,11 +464,11 @@
nullValue();
}
- return leaveDefault(ifNode);
+ return leave();
}
@Override
- public Node enterIndexNode(final IndexNode indexNode) {
+ public boolean enterIndexNode(final IndexNode indexNode) {
enterDefault(indexNode);
type("MemberExpression");
@@ -497,11 +484,11 @@
property("computed", true);
- return leaveDefault(indexNode);
+ return leave();
}
@Override
- public Node enterLabelNode(final LabelNode labelNode) {
+ public boolean enterLabelNode(final LabelNode labelNode) {
enterDefault(labelNode);
type("LabeledStatement");
@@ -514,17 +501,17 @@
property("body");
labelNode.getBody().accept(this);
- return leaveDefault(labelNode);
+ return leave();
}
@Override
- public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
- return null;
+ public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
+ return false;
}
@SuppressWarnings("rawtypes")
@Override
- public Node enterLiteralNode(final LiteralNode literalNode) {
+ public boolean enterLiteralNode(final LiteralNode literalNode) {
enterDefault(literalNode);
if (literalNode instanceof LiteralNode.ArrayLiteralNode) {
@@ -556,11 +543,11 @@
}
}
- return leaveDefault(literalNode);
+ return leave();
}
@Override
- public Node enterObjectNode(final ObjectNode objectNode) {
+ public boolean enterObjectNode(final ObjectNode objectNode) {
enterDefault(objectNode);
type("ObjectExpression");
@@ -568,11 +555,11 @@
array("properties", objectNode.getElements());
- return leaveDefault(objectNode);
+ return leave();
}
@Override
- public Node enterPropertyNode(final PropertyNode propertyNode) {
+ public boolean enterPropertyNode(final PropertyNode propertyNode) {
final Node key = propertyNode.getKey();
final Node value = propertyNode.getValue();
@@ -634,11 +621,11 @@
}
}
- return null;
+ return false;
}
@Override
- public Node enterReturnNode(final ReturnNode returnNode) {
+ public boolean enterReturnNode(final ReturnNode returnNode) {
enterDefault(returnNode);
type("ReturnStatement");
@@ -652,31 +639,29 @@
nullValue();
}
- return leaveDefault(returnNode);
+ return leave();
}
@Override
- public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
+ public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
final RuntimeNode.Request req = runtimeNode.getRequest();
if (req == RuntimeNode.Request.DEBUGGER) {
enterDefault(runtimeNode);
-
type("DebuggerStatement");
-
- return leaveDefault(runtimeNode);
+ return leave();
}
- return null;
+ return false;
}
@Override
- public Node enterSplitNode(final SplitNode splitNode) {
- return null;
+ public boolean enterSplitNode(final SplitNode splitNode) {
+ return false;
}
@Override
- public Node enterSwitchNode(final SwitchNode switchNode) {
+ public boolean enterSwitchNode(final SwitchNode switchNode) {
enterDefault(switchNode);
type("SwitchStatement");
@@ -688,11 +673,11 @@
array("cases", switchNode.getCases());
- return leaveDefault(switchNode);
+ return leave();
}
@Override
- public Node enterTernaryNode(final TernaryNode ternaryNode) {
+ public boolean enterTernaryNode(final TernaryNode ternaryNode) {
enterDefault(ternaryNode);
type("ConditionalExpression");
@@ -709,11 +694,11 @@
property("alternate");
ternaryNode.third().accept(this);
- return leaveDefault(ternaryNode);
+ return leave();
}
@Override
- public Node enterThrowNode(final ThrowNode throwNode) {
+ public boolean enterThrowNode(final ThrowNode throwNode) {
enterDefault(throwNode);
type("ThrowStatement");
@@ -722,11 +707,11 @@
property("argument");
throwNode.getExpression().accept(this);
- return leaveDefault(throwNode);
+ return leave();
}
@Override
- public Node enterTryNode(final TryNode tryNode) {
+ public boolean enterTryNode(final TryNode tryNode) {
enterDefault(tryNode);
type("TryStatement");
@@ -747,11 +732,11 @@
nullValue();
}
- return leaveDefault(tryNode);
+ return leave();
}
@Override
- public Node enterUnaryNode(final UnaryNode unaryNode) {
+ public boolean enterUnaryNode(final UnaryNode unaryNode) {
enterDefault(unaryNode);
final TokenType tokenType = unaryNode.tokenType();
@@ -769,25 +754,25 @@
final boolean prefix;
final String operator;
switch (tokenType) {
- case INCPOSTFIX:
- prefix = false;
- operator = "++";
- break;
- case DECPOSTFIX:
- prefix = false;
- operator = "--";
- break;
- case INCPREFIX:
- operator = "++";
- prefix = true;
- break;
- case DECPREFIX:
- operator = "--";
- prefix = true;
- break;
- default:
- prefix = false;
- operator = tokenType.getName();
+ case INCPOSTFIX:
+ prefix = false;
+ operator = "++";
+ break;
+ case DECPOSTFIX:
+ prefix = false;
+ operator = "--";
+ break;
+ case INCPREFIX:
+ operator = "++";
+ prefix = true;
+ break;
+ case DECPREFIX:
+ operator = "--";
+ prefix = true;
+ break;
+ default:
+ prefix = false;
+ operator = tokenType.getName();
}
type(unaryNode.isAssignment()? "UpdateExpression" : "UnaryExpression");
@@ -803,11 +788,11 @@
unaryNode.rhs().accept(this);
}
- return leaveDefault(unaryNode);
+ return leave();
}
@Override
- public Node enterVarNode(final VarNode varNode) {
+ public boolean enterVarNode(final VarNode varNode) {
enterDefault(varNode);
type("VariableDeclaration");
@@ -839,28 +824,37 @@
// declarations
arrayEnd();
- return leaveDefault(varNode);
+ return leave();
}
@Override
- public Node enterWhileNode(final WhileNode whileNode) {
+ public boolean enterWhileNode(final WhileNode whileNode) {
enterDefault(whileNode);
- type("WhileStatement");
+ type(whileNode.isDoWhile() ? "DoWhileStatement" : "WhileStatement");
comma();
- property("test");
- whileNode.getTest().accept(this);
- comma();
+ if (whileNode.isDoWhile()) {
+ property("body");
+ whileNode.getBody().accept(this);
+ comma();
- property("block");
- whileNode.getBody().accept(this);
+ property("test");
+ whileNode.getTest().accept(this);
+ } else {
+ property("test");
+ whileNode.getTest().accept(this);
+ comma();
- return leaveDefault(whileNode);
+ property("block");
+ whileNode.getBody().accept(this);
+ }
+
+ return leave();
}
@Override
- public Node enterWithNode(final WithNode withNode) {
+ public boolean enterWithNode(final WithNode withNode) {
enterDefault(withNode);
type("WithStatement");
@@ -873,8 +867,8 @@
property("body");
withNode.getBody().accept(this);
- return leaveDefault(withNode);
- }
+ return leave();
+ }
// Internals below
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java Thu May 16 11:47:51 2013 +0100
@@ -26,30 +26,22 @@
package jdk.nashorn.internal.ir.debug;
import java.util.List;
-import jdk.nashorn.internal.ir.AccessNode;
+
+import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
-import jdk.nashorn.internal.ir.BreakNode;
-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.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IfNode;
-import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LineNumberNode;
import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.ReturnNode;
-import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.Symbol;
-import jdk.nashorn.internal.ir.ThrowNode;
import jdk.nashorn.internal.ir.TryNode;
-import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.WithNode;
@@ -136,21 +128,20 @@
/*
* Visits.
*/
+
@Override
- public Node enterAccessNode(final AccessNode accessNode) {
- accessNode.toString(sb);
- return null;
+ public boolean enterDefault(final Node node) {
+ node.toString(sb);
+ return false;
}
@Override
- public Node enterBlock(final Block block) {
+ public boolean enterBlock(final Block block) {
sb.append(' ');
sb.append('{');
indent += TABWIDTH;
- final boolean isFunction = block instanceof FunctionNode;
-
final List<Node> statements = block.getStatements();
boolean lastLineNumber = false;
@@ -161,14 +152,14 @@
indent();
}
- if (statement instanceof UnaryNode) {
- statement.toString(sb);
- } else {
- statement.accept(this);
- }
+ statement.accept(this);
lastLineNumber = statement instanceof LineNumberNode;
+ if (statement instanceof FunctionNode) {
+ continue;
+ }
+
final Symbol symbol = statement.getSymbol();
if (symbol != null) {
@@ -200,72 +191,42 @@
indent();
sb.append("}");
- if (isFunction) {
- sb.append(EOLN);
- }
-
- return null;
+ return false;
}
@Override
- public Node enterBreakNode(final BreakNode breakNode) {
- breakNode.toString(sb);
- return null;
- }
-
- @Override
- public Node enterCallNode(final CallNode callNode) {
- callNode.toString(sb);
- return null;
- }
-
- @Override
- public Node enterContinueNode(final ContinueNode continueNode) {
- continueNode.toString(sb);
- return null;
+ public boolean enterBinaryNode(final BinaryNode binaryNode) {
+ binaryNode.lhs().accept(this);
+ sb.append(' ');
+ sb.append(binaryNode.tokenType());
+ sb.append(' ');
+ binaryNode.rhs().accept(this);
+ return false;
}
@Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- sb.append("do");
- doWhileNode.getBody().accept(this);
- sb.append(' ');
- doWhileNode.toString(sb);
-
- return null;
+ public boolean enterExecuteNode(final ExecuteNode executeNode) {
+ executeNode.getExpression().accept(this);
+ return false;
}
@Override
- public Node enterExecuteNode(final ExecuteNode executeNode) {
- final Node expression = executeNode.getExpression();
-
- if (expression instanceof UnaryNode) {
- expression.toString(sb);
- } else {
- expression.accept(this);
- }
-
- return null;
+ public boolean enterForNode(final ForNode forNode) {
+ forNode.toString(sb);
+ forNode.getBody().accept(this);
+ return false;
}
@Override
- public Node enterForNode(final ForNode forNode) {
- forNode.toString(sb);
- forNode.getBody().accept(this);
-
- return null;
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ functionNode.toString(sb);
+ enterBlock(functionNode.getBody());
+ sb.append(EOLN);
+ return false;
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
- functionNode.toString(sb);
- enterBlock(functionNode);
-
- return null;
- }
-
- @Override
- public Node enterIfNode(final IfNode ifNode) {
+ public boolean enterIfNode(final IfNode ifNode) {
ifNode.toString(sb);
ifNode.getPass().accept(this);
@@ -276,55 +237,36 @@
fail.accept(this);
}
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode indexNode) {
- indexNode.toString(sb);
- return null;
- }
-
- @Override
- public Node enterLabelNode(final LabelNode labeledNode) {
+ public boolean enterLabelNode(final LabelNode labeledNode) {
indent -= TABWIDTH;
indent();
indent += TABWIDTH;
labeledNode.toString(sb);
labeledNode.getBody().accept(this);
- return null;
+ return false;
}
@Override
- public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
+ public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
if (printLineNumbers) {
lineNumberNode.toString(sb);
}
- return null;
- }
-
-
- @Override
- public Node enterReturnNode(final ReturnNode returnNode) {
- returnNode.toString(sb);
- return null;
+ return false;
}
@Override
- public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
- runtimeNode.toString(sb);
- return null;
- }
-
- @Override
- public Node enterSplitNode(final SplitNode splitNode) {
+ public boolean enterSplitNode(final SplitNode splitNode) {
splitNode.toString(sb);
sb.append(EOLN);
indent += TABWIDTH;
indent();
- return splitNode;
+ return true;
}
@Override
@@ -337,7 +279,7 @@
}
@Override
- public Node enterSwitchNode(final SwitchNode switchNode) {
+ public boolean enterSwitchNode(final SwitchNode switchNode) {
switchNode.toString(sb);
sb.append(" {");
@@ -357,24 +299,18 @@
indent();
sb.append("}");
- return null;
- }
-
- @Override
- public Node enterThrowNode(final ThrowNode throwNode) {
- throwNode.toString(sb);
- return null;
+ return false;
}
@Override
- public Node enterTryNode(final TryNode tryNode) {
+ public boolean enterTryNode(final TryNode tryNode) {
tryNode.toString(sb);
tryNode.getBody().accept(this);
final List<Block> catchBlocks = tryNode.getCatchBlocks();
for (final Block catchBlock : catchBlocks) {
- final CatchNode catchNode = (CatchNode) catchBlock.getStatements().get(0);
+ final CatchNode catchNode = (CatchNode)catchBlock.getStatements().get(0);
catchNode.toString(sb);
catchNode.getBody().accept(this);
}
@@ -386,35 +322,42 @@
finallyBody.accept(this);
}
- return null;
+ return false;
}
@Override
- public Node enterVarNode(final VarNode varNode) {
+ public boolean enterVarNode(final VarNode varNode) {
sb.append("var ");
varNode.getName().toString(sb);
final Node init = varNode.getInit();
- if(init != null) {
+ if (init != null) {
sb.append(" = ");
init.accept(this);
}
- return null;
+ return false;
}
@Override
- public Node enterWhileNode(final WhileNode whileNode) {
- whileNode.toString(sb);
- whileNode.getBody().accept(this);
+ public boolean enterWhileNode(final WhileNode whileNode) {
+ if (whileNode.isDoWhile()) {
+ sb.append("do");
+ whileNode.getBody().accept(this);
+ sb.append(' ');
+ whileNode.toString(sb);
+ } else {
+ whileNode.toString(sb);
+ whileNode.getBody().accept(this);
+ }
- return null;
+ return false;
}
@Override
- public Node enterWithNode(final WithNode withNode) {
+ public boolean enterWithNode(final WithNode withNode) {
withNode.toString(sb);
withNode.getBody().accept(this);
- return null;
+ return false;
}
}
--- a/nashorn/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java Thu May 16 11:47:51 2013 +0100
@@ -25,9 +25,8 @@
package jdk.nashorn.internal.ir.visitor;
-import jdk.nashorn.internal.codegen.CompileUnit;
-import jdk.nashorn.internal.codegen.MethodEmitter;
import jdk.nashorn.internal.ir.BinaryNode;
+import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.UnaryNode;
@@ -45,15 +44,14 @@
/**
* Constructor
*
- * @param compileUnit compile unit
- * @param method method emitter
+ * @param lc a custom lexical context
*/
- public NodeOperatorVisitor(final CompileUnit compileUnit, final MethodEmitter method) {
- super(compileUnit, method);
+ public NodeOperatorVisitor(final LexicalContext lc) {
+ super(lc);
}
@Override
- public final Node enterUnaryNode(final UnaryNode unaryNode) {
+ public final boolean enterUnaryNode(final UnaryNode unaryNode) {
switch (unaryNode.tokenType()) {
case ADD:
return enterADD(unaryNode);
@@ -119,7 +117,7 @@
}
@Override
- public final Node enterBinaryNode(final BinaryNode binaryNode) {
+ public final boolean enterBinaryNode(final BinaryNode binaryNode) {
switch (binaryNode.tokenType()) {
case ADD:
return enterADD(binaryNode);
@@ -287,17 +285,6 @@
}
/*
- @Override
- public Node enter(final TernaryNode ternaryNode) {
- return enterDefault(ternaryNode);
- }
-
- @Override
- public Node leave(final TernaryNode ternaryNode) {
- return leaveDefault(ternaryNode);
- }*/
-
- /*
* Unary entries and exists.
*/
@@ -305,9 +292,9 @@
* Unary enter - callback for entering a unary +
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterADD(final UnaryNode unaryNode) {
+ public boolean enterADD(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -325,9 +312,9 @@
* Unary enter - callback for entering a ~ operator
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterBIT_NOT(final UnaryNode unaryNode) {
+ public boolean enterBIT_NOT(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -345,9 +332,9 @@
* Unary enter - callback for entering a conversion
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterCONVERT(final UnaryNode unaryNode) {
+ public boolean enterCONVERT(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -365,9 +352,9 @@
* Unary enter - callback for entering a ++ or -- operator
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterDECINC(final UnaryNode unaryNode) {
+ public boolean enterDECINC(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -387,7 +374,7 @@
* @param unaryNode the node
* @return processed node
*/
- public Node enterDELETE(final UnaryNode unaryNode) {
+ public boolean enterDELETE(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -405,9 +392,9 @@
* Unary enter - callback for entering a discard operator
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterDISCARD(final UnaryNode unaryNode) {
+ public boolean enterDISCARD(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -425,9 +412,9 @@
* Unary enter - callback for entering a new operator
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterNEW(final UnaryNode unaryNode) {
+ public boolean enterNEW(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -445,9 +432,9 @@
* Unary enter - callback for entering a ! operator
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterNOT(final UnaryNode unaryNode) {
+ public boolean enterNOT(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -465,9 +452,9 @@
* Unary enter - callback for entering a unary -
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterSUB(final UnaryNode unaryNode) {
+ public boolean enterSUB(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -485,9 +472,9 @@
* Unary enter - callback for entering a typeof
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterTYPEOF(final UnaryNode unaryNode) {
+ public boolean enterTYPEOF(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -505,9 +492,9 @@
* Unary enter - callback for entering a void
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterVOID(final UnaryNode unaryNode) {
+ public boolean enterVOID(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -525,9 +512,9 @@
* Binary enter - callback for entering + operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterADD(final BinaryNode binaryNode) {
+ public boolean enterADD(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -545,9 +532,9 @@
* Binary enter - callback for entering {@literal &&} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterAND(final BinaryNode binaryNode) {
+ public boolean enterAND(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -565,9 +552,9 @@
* Binary enter - callback for entering an assignment
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN(final BinaryNode binaryNode) {
+ public boolean enterASSIGN(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -585,9 +572,9 @@
* Binary enter - callback for entering += operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -605,9 +592,9 @@
* Binary enter - callback for entering {@literal &=} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -625,9 +612,9 @@
* Binary enter - callback for entering |= operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -645,9 +632,9 @@
* Binary enter - callback for entering ^= operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -665,9 +652,9 @@
* Binary enter - callback for entering /= operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -685,9 +672,9 @@
* Binary enter - callback for entering %= operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -705,9 +692,9 @@
* Binary enter - callback for entering *= operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -725,9 +712,9 @@
* Binary enter - callback for entering {@literal >>=} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -745,9 +732,9 @@
* Binary enter - callback for entering a {@literal <<=} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -765,9 +752,9 @@
* Binary enter - callback for entering {@literal >>>=} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -785,9 +772,9 @@
* Binary enter - callback for entering -= operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -805,9 +792,9 @@
* Binary enter - callback for entering a bind operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterBIND(final BinaryNode binaryNode) {
+ public boolean enterBIND(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -825,9 +812,9 @@
* Binary enter - callback for entering {@literal &} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterBIT_AND(final BinaryNode binaryNode) {
+ public boolean enterBIT_AND(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -845,9 +832,9 @@
* Binary enter - callback for entering | operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterBIT_OR(final BinaryNode binaryNode) {
+ public boolean enterBIT_OR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -865,9 +852,9 @@
* Binary enter - callback for entering ^ operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterBIT_XOR(final BinaryNode binaryNode) {
+ public boolean enterBIT_XOR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -886,9 +873,9 @@
* (a, b) where the result is a
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterCOMMALEFT(final BinaryNode binaryNode) {
+ public boolean enterCOMMALEFT(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -908,9 +895,9 @@
* (a, b) where the result is b
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterCOMMARIGHT(final BinaryNode binaryNode) {
+ public boolean enterCOMMARIGHT(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -929,9 +916,9 @@
* Binary enter - callback for entering a division
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterDIV(final BinaryNode binaryNode) {
+ public boolean enterDIV(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -949,9 +936,9 @@
* Binary enter - callback for entering == operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterEQ(final BinaryNode binaryNode) {
+ public boolean enterEQ(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -969,9 +956,9 @@
* Binary enter - callback for entering === operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterEQ_STRICT(final BinaryNode binaryNode) {
+ public boolean enterEQ_STRICT(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -989,9 +976,9 @@
* Binary enter - callback for entering {@literal >=} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterGE(final BinaryNode binaryNode) {
+ public boolean enterGE(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1009,9 +996,9 @@
* Binary enter - callback for entering {@literal >} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterGT(final BinaryNode binaryNode) {
+ public boolean enterGT(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1029,9 +1016,9 @@
* Binary enter - callback for entering in operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterIN(final BinaryNode binaryNode) {
+ public boolean enterIN(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1049,9 +1036,9 @@
* Binary enter - callback for entering instanceof operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterINSTANCEOF(final BinaryNode binaryNode) {
+ public boolean enterINSTANCEOF(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1069,9 +1056,9 @@
* Binary enter - callback for entering {@literal <=} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterLE(final BinaryNode binaryNode) {
+ public boolean enterLE(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1089,9 +1076,9 @@
* Binary enter - callback for entering {@literal <} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterLT(final BinaryNode binaryNode) {
+ public boolean enterLT(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1108,9 +1095,9 @@
* Binary enter - callback for entering % operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterMOD(final BinaryNode binaryNode) {
+ public boolean enterMOD(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1128,9 +1115,9 @@
* Binary enter - callback for entering * operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterMUL(final BinaryNode binaryNode) {
+ public boolean enterMUL(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1148,9 +1135,9 @@
* Binary enter - callback for entering != operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterNE(final BinaryNode binaryNode) {
+ public boolean enterNE(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1168,9 +1155,9 @@
* Binary enter - callback for entering a !== operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterNE_STRICT(final BinaryNode binaryNode) {
+ public boolean enterNE_STRICT(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1188,9 +1175,9 @@
* Binary enter - callback for entering || operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterOR(final BinaryNode binaryNode) {
+ public boolean enterOR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1208,9 +1195,9 @@
* Binary enter - callback for entering {@literal >>} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterSAR(final BinaryNode binaryNode) {
+ public boolean enterSAR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1228,9 +1215,9 @@
* Binary enter - callback for entering {@literal <<} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterSHL(final BinaryNode binaryNode) {
+ public boolean enterSHL(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1247,9 +1234,9 @@
* Binary enter - callback for entering {@literal >>>} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterSHR(final BinaryNode binaryNode) {
+ public boolean enterSHR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1267,9 +1254,9 @@
* Binary enter - callback for entering - operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterSUB(final BinaryNode binaryNode) {
+ public boolean enterSUB(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
--- a/nashorn/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Thu May 16 11:47:51 2013 +0100
@@ -25,8 +25,6 @@
package jdk.nashorn.internal.ir.visitor;
-import jdk.nashorn.internal.codegen.CompileUnit;
-import jdk.nashorn.internal.codegen.MethodEmitter;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
@@ -35,7 +33,6 @@
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;
@@ -44,6 +41,7 @@
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LabelNode;
+import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LineNumberNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.Node;
@@ -65,41 +63,30 @@
* Visitor used to navigate the IR.
*/
public abstract class NodeVisitor {
- /** Current functionNode. */
- private FunctionNode currentFunctionNode;
-
- /** Current compile unit used for class generation. */
- private CompileUnit compileUnit;
+ private final LexicalContext lc;
/**
- * Current method visitor used for method generation.
- * <p>
- * TODO: protected is just for convenience and readability, so that
- * subclasses can directly use 'method' - might want to change that
- */
- protected MethodEmitter method;
-
- /** Current block. */
- private Block currentBlock;
-
- /**
- * Constructor.
+ * Constructor
*/
public NodeVisitor() {
- this(null, null);
+ this(new LexicalContext());
}
/**
* Constructor
*
- * @param compileUnit compile unit for this node visitor
- * @param method method emitter for this node visitor
+ * @param lc a custom lexical context
*/
- public NodeVisitor(final CompileUnit compileUnit, final MethodEmitter method) {
- super();
+ public NodeVisitor(final LexicalContext lc) {
+ this.lc = lc;
+ }
- this.compileUnit = compileUnit;
- this.method = method;
+ /**
+ * Get the lexical context of this node visitor
+ * @return lexical context
+ */
+ public LexicalContext getLexicalContext() {
+ return lc;
}
/**
@@ -118,10 +105,10 @@
*
* @see NodeVisitor#leaveDefault(Node)
* @param node the node to visit
- * @return the node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- protected Node enterDefault(final Node node) {
- return node;
+ protected boolean enterDefault(final Node node) {
+ return true;
}
/**
@@ -150,9 +137,9 @@
* Callback for entering an AccessNode
*
* @param accessNode the node
- * @return processed node, null if traversal should end, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterAccessNode(final AccessNode accessNode) {
+ public boolean enterAccessNode(final AccessNode accessNode) {
return enterDefault(accessNode);
}
@@ -170,9 +157,9 @@
* Callback for entering a Block
*
* @param block the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterBlock(final Block block) {
+ public boolean enterBlock(final Block block) {
return enterDefault(block);
}
@@ -192,7 +179,7 @@
* @param binaryNode the node
* @return processed node
*/
- public Node enterBinaryNode(final BinaryNode binaryNode) {
+ public boolean enterBinaryNode(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -210,9 +197,9 @@
* Callback for entering a BreakNode
*
* @param breakNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterBreakNode(final BreakNode breakNode) {
+ public boolean enterBreakNode(final BreakNode breakNode) {
return enterDefault(breakNode);
}
@@ -230,9 +217,9 @@
* Callback for entering a CallNode
*
* @param callNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterCallNode(final CallNode callNode) {
+ public boolean enterCallNode(final CallNode callNode) {
return enterDefault(callNode);
}
@@ -250,9 +237,9 @@
* Callback for entering a CaseNode
*
* @param caseNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterCaseNode(final CaseNode caseNode) {
+ public boolean enterCaseNode(final CaseNode caseNode) {
return enterDefault(caseNode);
}
@@ -270,9 +257,9 @@
* Callback for entering a CatchNode
*
* @param catchNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterCatchNode(final CatchNode catchNode) {
+ public boolean enterCatchNode(final CatchNode catchNode) {
return enterDefault(catchNode);
}
@@ -290,9 +277,9 @@
* Callback for entering a ContinueNode
*
* @param continueNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterContinueNode(final ContinueNode continueNode) {
+ public boolean enterContinueNode(final ContinueNode continueNode) {
return enterDefault(continueNode);
}
@@ -307,32 +294,12 @@
}
/**
- * Callback for entering a DoWhileNode
- *
- * @param doWhileNode the node
- * @return processed node
- */
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- return enterDefault(doWhileNode);
- }
-
- /**
- * Callback for leaving a DoWhileNode
- *
- * @param doWhileNode the node
- * @return processed node, which will replace the original one, or the original node
- */
- public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
- return leaveDefault(doWhileNode);
- }
-
- /**
* Callback for entering an EmptyNode
*
* @param emptyNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterEmptyNode(final EmptyNode emptyNode) {
+ public boolean enterEmptyNode(final EmptyNode emptyNode) {
return enterDefault(emptyNode);
}
@@ -350,9 +317,9 @@
* Callback for entering an ExecuteNode
*
* @param executeNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterExecuteNode(final ExecuteNode executeNode) {
+ public boolean enterExecuteNode(final ExecuteNode executeNode) {
return enterDefault(executeNode);
}
@@ -370,9 +337,9 @@
* Callback for entering a ForNode
*
* @param forNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterForNode(final ForNode forNode) {
+ public boolean enterForNode(final ForNode forNode) {
return enterDefault(forNode);
}
@@ -390,9 +357,9 @@
* Callback for entering a FunctionNode
*
* @param functionNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterFunctionNode(final FunctionNode functionNode) {
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
return enterDefault(functionNode);
}
@@ -410,9 +377,9 @@
* Callback for entering an IdentNode
*
* @param identNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterIdentNode(final IdentNode identNode) {
+ public boolean enterIdentNode(final IdentNode identNode) {
return enterDefault(identNode);
}
@@ -429,10 +396,10 @@
/**
* Callback for entering an IfNode
*
- * @param ifNode the node
- * @return processed node, null if traversal should end
+ * @param ifNode the node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterIfNode(final IfNode ifNode) {
+ public boolean enterIfNode(final IfNode ifNode) {
return enterDefault(ifNode);
}
@@ -450,9 +417,9 @@
* Callback for entering an IndexNode
*
* @param indexNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterIndexNode(final IndexNode indexNode) {
+ public boolean enterIndexNode(final IndexNode indexNode) {
return enterDefault(indexNode);
}
@@ -470,9 +437,9 @@
* Callback for entering a LabelNode
*
* @param labelNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterLabelNode(final LabelNode labelNode) {
+ public boolean enterLabelNode(final LabelNode labelNode) {
return enterDefault(labelNode);
}
@@ -490,9 +457,9 @@
* Callback for entering a LineNumberNode
*
* @param lineNumberNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
+ public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
return enterDefault(lineNumberNode);
}
@@ -510,9 +477,9 @@
* Callback for entering a LiteralNode
*
* @param literalNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterLiteralNode(final LiteralNode<?> literalNode) {
+ public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
return enterDefault(literalNode);
}
@@ -530,9 +497,9 @@
* Callback for entering an ObjectNode
*
* @param objectNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterObjectNode(final ObjectNode objectNode) {
+ public boolean enterObjectNode(final ObjectNode objectNode) {
return enterDefault(objectNode);
}
@@ -550,9 +517,9 @@
* Callback for entering a PropertyNode
*
* @param propertyNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterPropertyNode(final PropertyNode propertyNode) {
+ public boolean enterPropertyNode(final PropertyNode propertyNode) {
return enterDefault(propertyNode);
}
@@ -570,9 +537,9 @@
* Callback for entering a ReturnNode
*
* @param returnNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterReturnNode(final ReturnNode returnNode) {
+ public boolean enterReturnNode(final ReturnNode returnNode) {
return enterDefault(returnNode);
}
@@ -590,9 +557,9 @@
* Callback for entering a RuntimeNode
*
* @param runtimeNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
+ public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
return enterDefault(runtimeNode);
}
@@ -610,9 +577,9 @@
* Callback for entering a SplitNode
*
* @param splitNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterSplitNode(final SplitNode splitNode) {
+ public boolean enterSplitNode(final SplitNode splitNode) {
return enterDefault(splitNode);
}
@@ -630,9 +597,9 @@
* Callback for entering a SwitchNode
*
* @param switchNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterSwitchNode(final SwitchNode switchNode) {
+ public boolean enterSwitchNode(final SwitchNode switchNode) {
return enterDefault(switchNode);
}
@@ -650,9 +617,9 @@
* Callback for entering a TernaryNode
*
* @param ternaryNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterTernaryNode(final TernaryNode ternaryNode) {
+ public boolean enterTernaryNode(final TernaryNode ternaryNode) {
return enterDefault(ternaryNode);
}
@@ -670,9 +637,9 @@
* Callback for entering a ThrowNode
*
* @param throwNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterThrowNode(final ThrowNode throwNode) {
+ public boolean enterThrowNode(final ThrowNode throwNode) {
return enterDefault(throwNode);
}
@@ -690,9 +657,9 @@
* Callback for entering a TryNode
*
* @param tryNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterTryNode(final TryNode tryNode) {
+ public boolean enterTryNode(final TryNode tryNode) {
return enterDefault(tryNode);
}
@@ -710,9 +677,9 @@
* Callback for entering a UnaryNode
*
* @param unaryNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterUnaryNode(final UnaryNode unaryNode) {
+ public boolean enterUnaryNode(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -730,9 +697,9 @@
* Callback for entering a VarNode
*
* @param varNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterVarNode(final VarNode varNode) {
+ public boolean enterVarNode(final VarNode varNode) {
return enterDefault(varNode);
}
@@ -750,9 +717,9 @@
* Callback for entering a WhileNode
*
* @param whileNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterWhileNode(final WhileNode whileNode) {
+ public boolean enterWhileNode(final WhileNode whileNode) {
return enterDefault(whileNode);
}
@@ -770,9 +737,9 @@
* Callback for entering a WithNode
*
* @param withNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterWithNode(final WithNode withNode) {
+ public boolean enterWithNode(final WithNode withNode) {
return enterDefault(withNode);
}
@@ -786,74 +753,5 @@
return leaveDefault(withNode);
}
- /**
- * Get the current function node for this NodeVisitor
- * @see FunctionNode
- * @return the function node being visited
- */
- public FunctionNode getCurrentFunctionNode() {
- return currentFunctionNode;
- }
-
- /**
- * Reset the current function node being visited for this NodeVisitor
- * @see FunctionNode
- * @param currentFunctionNode a new function node to traverse
- */
- public void setCurrentFunctionNode(final FunctionNode currentFunctionNode) {
- this.currentFunctionNode = currentFunctionNode;
- }
-
- /**
- * Get the current compile unit for this NodeVisitor
- * @see CompileUnit
- * @return a compile unit, or null if not a compiling NodeVisitor
- */
- public CompileUnit getCurrentCompileUnit() {
- return compileUnit;
- }
-
- /**
- * Set the current compile unit for this NodeVisitor
- * @see CompileUnit
- * @param compileUnit a new compile unit
- */
- public void setCurrentCompileUnit(final CompileUnit compileUnit) {
- this.compileUnit = compileUnit;
- }
-
- /**
- * Get the current method emitter for this NodeVisitor
- * @see MethodEmitter
- * @return the method emitter
- */
- public MethodEmitter getCurrentMethodEmitter() {
- return method;
- }
-
- /**
- * Reset the current method emitter for this NodeVisitor
- * @see MethodEmitter
- * @param method a new method emitter
- */
- public void setCurrentMethodEmitter(final MethodEmitter method) {
- this.method = method;
- }
-
- /**
- * Get the current Block being traversed for this NodeVisitor
- * @return the current block
- */
- public Block getCurrentBlock() {
- return currentBlock;
- }
-
- /**
- * Reset the Block to be traversed for this NodeVisitor
- * @param currentBlock the new current block
- */
- public void setCurrentBlock(final Block currentBlock) {
- this.currentBlock = currentBlock;
- }
}
--- a/nashorn/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java Thu May 16 11:47:51 2013 +0100
@@ -137,7 +137,7 @@
*/
static Object traceReturn(final DebugLogger logger, final Object value) {
final String str = "\treturn: " + stripName(value) + " [type=" + (value == null ? "null" : stripName(value.getClass()) + ']');
- logger.log(str, TRACE_LEVEL);
+ logger.log(TRACE_LEVEL, str);
return value;
}
@@ -173,7 +173,7 @@
}
assert logger != null;
- logger.log(sb.toString(), TRACE_LEVEL);
+ logger.log(TRACE_LEVEL, sb);
stacktrace(logger);
}
@@ -184,7 +184,7 @@
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final PrintStream ps = new PrintStream(baos);
new Throwable().printStackTrace(ps);
- logger.log(baos.toString(), TRACE_LEVEL);
+ logger.log(TRACE_LEVEL, baos.toString());
}
private static String argString(final Object arg) {
@@ -614,7 +614,7 @@
@Override
public SwitchPoint createSwitchPoint() {
final SwitchPoint sp = super.createSwitchPoint();
- LOG.log("createSwitchPoint " + sp, TRACE_LEVEL);
+ LOG.log(TRACE_LEVEL, "createSwitchPoint ", sp);
return sp;
}
@@ -627,7 +627,7 @@
@Override
public MethodType type(final Class<?> returnType, final Class<?>... paramTypes) {
final MethodType mt = super.type(returnType, paramTypes);
- LOG.log("methodType " + returnType + ' ' + Arrays.toString(paramTypes) + ' ' + mt, TRACE_LEVEL);
+ LOG.log(TRACE_LEVEL, "methodType ", returnType, " ", Arrays.toString(paramTypes), " ", mt);
return mt;
}
}
@@ -638,7 +638,7 @@
private static class TraceCreateMethodHandleFunctionality extends TraceMethodHandleFunctionality {
@Override
public MethodHandle debug(final MethodHandle master, final String str, final Object... args) {
- LOG.log(str + ' ' + describe(args), TRACE_LEVEL);
+ LOG.log(TRACE_LEVEL, str, " ", describe(args));
stacktrace(LOG);
return master;
}
--- a/nashorn/src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java Thu May 16 11:47:51 2013 +0100
@@ -40,7 +40,7 @@
BoundScriptFunctionImpl(ScriptFunctionData data, ScriptFunction targetFunction) {
super(data);
- this.prototype = ScriptRuntime.UNDEFINED;
+ setPrototype(ScriptRuntime.UNDEFINED);
this.targetFunction = targetFunction;
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeArray.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeArray.java Thu May 16 11:47:51 2013 +0100
@@ -418,7 +418,7 @@
long length;
if (len instanceof Integer || len instanceof Long) {
length = ((Number) len).longValue();
- if (length >= 0 && length < 0xffff_ffffL) {
+ if (length >= 0 && length < JSType.MAX_UINT) {
return new NativeArray(length);
}
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeDate.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeDate.java Thu May 16 11:47:51 2013 +0100
@@ -182,7 +182,8 @@
@Override
public String safeToString() {
- return "[Date " + toISOStringImpl(this) + "]";
+ final String str = isValidDate() ? toISOStringImpl(this) : INVALID_DATE;
+ return "[Date " + str + "]";
}
@Override
@@ -518,7 +519,7 @@
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static Object getTimezoneOffset(final Object self) {
final NativeDate nd = getNativeDate(self);
- if (nd != null) {
+ if (nd != null && nd.isValidDate()) {
final long msec = (long) nd.getTime();
return - nd.getTimeZone().getOffset(msec) / msPerMinute;
}
@@ -534,8 +535,8 @@
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static Object setTime(final Object self, final Object time) {
+ final NativeDate nd = getNativeDate(self);
final double num = timeClip(JSType.toNumber(time));
- final NativeDate nd = getNativeDate(self);
nd.setTime(num);
return num;
}
@@ -550,9 +551,7 @@
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
public static Object setMilliseconds(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, MILLISECOND, args, true);
- }
+ setFields(nd, MILLISECOND, args, true);
return nd.getTime();
}
@@ -566,9 +565,7 @@
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
public static Object setUTCMilliseconds(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, MILLISECOND, args, false);
- }
+ setFields(nd, MILLISECOND, args, false);
return nd.getTime();
}
@@ -582,9 +579,7 @@
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
public static Object setSeconds(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, SECOND, args, true);
- }
+ setFields(nd, SECOND, args, true);
return nd.getTime();
}
@@ -598,9 +593,7 @@
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
public static Object setUTCSeconds(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, SECOND, args, false);
- }
+ setFields(nd, SECOND, args, false);
return nd.getTime();
}
@@ -614,9 +607,7 @@
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
public static Object setMinutes(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, MINUTE, args, true);
- }
+ setFields(nd, MINUTE, args, true);
return nd.getTime();
}
@@ -630,9 +621,7 @@
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
public static Object setUTCMinutes(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, MINUTE, args, false);
- }
+ setFields(nd, MINUTE, args, false);
return nd.getTime();
}
@@ -646,9 +635,7 @@
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4)
public static Object setHours(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, HOUR, args, true);
- }
+ setFields(nd, HOUR, args, true);
return nd.getTime();
}
@@ -662,9 +649,7 @@
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4)
public static Object setUTCHours(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, HOUR, args, false);
- }
+ setFields(nd, HOUR, args, false);
return nd.getTime();
}
@@ -678,9 +663,7 @@
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
public static Object setDate(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, DAY, args, true);
- }
+ setFields(nd, DAY, args, true);
return nd.getTime();
}
@@ -694,9 +677,7 @@
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
public static Object setUTCDate(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, DAY, args, false);
- }
+ setFields(nd, DAY, args, false);
return nd.getTime();
}
@@ -710,9 +691,7 @@
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
public static Object setMonth(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, MONTH, args, true);
- }
+ setFields(nd, MONTH, args, true);
return nd.getTime();
}
@@ -726,9 +705,7 @@
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
public static Object setUTCMonth(final Object self, final Object... args) {
final NativeDate nd = ensureNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, MONTH, args, false);
- }
+ setFields(nd, MONTH, args, false);
return nd.getTime();
}
@@ -746,7 +723,11 @@
setFields(nd, YEAR, args, true);
} else {
final double[] d = convertArgs(args, 0, YEAR, YEAR, 3);
- nd.setTime(timeClip(utc(makeDate(makeDay(d[0], d[1], d[2]), 0), nd.getTimeZone())));
+ if (d != null) {
+ nd.setTime(timeClip(utc(makeDate(makeDay(d[0], d[1], d[2]), 0), nd.getTimeZone())));
+ } else {
+ nd.setTime(NaN);
+ }
}
return nd.getTime();
}
@@ -781,13 +762,13 @@
public static Object setYear(final Object self, final Object year) {
final NativeDate nd = getNativeDate(self);
if (isNaN(nd.getTime())) {
- return null;
+ nd.setTime(utc(0, nd.getTimeZone()));
}
final double yearNum = JSType.toNumber(year);
if (isNaN(yearNum)) {
nd.setTime(NaN);
- return nd;
+ return nd.getTime();
}
int yearInt = JSType.toInteger(yearNum);
if (0 <= yearInt && yearInt <= 99) {
@@ -795,7 +776,7 @@
}
setFields(nd, YEAR, new Object[] {yearInt}, true);
- return nd;
+ return nd.getTime();
}
/**
@@ -851,11 +832,11 @@
}
final ScriptObject sobj = (ScriptObject)selfObj;
final Object value = sobj.getDefaultValue(Number.class);
-
- final double num = (value instanceof Number) ? ((Number)value).doubleValue() : NaN;
-
- if (isInfinite(num) || isNaN(num)) {
- return null;
+ if (value instanceof Number) {
+ final double num = ((Number)value).doubleValue();
+ if (isInfinite(num) || isNaN(num)) {
+ return null;
+ }
}
try {
@@ -1297,6 +1278,10 @@
final double time = local ? nd.getLocalTime() : nd.getTime();
final double d[] = convertArgs(args, time, fieldId, start, length);
+ if (! nd.isValidDate()) {
+ return;
+ }
+
double newTime;
if (d == null) {
newTime = NaN;
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java Thu May 16 11:47:51 2013 +0100
@@ -247,7 +247,6 @@
out.println("Scope count " + ScriptObject.getScopeCount());
out.println("ScriptObject listeners added " + PropertyListenerManager.getListenersAdded());
out.println("ScriptObject listeners removed " + PropertyListenerManager.getListenersRemoved());
- out.println("ScriptObject listeners dead " + PropertyListenerManager.getListenersDead());
out.println("ScriptFunction count " + ScriptObject.getCount());
out.println("ScriptFunction invokes " + ScriptFunction.getInvokes());
out.println("ScriptFunction allocations " + ScriptFunction.getAllocations());
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeFunction.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeFunction.java Thu May 16 11:47:51 2013 +0100
@@ -81,23 +81,13 @@
Object[] args = null;
- if (ScriptObject.isArray(array)) {
- args = ((NativeArray)array).asObjectArray();
- } else if (array instanceof ScriptObject) {
+ if (array instanceof ScriptObject) {
// look for array-like object
final ScriptObject sobj = (ScriptObject)array;
final Object len = sobj.getLength();
-
- if (len == UNDEFINED || len == null) {
- throw typeError("function.apply.expects.array");
- }
+ final int n = (int)JSType.toUint32(len);
- final int n = (int)JSType.toUint32(len);
- if (n != JSType.toNumber(len)) {
- throw typeError("function.apply.expects.array");
- }
-
- args = new Object[(int)JSType.toUint32(len)];
+ args = new Object[n];
for (int i = 0; i < args.length; i++) {
args[i] = sobj.get(i);
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Thu May 16 11:47:51 2013 +0100
@@ -148,11 +148,7 @@
if (overrides instanceof ScriptObject) {
this.overrides = true;
final ScriptObject sobj = (ScriptObject)overrides;
- final Iterator<String> iter = sobj.propertyIterator();
- while (iter.hasNext()) {
- final String prop = iter.next();
- super.set(prop, sobj.get(prop), false);
- }
+ this.addBoundProperties(sobj);
} else {
this.overrides = false;
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeJSON.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeJSON.java Thu May 16 11:47:51 2013 +0100
@@ -229,7 +229,7 @@
final JSType type = JSType.of(value);
if (type == JSType.OBJECT) {
if (isArray(value)) {
- return JA((NativeArray)value, state);
+ return JA((ScriptObject)value, state);
} else if (value instanceof ScriptObject) {
return JO((ScriptObject)value, state);
}
@@ -315,7 +315,7 @@
}
// Spec: The abstract operation JA(value) serializes an array.
- private static Object JA(final NativeArray value, final StringifyState state) {
+ private static Object JA(final ScriptObject value, final StringifyState state) {
if (state.stack.containsKey(value)) {
throw typeError("JSON.stringify.cyclic");
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeRegExp.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeRegExp.java Thu May 16 11:47:51 2013 +0100
@@ -523,23 +523,28 @@
}
private RegExpResult execInner(final String string) {
+ final boolean isGlobal = regexp.isGlobal();
int start = getLastIndex();
- if (! regexp.isGlobal()) {
+ if (!isGlobal) {
start = 0;
}
if (start < 0 || start > string.length()) {
- setLastIndex(0);
+ if (isGlobal) {
+ setLastIndex(0);
+ }
return null;
}
final RegExpMatcher matcher = regexp.match(string);
if (matcher == null || !matcher.search(start)) {
- setLastIndex(0);
+ if (isGlobal) {
+ setLastIndex(0);
+ }
return null;
}
- if (regexp.isGlobal()) {
+ if (isGlobal) {
setLastIndex(matcher.end());
}
@@ -548,6 +553,22 @@
return match;
}
+ // String.prototype.split method ignores the global flag and should not update lastIndex property.
+ private RegExpResult execSplit(final String string, int start) {
+ if (start < 0 || start > string.length()) {
+ return null;
+ }
+
+ final RegExpMatcher matcher = regexp.match(string);
+ if (matcher == null || !matcher.search(start)) {
+ return null;
+ }
+
+ final RegExpResult match = new RegExpResult(string, matcher.start(), groups(matcher));
+ globalObject.setLastRegExpResult(match);
+ return match;
+ }
+
/**
* Convert java.util.regex.Matcher groups to JavaScript groups.
* That is, replace null and groups that didn't match with undefined.
@@ -600,7 +621,7 @@
* @return True if a match is found.
*/
public Object test(final String string) {
- return exec(string) != null;
+ return execInner(string) != null;
}
/**
@@ -765,35 +786,31 @@
* @return Array of substrings.
*/
Object split(final String string, final long limit) {
- return split(this, string, limit);
- }
-
- private static Object split(final NativeRegExp regexp0, final String input, final long limit) {
- final List<Object> matches = new ArrayList<>();
-
- final NativeRegExp regexp = new NativeRegExp(regexp0);
- regexp.setGlobal(true);
-
if (limit == 0L) {
return new NativeArray();
}
+ final List<Object> matches = new ArrayList<>();
+
RegExpResult match;
- final int inputLength = input.length();
+ final int inputLength = string.length();
int lastLength = -1;
+ int lastIndex = 0;
int lastLastIndex = 0;
- while ((match = regexp.execInner(input)) != null) {
- final int lastIndex = match.getIndex() + match.length();
+ while ((match = execSplit(string, lastIndex)) != null) {
+ lastIndex = match.getIndex() + match.length();
if (lastIndex > lastLastIndex) {
- matches.add(input.substring(lastLastIndex, match.getIndex()));
- if (match.getGroups().length > 1 && match.getIndex() < inputLength) {
- matches.addAll(Arrays.asList(match.getGroups()).subList(1, match.getGroups().length));
+ matches.add(string.substring(lastLastIndex, match.getIndex()));
+ final Object[] groups = match.getGroups();
+ if (groups.length > 1 && match.getIndex() < inputLength) {
+ for (int index = 1; index < groups.length && matches.size() < limit; index++) {
+ matches.add(groups[index]);
+ }
}
lastLength = match.length();
- lastLastIndex = lastIndex;
if (matches.size() >= limit) {
break;
@@ -801,8 +818,10 @@
}
// bump the index to avoid infinite loop
- if (regexp.getLastIndex() == match.getIndex()) {
- regexp.setLastIndex(match.getIndex() + 1);
+ if (lastIndex == lastLastIndex) {
+ lastIndex++;
+ } else {
+ lastLastIndex = lastIndex;
}
}
@@ -810,12 +829,12 @@
// check special case if we need to append an empty string at the
// end of the match
// if the lastIndex was the entire string
- if (lastLastIndex == input.length()) {
- if (lastLength > 0 || regexp.test("") == Boolean.FALSE) {
+ if (lastLastIndex == string.length()) {
+ if (lastLength > 0 || execSplit("", 0) == null) {
matches.add("");
}
} else {
- matches.add(input.substring(lastLastIndex, inputLength));
+ matches.add(string.substring(lastLastIndex, inputLength));
}
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java Thu May 16 11:47:51 2013 +0100
@@ -51,6 +51,7 @@
NativeRegExpExecResult(final RegExpResult result) {
setProto(Global.instance().getArrayPrototype());
+ setIsArray();
this.setArray(ArrayData.allocate(result.getGroups().clone()));
this.index = result.getIndex();
this.input = result.getInput();
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeString.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeString.java Thu May 16 11:47:51 2013 +0100
@@ -25,11 +25,11 @@
package jdk.nashorn.internal.objects;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndexNoThrow;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@@ -41,6 +41,7 @@
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Constructor;
import jdk.nashorn.internal.objects.annotations.Function;
@@ -55,7 +56,6 @@
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
-import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.runtime.linker.NashornGuards;
import jdk.nashorn.internal.runtime.linker.PrimitiveLookup;
@@ -179,7 +179,6 @@
return ((ScriptObject) Global.toObject(self)).get(key);
}
- @SuppressWarnings("unused")
private static Object get(final Object self, final int key) {
final CharSequence cs = JSType.toCharSequence(self);
if (key >= 0 && key < cs.length()) {
@@ -842,7 +841,7 @@
final long lim = (limit == UNDEFINED) ? JSType.MAX_UINT : JSType.toUint32(limit);
if (separator == UNDEFINED) {
- return new NativeArray(new Object[]{str});
+ return lim == 0 ? new NativeArray() : new NativeArray(new Object[]{str});
}
if (separator instanceof NativeRegExp) {
@@ -855,8 +854,9 @@
private static Object splitString(String str, String separator, long limit) {
if (separator.isEmpty()) {
- final Object[] array = new Object[str.length()];
- for (int i = 0; i < array.length; i++) {
+ final int length = (int) Math.min(str.length(), limit);
+ final Object[] array = new Object[length];
+ for (int i = 0; i < length; i++) {
array[i] = String.valueOf(str.charAt(i));
}
return new NativeArray(array);
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeUint32Array.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeUint32Array.java Thu May 16 11:47:51 2013 +0100
@@ -29,6 +29,7 @@
import jdk.nashorn.internal.objects.annotations.Constructor;
import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
+import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
@@ -71,17 +72,17 @@
@Override
protected long getLongImpl(final int key) {
- return getIntImpl(key) & 0xffff_ffffL;
+ return getIntImpl(key) & JSType.MAX_UINT;
}
@Override
protected double getDoubleImpl(final int key) {
- return getIntImpl(key) & 0xffff_ffffL;
+ return getIntImpl(key) & JSType.MAX_UINT;
}
@Override
protected Object getObjectImpl(final int key) {
- return getIntImpl(key) & 0xffff_ffffL;
+ return getIntImpl(key) & JSType.MAX_UINT;
}
@Override
--- a/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Thu May 16 11:47:51 2013 +0100
@@ -42,6 +42,10 @@
* function objects -- to expose properties like "prototype", "length" etc.
*/
public class ScriptFunctionImpl extends ScriptFunction {
+
+ /** Reference to constructor prototype. */
+ private Object prototype;
+
// property map for strict mode functions
private static final PropertyMap strictmodemap$;
// property map for bound functions
@@ -49,6 +53,9 @@
// property map for non-strict, non-bound functions.
private static final PropertyMap nasgenmap$;
+ // Marker object for lazily initialized prototype object
+ private static final Object LAZY_PROTOTYPE = new Object();
+
/**
* Constructor called by Nasgen generated code, no membercount, use the default map.
* Creates builtin functions only.
@@ -83,8 +90,8 @@
* @param methodHandle handle for invocation
* @param scope scope object
* @param specs specialized versions of this method, if available, null otherwise
- * @param strict are we in strict mode
- * @param builtin is this a built-in function
+ * @param isStrict are we in strict mode
+ * @param isBuiltin is this a built-in function
* @param isConstructor can the function be used as a constructor (most can; some built-ins are restricted).
*/
ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
@@ -235,10 +242,23 @@
return Global.objectPrototype();
}
+ @Override
+ public final Object getPrototype() {
+ if (prototype == LAZY_PROTOTYPE) {
+ prototype = new PrototypeObject(this);
+ }
+ return prototype;
+ }
+
+ @Override
+ public final void setPrototype(final Object prototype) {
+ this.prototype = prototype;
+ }
+
// Internals below..
private void init() {
this.setProto(Global.instance().getFunctionPrototype());
- this.setPrototype(new PrototypeObject(this));
+ this.prototype = LAZY_PROTOTYPE;
if (isStrict()) {
final ScriptFunction func = getTypeErrorThrower();
--- a/nashorn/src/jdk/nashorn/internal/parser/AbstractParser.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/parser/AbstractParser.java Thu May 16 11:47:51 2013 +0100
@@ -37,8 +37,8 @@
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.JSErrorType;
import jdk.nashorn.internal.runtime.ParserException;
+import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.regexp.RegExpFactory;
-import jdk.nashorn.internal.runtime.Source;
/**
* Base class for parsers.
@@ -197,9 +197,10 @@
*
* @param message Error message.
* @param errorToken Offending token.
+ * @return ParserException upon failure. Caller should throw and not ignore
*/
- protected final void error(final String message, final long errorToken) {
- error(JSErrorType.SYNTAX_ERROR, message, errorToken);
+ protected final ParserException error(final String message, final long errorToken) {
+ return error(JSErrorType.SYNTAX_ERROR, message, errorToken);
}
/**
@@ -208,22 +209,24 @@
* @param errorType The error type
* @param message Error message.
* @param errorToken Offending token.
+ * @return ParserException upon failure. Caller should throw and not ignore
*/
- protected final void error(final JSErrorType errorType, final String message, final long errorToken) {
+ protected final ParserException error(final JSErrorType errorType, final String message, final long errorToken) {
final int position = Token.descPosition(errorToken);
final int lineNum = source.getLine(position);
final int columnNum = source.getColumn(position);
final String formatted = ErrorManager.format(message, source, lineNum, columnNum, errorToken);
- throw new ParserException(errorType, formatted, source, lineNum, columnNum, errorToken);
+ return new ParserException(errorType, formatted, source, lineNum, columnNum, errorToken);
}
/**
* Report an error.
*
* @param message Error message.
+ * @return ParserException upon failure. Caller should throw and not ignore
*/
- protected final void error(final String message) {
- error(JSErrorType.SYNTAX_ERROR, message);
+ protected final ParserException error(final String message) {
+ return error(JSErrorType.SYNTAX_ERROR, message);
}
/**
@@ -231,13 +234,24 @@
*
* @param errorType The error type
* @param message Error message.
+ * @return ParserException upon failure. Caller should throw and not ignore
*/
- protected final void error(final JSErrorType errorType, final String message) {
+ protected final ParserException error(final JSErrorType errorType, final String message) {
// TODO - column needs to account for tabs.
final int position = Token.descPosition(token);
final int column = position - linePosition;
final String formatted = ErrorManager.format(message, source, line, column, token);
- throw new ParserException(errorType, formatted, source, line, column, token);
+ return new ParserException(errorType, formatted, source, line, column, token);
+ }
+
+ /**
+ * Report a warning to the error manager.
+ *
+ * @param errorType The error type of the warning
+ * @param message Warning message.
+ */
+ protected final void warning(final JSErrorType errorType, final String message, final long errorToken) {
+ errors.warning(error(errorType, message, errorToken));
}
/**
@@ -270,7 +284,7 @@
*/
protected final void expect(final TokenType expected) throws ParserException {
if (type != expected) {
- error(expectMessage(expected));
+ throw error(expectMessage(expected));
}
next();
@@ -285,7 +299,7 @@
*/
protected final Object expectValue(final TokenType expected) throws ParserException {
if (type != expected) {
- error(expectMessage(expected));
+ throw error(expectMessage(expected));
}
final Object value = getValue();
@@ -429,7 +443,7 @@
try {
RegExpFactory.validate(regex.getExpression(), regex.getOptions());
} catch (final ParserException e) {
- error(e.getMessage());
+ throw error(e.getMessage());
}
}
node = LiteralNode.newInstance(source, literalToken, finish, (LexerToken)value);
--- a/nashorn/src/jdk/nashorn/internal/parser/JSONParser.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/parser/JSONParser.java Thu May 16 11:47:51 2013 +0100
@@ -170,8 +170,7 @@
}
case '"':
case '\\':
- error(AbstractParser.message("unexpected.token", str));
- break;
+ throw error(AbstractParser.message("unexpected.token", str));
}
}
@@ -222,14 +221,12 @@
return new UnaryNode(source, literalToken, LiteralNode.newInstance(source, realToken, finish, (Number)value));
}
- error(AbstractParser.message("expected", "number", type.getNameOrType()));
- break;
+ throw error(AbstractParser.message("expected", "number", type.getNameOrType()));
default:
break;
}
- error(AbstractParser.message("expected", "json literal", type.getNameOrType()));
- return null;
+ throw error(AbstractParser.message("expected", "json literal", type.getNameOrType()));
}
/**
@@ -265,7 +262,7 @@
elements.add(jsonLiteral());
// Comma between array elements is mandatory in JSON.
if (type != COMMARIGHT && type != RBRACKET) {
- error(AbstractParser.message("expected", ", or ]", type.getNameOrType()));
+ throw error(AbstractParser.message("expected", ", or ]", type.getNameOrType()));
}
break;
}
@@ -306,7 +303,7 @@
// Comma between property assigments is mandatory in JSON.
if (type != RBRACE && type != COMMARIGHT) {
- error(AbstractParser.message("expected", ", or }", type.getNameOrType()));
+ throw error(AbstractParser.message("expected", ", or }", type.getNameOrType()));
}
break;
}
@@ -334,13 +331,11 @@
if (name != null) {
expect(COLON);
final Node value = jsonLiteral();
- return new PropertyNode(source, propertyToken, value.getFinish(), name, value);
+ return new PropertyNode(source, propertyToken, value.getFinish(), name, value, null, null);
}
// Raise an error.
- error(AbstractParser.message("expected", "string", type.getNameOrType()));
-
- return null;
+ throw error(AbstractParser.message("expected", "string", type.getNameOrType()));
}
}
--- a/nashorn/src/jdk/nashorn/internal/parser/Lexer.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/parser/Lexer.java Thu May 16 11:47:51 2013 +0100
@@ -57,6 +57,9 @@
*/
@SuppressWarnings("fallthrough")
public class Lexer extends Scanner {
+ private static final long MIN_INT_L = Integer.MIN_VALUE;
+ private static final long MAX_INT_L = Integer.MAX_VALUE;
+
private static final boolean XML_LITERALS = Options.getBooleanProperty("nashorn.lexer.xmlliterals");
/** Content source. */
@@ -984,27 +987,27 @@
*/
private static Number valueOf(final String valueString, final int radix) throws NumberFormatException {
try {
- return Integer.valueOf(valueString, radix);
+ final long value = Long.parseLong(valueString, radix);
+ if(value >= MIN_INT_L && value <= MAX_INT_L) {
+ return Integer.valueOf((int)value);
+ }
+ return Long.valueOf(value);
} catch (final NumberFormatException e) {
- try {
- return Long.valueOf(valueString, radix);
- } catch (final NumberFormatException e2) {
- if (radix == 10) {
- return Double.valueOf(valueString);
- }
-
- double value = 0.0;
+ if (radix == 10) {
+ return Double.valueOf(valueString);
+ }
- for (int i = 0; i < valueString.length(); i++) {
- final char ch = valueString.charAt(i);
- // Preverified, should always be a valid digit.
- final int digit = convertDigit(ch, radix);
- value *= radix;
- value += digit;
- }
+ double value = 0.0;
- return value;
+ for (int i = 0; i < valueString.length(); i++) {
+ final char ch = valueString.charAt(i);
+ // Preverified, should always be a valid digit.
+ final int digit = convertDigit(ch, radix);
+ value *= radix;
+ value += digit;
}
+
+ return value;
}
}
--- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java Thu May 16 11:47:51 2013 +0100
@@ -54,24 +54,23 @@
import static jdk.nashorn.internal.parser.TokenType.WHILE;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Stack;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.Namespace;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
+import jdk.nashorn.internal.ir.BlockLexicalContext;
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;
@@ -84,6 +83,7 @@
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LineNumberNode;
import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ObjectNode;
import jdk.nashorn.internal.ir.PropertyKey;
@@ -117,9 +117,10 @@
/** Is scripting mode. */
private final boolean scripting;
- private final LexicalContext lexicalContext = new LexicalContext();
private List<Node> functionDeclarations;
+ private final BlockLexicalContext lc = new BlockLexicalContext();
+
/** Namespace for function names where not explicitly given */
private final Namespace namespace;
@@ -146,7 +147,7 @@
*/
public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict) {
super(source, errors, strict);
- this.env = env;
+ this.env = env;
this.namespace = new Namespace(env.getNamespace());
this.scripting = env._scripting;
}
@@ -162,7 +163,7 @@
* @return function node resulting from successful parse
*/
public FunctionNode parse() {
- return parse(RUN_SCRIPT.tag());
+ return parse(RUN_SCRIPT.symbolName());
}
/**
@@ -176,7 +177,7 @@
*/
public FunctionNode parse(final String scriptName) {
final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L;
- LOG.info(this + " begin for '" + scriptName + "'");
+ LOG.info(this, " begin for '", scriptName, "'");
try {
stream = new TokenStream();
@@ -214,7 +215,7 @@
final String end = this + " end '" + scriptName + "'";
if (Timing.isEnabled()) {
Timing.accumulateTime(toString(), System.currentTimeMillis() - t0);
- LOG.info(end + "' in " + (System.currentTimeMillis() - t0) + " ms");
+ LOG.info(end, "' in ", (System.currentTimeMillis() - t0), " ms");
} else {
LOG.info(end);
}
@@ -275,8 +276,7 @@
*/
private Block newBlock() {
final Block block = new Block(source, token, Token.descPosition(token));
- lexicalContext.push(block);
- return block;
+ return lc.push(block);
}
/**
@@ -285,36 +285,60 @@
* @param ident Name of function.
* @return New block.
*/
- private FunctionNode newFunctionBlock(final IdentNode ident) {
+ private FunctionNode newFunctionNode(final long startToken, final IdentNode ident, final List<IdentNode> parameters, final FunctionNode.Kind kind) {
// Build function name.
final StringBuilder sb = new StringBuilder();
- final FunctionNode parentFunction = getFunction();
- if(parentFunction != null && !parentFunction.isProgram()) {
+ final FunctionNode parentFunction = lc.getCurrentFunction();
+ if (parentFunction != null && !parentFunction.isProgram()) {
sb.append(parentFunction.getName()).append('$');
}
- sb.append(ident != null ? ident.getName() : FUNCTION_PREFIX.tag());
+ sb.append(ident != null ? ident.getName() : FUNCTION_PREFIX.symbolName());
final String name = namespace.uniqueName(sb.toString());
- assert parentFunction != null || name.equals(RUN_SCRIPT.tag()) : "name = " + name;// must not rename runScript().
+ assert parentFunction != null || name.equals(RUN_SCRIPT.symbolName()) : "name = " + name;// must not rename runScript().
+
+ int flags = 0;
+ if (parentFunction == null) {
+ flags |= FunctionNode.IS_PROGRAM;
+ }
+ if (isStrictMode) {
+ flags |= FunctionNode.IS_STRICT;
+ }
// Start new block.
- final FunctionNode functionBlock = new FunctionNode(source, token, Token.descPosition(token), namespace, ident, name);
- if(parentFunction == null) {
- functionBlock.setProgram();
- }
- functionBlock.setStrictMode(isStrictMode);
- functionBlock.setState(errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED);
- lexicalContext.push(functionBlock);
-
- return functionBlock;
+ FunctionNode functionNode =
+ new FunctionNode(
+ source,
+ token,
+ Token.descPosition(token),
+ startToken,
+ namespace,
+ ident,
+ name,
+ parameters,
+ kind,
+ flags);
+
+ functionNode = functionNode.setState(lc, errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED);
+ lc.push(functionNode);
+ // Create new block, and just put it on the context stack, restoreFunctionNode() will associate it with the
+ // FunctionNode.
+ newBlock();
+ return functionNode;
}
/**
* Restore the current block.
*/
- private void restoreBlock(Block block) {
- lexicalContext.pop(block);
+ private Block restoreBlock(final Block block) {
+ return lc.pop(block);//.setFlag(lc, flags);
+ }
+
+ private FunctionNode restoreFunctionNode(final FunctionNode functionNode, final long lastToken) {
+ final Block newBody = restoreBlock(lc.getFunctionBody(functionNode));
+
+ return lc.pop(functionNode).setBody(lc, newBody).setLastToken(lc, lastToken);
}
/**
@@ -323,23 +347,17 @@
*/
private Block getBlock(final boolean needsBraces) {
// Set up new block. Captures LBRACE.
- final Block newBlock = newBlock();
+ Block newBlock = newBlock();
try {
- pushControlNode(newBlock);
-
// Block opening brace.
if (needsBraces) {
expect(LBRACE);
}
-
- try {
- // Accumulate block statements.
- statementList();
- } finally {
- popControlNode();
- }
+ // Accumulate block statements.
+ statementList();
+
} finally {
- restoreBlock(newBlock);
+ newBlock = restoreBlock(newBlock);
}
final int possibleEnd = Token.descPosition(token) + Token.descLength(token);
@@ -363,15 +381,12 @@
return getBlock(true);
}
// Set up new block. Captures first token.
- final Block newBlock = newBlock();
-
+ Block newBlock = newBlock();
try {
- // Accumulate statements.
statement();
} finally {
- restoreBlock(newBlock);
+ newBlock = restoreBlock(newBlock);
}
-
return newBlock;
}
@@ -382,11 +397,8 @@
private void detectSpecialFunction(final IdentNode ident) {
final String name = ident.getName();
- if (EVAL.tag().equals(name)) {
- final Iterator<FunctionNode> it = lexicalContext.getFunctions();
- if(it.hasNext()) {
- it.next().setHasEval(it);
- }
+ if (EVAL.symbolName().equals(name)) {
+ markEval(lc);
}
}
@@ -397,8 +409,8 @@
private void detectSpecialProperty(final IdentNode ident) {
final String name = ident.getName();
- if (ARGUMENTS.tag().equals(name)) {
- getFunction().setUsesArguments();
+ if (ARGUMENTS.symbolName().equals(name)) {
+ lc.setFlag(lc.getCurrentFunction(), FunctionNode.USES_ARGUMENTS);
}
}
@@ -439,7 +451,7 @@
lhs instanceof IndexNode ||
lhs instanceof IdentNode)) {
if (env._early_lvalue_error) {
- error(JSErrorType.REFERENCE_ERROR, AbstractParser.message("invalid.lvalue"), lhs.getToken());
+ throw error(JSErrorType.REFERENCE_ERROR, AbstractParser.message("invalid.lvalue"), lhs.getToken());
}
return referenceError(lhs, rhs);
}
@@ -469,144 +481,14 @@
* @return Reduced expression.
*/
private Node incDecExpression(final long firstToken, final TokenType tokenType, final Node expression, final boolean isPostfix) {
- long incDecToken = firstToken;
if (isPostfix) {
- incDecToken = Token.recast(incDecToken, tokenType == DECPREFIX ? DECPOSTFIX : INCPOSTFIX);
- }
-
- final UnaryNode node = new UnaryNode(source, incDecToken, expression);
- if (isPostfix) {
- node.setStart(expression.getStart());
- node.setFinish(Token.descPosition(incDecToken) + Token.descLength(incDecToken));
- }
-
- return node;
- }
-
- /**
- * Find a label node in the label stack.
- * @param ident Ident to find.
- * @return null or the found label node.
- */
- private LabelNode findLabel(final IdentNode ident) {
- for (final LabelNode labelNode : getFunction().getLabelStack()) {
- if (labelNode.getLabel().equals(ident)) {
- return labelNode;
- }
+ return new UnaryNode(source, Token.recast(firstToken, tokenType == DECPREFIX ? DECPOSTFIX : INCPOSTFIX), expression.getStart(), Token.descPosition(firstToken) + Token.descLength(firstToken), expression);
}
- return null;
- }
-
- /**
- * Add a label to the label stack.
- * @param labelNode Label to add.
- */
- private void pushLabel(final LabelNode labelNode) {
- getFunction().getLabelStack().push(labelNode);
- }
-
- /**
- * Remove a label from the label stack.
- */
- private void popLabel() {
- getFunction().getLabelStack().pop();
- }
-
- /**
- * Track the current nesting of controls for break and continue.
- * @param node For, while, do or switch node.
- */
- private void pushControlNode(final Node node) {
- final boolean isLoop = node instanceof WhileNode;
- final boolean isBreakable = node instanceof BreakableNode || node instanceof Block;
- final FunctionNode function = getFunction();
- function.getControlStack().push(node);
-
- for (final LabelNode labelNode : function.getLabelStack()) {
- if (isBreakable && labelNode.getBreakNode() == null) {
- labelNode.setBreakNode(node);
- }
-
- if (isLoop && labelNode.getContinueNode() == null) {
- labelNode.setContinueNode(node);
- }
- }
+ return new UnaryNode(source, firstToken, expression);
}
/**
- * Finish with control.
- */
- private void popControlNode() {
- // Get control stack.
- final Stack<Node> controlStack = getFunction().getControlStack();
-
- // Can be empty if missing brace.
- if (!controlStack.isEmpty()) {
- controlStack.pop();
- }
- }
-
- private void popControlNode(final Node node) {
- // Get control stack.
- final Stack<Node> controlStack = getFunction().getControlStack();
-
- // Can be empty if missing brace.
- if (!controlStack.isEmpty() && controlStack.peek() == node) {
- controlStack.pop();
- }
- }
-
- private boolean isInWithBlock() {
- final Stack<Node> controlStack = getFunction().getControlStack();
- for (int i = controlStack.size() - 1; i >= 0; i--) {
- final Node node = controlStack.get(i);
-
- if (node instanceof WithNode) {
- return true;
- }
- }
-
- return false;
- }
-
- private <T extends Node> T findControl(final Class<T> ctype) {
- final Stack<Node> controlStack = getFunction().getControlStack();
- for (int i = controlStack.size() - 1; i >= 0; i--) {
- final Node node = controlStack.get(i);
-
- if (ctype.isAssignableFrom(node.getClass())) {
- return ctype.cast(node);
- }
- }
-
- return null;
- }
-
- private <T extends Node> List<T> findControls(final Class<T> ctype, final Node to) {
- final List<T> nodes = new ArrayList<>();
- final Stack<Node> controlStack = getFunction().getControlStack();
- for (int i = controlStack.size() - 1; i >= 0; i--) {
- final Node node = controlStack.get(i);
-
- if (to == node) {
- break; //stop looking
- }
-
- if (ctype.isAssignableFrom(node.getClass())) {
- nodes.add(ctype.cast(node));
- }
- }
-
- return nodes;
- }
-
- private <T extends Node> int countControls(final Class<T> ctype, final Node to) {
- return findControls(ctype, to).size();
- }
-
-
- /**
* -----------------------------------------------------------------------
*
* Grammar based on
@@ -630,18 +512,23 @@
final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength());
// Set up the script to append elements.
- final FunctionNode script = newFunctionBlock(new IdentNode(source, functionToken, Token.descPosition(functionToken), scriptName));
-
- script.setKind(FunctionNode.Kind.SCRIPT);
- script.setFirstToken(functionToken);
+ FunctionNode script = newFunctionNode(
+ functionToken,
+ new IdentNode(source, functionToken, Token.descPosition(functionToken), scriptName),
+ new ArrayList<IdentNode>(),
+ FunctionNode.Kind.SCRIPT);
+
functionDeclarations = new ArrayList<>();
sourceElements();
- script.prependStatements(functionDeclarations);
+ addFunctionDeclarations(script);
functionDeclarations = null;
+
expect(EOF);
- script.setLastToken(token);
+
script.setFinish(source.getLength() - 1);
+ script = restoreFunctionNode(script, token); //commit code
+ script = script.setBody(lc, script.getBody().setNeedsScope(lc));
return script;
}
@@ -670,24 +557,6 @@
}
/**
- * Return last node in a statement list.
- *
- * @param statements Statement list.
- *
- * @return Last (non-debug) statement or null if empty block.
- */
- private static Node lastStatement(final List<Node> statements) {
- for (int lastIndex = statements.size() - 1; lastIndex >= 0; lastIndex--) {
- final Node node = statements.get(lastIndex);
- if (!node.isDebug()) {
- return node;
- }
- }
-
- return null;
- }
-
- /**
* SourceElements :
* SourceElement
* SourceElements SourceElement
@@ -716,7 +585,7 @@
// check for directive prologues
if (checkDirective) {
// skip any debug statement like line number to get actual first line
- final Node lastStatement = lastStatement(getBlock().getStatements());
+ final Node lastStatement = lc.getLastStatement();
// get directive prologue, if any
final String directive = getDirective(lastStatement);
@@ -736,8 +605,8 @@
// handle use strict directive
if ("use strict".equals(directive)) {
isStrictMode = true;
- final FunctionNode function = getFunction();
- function.setStrictMode(true);
+ final FunctionNode function = lc.getCurrentFunction();
+ lc.setFlag(lc.getCurrentFunction(), FunctionNode.IS_STRICT);
// We don't need to check these, if lexical environment is already strict
if (!oldStrictMode && directiveStmts != null) {
@@ -759,7 +628,7 @@
}
}
} catch (final Exception e) {
- // Recover parsing.
+ //recover parsing
recover(e);
}
@@ -806,14 +675,13 @@
if (type == FUNCTION) {
// As per spec (ECMA section 12), function declarations as arbitrary statement
// is not "portable". Implementation can issue a warning or disallow the same.
- if (isStrictMode && !topLevel) {
- error(AbstractParser.message("strict.no.func.here"), token);
- }
functionExpression(true, topLevel);
return;
}
- getBlock().addStatement(lineNumberNode);
+ if (lineNumberNode != null) {
+ appendStatement(lineNumberNode);
+ }
switch (type) {
case LBRACE:
@@ -893,13 +761,9 @@
* Parse a statement block.
*/
private void block() {
- // Get statements in block.
final Block newBlock = getBlock(true);
-
// Force block execution.
- final ExecuteNode executeNode = new ExecuteNode(source, newBlock.getToken(), finish, newBlock);
-
- getBlock().addStatement(executeNode);
+ appendStatement(new ExecuteNode(source, newBlock.getToken(), finish, newBlock));
}
/**
@@ -942,7 +806,7 @@
switch (ident.getName()) {
case "eval":
case "arguments":
- error(AbstractParser.message("strict.name", ident.getName(), contextString), ident.getToken());
+ throw error(AbstractParser.message("strict.name", ident.getName(), contextString), ident.getToken());
default:
break;
}
@@ -995,8 +859,7 @@
// Allocate var node.
final VarNode var = new VarNode(source, varToken, finish, name, init);
vars.add(var);
- // Add to current block.
- getBlock().addStatement(var);
+ appendStatement(var);
if (type != COMMARIGHT) {
break;
@@ -1009,7 +872,7 @@
boolean semicolon = type == SEMICOLON;
endOfLine();
if (semicolon) {
- getBlock().setFinish(finish);
+ lc.getCurrentBlock().setFinish(finish);
}
}
@@ -1026,8 +889,7 @@
*/
private void emptyStatement() {
if (env._empty_statements) {
- getBlock().addStatement(new EmptyNode(source, token,
- Token.descPosition(token) + Token.descLength(token)));
+ appendStatement(new EmptyNode(source, token, Token.descPosition(token) + Token.descLength(token)));
}
// SEMICOLON checked in caller.
@@ -1052,7 +914,7 @@
ExecuteNode executeNode = null;
if (expression != null) {
executeNode = new ExecuteNode(source, expressionToken, finish, expression);
- getBlock().addStatement(executeNode);
+ appendStatement(executeNode);
} else {
expect(null);
}
@@ -1061,7 +923,7 @@
if (executeNode != null) {
executeNode.setFinish(finish);
- getBlock().setFinish(finish);
+ lc.getCurrentBlock().setFinish(finish);
}
}
@@ -1081,29 +943,17 @@
next();
expect(LPAREN);
-
- // Get the test expression.
final Node test = expression();
-
expect(RPAREN);
-
- // Get the pass statement.
final Block pass = getStatement();
- // Assume no else.
Block fail = null;
-
if (type == ELSE) {
next();
-
- // Get the else block.
fail = getStatement();
}
- // Construct and add new if node.
- final IfNode ifNode = new IfNode(source, ifToken, fail != null ? fail.getFinish() : pass.getFinish(), test, pass, fail);
-
- getBlock().addStatement(ifNode);
+ appendStatement(new IfNode(source, ifToken, fail != null ? fail.getFinish() : pass.getFinish(), test, pass, fail));
}
/**
@@ -1120,12 +970,12 @@
*/
private void forStatement() {
// Create FOR node, capturing FOR token.
- final ForNode forNode = new ForNode(source, token, Token.descPosition(token));
-
- pushControlNode(forNode);
+ ForNode forNode = new ForNode(source, token, Token.descPosition(token), null, null, null, null, ForNode.IS_FOR);
+
// Set up new block for scope of vars. Captures first token.
- final Block outer = newBlock();
+ Block outer = newBlock();
+ lc.push(forNode);
try {
// FOR tested in caller.
@@ -1134,31 +984,97 @@
// Nashorn extension: for each expression.
// iterate property values rather than property names.
if (!env._no_syntax_extensions && type == IDENT && "each".equals(getValue())) {
- forNode.setIsForEach();
+ forNode = forNode.setIsForEach(lc);
next();
}
expect(LPAREN);
- /// Capture control information.
- forControl(forNode);
+ List<VarNode> vars = null;
+
+ switch (type) {
+ case VAR:
+ // Var statements captured in for outer block.
+ vars = variableStatement(false);
+ break;
+ case SEMICOLON:
+ break;
+ default:
+ final Node expression = expression(unaryExpression(), COMMARIGHT.getPrecedence(), true);
+ forNode = forNode.setInit(lc, expression);
+ break;
+ }
+
+ switch (type) {
+ case SEMICOLON:
+ // for (init; test; modify)
+ expect(SEMICOLON);
+ if (type != SEMICOLON) {
+ forNode = forNode.setTest(lc, expression());
+ }
+ expect(SEMICOLON);
+ if (type != RPAREN) {
+ forNode = forNode.setModify(lc, expression());
+ }
+ break;
+
+ case IN:
+ forNode = forNode.setIsForIn(lc);
+ if (vars != null) {
+ // for (var i in obj)
+ if (vars.size() == 1) {
+ forNode = forNode.setInit(lc, new IdentNode(vars.get(0).getName()));
+ } else {
+ // for (var i, j in obj) is invalid
+ throw error(AbstractParser.message("many.vars.in.for.in.loop"), vars.get(1).getToken());
+ }
+
+ } else {
+ // for (expr in obj)
+ final Node init = forNode.getInit();
+ assert init != null : "for..in init expression can not be null here";
+
+ // check if initial expression is a valid L-value
+ if (!(init instanceof AccessNode ||
+ init instanceof IndexNode ||
+ init instanceof IdentNode)) {
+ throw error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
+ }
+
+ if (init instanceof IdentNode) {
+ if (!checkIdentLValue((IdentNode)init)) {
+ throw error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
+ }
+ verifyStrictIdent((IdentNode)init, "for-in iterator");
+ }
+ }
+
+ next();
+
+ // Get the collection expression.
+ forNode = forNode.setModify(lc, expression());
+ break;
+
+ default:
+ expect(SEMICOLON);
+ break;
+ }
expect(RPAREN);
// Set the for body.
final Block body = getStatement();
- forNode.setBody(body);
+ forNode = forNode.setBody(lc, body);
forNode.setFinish(body.getFinish());
outer.setFinish(body.getFinish());
- // Add for to current block.
- getBlock().addStatement(forNode);
+ appendStatement(forNode);
} finally {
- restoreBlock(outer);
- popControlNode();
+ lc.pop(forNode);
+ outer = restoreBlock(outer);
}
- getBlock().addStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
+ appendStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
}
/**
@@ -1175,87 +1091,7 @@
* comprehensions.
* @param forNode Owning FOR.
*/
- private void forControl(final ForNode forNode) {
- List<VarNode> vars = null;
-
- switch (type) {
- case VAR:
- // Var statements captured in for outer block.
- vars = variableStatement(false);
- break;
-
- case SEMICOLON:
- break;
-
- default:
- final Node expression = expression(unaryExpression(), COMMARIGHT.getPrecedence(), true);
- forNode.setInit(expression);
- }
-
- switch (type) {
- case SEMICOLON:
- // for (init; test; modify)
- expect(SEMICOLON);
-
- // Get the test expression.
- if (type != SEMICOLON) {
- forNode.setTest(expression());
- }
-
- expect(SEMICOLON);
-
- // Get the modify expression.
- if (type != RPAREN) {
- final Node expression = expression();
- forNode.setModify(expression);
- }
-
- break;
-
- case IN:
- forNode.setIsForIn();
-
- if (vars != null) {
- // for (var i in obj)
- if (vars.size() == 1) {
- forNode.setInit(new IdentNode(vars.get(0).getName()));
- } else {
- // for (var i, j in obj) is invalid
- error(AbstractParser.message("many.vars.in.for.in.loop"), vars.get(1).getToken());
- }
-
- } else {
- // for (expr in obj)
- final Node init = forNode.getInit();
- assert init != null : "for..in init expression can not be null here";
-
- // check if initial expression is a valid L-value
- if (!(init instanceof AccessNode ||
- init instanceof IndexNode ||
- init instanceof IdentNode)) {
- error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
- }
-
- if (init instanceof IdentNode) {
- if (!checkIdentLValue((IdentNode)init)) {
- error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
- }
- verifyStrictIdent((IdentNode)init, "for-in iterator");
- }
- }
-
- next();
-
- // Get the collection expression.
- forNode.setModify(expression());
- break;
-
- default:
- expect(SEMICOLON);
- break;
- }
-
- }
+
/**
* ...IterationStatement :
@@ -1274,27 +1110,17 @@
next();
// Construct WHILE node.
- final WhileNode whileNode = new WhileNode(source, whileToken, Token.descPosition(whileToken));
- pushControlNode(whileNode);
+ WhileNode whileNode = new WhileNode(source, whileToken, Token.descPosition(whileToken), false);
+ lc.push(whileNode);
try {
expect(LPAREN);
-
- // Get the test expression.
- final Node test = expression();
- whileNode.setTest(test);
-
+ whileNode = whileNode.setTest(lc, expression());
expect(RPAREN);
-
- // Get WHILE body.
- final Block statements = getStatement();
- whileNode.setBody(statements);
- whileNode.setFinish(statements.getFinish());
-
- // Add WHILE node.
- getBlock().addStatement(whileNode);
+ whileNode = whileNode.setBody(lc, getStatement());
+ appendStatement(whileNode);
} finally {
- popControlNode();
+ lc.pop(whileNode);
}
}
@@ -1314,34 +1140,25 @@
// DO tested in the caller.
next();
- final WhileNode doWhileNode = new DoWhileNode(source, doToken, Token.descPosition(doToken));
- pushControlNode(doWhileNode);
+ WhileNode doWhileNode = new WhileNode(source, doToken, Token.descPosition(doToken), true);
+ lc.push(doWhileNode);
try {
// Get DO body.
- final Block statements = getStatement();
- doWhileNode.setBody(statements);
+ doWhileNode = doWhileNode.setBody(lc, getStatement());
expect(WHILE);
-
expect(LPAREN);
-
- // Get the test expression.
- final Node test = expression();
- doWhileNode.setTest(test);
-
+ doWhileNode = doWhileNode.setTest(lc, expression());
expect(RPAREN);
if (type == SEMICOLON) {
endOfLine();
}
-
doWhileNode.setFinish(finish);
-
- // Add DO node.
- getBlock().addStatement(doWhileNode);
+ appendStatement(doWhileNode);
} finally {
- popControlNode();
+ lc.pop(doWhileNode);
}
}
@@ -1370,28 +1187,26 @@
default:
final IdentNode ident = getIdent();
- labelNode = findLabel(ident);
+ labelNode = lc.findLabel(ident.getName());
if (labelNode == null) {
- error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
+ throw error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
}
break;
}
- final Node targetNode = labelNode != null ? labelNode.getContinueNode() : findControl(WhileNode.class);
+ final IdentNode label = labelNode == null ? null : labelNode.getLabel();
+ final LoopNode targetNode = lc.getContinueTo(label);
if (targetNode == null) {
- error(AbstractParser.message("illegal.continue.stmt"), continueToken);
+ throw error(AbstractParser.message("illegal.continue.stmt"), continueToken);
}
endOfLine();
// Construct and add CONTINUE node.
- final ContinueNode continueNode = new ContinueNode(source, continueToken, finish, labelNode, targetNode, findControl(TryNode.class));
- continueNode.setScopeNestingLevel(countControls(WithNode.class, targetNode));
-
- getBlock().addStatement(continueNode);
+ appendStatement(new ContinueNode(source, continueToken, finish, label == null ? null : new IdentNode(label)));
}
/**
@@ -1418,28 +1233,27 @@
default:
final IdentNode ident = getIdent();
- labelNode = findLabel(ident);
+ labelNode = lc.findLabel(ident.getName());
if (labelNode == null) {
- error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
+ throw error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
}
break;
}
- final Node targetNode = labelNode != null ? labelNode.getBreakNode() : findControl(BreakableNode.class);
-
+ //either an explicit label - then get its node or just a "break" - get first breakable
+ //targetNode is what we are breaking out from.
+ final IdentNode label = labelNode == null ? null : labelNode.getLabel();
+ final BreakableNode targetNode = lc.getBreakable(label);
if (targetNode == null) {
- error(AbstractParser.message("illegal.break.stmt"), breakToken);
+ throw error(AbstractParser.message("illegal.break.stmt"), breakToken);
}
endOfLine();
// Construct and add BREAK node.
- final BreakNode breakNode = new BreakNode(source, breakToken, finish, labelNode, targetNode, findControl(TryNode.class));
- breakNode.setScopeNestingLevel(countControls(WithNode.class, targetNode));
-
- getBlock().addStatement(breakNode);
+ appendStatement(new BreakNode(source, breakToken, finish, label == null ? null : new IdentNode(label)));
}
/**
@@ -1452,8 +1266,8 @@
*/
private void returnStatement() {
// check for return outside function
- if (getFunction().getKind() == FunctionNode.Kind.SCRIPT) {
- error(AbstractParser.message("invalid.return"));
+ if (lc.getCurrentFunction().getKind() == FunctionNode.Kind.SCRIPT) {
+ throw error(AbstractParser.message("invalid.return"));
}
// Capture RETURN token.
@@ -1478,8 +1292,7 @@
endOfLine();
// Construct and add RETURN node.
- final ReturnNode returnNode = new ReturnNode(source, returnToken, finish, expression, findControl(TryNode.class));
- getBlock().addStatement(returnNode);
+ appendStatement(new ReturnNode(source, returnToken, finish, expression));
}
/**
@@ -1513,8 +1326,7 @@
endOfLine();
// Construct and add YIELD node.
- final ReturnNode yieldNode = new ReturnNode(source, yieldToken, finish, expression, findControl(TryNode.class));
- getBlock().addStatement(yieldNode);
+ appendStatement(new ReturnNode(source, yieldToken, finish, expression));
}
/**
@@ -1533,35 +1345,23 @@
// ECMA 12.10.1 strict mode restrictions
if (isStrictMode) {
- error(AbstractParser.message("strict.no.with"), withToken);
+ throw error(AbstractParser.message("strict.no.with"), withToken);
}
// Get WITH expression.
- final WithNode withNode = new WithNode(source, withToken, finish, null, null);
- final Iterator<FunctionNode> it = lexicalContext.getFunctions();
- if(it.hasNext()) {
- it.next().setHasWith(it);
- }
+ WithNode withNode = new WithNode(source, withToken, finish);
try {
- pushControlNode(withNode);
-
+ lc.push(withNode);
expect(LPAREN);
-
- final Node expression = expression();
- withNode.setExpression(expression);
-
+ withNode = withNode.setExpression(lc, expression());
expect(RPAREN);
-
- // Get WITH body.
- final Block statements = getStatement();
- withNode.setBody(statements);
- withNode.setFinish(finish);
+ withNode = withNode.setBody(lc, getStatement());
} finally {
- popControlNode(withNode);
+ lc.pop(withNode);
}
- getBlock().addStatement(withNode);
+ appendStatement(withNode);
}
/**
@@ -1587,22 +1387,17 @@
* Parse SWITCH statement.
*/
private void switchStatement() {
- // Capture SWITCH token.
final long switchToken = token;
// SWITCH tested in caller.
next();
// Create and add switch statement.
- final SwitchNode switchNode = new SwitchNode(source, switchToken, Token.descPosition(switchToken));
- pushControlNode(switchNode);
+ SwitchNode switchNode = new SwitchNode(source, switchToken, Token.descPosition(switchToken), null, new ArrayList<CaseNode>(), null);
+ lc.push(switchNode);
try {
expect(LPAREN);
-
- // Get switch expression.
- final Node switchExpression = expression();
- switchNode.setExpression(switchExpression);
-
+ switchNode = switchNode.setExpression(lc, expression());
expect(RPAREN);
expect(LBRACE);
@@ -1619,19 +1414,14 @@
switch (type) {
case CASE:
next();
-
- // Get case expression.
caseExpression = expression();
-
break;
case DEFAULT:
if (defaultCase != null) {
- error(AbstractParser.message("duplicate.default.in.switch"));
+ throw error(AbstractParser.message("duplicate.default.in.switch"));
}
-
next();
-
break;
default:
@@ -1654,16 +1444,13 @@
cases.add(caseNode);
}
- switchNode.setCases(cases);
- switchNode.setDefaultCase(defaultCase);
-
+ switchNode = switchNode.setCases(lc, cases, defaultCase);
next();
-
switchNode.setFinish(finish);
- getBlock().addStatement(switchNode);
+ appendStatement(switchNode);
} finally {
- popControlNode();
+ lc.pop(switchNode);
}
}
@@ -1683,23 +1470,19 @@
expect(COLON);
- if (findLabel(ident) != null) {
- error(AbstractParser.message("duplicate.label", ident.getName()), labelToken);
+ if (lc.findLabel(ident.getName()) != null) {
+ throw error(AbstractParser.message("duplicate.label", ident.getName()), labelToken);
}
+ LabelNode labelNode = new LabelNode(source, labelToken, finish, ident, null);
try {
- // Create and add label.
- final LabelNode labelNode = new LabelNode(source, labelToken, finish, ident, null);
- pushLabel(labelNode);
- // Get and save body.
- final Block statements = getStatement();
- labelNode.setBody(statements);
+ lc.push(labelNode);
+ labelNode = labelNode.setBody(lc, getStatement());
labelNode.setFinish(finish);
-
- getBlock().addStatement(labelNode);
+ appendStatement(labelNode);
} finally {
- // Remove label.
- popLabel();
+ assert lc.peek() instanceof LabelNode;
+ lc.pop(labelNode);
}
}
@@ -1732,14 +1515,12 @@
}
if (expression == null) {
- error(AbstractParser.message("expected.operand", type.getNameOrType()));
+ throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
}
endOfLine();
- // Construct and add THROW node.
- final ThrowNode throwNode = new ThrowNode(source, throwToken, finish, expression, findControl(TryNode.class));
- getBlock().addStatement(throwNode);
+ appendStatement(new ThrowNode(source, throwToken, finish, expression));
}
/**
@@ -1766,28 +1547,18 @@
next();
// Container block needed to act as target for labeled break statements
- final Block outer = newBlock();
- pushControlNode(outer);
+ Block outer = newBlock();
// Create try.
- final TryNode tryNode = new TryNode(source, tryToken, Token.descPosition(tryToken), findControl(TryNode.class));
- pushControlNode(tryNode);
try {
- // Get TRY body.
- final Block tryBody = getBlock(true);
-
- // Prepare to accumulate catches.
+ final Block tryBody = getBlock(true);
final List<Block> catchBlocks = new ArrayList<>();
while (type == CATCH) {
- // Capture CATCH token.
final long catchToken = token;
next();
-
expect(LPAREN);
-
- // Get exception ident.
final IdentNode exception = getIdent();
// ECMA 12.4.1 strict mode restrictions
@@ -1795,28 +1566,23 @@
// Check for conditional catch.
Node ifExpression = null;
-
if (type == IF) {
next();
-
// Get the exception condition.
ifExpression = expression();
}
expect(RPAREN);
- final Block catchBlock = newBlock();
+ Block catchBlock = newBlock();
try {
-
// Get CATCH body.
final Block catchBody = getBlock(true);
-
- // Create and add catch.
final CatchNode catchNode = new CatchNode(source, catchToken, finish, exception, ifExpression, catchBody);
- getBlock().addStatement(catchNode);
+ appendStatement(catchNode);
+ } finally {
+ catchBlock = restoreBlock(catchBlock);
catchBlocks.add(catchBlock);
- } finally {
- restoreBlock(catchBlock);
}
// If unconditional catch then should to be the end.
@@ -1825,38 +1591,32 @@
}
}
- popControlNode();
-
// Prepare to capture finally statement.
Block finallyStatements = null;
if (type == FINALLY) {
next();
-
- // Get FINALLY body.
finallyStatements = getBlock(true);
}
// Need at least one catch or a finally.
if (catchBlocks.isEmpty() && finallyStatements == null) {
- error(AbstractParser.message("missing.catch.or.finally"), tryToken);
+ throw error(AbstractParser.message("missing.catch.or.finally"), tryToken);
}
- tryNode.setBody(tryBody);
- tryNode.setCatchBlocks(catchBlocks);
- tryNode.setFinallyBody(finallyStatements);
+ final TryNode tryNode = new TryNode(source, tryToken, Token.descPosition(tryToken), tryBody, catchBlocks, finallyStatements);
+ // Add try.
+ assert lc.peek() == outer;
+ appendStatement(tryNode);
+
tryNode.setFinish(finish);
outer.setFinish(finish);
- // Add try.
- outer.addStatement(tryNode);
} finally {
- popControlNode(tryNode);
- restoreBlock(outer);
- popControlNode(outer);
+ outer = restoreBlock(outer);
}
- getBlock().addStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
+ appendStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
}
/**
@@ -1872,11 +1632,8 @@
final long debuggerToken = token;
// DEBUGGER tested in caller.
next();
-
endOfLine();
-
- final RuntimeNode runtimeNode = new RuntimeNode(source, debuggerToken, finish, RuntimeNode.Request.DEBUGGER, new ArrayList<Node>());
- getBlock().addStatement(runtimeNode);
+ appendStatement(new RuntimeNode(source, debuggerToken, finish, RuntimeNode.Request.DEBUGGER, new ArrayList<Node>()));
}
/**
@@ -1912,7 +1669,7 @@
return ident;
case OCTAL:
if (isStrictMode) {
- error(AbstractParser.message("strict.no.octal"), token);
+ throw error(AbstractParser.message("strict.no.octal"), token);
}
case STRING:
case ESCSTRING:
@@ -2036,7 +1793,7 @@
default:
if (!elision) {
- error(AbstractParser.message("expected.comma", type.getNameOrType()));
+ throw error(AbstractParser.message("expected.comma", type.getNameOrType()));
}
// Add expression element.
final Node expression = assignmentExpression(false);
@@ -2077,8 +1834,8 @@
// Object context.
// Prepare to accumulate elements.
- final List<Node> elements = new ArrayList<>();
- final Map<Object, PropertyNode> map = new HashMap<>();
+ // final List<Node> elements = new ArrayList<>();
+ final Map<String, PropertyNode> map = new LinkedHashMap<>();
// Create a block for the object literal.
boolean commaSeen = true;
@@ -2096,25 +1853,30 @@
default:
if (!commaSeen) {
- error(AbstractParser.message("expected.comma", type.getNameOrType()));
- }
-
- commaSeen = false;
- // Get and add the next property.
- final PropertyNode property = propertyAssignment();
- final Object key = property.getKeyName();
- final PropertyNode existingProperty = map.get(key);
-
- if (existingProperty != null) {
+ throw error(AbstractParser.message("expected.comma", type.getNameOrType()));
+ }
+
+ commaSeen = false;
+ // Get and add the next property.
+ final PropertyNode property = propertyAssignment();
+ final String key = property.getKeyName();
+ final PropertyNode existingProperty = map.get(key);
+
+ if (existingProperty == null) {
+ map.put(key, property);
+ // elements.add(property);
+ break;
+ }
+
// ECMA section 11.1.5 Object Initialiser
// point # 4 on property assignment production
- final Node value = property.getValue();
- final Node getter = property.getGetter();
- final Node setter = property.getSetter();
-
- final Node prevValue = existingProperty.getValue();
- final Node prevGetter = existingProperty.getGetter();
- final Node prevSetter = existingProperty.getSetter();
+ final Node value = property.getValue();
+ final FunctionNode getter = property.getGetter();
+ final FunctionNode setter = property.getSetter();
+
+ final Node prevValue = existingProperty.getValue();
+ final FunctionNode prevGetter = existingProperty.getGetter();
+ final FunctionNode prevSetter = existingProperty.getSetter();
boolean redefinitionOk = true;
// ECMA 11.1.5 strict mode restrictions
@@ -2125,7 +1887,7 @@
}
final boolean isPrevAccessor = prevGetter != null || prevSetter != null;
- final boolean isAccessor = getter != null || setter != null;
+ final boolean isAccessor = getter != null || setter != null;
// data property redefined as accessor property
if (prevValue != null && isAccessor) {
@@ -2145,40 +1907,33 @@
}
if (!redefinitionOk) {
- error(AbstractParser.message("property.redefinition", key.toString()), property.getToken());
+ throw error(AbstractParser.message("property.redefinition", key.toString()), property.getToken());
}
+ PropertyNode newProperty = existingProperty;
if (value != null) {
- final Node existingValue = existingProperty.getValue();
-
- if (existingValue == null) {
- existingProperty.setValue(value);
+ if (prevValue == null) {
+ map.put(key, newProperty = newProperty.setValue(value));
} else {
- final long propertyToken = Token.recast(existingProperty.getToken(), COMMARIGHT);
- existingProperty.setValue(new BinaryNode(source, propertyToken, existingValue, value));
+ final long propertyToken = Token.recast(newProperty.getToken(), COMMARIGHT);
+ map.put(key, newProperty = newProperty.setValue(new BinaryNode(source, propertyToken, prevValue, value)));
}
- existingProperty.setGetter(null);
- existingProperty.setSetter(null);
+ map.put(key, newProperty = newProperty.setGetter(null).setSetter(null));
}
if (getter != null) {
- existingProperty.setGetter(getter);
+ map.put(key, newProperty = newProperty.setGetter(getter));
}
if (setter != null) {
- existingProperty.setSetter(setter);
+ map.put(key, newProperty = newProperty.setSetter(setter));
}
- } else {
- map.put(key, property);
- elements.add(property);
- }
-
- break;
+ break;
}
}
- return new ObjectNode(source, objectToken, finish, elements);
+ return new ObjectNode(source, objectToken, finish, new ArrayList<Node>(map.values()));
}
/**
@@ -2198,7 +1953,7 @@
return getIdent();
case OCTAL:
if (isStrictMode) {
- error(AbstractParser.message("strict.no.octal"), token);
+ throw error(AbstractParser.message("strict.no.octal"), token);
}
case STRING:
case ESCSTRING:
@@ -2235,8 +1990,6 @@
final long propertyToken = token;
FunctionNode functionNode;
- List<IdentNode> parameters;
- PropertyNode propertyNode;
PropertyKey propertyName;
if (type == IDENT) {
@@ -2253,11 +2006,8 @@
final IdentNode getNameNode = new IdentNode(source, ((Node)getIdent).getToken(), finish, "get " + getterName);
expect(LPAREN);
expect(RPAREN);
- parameters = new ArrayList<>();
- functionNode = functionBody(getSetToken, getNameNode, parameters, FunctionNode.Kind.GETTER);
- propertyNode = new PropertyNode(source, propertyToken, finish, getIdent, null);
- propertyNode.setGetter(functionNode);
- return propertyNode;
+ functionNode = functionBody(getSetToken, getNameNode, new ArrayList<IdentNode>(), FunctionNode.Kind.GETTER);
+ return new PropertyNode(source, propertyToken, finish, getIdent, null, functionNode, null);
case "set":
final PropertyKey setIdent = propertyName();
@@ -2267,12 +2017,10 @@
final IdentNode argIdent = getIdent();
verifyStrictIdent(argIdent, "setter argument");
expect(RPAREN);
- parameters = new ArrayList<>();
+ List<IdentNode> parameters = new ArrayList<>();
parameters.add(argIdent);
functionNode = functionBody(getSetToken, setNameNode, parameters, FunctionNode.Kind.SETTER);
- propertyNode = new PropertyNode(source, propertyToken, finish, setIdent, null);
- propertyNode.setSetter(functionNode);
- return propertyNode;
+ return new PropertyNode(source, propertyToken, finish, setIdent, null, null, functionNode);
default:
break;
@@ -2286,9 +2034,7 @@
expect(COLON);
- final Node value = assignmentExpression(false);
- propertyNode = new PropertyNode(source, propertyToken, finish, propertyName, value);
- return propertyNode;
+ return new PropertyNode(source, propertyToken, finish, propertyName, assignmentExpression(false), null, null);
}
/**
@@ -2321,9 +2067,6 @@
}
lhs = new CallNode(source, callToken, finish, lhs, arguments);
- if (isInWithBlock()) {
- ((CallNode)lhs).setInWithBlock();
- }
}
loop:
@@ -2338,9 +2081,6 @@
// Create call node.
lhs = new CallNode(source, callToken, finish, lhs, arguments);
- if (isInWithBlock()) {
- ((CallNode)lhs).setInWithBlock();
- }
break;
@@ -2420,9 +2160,6 @@
}
final CallNode callNode = new CallNode(source, constructor.getToken(), finish, constructor, arguments);
- if (isInWithBlock()) {
- callNode.setInWithBlock();
- }
return new UnaryNode(source, newToken, callNode);
}
@@ -2482,8 +2219,7 @@
case PERIOD:
if (lhs == null) {
- error(AbstractParser.message("expected.operand", type.getNameOrType()));
- return null;
+ throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
}
next();
@@ -2585,29 +2321,33 @@
}
expect(LPAREN);
-
final List<IdentNode> parameters = formalParameterList();
-
expect(RPAREN);
- final FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL);
+ FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL);
if (isStatement) {
- if(topLevel) {
- functionNode.setIsDeclared();
+ if (topLevel) {
+ functionNode = functionNode.setFlag(lc, FunctionNode.IS_DECLARED);
+ } else if (isStrictMode) {
+ throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("strict.no.func.decl.here"), functionToken);
+ } else if (env._function_statement == ScriptEnvironment.FunctionStatementBehavior.ERROR) {
+ throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("no.func.decl.here"), functionToken);
+ } else if (env._function_statement == ScriptEnvironment.FunctionStatementBehavior.WARNING) {
+ warning(JSErrorType.SYNTAX_ERROR, AbstractParser.message("no.func.decl.here.warn"), functionToken);
}
- if(ARGUMENTS.tag().equals(name.getName())) {
- getFunction().setDefinesArguments();
+ if (ARGUMENTS.symbolName().equals(name.getName())) {
+ lc.setFlag(lc.getCurrentFunction(), FunctionNode.DEFINES_ARGUMENTS);
}
}
if (isAnonymous) {
- functionNode.setIsAnonymous();
+ functionNode = functionNode.setFlag(lc, FunctionNode.IS_ANONYMOUS);
}
final int arity = parameters.size();
- final boolean strict = functionNode.isStrictMode();
+ final boolean strict = functionNode.isStrict();
if (arity > 1) {
final HashSet<String> parametersSet = new HashSet<>(arity);
@@ -2615,39 +2355,37 @@
final IdentNode parameter = parameters.get(i);
String parameterName = parameter.getName();
- if (ARGUMENTS.tag().equals(parameterName)) {
- functionNode.setDefinesArguments();
+ if (ARGUMENTS.symbolName().equals(parameterName)) {
+ functionNode = functionNode.setFlag(lc, FunctionNode.DEFINES_ARGUMENTS);
}
if (parametersSet.contains(parameterName)) {
// redefinition of parameter name
if (strict) {
- error(AbstractParser.message("strict.param.redefinition", parameterName), parameter.getToken());
- } else {
- // rename in non-strict mode
- parameterName = functionNode.uniqueName(parameterName);
- final long parameterToken = parameter.getToken();
- parameters.set(i, new IdentNode(source, parameterToken, Token.descPosition(parameterToken), functionNode.uniqueName(parameterName)));
+ throw error(AbstractParser.message("strict.param.redefinition", parameterName), parameter.getToken());
}
+ // rename in non-strict mode
+ parameterName = functionNode.uniqueName(parameterName);
+ final long parameterToken = parameter.getToken();
+ parameters.set(i, new IdentNode(source, parameterToken, Token.descPosition(parameterToken), functionNode.uniqueName(parameterName)));
}
parametersSet.add(parameterName);
}
} else if (arity == 1) {
- if (ARGUMENTS.tag().equals(parameters.get(0).getName())) {
- functionNode.setDefinesArguments();
+ if (ARGUMENTS.symbolName().equals(parameters.get(0).getName())) {
+ functionNode = functionNode.setFlag(lc, FunctionNode.DEFINES_ARGUMENTS);
}
}
if (isStatement) {
- final VarNode varNode = new VarNode(source, functionToken, finish, name, functionNode, true);
- if(topLevel) {
+ final VarNode varNode = new VarNode(source, functionToken, finish, name, functionNode, VarNode.IS_STATEMENT);
+ if (topLevel) {
functionDeclarations.add(lineNumber);
functionDeclarations.add(varNode);
} else {
- final Block block = getBlock();
- block.addStatement(lineNumber);
- block.addStatement(varNode);
+ appendStatement(lineNumber);
+ appendStatement(varNode);
}
}
@@ -2701,13 +2439,11 @@
*/
private FunctionNode functionBody(final long firstToken, final IdentNode ident, final List<IdentNode> parameters, final FunctionNode.Kind kind) {
FunctionNode functionNode = null;
+ long lastToken = 0L;
try {
// Create a new function block.
- functionNode = newFunctionBlock(ident);
- functionNode.setParameters(parameters);
- functionNode.setKind(kind);
- functionNode.setFirstToken(firstToken);
+ functionNode = newFunctionNode(firstToken, ident, parameters, kind);
// Nashorn extension: expression closures
if (!env._no_syntax_extensions && type != LBRACE) {
@@ -2720,14 +2456,12 @@
// just expression as function body
final Node expr = expression();
-
+ assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode);
// create a return statement - this creates code in itself and does not need to be
// wrapped into an ExecuteNode
- final ReturnNode returnNode = new ReturnNode(source, expr.getToken(), finish, expr, null);
-
- // add the return statement
- functionNode.addStatement(returnNode);
- functionNode.setLastToken(token);
+ final ReturnNode returnNode = new ReturnNode(source, expr.getToken(), finish, expr);
+ appendStatement(returnNode);
+ lastToken = token;
functionNode.setFinish(Token.descPosition(token) + Token.descLength(token));
} else {
@@ -2738,23 +2472,35 @@
functionDeclarations = new ArrayList<>();
try {
sourceElements();
- functionNode.prependStatements(functionDeclarations);
+ addFunctionDeclarations(functionNode);
} finally {
functionDeclarations = prevFunctionDecls;
}
- functionNode.setLastToken(token);
+ lastToken = token;
expect(RBRACE);
functionNode.setFinish(finish);
}
} finally {
- restoreBlock(functionNode);
+ functionNode = restoreFunctionNode(functionNode, lastToken);
}
-
return functionNode;
}
+ private void addFunctionDeclarations(final FunctionNode functionNode) {
+ assert lc.peek() == lc.getFunctionBody(functionNode);
+ VarNode lastDecl = null;
+ for (int i = functionDeclarations.size() - 1; i >= 0; i--) {
+ Node decl = functionDeclarations.get(i);
+ if (lastDecl == null && decl instanceof VarNode) {
+ decl = lastDecl = ((VarNode)decl).setFlag(VarNode.IS_LAST_FUNCTION_DECLARATION);
+ lc.setFlag(functionNode, FunctionNode.HAS_FUNCTION_DECLARATIONS);
+ }
+ prependStatement(decl);
+ }
+ }
+
private RuntimeNode referenceError(final Node lhs, final Node rhs) {
final ArrayList<Node> args = new ArrayList<>();
args.add(lhs);
@@ -2764,9 +2510,7 @@
args.add(rhs);
}
args.add(LiteralNode.newInstance(source, lhs.getToken(), lhs.getFinish(), lhs.toString()));
- final RuntimeNode runtimeNode = new RuntimeNode(source, lhs.getToken(),
- lhs.getFinish(), RuntimeNode.Request.REFERENCE_ERROR, args);
- return runtimeNode;
+ return new RuntimeNode(source, lhs.getToken(), lhs.getFinish(), RuntimeNode.Request.REFERENCE_ERROR, args);
}
/*
@@ -2815,19 +2559,7 @@
case BIT_NOT:
case NOT:
next();
-
final Node expr = unaryExpression();
-
- /*
- // Not sure if "delete <ident>" is a compile-time error or a
- // runtime error in strict mode.
-
- if (isStrictMode) {
- if (unaryTokenType == DELETE && expr instanceof IdentNode) {
- error(message("strict.cant.delete.ident", ((IdentNode)expr).getName()), expr.getToken());
- }
- }
- */
return new UnaryNode(source, unaryToken, expr);
case INCPREFIX:
@@ -2890,7 +2622,7 @@
}
if (expression == null) {
- error(AbstractParser.message("expected.operand", type.getNameOrType()));
+ throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
}
return expression;
@@ -2992,6 +2724,7 @@
// Include commas in expression parsing.
return expression(unaryExpression(), COMMARIGHT.getPrecedence(), false);
}
+
private Node expression(final Node exprLhs, final int minPrecedence, final boolean noIn) {
// Get the precedence of the next operator.
int precedence = type.getPrecedence();
@@ -3087,11 +2820,26 @@
return "[JavaScript Parsing]";
}
- private Block getBlock() {
- return lexicalContext.getCurrentBlock();
+ private static void markEval(final LexicalContext lc) {
+ final Iterator<FunctionNode> iter = lc.getFunctions();
+ boolean flaggedCurrentFn = false;
+ while (iter.hasNext()) {
+ final FunctionNode fn = iter.next();
+ if (!flaggedCurrentFn) {
+ lc.setFlag(fn, FunctionNode.HAS_EVAL);
+ flaggedCurrentFn = true;
+ } else {
+ lc.setFlag(fn, FunctionNode.HAS_NESTED_EVAL);
+ }
+ lc.setFlag(lc.getFunctionBody(fn), Block.NEEDS_SCOPE);
+ }
}
- private FunctionNode getFunction() {
- return lexicalContext.getCurrentFunction();
+ private void prependStatement(final Node statement) {
+ lc.prependStatement(statement);
+ }
+
+ private void appendStatement(final Node statement) {
+ lc.appendStatement(statement);
}
}
--- a/nashorn/src/jdk/nashorn/internal/parser/TokenType.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/parser/TokenType.java Thu May 16 11:47:51 2013 +0100
@@ -280,6 +280,11 @@
return values;
}
+ @Override
+ public String toString() {
+ return name;
+ }
+
static {
// Avoid cloning of enumeration.
values = TokenType.values();
--- a/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java Thu May 16 11:47:51 2013 +0100
@@ -54,10 +54,24 @@
* @see SpillProperty
*/
public class AccessorProperty extends Property {
+ private static final MethodHandles.Lookup lookup = MethodHandles.lookup();
private static final MethodHandle REPLACE_MAP = findOwnMH("replaceMap", Object.class, Object.class, PropertyMap.class, String.class, Class.class, Class.class);
private static final int NOOF_TYPES = getNumberOfAccessorTypes();
+ /**
+ * Properties in different maps for the same structure class will share their field getters and setters. This could
+ * be further extended to other method handles that are looked up in the AccessorProperty constructor, but right now
+ * these are the most frequently retrieved ones, and lookup of method handle natives only registers in the profiler
+ * for them.
+ */
+ private static ClassValue<GettersSetters> GETTERS_SETTERS = new ClassValue<GettersSetters>() {
+ @Override
+ protected GettersSetters computeValue(Class<?> structure) {
+ return new GettersSetters(structure);
+ }
+ };
+
/** Property getter cache */
private MethodHandle[] getters = new MethodHandle[NOOF_TYPES];
@@ -152,6 +166,22 @@
setCurrentType(getterType);
}
+ private static class GettersSetters {
+ final MethodHandle[] getters;
+ final MethodHandle[] setters;
+
+ public GettersSetters(Class<?> structure) {
+ final int fieldCount = ObjectClassGenerator.getFieldCount(structure);
+ getters = new MethodHandle[fieldCount];
+ setters = new MethodHandle[fieldCount];
+ for(int i = 0; i < fieldCount; ++i) {
+ final String fieldName = ObjectClassGenerator.getFieldName(i, Type.OBJECT);
+ getters[i] = MH.getter(lookup, structure, fieldName, Type.OBJECT.getTypeClass());
+ setters[i] = MH.setter(lookup, structure, fieldName, Type.OBJECT.getTypeClass());
+ }
+ }
+ }
+
/**
* Constructor for dual field AccessorPropertys.
*
@@ -171,22 +201,19 @@
primitiveGetter = null;
primitiveSetter = null;
- final MethodHandles.Lookup lookup = MethodHandles.lookup();
-
if (isParameter() && hasArguments()) {
- final MethodHandle arguments = MH.getter(MethodHandles.lookup(), structure, "arguments", Object.class);
+ final MethodHandle arguments = MH.getter(lookup, structure, "arguments", Object.class);
final MethodHandle argumentsSO = MH.asType(arguments, arguments.type().changeReturnType(ScriptObject.class));
objectGetter = MH.insertArguments(MH.filterArguments(ScriptObject.GET_ARGUMENT.methodHandle(), 0, argumentsSO), 1, slot);
objectSetter = MH.insertArguments(MH.filterArguments(ScriptObject.SET_ARGUMENT.methodHandle(), 0, argumentsSO), 1, slot);
} else {
- final String fieldNameObject = ObjectClassGenerator.getFieldName(slot, Type.OBJECT);
- final String fieldNamePrimitive = ObjectClassGenerator.getFieldName(slot, ObjectClassGenerator.PRIMITIVE_TYPE);
-
- objectGetter = MH.getter(lookup, structure, fieldNameObject, Type.OBJECT.getTypeClass());
- objectSetter = MH.setter(lookup, structure, fieldNameObject, Type.OBJECT.getTypeClass());
+ final GettersSetters gs = GETTERS_SETTERS.get(structure);
+ objectGetter = gs.getters[slot];
+ objectSetter = gs.setters[slot];
if (!OBJECT_FIELDS_ONLY) {
+ final String fieldNamePrimitive = ObjectClassGenerator.getFieldName(slot, ObjectClassGenerator.PRIMITIVE_TYPE);
primitiveGetter = MH.getter(lookup, structure, fieldNamePrimitive, PRIMITIVE_TYPE.getTypeClass());
primitiveSetter = MH.setter(lookup, structure, fieldNamePrimitive, PRIMITIVE_TYPE.getTypeClass());
}
@@ -365,7 +392,7 @@
}
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
- return MH.findStatic(MethodHandles.lookup(), AccessorProperty.class, name, MH.type(rtype, types));
+ return MH.findStatic(lookup, AccessorProperty.class, name, MH.type(rtype, types));
}
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java Thu May 16 11:47:51 2013 +0100
@@ -54,10 +54,7 @@
import jdk.nashorn.internal.ir.debug.ASTWriter;
import jdk.nashorn.internal.ir.debug.PrintVisitor;
import jdk.nashorn.internal.parser.Parser;
-import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
import jdk.nashorn.internal.runtime.options.Options;
-import sun.reflect.CallerSensitive;
-import sun.reflect.Reflection;
/**
* This class manages the global state of execution. Context is immutable.
@@ -114,24 +111,9 @@
* Get the current global scope
* @return the current global scope
*/
- @CallerSensitive
public static ScriptObject getGlobal() {
- final SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- // skip getCallerClass and getGlobal and get to the real caller
- Class<?> caller = Reflection.getCallerClass();
- ClassLoader callerLoader = caller.getClassLoader();
-
- // Allow this method only for nashorn's own classes, objects
- // package classes and Java adapter classes. Rest should
- // have the necessary security permission.
- if (callerLoader != myLoader &&
- !(callerLoader instanceof StructureLoader) &&
- !(JavaAdapterFactory.isAdapterClass(caller))) {
- sm.checkPermission(new RuntimePermission("nashorn.getGlobal"));
- }
- }
-
+ // This class in a package.access protected package.
+ // Trusted code only can call this method.
return getGlobalTrusted();
}
@@ -399,7 +381,7 @@
// We need to get strict mode flag from compiled class. This is
// because eval code may start with "use strict" directive.
try {
- strictFlag = clazz.getField(STRICT_MODE.tag()).getBoolean(null);
+ strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null);
} catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
//ignored
strictFlag = false;
@@ -432,6 +414,28 @@
return ScriptRuntime.apply(func, evalThis);
}
+ private Source loadInternal(final String srcStr, final String prefix, final String resourcePath) {
+ if (srcStr.startsWith(prefix)) {
+ final String resource = resourcePath + srcStr.substring(prefix.length());
+ // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme
+ // These scripts are always available and are loaded from nashorn.jar's resources.
+ return AccessController.doPrivileged(
+ new PrivilegedAction<Source>() {
+ @Override
+ public Source run() {
+ try {
+ final URL resURL = Context.class.getResource(resource);
+ return (resURL != null)? new Source(srcStr, resURL) : null;
+ } catch (final IOException exp) {
+ return null;
+ }
+ }
+ });
+ }
+
+ return null;
+ }
+
/**
* Implementation of {@code load} Nashorn extension. Load a script file from a source
* expression
@@ -444,33 +448,18 @@
* @throws IOException if source cannot be found or loaded
*/
public Object load(final ScriptObject scope, final Object from) throws IOException {
- Object src = (from instanceof ConsString)? from.toString() : from;
+ final Object src = (from instanceof ConsString)? from.toString() : from;
Source source = null;
// load accepts a String (which could be a URL or a file name), a File, a URL
// or a ScriptObject that has "name" and "source" (string valued) properties.
if (src instanceof String) {
final String srcStr = (String)src;
- final File file = new File(srcStr);
+ final File file = new File(srcStr);
if (srcStr.indexOf(':') != -1) {
- if (srcStr.startsWith("nashorn:")) {
- final String resource = "resources/" + srcStr.substring("nashorn:".length());
- // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme
- // These scripts are always available and are loaded from nashorn.jar's resources.
- source = AccessController.doPrivileged(
- new PrivilegedAction<Source>() {
- @Override
- public Source run() {
- try {
- final URL resURL = Context.class.getResource(resource);
- return (resURL != null)? new Source(srcStr, resURL) : null;
- } catch (final IOException exp) {
- return null;
- }
- }
- });
- } else {
- URL url = null;
+ if ((source = loadInternal(srcStr, "nashorn:", "resources/")) == null &&
+ (source = loadInternal(srcStr, "fx:", "resources/fx/")) == null) {
+ URL url;
try {
//check for malformed url. if malformed, it may still be a valid file
url = new URL(srcStr);
@@ -713,7 +702,7 @@
MH.findStatic(
MethodHandles.lookup(),
script,
- RUN_SCRIPT.tag(),
+ RUN_SCRIPT.symbolName(),
MH.type(
Object.class,
ScriptFunction.class,
@@ -722,13 +711,13 @@
boolean strict;
try {
- strict = script.getField(STRICT_MODE.tag()).getBoolean(null);
+ strict = script.getField(STRICT_MODE.symbolName()).getBoolean(null);
} catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
strict = false;
}
// Package as a JavaScript function and pass function back to shell.
- return ((GlobalObject)Context.getGlobalTrusted()).newScriptFunction(RUN_SCRIPT.tag(), runMethodHandle, scope, strict);
+ return ((GlobalObject)Context.getGlobalTrusted()).newScriptFunction(RUN_SCRIPT.symbolName(), runMethodHandle, scope, strict);
}
private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) {
@@ -746,13 +735,13 @@
global = (GlobalObject)Context.getGlobalTrusted();
script = global.findCachedClass(source);
if (script != null) {
- Compiler.LOG.fine("Code cache hit for " + source + " avoiding recompile.");
+ Compiler.LOG.fine("Code cache hit for ", source, " avoiding recompile.");
return script;
}
}
final FunctionNode functionNode = new Parser(env, source, errMan, strict).parse();
- if (errors.hasErrors() || env._parse_only) {
+ if (errors.hasErrors()) {
return null;
}
@@ -764,6 +753,10 @@
getErr().println(new PrintVisitor(functionNode));
}
+ if (env._parse_only) {
+ return null;
+ }
+
final URL url = source.getURL();
final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
final CodeSource cs = url == null ? null : new CodeSource(url, (CodeSigner[])null);
--- a/nashorn/src/jdk/nashorn/internal/runtime/DebugLogger.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/DebugLogger.java Thu May 16 11:47:51 2013 +0100
@@ -135,7 +135,16 @@
* @param str the string to log
*/
public void finest(final String str) {
- log(str, Level.FINEST);
+ log(Level.FINEST, str);
+ }
+
+ /**
+ * Shorthand for outputting a log string as log level
+ * {@link java.util.logging.Level#FINEST} on this logger
+ * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
+ */
+ public void finest(final Object... objs) {
+ log(Level.FINEST, objs);
}
/**
@@ -144,7 +153,16 @@
* @param str the string to log
*/
public void finer(final String str) {
- log(str, Level.FINER);
+ log(Level.FINER, str);
+ }
+
+ /**
+ * Shorthand for outputting a log string as log level
+ * {@link java.util.logging.Level#FINER} on this logger
+ * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
+ */
+ public void finer(final Object... objs) {
+ log(Level.FINER, objs);
}
/**
@@ -153,7 +171,16 @@
* @param str the string to log
*/
public void fine(final String str) {
- log(str, Level.FINE);
+ log(Level.FINE, str);
+ }
+
+ /**
+ * Shorthand for outputting a log string as log level
+ * {@link java.util.logging.Level#FINE} on this logger
+ * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
+ */
+ public void fine(final Object... objs) {
+ log(Level.FINE, objs);
}
/**
@@ -162,7 +189,16 @@
* @param str the string to log
*/
public void config(final String str) {
- log(str, Level.CONFIG);
+ log(Level.CONFIG, str);
+ }
+
+ /**
+ * Shorthand for outputting a log string as log level
+ * {@link java.util.logging.Level#CONFIG} on this logger
+ * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
+ */
+ public void config(final Object... objs) {
+ log(Level.CONFIG, objs);
}
/**
@@ -171,7 +207,16 @@
* @param str the string to log
*/
public void info(final String str) {
- log(str, Level.INFO);
+ log(Level.INFO, str);
+ }
+
+ /**
+ * Shorthand for outputting a log string as log level
+ * {@link java.util.logging.Level#FINE} on this logger
+ * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
+ */
+ public void info(final Object... objs) {
+ log(Level.INFO, objs);
}
/**
@@ -180,7 +225,16 @@
* @param str the string to log
*/
public void warning(final String str) {
- log(str, Level.WARNING);
+ log(Level.WARNING, str);
+ }
+
+ /**
+ * Shorthand for outputting a log string as log level
+ * {@link java.util.logging.Level#FINE} on this logger
+ * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
+ */
+ public void warning(final Object... objs) {
+ log(Level.WARNING, objs);
}
/**
@@ -189,20 +243,28 @@
* @param str the string to log
*/
public void severe(final String str) {
- log(str, Level.SEVERE);
+ log(Level.SEVERE, str);
+ }
+
+ /**
+ * Shorthand for outputting a log string as log level
+ * {@link java.util.logging.Level#FINE} on this logger
+ * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
+ */
+ public void severe(final Object... objs) {
+ log(Level.SEVERE, objs);
}
/**
* Output log line on this logger at a given level of verbosity
* @see java.util.logging.Level
*
+ * @param level minimum log level required for logging to take place
* @param str string to log
- * @param level minimum log level required for logging to take place
*/
- public void log(final String str, final Level level) {
+ public void log(final Level level, final String str) {
if (isEnabled) {
final StringBuilder sb = new StringBuilder();
-
for (int i = 0 ; i < indent ; i++) {
sb.append(' ');
}
@@ -210,4 +272,24 @@
logger.log(level, sb.toString());
}
}
+
+ /**
+ * Output log line on this logger at a given level of verbosity
+ * @see java.util.logging.Level
+ *
+ * @param level minimum log level required for logging to take place
+ * @param objs objects for which to invoke toString and concatenate to log
+ */
+ public void log(final Level level, final Object... objs) {
+ if (isEnabled) {
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0 ; i < indent ; i++) {
+ sb.append(' ');
+ }
+ for (final Object obj : objs) {
+ sb.append(obj);
+ }
+ logger.log(level, sb.toString());
+ }
+ }
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/FindProperty.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/FindProperty.java Thu May 16 11:47:51 2013 +0100
@@ -105,6 +105,22 @@
}
/**
+ * Return the appropriate receiver for a getter.
+ * @return appropriate receiver
+ */
+ public ScriptObject getGetterReceiver() {
+ return property != null && property.hasGetterFunction() ? self : prototype;
+ }
+
+ /**
+ * Return the appropriate receiver for a setter.
+ * @return appropriate receiver
+ */
+ public ScriptObject getSetterReceiver() {
+ return property != null && property.hasSetterFunction() ? self : prototype;
+ }
+
+ /**
* Return the property that was found
* @return property
*/
--- a/nashorn/src/jdk/nashorn/internal/runtime/JSONFunctions.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/JSONFunctions.java Thu May 16 11:47:51 2013 +0100
@@ -105,9 +105,7 @@
// This is the abstract "Walk" operation from the spec.
private static Object walk(final ScriptObject holder, final Object name, final ScriptFunction reviver) {
final Object val = holder.get(name);
- if (val == ScriptRuntime.UNDEFINED) {
- return val;
- } else if (val instanceof ScriptObject) {
+ if (val instanceof ScriptObject) {
final ScriptObject valueObj = (ScriptObject)val;
final boolean strict = valueObj.isStrictContext();
final Iterator<String> iter = valueObj.propertyIterator();
@@ -122,33 +120,15 @@
valueObj.set(key, newElement, strict);
}
}
-
- return valueObj;
- } else if (isArray(val)) {
- final ScriptObject valueArray = (ScriptObject)val;
- final boolean strict = valueArray.isStrictContext();
- final Iterator<String> iter = valueArray.propertyIterator();
-
- while (iter.hasNext()) {
- final String key = iter.next();
- final Object newElement = walk(valueArray, valueArray.get(key), reviver);
+ }
- if (newElement == ScriptRuntime.UNDEFINED) {
- valueArray.delete(key, strict);
- } else {
- valueArray.set(key, newElement, strict);
- }
- }
- return valueArray;
- } else {
- try {
- // Object.class, ScriptFunction.class, ScriptObject.class, String.class, Object.class);
- return REVIVER_INVOKER.invokeExact(reviver, holder, JSType.toString(name), val);
- } catch(Error|RuntimeException t) {
- throw t;
- } catch(final Throwable t) {
- throw new RuntimeException(t);
- }
+ try {
+ // Object.class, ScriptFunction.class, ScriptObject.class, String.class, Object.class);
+ return REVIVER_INVOKER.invokeExact(reviver, holder, JSType.toString(name), val);
+ } catch(Error|RuntimeException t) {
+ throw t;
+ } catch(final Throwable t) {
+ throw new RuntimeException(t);
}
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/JSType.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/JSType.java Thu May 16 11:47:51 2013 +0100
@@ -102,6 +102,8 @@
/** JavaScript compliant conversion function from Object to primitive */
public static final Call TO_PRIMITIVE = staticCall(JSType.class, "toPrimitive", Object.class, Object.class);
+ private static final double INT32_LIMIT = 4294967296.0;
+
/**
* The external type name as returned by ECMAScript "typeof" operator
*
@@ -612,10 +614,7 @@
* @return an int32
*/
public static int toInt32(final double num) {
- if (Double.isInfinite(num)) {
- return 0;
- }
- return (int)(long)num;
+ return (int)doubleToInt32(num);
}
/**
@@ -658,10 +657,7 @@
* @return a uint32
*/
public static long toUint32(final double num) {
- if (Double.isInfinite(num)) {
- return 0L;
- }
- return ((long)num) & 0xffff_ffffL;
+ return doubleToInt32(num) & MAX_UINT;
}
/**
@@ -702,10 +698,22 @@
* @return a uint16
*/
public static int toUint16(final double num) {
- if (Double.isInfinite(num)) {
+ return ((int)doubleToInt32(num)) & 0xffff;
+ }
+
+ private static long doubleToInt32(final double num) {
+ final int exponent = Math.getExponent(num);
+ if (exponent < 31) {
+ return (long) num; // Fits into 32 bits
+ }
+ if (exponent >= 84) {
+ // Either infinite or NaN or so large that shift / modulo will produce 0
+ // (52 bit mantissa + 32 bit target width).
return 0;
}
- return ((int)(long)num) & 0xffff;
+ // This is rather slow and could probably be sped up using bit-fiddling.
+ final double d = (num >= 0) ? Math.floor(num) : Math.ceil(num);
+ return (long)(d % INT32_LIMIT);
}
/**
--- a/nashorn/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java Thu May 16 11:47:51 2013 +0100
@@ -170,7 +170,7 @@
Class<?> javaClass = null;
try {
javaClass = context.findClass(fullName);
- } catch (final ClassNotFoundException e) {
+ } catch (final NoClassDefFoundError | ClassNotFoundException e) {
//ignored
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/PropertyListenerManager.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/PropertyListenerManager.java Thu May 16 11:47:51 2013 +0100
@@ -25,20 +25,20 @@
package jdk.nashorn.internal.runtime;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
/**
* Helper class to manage property listeners and notification.
*/
public class PropertyListenerManager implements PropertyListener {
+ /** property listeners for this object. */
+ private Map<PropertyListener,Boolean> listeners;
+
// These counters are updated in debug mode
private static int listenersAdded;
private static int listenersRemoved;
- private static int listenersDead;
/**
* @return the listenersAdded
@@ -54,16 +54,6 @@
return listenersRemoved;
}
- /**
- * @return the listenersDead
- */
- public static int getListenersDead() {
- return listenersDead;
- }
-
- /** property listeners for this object. */
- private List<WeakReference<PropertyListener>> listeners;
-
// Property listener management methods
/**
@@ -73,12 +63,13 @@
*/
public final void addPropertyListener(final PropertyListener listener) {
if (listeners == null) {
- listeners = new ArrayList<>();
+ listeners = new WeakHashMap<>();
}
+
if (Context.DEBUG) {
listenersAdded++;
}
- listeners.add(new WeakReference<>(listener));
+ listeners.put(listener, Boolean.TRUE);
}
/**
@@ -88,15 +79,10 @@
*/
public final void removePropertyListener(final PropertyListener listener) {
if (listeners != null) {
- final Iterator<WeakReference<PropertyListener>> iter = listeners.iterator();
- while (iter.hasNext()) {
- if (iter.next().get() == listener) {
- if (Context.DEBUG) {
- listenersRemoved++;
- }
- iter.remove();
- }
+ if (Context.DEBUG) {
+ listenersRemoved++;
}
+ listeners.remove(listener);
}
}
@@ -108,18 +94,8 @@
*/
protected final void notifyPropertyAdded(final ScriptObject object, final Property prop) {
if (listeners != null) {
- final Iterator<WeakReference<PropertyListener>> iter = listeners.iterator();
- while (iter.hasNext()) {
- final WeakReference<PropertyListener> weakRef = iter.next();
- final PropertyListener listener = weakRef.get();
- if (listener == null) {
- if (Context.DEBUG) {
- listenersDead++;
- }
- iter.remove();
- } else {
- listener.propertyAdded(object, prop);
- }
+ for (PropertyListener listener : listeners.keySet()) {
+ listener.propertyAdded(object, prop);
}
}
}
@@ -132,18 +108,8 @@
*/
protected final void notifyPropertyDeleted(final ScriptObject object, final Property prop) {
if (listeners != null) {
- final Iterator<WeakReference<PropertyListener>> iter = listeners.iterator();
- while (iter.hasNext()) {
- final WeakReference<PropertyListener> weakRef = iter.next();
- final PropertyListener listener = weakRef.get();
- if (listener == null) {
- if (Context.DEBUG) {
- listenersDead++;
- }
- iter.remove();
- } else {
- listener.propertyDeleted(object, prop);
- }
+ for (PropertyListener listener : listeners.keySet()) {
+ listener.propertyDeleted(object, prop);
}
}
}
@@ -157,18 +123,8 @@
*/
protected final void notifyPropertyModified(final ScriptObject object, final Property oldProp, final Property newProp) {
if (listeners != null) {
- final Iterator<WeakReference<PropertyListener>> iter = listeners.iterator();
- while (iter.hasNext()) {
- final WeakReference<PropertyListener> weakRef = iter.next();
- final PropertyListener listener = weakRef.get();
- if (listener == null) {
- if (Context.DEBUG) {
- listenersDead++;
- }
- iter.remove();
- } else {
- listener.propertyModified(object, oldProp, newProp);
- }
+ for (PropertyListener listener : listeners.keySet()) {
+ listener.propertyModified(object, oldProp, newProp);
}
}
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Thu May 16 11:47:51 2013 +0100
@@ -25,10 +25,11 @@
package jdk.nashorn.internal.runtime;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
-
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.FunctionSignature;
@@ -37,8 +38,6 @@
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
-
/**
* This is a subclass that represents a script function that may be regenerated,
* for example with specialization based on call site types, or lazily generated.
@@ -47,7 +46,7 @@
*/
public final class RecompilableScriptFunctionData extends ScriptFunctionData {
- private final FunctionNode functionNode;
+ private FunctionNode functionNode;
private final PropertyMap allocatorMap;
private final CodeInstaller<ScriptEnvironment> installer;
private final String allocatorClassName;
@@ -70,7 +69,7 @@
"" :
functionNode.getIdent().getName(),
functionNode.getParameters().size(),
- functionNode.isStrictMode(),
+ functionNode.isStrict(),
false,
true);
@@ -129,7 +128,7 @@
private void ensureHasAllocator() throws ClassNotFoundException {
if (allocator == null && allocatorClassName != null) {
- this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.tag(), MH.type(ScriptObject.class, PropertyMap.class));
+ this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class));
}
}
@@ -148,8 +147,11 @@
// therefore, currently method specialization is disabled. TODO
if (functionNode.isLazy()) {
- Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '" + functionNode.getName() + "'");
- new Compiler(installer, functionNode).compile().install();
+ Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'");
+ final Compiler compiler = new Compiler(installer, functionNode);
+ functionNode = compiler.compile();
+ assert !functionNode.isLazy();
+ compiler.install();
// we don't need to update any flags - varArgs and needsCallee are instrincic
// in the function world we need to get a destination node from the compile instead
@@ -159,7 +161,7 @@
// we can't get here unless we have bytecode, either from eager compilation or from
// running a lazy compile on the lines above
- assert functionNode.hasState(CompilationState.INSTALLED);
+ assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
// code exists - look it up and add it into the automatically sorted invoker list
code.add(
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Thu May 16 11:47:51 2013 +0100
@@ -82,6 +82,36 @@
/** Show full Nashorn version */
public final boolean _fullversion;
+ /** Launch using as fx application */
+ public final boolean _fx;
+
+ /**
+ * Behavior when encountering a function declaration in a lexical context where only statements are acceptable
+ * (function declarations are source elements, but not statements).
+ */
+ public enum FunctionStatementBehavior {
+ /**
+ * Accept the function declaration silently and treat it as if it were a function expression assigned to a local
+ * variable.
+ */
+ ACCEPT,
+ /**
+ * Log a parser warning, but accept the function declaration and treat it as if it were a function expression
+ * assigned to a local variable.
+ */
+ WARNING,
+ /**
+ * Raise a {@code SyntaxError}.
+ */
+ ERROR
+ }
+
+ /**
+ * Behavior when encountering a function declaration in a lexical context where only statements are acceptable
+ * (function declarations are source elements, but not statements).
+ */
+ public final FunctionStatementBehavior _function_statement;
+
/** Should lazy compilation take place */
public final boolean _lazy_compilation;
@@ -158,6 +188,14 @@
_early_lvalue_error = options.getBoolean("early.lvalue.error");
_empty_statements = options.getBoolean("empty.statements");
_fullversion = options.getBoolean("fullversion");
+ if(options.getBoolean("function.statement.error")) {
+ _function_statement = FunctionStatementBehavior.ERROR;
+ } else if(options.getBoolean("function.statement.warning")) {
+ _function_statement = FunctionStatementBehavior.WARNING;
+ } else {
+ _function_statement = FunctionStatementBehavior.ACCEPT;
+ }
+ _fx = options.getBoolean("fx");
_lazy_compilation = options.getBoolean("lazy.compilation");
_loader_per_compile = options.getBoolean("loader.per.compile");
_no_syntax_extensions = options.getBoolean("no.syntax.extensions");
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Thu May 16 11:47:51 2013 +0100
@@ -71,9 +71,6 @@
private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class);
- /** Reference to constructor prototype. */
- protected Object prototype;
-
/** The parent scope. */
private final ScriptObject scope;
@@ -221,6 +218,7 @@
final ScriptObject object = data.allocate();
if (object != null) {
+ Object prototype = getPrototype();
if (prototype instanceof ScriptObject) {
object.setProto((ScriptObject)prototype);
}
@@ -282,24 +280,18 @@
* Get the prototype object for this function
* @return prototype
*/
- public final Object getPrototype() {
- return prototype;
- }
+ public abstract Object getPrototype();
/**
* Set the prototype object for this function
* @param prototype new prototype object
- * @return the prototype parameter
*/
- public final Object setPrototype(final Object prototype) {
- this.prototype = prototype;
- return prototype;
- }
+ public abstract void setPrototype(Object prototype);
/**
* Return the most appropriate invoke handle if there are specializations
* @param type most specific method type to look for invocation with
- * @param callsite args for trampoline invocation
+ * @param args args for trampoline invocation
* @return invoke method handle
*/
private MethodHandle getBestInvoker(final MethodType type, final Object[] args) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Thu May 16 11:47:51 2013 +0100
@@ -28,6 +28,7 @@
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE;
@@ -39,7 +40,6 @@
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndexNoThrow;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@@ -61,12 +61,13 @@
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
+import jdk.nashorn.internal.lookup.Lookup;
+import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.objects.AccessorPropertyDescriptor;
import jdk.nashorn.internal.objects.DataPropertyDescriptor;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
-import jdk.nashorn.internal.lookup.Lookup;
-import jdk.nashorn.internal.lookup.MethodHandleFactory;
+import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.linker.NashornGuards;
@@ -901,7 +902,7 @@
final MethodHandle getter = find.getGetter(int.class);
if (getter != null) {
try {
- return (int)getter.invokeExact((Object)find.getOwner());
+ return (int)getter.invokeExact((Object)find.getGetterReceiver());
} catch (final Error|RuntimeException e) {
throw e;
} catch (final Throwable e) {
@@ -916,7 +917,7 @@
final MethodHandle getter = find.getGetter(long.class);
if (getter != null) {
try {
- return (long)getter.invokeExact((Object)find.getOwner());
+ return (long)getter.invokeExact((Object)find.getGetterReceiver());
} catch (final Error|RuntimeException e) {
throw e;
} catch (final Throwable e) {
@@ -931,7 +932,7 @@
final MethodHandle getter = find.getGetter(double.class);
if (getter != null) {
try {
- return (double)getter.invokeExact((Object)find.getOwner());
+ return (double)getter.invokeExact((Object)find.getGetterReceiver());
} catch (final Error|RuntimeException e) {
throw e;
} catch (final Throwable e) {
@@ -953,7 +954,7 @@
final MethodHandle getter = find.getGetter(Object.class);
if (getter != null) {
try {
- return getter.invokeExact((Object)find.getOwner());
+ return getter.invokeExact((Object)find.getGetterReceiver());
} catch (final Error|RuntimeException e) {
throw e;
} catch (final Throwable e) {
@@ -1679,12 +1680,7 @@
* @return GuardedInvocation to be invoked at call site.
*/
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
- final String name = desc.getNameToken(2);
-
- if (request.isCallSiteUnstable()) {
- return findMegaMorphicGetMethod(desc, name);
- }
-
+ final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final FindProperty find = findProperty(name, true);
MethodHandle methodHandle;
@@ -1700,6 +1696,10 @@
throw new AssertionError(); // never invoked with any other operation
}
+ if (request.isCallSiteUnstable()) {
+ return findMegaMorphicGetMethod(desc, name);
+ }
+
final Class<?> returnType = desc.getMethodType().returnType();
final Property property = find.getProperty();
methodHandle = find.getGetter(returnType);
@@ -1727,7 +1727,9 @@
}
private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) {
- final GuardedInvocation inv = findGetIndexMethod(desc.getMethodType().insertParameterTypes(1, Object.class));
+ final MethodType mhType = desc.getMethodType().insertParameterTypes(1, Object.class);
+ final GuardedInvocation inv = findGetIndexMethod(mhType);
+
return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
}
@@ -1890,8 +1892,8 @@
}
private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
- final GuardedInvocation inv = findSetIndexMethod(desc.getMethodType().insertParameterTypes(1, Object.class),
- NashornCallSiteDescriptor.isStrict(desc));
+ final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
+ final GuardedInvocation inv = findSetIndexMethod(type, NashornCallSiteDescriptor.isStrict(desc));
return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
}
@@ -1949,7 +1951,7 @@
* @return GuardedInvocation to be invoked at call site.
*/
public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
- final String name = desc.getNameToken(2);
+ final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
@@ -1973,6 +1975,24 @@
return createEmptyGetter(desc, name);
}
+ /**
+ * Invoke fall back if a property is not found.
+ * @param name Name of property.
+ * @return Result from call.
+ */
+ private Object invokeNoSuchProperty(final String name) {
+ final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
+
+ if (find != null) {
+ final Object func = getObjectValue(find);
+
+ if (func instanceof ScriptFunction) {
+ return ScriptRuntime.apply((ScriptFunction)func, this, name);
+ }
+ }
+
+ return UNDEFINED;
+ }
private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(name), NashornGuards.getMapGuard(getMap()));
@@ -2120,8 +2140,11 @@
*
* Make sure arguments are paired correctly.
* @param methodHandle MethodHandle to adjust.
- * @param callType MethodType of caller.
- * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred.
+ * @param callType MethodType of the call site.
+ * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the
+ * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a
+ * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites
+ * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters.
*
* @return method handle with adjusted arguments
*/
@@ -2136,7 +2159,7 @@
final int callCount = callType.parameterCount();
final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
- final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : (callCount > 1 &&
+ final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : (callCount > 0 &&
callType.parameterType(callCount - 1).isArray());
if (callCount < parameterCount) {
@@ -2239,310 +2262,314 @@
setArray(getArray().shrink(newLength));
getArray().setLength(newLength);
}
- }
+ }
+
+ private int getInt(final int index, final String key) {
+ if (isValidArrayIndex(index)) {
+ for (ScriptObject object = this; ; ) {
+ final FindProperty find = object.findProperty(key, false, false, this);
+
+ if (find != null) {
+ return getIntValue(find);
+ }
+
+ if ((object = object.getProto()) == null) {
+ break;
+ }
+
+ final ArrayData array = object.getArray();
+
+ if (array.has(index)) {
+ return array.getInt(index);
+ }
+ }
+ } else {
+ final FindProperty find = findProperty(key, true);
+
+ if (find != null) {
+ return getIntValue(find);
+ }
+ }
+
+ return JSType.toInt32(invokeNoSuchProperty(key));
+ }
@Override
public int getInt(final Object key) {
final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getInt(index);
+ final ArrayData array = getArray();
+
+ if (array.has(index)) {
+ return array.getInt(index);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getIntValue(find);
- }
-
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getInt(key) : 0;
+ return getInt(index, convertKey(key));
}
@Override
public int getInt(final double key) {
final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getInt(index);
+ final ArrayData array = getArray();
+
+ if (array.has(index)) {
+ return array.getInt(index);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getIntValue(find);
- }
-
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getInt(key) : 0;
+ return getInt(index, convertKey(key));
}
@Override
public int getInt(final long key) {
final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getInt(index);
+ final ArrayData array = getArray();
+
+ if (array.has(index)) {
+ return array.getInt(index);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getIntValue(find);
- }
-
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getInt(key) : 0;
+ return getInt(index, convertKey(key));
}
@Override
public int getInt(final int key) {
- final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getInt(index);
+ final ArrayData array = getArray();
+
+ if (array.has(key)) {
+ return array.getInt(key);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getIntValue(find);
+ return getInt(key, convertKey(key));
+ }
+
+ private long getLong(final int index, final String key) {
+ if (isValidArrayIndex(index)) {
+ for (ScriptObject object = this; ; ) {
+ final FindProperty find = object.findProperty(key, false, false, this);
+
+ if (find != null) {
+ return getLongValue(find);
+ }
+
+ if ((object = object.getProto()) == null) {
+ break;
+ }
+
+ final ArrayData array = object.getArray();
+
+ if (array.has(index)) {
+ return array.getLong(index);
+ }
+ }
+ } else {
+ final FindProperty find = findProperty(key, true);
+
+ if (find != null) {
+ return getLongValue(find);
+ }
}
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getInt(key) : 0;
+ return JSType.toLong(invokeNoSuchProperty(key));
}
@Override
public long getLong(final Object key) {
final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getLong(index);
+ final ArrayData array = getArray();
+
+ if (array.has(index)) {
+ return array.getLong(index);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getLongValue(find);
- }
-
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getLong(key) : 0L;
+ return getLong(index, convertKey(key));
}
@Override
public long getLong(final double key) {
final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getLong(index);
+ final ArrayData array = getArray();
+
+ if (array.has(index)) {
+ return array.getLong(index);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getLongValue(find);
- }
-
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getLong(key) : 0L;
+ return getLong(index, convertKey(key));
}
@Override
public long getLong(final long key) {
final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getLong(index);
+ final ArrayData array = getArray();
+
+ if (array.has(index)) {
+ return array.getLong(index);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getLongValue(find);
- }
-
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getLong(key) : 0L;
+ return getLong(index, convertKey(key));
}
@Override
public long getLong(final int key) {
- final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getLong(index);
+ final ArrayData array = getArray();
+
+ if (array.has(key)) {
+ return array.getLong(key);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getLongValue(find);
+ return getLong(key, convertKey(key));
+ }
+
+ private double getDouble(final int index, final String key) {
+ if (isValidArrayIndex(index)) {
+ for (ScriptObject object = this; ; ) {
+ final FindProperty find = object.findProperty(key, false, false, this);
+
+ if (find != null) {
+ return getDoubleValue(find);
+ }
+
+ if ((object = object.getProto()) == null) {
+ break;
+ }
+
+ final ArrayData array = object.getArray();
+
+ if (array.has(index)) {
+ return array.getDouble(index);
+ }
+ }
+ } else {
+ final FindProperty find = findProperty(key, true);
+
+ if (find != null) {
+ return getDoubleValue(find);
+ }
}
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getLong(key) : 0L;
+ return JSType.toNumber(invokeNoSuchProperty(key));
}
@Override
public double getDouble(final Object key) {
final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getDouble(index);
+ final ArrayData array = getArray();
+
+ if (array.has(index)) {
+ return array.getDouble(index);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getDoubleValue(find);
- }
-
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getDouble(key) : Double.NaN;
+ return getDouble(index, convertKey(key));
}
@Override
public double getDouble(final double key) {
final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getDouble(index);
+ final ArrayData array = getArray();
+
+ if (array.has(index)) {
+ return array.getDouble(index);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getDoubleValue(find);
- }
-
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getDouble(key) : Double.NaN;
+ return getDouble(index, convertKey(key));
}
@Override
public double getDouble(final long key) {
final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getDouble(index);
+ final ArrayData array = getArray();
+
+ if (array.has(index)) {
+ return array.getDouble(index);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getDoubleValue(find);
- }
-
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getDouble(key) : Double.NaN;
+ return getDouble(index, convertKey(key));
}
@Override
public double getDouble(final int key) {
- final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getDouble(index);
+ final ArrayData array = getArray();
+
+ if (array.has(key)) {
+ return array.getDouble(key);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getDoubleValue(find);
+ return getDouble(key, convertKey(key));
+ }
+
+ private Object get(final int index, final String key) {
+ if (isValidArrayIndex(index)) {
+ for (ScriptObject object = this; ; ) {
+ final FindProperty find = object.findProperty(key, false, false, this);
+
+ if (find != null) {
+ return getObjectValue(find);
+ }
+
+ if ((object = object.getProto()) == null) {
+ break;
+ }
+
+ final ArrayData array = object.getArray();
+
+ if (array.has(index)) {
+ return array.getObject(index);
+ }
+ }
+ } else {
+ final FindProperty find = findProperty(key, true);
+
+ if (find != null) {
+ return getObjectValue(find);
+ }
}
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getDouble(key) : Double.NaN;
+ return invokeNoSuchProperty(key);
}
@Override
public Object get(final Object key) {
final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getObject(index);
+ final ArrayData array = getArray();
+
+ if (array.has(index)) {
+ return array.getObject(index);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getObjectValue(find);
- }
-
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.get(key) : UNDEFINED;
+ return get(index, convertKey(key));
}
@Override
public Object get(final double key) {
final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getObject(index);
+ final ArrayData array = getArray();
+
+ if (array.has(index)) {
+ return array.getObject(index);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getObjectValue(find);
- }
-
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.get(key) : UNDEFINED;
+ return get(index, convertKey(key));
}
@Override
public Object get(final long key) {
final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getObject(index);
+ final ArrayData array = getArray();
+
+ if (array.has(index)) {
+ return array.getObject(index);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getObjectValue(find);
- }
-
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.get(key) : UNDEFINED;
+ return get(index, convertKey(key));
}
@Override
public Object get(final int key) {
- final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getObject(index);
+ final ArrayData array = getArray();
+
+ if (array.has(key)) {
+ return array.getObject(key);
}
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getObjectValue(find);
- }
-
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.get(key) : UNDEFINED;
+ return get(key, convertKey(key));
}
/**
@@ -2554,7 +2581,7 @@
*/
private void doesNotHave(final int index, final Object value, final boolean strict) {
final long oldLength = getArray().length();
- final long longIndex = index & 0xffff_ffffL;
+ final long longIndex = index & JSType.MAX_UINT;
if (!getArray().has(index)) {
final String key = convertKey(longIndex);
@@ -2613,8 +2640,6 @@
f = null;
}
- MethodHandle setter;
-
if (f != null) {
if (!f.getProperty().isWritable()) {
if (strict) {
@@ -2624,9 +2649,9 @@
return;
}
- setter = f.getSetter(Object.class, strict); //TODO specfields
try {
- setter.invokeExact((Object)f.getOwner(), value);
+ final MethodHandle setter = f.getSetter(Object.class, strict); //TODO specfields
+ setter.invokeExact((Object)f.getSetterReceiver(), value);
} catch (final Error|RuntimeException e) {
throw e;
} catch (final Throwable e) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/StructureLoader.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/StructureLoader.java Thu May 16 11:47:51 2013 +0100
@@ -47,7 +47,7 @@
*
*/
final class StructureLoader extends NashornLoader {
- private static final String JS_OBJECT_PREFIX_EXTERNAL = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_PREFIX.tag();
+ private static final String JS_OBJECT_PREFIX_EXTERNAL = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_PREFIX.symbolName();
private static final String OBJECTS_PACKAGE_EXTERNAL = binaryName(OBJECTS_PACKAGE);
/**
@@ -110,7 +110,7 @@
@Override
protected Class<?> findClass(final String name) throws ClassNotFoundException {
if (name.startsWith(JS_OBJECT_PREFIX_EXTERNAL)) {
- final int start = name.indexOf(JS_OBJECT_PREFIX.tag()) + JS_OBJECT_PREFIX.tag().length();
+ final int start = name.indexOf(JS_OBJECT_PREFIX.symbolName()) + JS_OBJECT_PREFIX.symbolName().length();
return generateClass(name, name.substring(start, name.length()));
}
return super.findClass(name);
--- a/nashorn/src/jdk/nashorn/internal/runtime/WithObject.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/WithObject.java Thu May 16 11:47:51 2013 +0100
@@ -232,11 +232,18 @@
return (Scope) proto;
}
+ private static GuardedInvocation fixReceiverType(final GuardedInvocation link, final MethodHandle filter) {
+ // The receiver may be an Object or a ScriptObject.
+ final MethodType invType = link.getInvocation().type();
+ final MethodType newInvType = invType.changeParameterType(0, filter.type().returnType());
+ return link.asType(newInvType);
+ }
+
private static GuardedInvocation fixExpressionCallSite(final NashornCallSiteDescriptor desc, final GuardedInvocation link) {
// If it's not a getMethod, just add an expression filter that converts WithObject in "this" position to its
// expression.
if(!"getMethod".equals(desc.getFirstOperator())) {
- return link.filterArguments(0, WITHEXPRESSIONFILTER);
+ return fixReceiverType(link, WITHEXPRESSIONFILTER).filterArguments(0, WITHEXPRESSIONFILTER);
}
final MethodHandle linkInvocation = link.getInvocation();
@@ -252,7 +259,8 @@
}
private static GuardedInvocation fixScopeCallSite(final GuardedInvocation link) {
- return link.replaceMethods(filter(link.getInvocation(), WITHSCOPEFILTER), filterGuard(link, WITHSCOPEFILTER));
+ final GuardedInvocation newLink = fixReceiverType(link, WITHSCOPEFILTER);
+ return link.replaceMethods(filter(newLink.getInvocation(), WITHSCOPEFILTER), filterGuard(newLink, WITHSCOPEFILTER));
}
private static MethodHandle filterGuard(final GuardedInvocation link, final MethodHandle filter) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/ArrayIndex.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/ArrayIndex.java Thu May 16 11:47:51 2013 +0100
@@ -84,7 +84,9 @@
* @return valid array index, or negative value if not valid
*/
public static int getArrayIndexNoThrow(final Object key) {
- if (key instanceof Number) {
+ if (key instanceof Integer) {
+ return getArrayIndexNoThrow(((Integer)key).intValue());
+ } else if (key instanceof Number) {
return getArrayIndexNoThrow(((Number)key).doubleValue());
} else if (key instanceof String) {
return (int)fromString((String)key);
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/MapIterator.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/MapIterator.java Thu May 16 11:47:51 2013 +0100
@@ -66,8 +66,7 @@
bumpIndex();
}
- // special case - balk at iterating to infinity or MAX_UINT
- return (length != JSType.MAX_UINT) && indexInArray();
+ return indexInArray();
}
@Override
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java Thu May 16 11:47:51 2013 +0100
@@ -273,7 +273,7 @@
}
private static Long indexToKey(final int index) {
- return Long.valueOf(index & 0xffff_ffffL);
+ return Long.valueOf(index & JSType.MAX_UINT);
}
@Override
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/ClassAndLoader.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/ClassAndLoader.java Thu May 16 11:47:51 2013 +0100
@@ -185,4 +185,4 @@
}
return classesAndLoaders.keySet();
}
-}
\ No newline at end of file
+}
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Thu May 16 11:47:51 2013 +0100
@@ -127,9 +127,9 @@
private static final Type METHOD_TYPE_TYPE = Type.getType(MethodType.class);
private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
private static final String GET_HANDLE_OBJECT_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
- OBJECT_TYPE, STRING_TYPE, METHOD_TYPE_TYPE, Type.BOOLEAN_TYPE);
+ OBJECT_TYPE, STRING_TYPE, METHOD_TYPE_TYPE);
private static final String GET_HANDLE_FUNCTION_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
- SCRIPT_FUNCTION_TYPE, METHOD_TYPE_TYPE, Type.BOOLEAN_TYPE);
+ SCRIPT_FUNCTION_TYPE, METHOD_TYPE_TYPE);
private static final String GET_CLASS_INITIALIZER_DESCRIPTOR = Type.getMethodDescriptor(SCRIPT_OBJECT_TYPE);
private static final Type RUNTIME_EXCEPTION_TYPE = Type.getType(RuntimeException.class);
private static final Type THROWABLE_TYPE = Type.getType(Throwable.class);
@@ -315,7 +315,6 @@
mv.dup();
mv.aconst(mi.getName());
mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
- mv.iconst(mi.method.isVarArgs() ? 1 : 0);
mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_OBJECT_DESCRIPTOR);
mv.putstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
}
@@ -459,7 +458,6 @@
mv.aconst(mi.getName());
}
mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
- mv.iconst(mi.method.isVarArgs() ? 1 : 0);
mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor);
}
mv.putfield(generatedClassName, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java Thu May 16 11:47:51 2013 +0100
@@ -50,12 +50,11 @@
* handles for their abstract method implementations.
* @param fn the script function
* @param type the method type it has to conform to
- * @param varArg if the Java method for which the function is being adapted is a variable arity method
* @return the appropriately adapted method handle for invoking the script function.
*/
- public static MethodHandle getHandle(final ScriptFunction fn, final MethodType type, final boolean varArg) {
+ public static MethodHandle getHandle(final ScriptFunction fn, final MethodType type) {
// JS "this" will be null for SAMs
- return adaptHandle(fn.getBoundInvokeHandle(null), type, varArg);
+ return adaptHandle(fn.getBoundInvokeHandle(null), type);
}
/**
@@ -66,12 +65,11 @@
* @param obj the script obj
* @param name the name of the property that contains the function
* @param type the method type it has to conform to
- * @param varArg if the Java method for which the function is being adapted is a variable arity method
* @return the appropriately adapted method handle for invoking the script function, or null if the value of the
* property is either null or undefined, or "toString" was requested as the name, but the object doesn't directly
* define it but just inherits it through prototype.
*/
- public static MethodHandle getHandle(final Object obj, final String name, final MethodType type, final boolean varArg) {
+ public static MethodHandle getHandle(final Object obj, final String name, final MethodType type) {
if (! (obj instanceof ScriptObject)) {
throw typeError("not.an.object", ScriptRuntime.safeToString(obj));
}
@@ -84,7 +82,7 @@
final Object fnObj = sobj.get(name);
if (fnObj instanceof ScriptFunction) {
- return adaptHandle(((ScriptFunction)fnObj).getBoundInvokeHandle(sobj), type, varArg);
+ return adaptHandle(((ScriptFunction)fnObj).getBoundInvokeHandle(sobj), type);
} else if(fnObj == null || fnObj instanceof Undefined) {
return null;
} else {
@@ -108,7 +106,7 @@
classOverrides.set(overrides);
}
- private static MethodHandle adaptHandle(final MethodHandle handle, final MethodType type, final boolean varArg) {
- return Bootstrap.getLinkerServices().asType(ScriptObject.pairArguments(handle, type, varArg), type);
+ private static MethodHandle adaptHandle(final MethodHandle handle, final MethodType type) {
+ return Bootstrap.getLinkerServices().asType(ScriptObject.pairArguments(handle, type, false), type);
}
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java Thu May 16 11:47:51 2013 +0100
@@ -43,9 +43,8 @@
public static final int CALLSITE_SCOPE = 0x01;
/** Flags that the call site is in code that uses ECMAScript strict mode. */
public static final int CALLSITE_STRICT = 0x02;
- /** Flags that a property getter or setter call site references a scope variable that is not in the global scope
- * (it is in a function lexical scope), and the function's scope object class is fixed and known in advance. Such
- * getters and setters can often be linked more optimally using these assumptions. */
+ /** Flags that a property getter or setter call site references a scope variable that is located at a known distance
+ * in the scope chain. Such getters and setters can often be linked more optimally using these assumptions. */
public static final int CALLSITE_FAST_SCOPE = 0x400;
/** Flags that the call site is profiled; Contexts that have {@code "profile.callsites"} boolean property set emit
--- a/nashorn/src/jdk/nashorn/internal/runtime/options/Options.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/options/Options.java Thu May 16 11:47:51 2013 +0100
@@ -243,7 +243,13 @@
*/
public String getString(final String key) {
final Option<?> option = get(key);
- return option != null ? (String)option.getValue() : null;
+ if(option != null) {
+ final String value = (String)option.getValue();
+ if(value != null) {
+ return value.intern();
+ }
+ }
+ return null;
}
/**
--- a/nashorn/src/jdk/nashorn/internal/runtime/regexp/joni/Parser.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/regexp/joni/Parser.java Thu May 16 11:47:51 2013 +0100
@@ -287,7 +287,10 @@
if (syntax.allowDoubleRangeOpInCC()) {
env.ccEscWarn("-");
- parseCharClassSbChar(cc, arg); // goto sb_char /* [0-9-a] is allowed as [0-9\-a] */
+ arg.inType = CCVALTYPE.SB;
+ arg.v = '-';
+ arg.vIsRaw = false;
+ parseCharClassValEntry2(cc, arg); // goto val_entry2 /* [0-9-a] is allowed as [0-9\-a] */
break;
}
newSyntaxException(ERR_UNMATCHED_RANGE_SPECIFIER_IN_CHAR_CLASS);
--- a/nashorn/src/jdk/nashorn/internal/runtime/resources/Options.properties Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/Options.properties Thu May 16 11:47:51 2013 +0100
@@ -144,6 +144,26 @@
desc="Print full version info of Nashorn." \
}
+nashorn.option.function.statement.error= { \
+ name="--function-statement-error", \
+ desc="Report an error when function declaration is used as a statement.", \
+ is_undocumented=true, \
+ default=false \
+}
+
+nashorn.option.function.statement.warning = { \
+ name="--function-statement-warning", \
+ desc="Warn when function declaration is used as a statement.", \
+ is_undocumented=true, \
+ default=false \
+}
+
+nashorn.option.fx = { \
+ name="-fx", \
+ desc="Launch script as an fx application.", \
+ default=false \
+}
+
nashorn.option.log = { \
name="--log", \
is_undocumented=true, \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/fx/base.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+Scene = Java.type("javafx.scene.Scene");
+Group = Java.type("javafx.scene.Group");
+Stage = Java.type("javafx.stage.Stage");
+
+Binding = Java.type("javafx.beans.binding.Binding");
+Bindings = Java.type("javafx.beans.binding.Bindings");
+BooleanBinding = Java.type("javafx.beans.binding.BooleanBinding");
+BooleanExpression = Java.type("javafx.beans.binding.BooleanExpression");
+DoubleBinding = Java.type("javafx.beans.binding.DoubleBinding");
+DoubleExpression = Java.type("javafx.beans.binding.DoubleExpression");
+FloatBinding = Java.type("javafx.beans.binding.FloatBinding");
+FloatExpression = Java.type("javafx.beans.binding.FloatExpression");
+IntegerBinding = Java.type("javafx.beans.binding.IntegerBinding");
+IntegerExpression = Java.type("javafx.beans.binding.IntegerExpression");
+ListBinding = Java.type("javafx.beans.binding.ListBinding");
+ListExpression = Java.type("javafx.beans.binding.ListExpression");
+LongBinding = Java.type("javafx.beans.binding.LongBinding");
+LongExpression = Java.type("javafx.beans.binding.LongExpression");
+MapBinding = Java.type("javafx.beans.binding.MapBinding");
+MapExpression = Java.type("javafx.beans.binding.MapExpression");
+NumberBinding = Java.type("javafx.beans.binding.NumberBinding");
+NumberExpression = Java.type("javafx.beans.binding.NumberExpression");
+NumberExpressionBase = Java.type("javafx.beans.binding.NumberExpressionBase");
+ObjectBinding = Java.type("javafx.beans.binding.ObjectBinding");
+ObjectExpression = Java.type("javafx.beans.binding.ObjectExpression");
+SetBinding = Java.type("javafx.beans.binding.SetBinding");
+SetExpression = Java.type("javafx.beans.binding.SetExpression");
+StringBinding = Java.type("javafx.beans.binding.StringBinding");
+StringExpression = Java.type("javafx.beans.binding.StringExpression");
+When = Java.type("javafx.beans.binding.When");
+DefaultProperty = Java.type("javafx.beans.DefaultProperty");
+InvalidationListener = Java.type("javafx.beans.InvalidationListener");
+Observable = Java.type("javafx.beans.Observable");
+JavaBeanBooleanProperty = Java.type("javafx.beans.property.adapter.JavaBeanBooleanProperty");
+JavaBeanBooleanPropertyBuilder = Java.type("javafx.beans.property.adapter.JavaBeanBooleanPropertyBuilder");
+JavaBeanDoubleProperty = Java.type("javafx.beans.property.adapter.JavaBeanDoubleProperty");
+JavaBeanDoublePropertyBuilder = Java.type("javafx.beans.property.adapter.JavaBeanDoublePropertyBuilder");
+JavaBeanFloatProperty = Java.type("javafx.beans.property.adapter.JavaBeanFloatProperty");
+JavaBeanFloatPropertyBuilder = Java.type("javafx.beans.property.adapter.JavaBeanFloatPropertyBuilder");
+JavaBeanIntegerProperty = Java.type("javafx.beans.property.adapter.JavaBeanIntegerProperty");
+JavaBeanIntegerPropertyBuilder = Java.type("javafx.beans.property.adapter.JavaBeanIntegerPropertyBuilder");
+JavaBeanLongProperty = Java.type("javafx.beans.property.adapter.JavaBeanLongProperty");
+JavaBeanLongPropertyBuilder = Java.type("javafx.beans.property.adapter.JavaBeanLongPropertyBuilder");
+JavaBeanObjectProperty = Java.type("javafx.beans.property.adapter.JavaBeanObjectProperty");
+JavaBeanObjectPropertyBuilder = Java.type("javafx.beans.property.adapter.JavaBeanObjectPropertyBuilder");
+JavaBeanProperty = Java.type("javafx.beans.property.adapter.JavaBeanProperty");
+JavaBeanStringProperty = Java.type("javafx.beans.property.adapter.JavaBeanStringProperty");
+JavaBeanStringPropertyBuilder = Java.type("javafx.beans.property.adapter.JavaBeanStringPropertyBuilder");
+ReadOnlyJavaBeanBooleanProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanBooleanProperty");
+ReadOnlyJavaBeanBooleanPropertyBuilder = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanBooleanPropertyBuilder");
+ReadOnlyJavaBeanDoubleProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanDoubleProperty");
+ReadOnlyJavaBeanDoublePropertyBuilder = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanDoublePropertyBuilder");
+ReadOnlyJavaBeanFloatProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanFloatProperty");
+ReadOnlyJavaBeanFloatPropertyBuilder = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanFloatPropertyBuilder");
+ReadOnlyJavaBeanIntegerProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanIntegerProperty");
+ReadOnlyJavaBeanIntegerPropertyBuilder = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanIntegerPropertyBuilder");
+ReadOnlyJavaBeanLongProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanLongProperty");
+ReadOnlyJavaBeanLongPropertyBuilder = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanLongPropertyBuilder");
+ReadOnlyJavaBeanObjectProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanObjectProperty");
+ReadOnlyJavaBeanObjectPropertyBuilder = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanObjectPropertyBuilder");
+ReadOnlyJavaBeanProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanProperty");
+ReadOnlyJavaBeanStringProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanStringProperty");
+ReadOnlyJavaBeanStringPropertyBuilder = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanStringPropertyBuilder");
+BooleanProperty = Java.type("javafx.beans.property.BooleanProperty");
+BooleanPropertyBase = Java.type("javafx.beans.property.BooleanPropertyBase");
+DoubleProperty = Java.type("javafx.beans.property.DoubleProperty");
+DoublePropertyBase = Java.type("javafx.beans.property.DoublePropertyBase");
+FloatProperty = Java.type("javafx.beans.property.FloatProperty");
+FloatPropertyBase = Java.type("javafx.beans.property.FloatPropertyBase");
+IntegerProperty = Java.type("javafx.beans.property.IntegerProperty");
+IntegerPropertyBase = Java.type("javafx.beans.property.IntegerPropertyBase");
+ListProperty = Java.type("javafx.beans.property.ListProperty");
+ListPropertyBase = Java.type("javafx.beans.property.ListPropertyBase");
+LongProperty = Java.type("javafx.beans.property.LongProperty");
+LongPropertyBase = Java.type("javafx.beans.property.LongPropertyBase");
+MapProperty = Java.type("javafx.beans.property.MapProperty");
+MapPropertyBase = Java.type("javafx.beans.property.MapPropertyBase");
+ObjectProperty = Java.type("javafx.beans.property.ObjectProperty");
+ObjectPropertyBase = Java.type("javafx.beans.property.ObjectPropertyBase");
+Property = Java.type("javafx.beans.property.Property");
+ReadOnlyBooleanProperty = Java.type("javafx.beans.property.ReadOnlyBooleanProperty");
+ReadOnlyBooleanPropertyBase = Java.type("javafx.beans.property.ReadOnlyBooleanPropertyBase");
+ReadOnlyBooleanWrapper = Java.type("javafx.beans.property.ReadOnlyBooleanWrapper");
+ReadOnlyDoubleProperty = Java.type("javafx.beans.property.ReadOnlyDoubleProperty");
+ReadOnlyDoublePropertyBase = Java.type("javafx.beans.property.ReadOnlyDoublePropertyBase");
+ReadOnlyDoubleWrapper = Java.type("javafx.beans.property.ReadOnlyDoubleWrapper");
+ReadOnlyFloatProperty = Java.type("javafx.beans.property.ReadOnlyFloatProperty");
+ReadOnlyFloatPropertyBase = Java.type("javafx.beans.property.ReadOnlyFloatPropertyBase");
+ReadOnlyFloatWrapper = Java.type("javafx.beans.property.ReadOnlyFloatWrapper");
+ReadOnlyIntegerProperty = Java.type("javafx.beans.property.ReadOnlyIntegerProperty");
+ReadOnlyIntegerPropertyBase = Java.type("javafx.beans.property.ReadOnlyIntegerPropertyBase");
+ReadOnlyIntegerWrapper = Java.type("javafx.beans.property.ReadOnlyIntegerWrapper");
+ReadOnlyListProperty = Java.type("javafx.beans.property.ReadOnlyListProperty");
+ReadOnlyListPropertyBase = Java.type("javafx.beans.property.ReadOnlyListPropertyBase");
+ReadOnlyListWrapper = Java.type("javafx.beans.property.ReadOnlyListWrapper");
+ReadOnlyLongProperty = Java.type("javafx.beans.property.ReadOnlyLongProperty");
+ReadOnlyLongPropertyBase = Java.type("javafx.beans.property.ReadOnlyLongPropertyBase");
+ReadOnlyLongWrapper = Java.type("javafx.beans.property.ReadOnlyLongWrapper");
+ReadOnlyMapProperty = Java.type("javafx.beans.property.ReadOnlyMapProperty");
+ReadOnlyMapPropertyBase = Java.type("javafx.beans.property.ReadOnlyMapPropertyBase");
+ReadOnlyMapWrapper = Java.type("javafx.beans.property.ReadOnlyMapWrapper");
+ReadOnlyObjectProperty = Java.type("javafx.beans.property.ReadOnlyObjectProperty");
+ReadOnlyObjectPropertyBase = Java.type("javafx.beans.property.ReadOnlyObjectPropertyBase");
+ReadOnlyObjectWrapper = Java.type("javafx.beans.property.ReadOnlyObjectWrapper");
+ReadOnlyProperty = Java.type("javafx.beans.property.ReadOnlyProperty");
+ReadOnlySetProperty = Java.type("javafx.beans.property.ReadOnlySetProperty");
+ReadOnlySetPropertyBase = Java.type("javafx.beans.property.ReadOnlySetPropertyBase");
+ReadOnlySetWrapper = Java.type("javafx.beans.property.ReadOnlySetWrapper");
+ReadOnlyStringProperty = Java.type("javafx.beans.property.ReadOnlyStringProperty");
+ReadOnlyStringPropertyBase = Java.type("javafx.beans.property.ReadOnlyStringPropertyBase");
+ReadOnlyStringWrapper = Java.type("javafx.beans.property.ReadOnlyStringWrapper");
+SetProperty = Java.type("javafx.beans.property.SetProperty");
+SetPropertyBase = Java.type("javafx.beans.property.SetPropertyBase");
+SimpleBooleanProperty = Java.type("javafx.beans.property.SimpleBooleanProperty");
+SimpleDoubleProperty = Java.type("javafx.beans.property.SimpleDoubleProperty");
+SimpleFloatProperty = Java.type("javafx.beans.property.SimpleFloatProperty");
+SimpleIntegerProperty = Java.type("javafx.beans.property.SimpleIntegerProperty");
+SimpleListProperty = Java.type("javafx.beans.property.SimpleListProperty");
+SimpleLongProperty = Java.type("javafx.beans.property.SimpleLongProperty");
+SimpleMapProperty = Java.type("javafx.beans.property.SimpleMapProperty");
+SimpleObjectProperty = Java.type("javafx.beans.property.SimpleObjectProperty");
+SimpleSetProperty = Java.type("javafx.beans.property.SimpleSetProperty");
+SimpleStringProperty = Java.type("javafx.beans.property.SimpleStringProperty");
+StringProperty = Java.type("javafx.beans.property.StringProperty");
+StringPropertyBase = Java.type("javafx.beans.property.StringPropertyBase");
+ChangeListener = Java.type("javafx.beans.value.ChangeListener");
+ObservableBooleanValue = Java.type("javafx.beans.value.ObservableBooleanValue");
+ObservableDoubleValue = Java.type("javafx.beans.value.ObservableDoubleValue");
+ObservableFloatValue = Java.type("javafx.beans.value.ObservableFloatValue");
+ObservableIntegerValue = Java.type("javafx.beans.value.ObservableIntegerValue");
+ObservableListValue = Java.type("javafx.beans.value.ObservableListValue");
+ObservableLongValue = Java.type("javafx.beans.value.ObservableLongValue");
+ObservableMapValue = Java.type("javafx.beans.value.ObservableMapValue");
+ObservableNumberValue = Java.type("javafx.beans.value.ObservableNumberValue");
+ObservableObjectValue = Java.type("javafx.beans.value.ObservableObjectValue");
+ObservableSetValue = Java.type("javafx.beans.value.ObservableSetValue");
+ObservableStringValue = Java.type("javafx.beans.value.ObservableStringValue");
+ObservableValue = Java.type("javafx.beans.value.ObservableValue");
+ObservableValueBase = Java.type("javafx.beans.value.ObservableValueBase");
+WeakChangeListener = Java.type("javafx.beans.value.WeakChangeListener");
+WritableBooleanValue = Java.type("javafx.beans.value.WritableBooleanValue");
+WritableDoubleValue = Java.type("javafx.beans.value.WritableDoubleValue");
+WritableFloatValue = Java.type("javafx.beans.value.WritableFloatValue");
+WritableIntegerValue = Java.type("javafx.beans.value.WritableIntegerValue");
+WritableListValue = Java.type("javafx.beans.value.WritableListValue");
+WritableLongValue = Java.type("javafx.beans.value.WritableLongValue");
+WritableMapValue = Java.type("javafx.beans.value.WritableMapValue");
+WritableNumberValue = Java.type("javafx.beans.value.WritableNumberValue");
+WritableObjectValue = Java.type("javafx.beans.value.WritableObjectValue");
+WritableSetValue = Java.type("javafx.beans.value.WritableSetValue");
+WritableStringValue = Java.type("javafx.beans.value.WritableStringValue");
+WritableValue = Java.type("javafx.beans.value.WritableValue");
+WeakInvalidationListener = Java.type("javafx.beans.WeakInvalidationListener");
+WeakListener = Java.type("javafx.beans.WeakListener");
+FXCollections = Java.type("javafx.collections.FXCollections");
+ListChangeListener = Java.type("javafx.collections.ListChangeListener");
+ListChangeListener$Change = Java.type("javafx.collections.ListChangeListener$Change");
+MapChangeListener = Java.type("javafx.collections.MapChangeListener");
+MapChangeListener$Change = Java.type("javafx.collections.MapChangeListener$Change");
+ModifiableObservableListBase = Java.type("javafx.collections.ModifiableObservableListBase");
+ObservableList = Java.type("javafx.collections.ObservableList");
+ObservableListBase = Java.type("javafx.collections.ObservableListBase");
+ObservableMap = Java.type("javafx.collections.ObservableMap");
+ObservableSet = Java.type("javafx.collections.ObservableSet");
+SetChangeListener = Java.type("javafx.collections.SetChangeListener");
+SetChangeListener$Change = Java.type("javafx.collections.SetChangeListener$Change");
+WeakListChangeListener = Java.type("javafx.collections.WeakListChangeListener");
+WeakMapChangeListener = Java.type("javafx.collections.WeakMapChangeListener");
+WeakSetChangeListener = Java.type("javafx.collections.WeakSetChangeListener");
+ActionEvent = Java.type("javafx.event.ActionEvent");
+Event = Java.type("javafx.event.Event");
+EventDispatchChain = Java.type("javafx.event.EventDispatchChain");
+EventDispatcher = Java.type("javafx.event.EventDispatcher");
+EventHandler = Java.type("javafx.event.EventHandler");
+EventTarget = Java.type("javafx.event.EventTarget");
+EventType = Java.type("javafx.event.EventType");
+WeakEventHandler = Java.type("javafx.event.WeakEventHandler");
+Builder = Java.type("javafx.util.Builder");
+BuilderFactory = Java.type("javafx.util.BuilderFactory");
+Callback = Java.type("javafx.util.Callback");
+BigDecimalStringConverter = Java.type("javafx.util.converter.BigDecimalStringConverter");
+BigIntegerStringConverter = Java.type("javafx.util.converter.BigIntegerStringConverter");
+BooleanStringConverter = Java.type("javafx.util.converter.BooleanStringConverter");
+ByteStringConverter = Java.type("javafx.util.converter.ByteStringConverter");
+CharacterStringConverter = Java.type("javafx.util.converter.CharacterStringConverter");
+CurrencyStringConverter = Java.type("javafx.util.converter.CurrencyStringConverter");
+DateStringConverter = Java.type("javafx.util.converter.DateStringConverter");
+DateTimeStringConverter = Java.type("javafx.util.converter.DateTimeStringConverter");
+DefaultStringConverter = Java.type("javafx.util.converter.DefaultStringConverter");
+DoubleStringConverter = Java.type("javafx.util.converter.DoubleStringConverter");
+FloatStringConverter = Java.type("javafx.util.converter.FloatStringConverter");
+FormatStringConverter = Java.type("javafx.util.converter.FormatStringConverter");
+IntegerStringConverter = Java.type("javafx.util.converter.IntegerStringConverter");
+LongStringConverter = Java.type("javafx.util.converter.LongStringConverter");
+NumberStringConverter = Java.type("javafx.util.converter.NumberStringConverter");
+PercentageStringConverter = Java.type("javafx.util.converter.PercentageStringConverter");
+ShortStringConverter = Java.type("javafx.util.converter.ShortStringConverter");
+TimeStringConverter = Java.type("javafx.util.converter.TimeStringConverter");
+Duration = Java.type("javafx.util.Duration");
+Pair = Java.type("javafx.util.Pair");
+StringConverter = Java.type("javafx.util.StringConverter");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/fx/bootstrap.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+// Check for fx presence.
+if (typeof javafx.application.Application != "function") {
+ print("JavaFX is not available.");
+ exit(1);
+}
+
+// Extend the javafx.application.Application class overriding init, start and stop.
+com.sun.javafx.application.LauncherImpl.launchApplication((Java.extend(javafx.application.Application, {
+ // Overridden javafx.application.Application.init();
+ init: function() {
+ // Java FX packages and classes must be defined here because
+ // they may not be viable until launch time due to clinit ordering.
+
+ load("fx:base.js");
+ },
+
+ // Overridden javafx.application.Application.start(Stage stage);
+ start: function(stage) {
+ // Set up stage global.
+ $STAGE = stage;
+
+ // Load user FX scripts.
+ for each (var script in $SCRIPTS) {
+ load(script);
+ }
+
+ // Call the global init function if present.
+ if ($GLOBAL.init) {
+ init();
+ }
+
+ // Call the global start function if present. Otherwise show the stage.
+ if ($GLOBAL.start) {
+ start(stage);
+ } else {
+ stage.show();
+ }
+ },
+
+ // Overridden javafx.application.Application.stop();
+ stop: function() {
+ // Call the global stop function if present.
+ if ($GLOBAL.stop) {
+ stop();
+ }
+ }
+
+ // No arguments passed to application (handled thru $ARG.)
+})).class, new (Java.type("java.lang.String[]"))(0));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/fx/controls.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+AreaChart = Java.type("javafx.scene.chart.AreaChart");
+AreaChartBuilder = Java.type("javafx.scene.chart.AreaChartBuilder");
+Axis = Java.type("javafx.scene.chart.Axis");
+Axis$TickMark = Java.type("javafx.scene.chart.Axis$TickMark");
+AxisBuilder = Java.type("javafx.scene.chart.AxisBuilder");
+BarChart = Java.type("javafx.scene.chart.BarChart");
+BarChartBuilder = Java.type("javafx.scene.chart.BarChartBuilder");
+BubbleChart = Java.type("javafx.scene.chart.BubbleChart");
+BubbleChartBuilder = Java.type("javafx.scene.chart.BubbleChartBuilder");
+CategoryAxis = Java.type("javafx.scene.chart.CategoryAxis");
+CategoryAxisBuilder = Java.type("javafx.scene.chart.CategoryAxisBuilder");
+Chart = Java.type("javafx.scene.chart.Chart");
+ChartBuilder = Java.type("javafx.scene.chart.ChartBuilder");
+LineChart = Java.type("javafx.scene.chart.LineChart");
+LineChartBuilder = Java.type("javafx.scene.chart.LineChartBuilder");
+NumberAxis = Java.type("javafx.scene.chart.NumberAxis");
+NumberAxis$DefaultFormatter = Java.type("javafx.scene.chart.NumberAxis$DefaultFormatter");
+NumberAxisBuilder = Java.type("javafx.scene.chart.NumberAxisBuilder");
+PieChart = Java.type("javafx.scene.chart.PieChart");
+PieChart$Data = Java.type("javafx.scene.chart.PieChart$Data");
+PieChartBuilder = Java.type("javafx.scene.chart.PieChartBuilder");
+ScatterChart = Java.type("javafx.scene.chart.ScatterChart");
+ScatterChartBuilder = Java.type("javafx.scene.chart.ScatterChartBuilder");
+StackedAreaChart = Java.type("javafx.scene.chart.StackedAreaChart");
+StackedAreaChartBuilder = Java.type("javafx.scene.chart.StackedAreaChartBuilder");
+StackedBarChart = Java.type("javafx.scene.chart.StackedBarChart");
+StackedBarChartBuilder = Java.type("javafx.scene.chart.StackedBarChartBuilder");
+ValueAxis = Java.type("javafx.scene.chart.ValueAxis");
+ValueAxisBuilder = Java.type("javafx.scene.chart.ValueAxisBuilder");
+XYChart = Java.type("javafx.scene.chart.XYChart");
+XYChart$Data = Java.type("javafx.scene.chart.XYChart$Data");
+XYChart$Series = Java.type("javafx.scene.chart.XYChart$Series");
+XYChartBuilder = Java.type("javafx.scene.chart.XYChartBuilder");
+Accordion = Java.type("javafx.scene.control.Accordion");
+AccordionBuilder = Java.type("javafx.scene.control.AccordionBuilder");
+Button = Java.type("javafx.scene.control.Button");
+ButtonBase = Java.type("javafx.scene.control.ButtonBase");
+ButtonBaseBuilder = Java.type("javafx.scene.control.ButtonBaseBuilder");
+ButtonBuilder = Java.type("javafx.scene.control.ButtonBuilder");
+Cell = Java.type("javafx.scene.control.Cell");
+CheckBoxListCell = Java.type("javafx.scene.control.cell.CheckBoxListCell");
+CheckBoxListCellBuilder = Java.type("javafx.scene.control.cell.CheckBoxListCellBuilder");
+CheckBoxTableCell = Java.type("javafx.scene.control.cell.CheckBoxTableCell");
+CheckBoxTableCellBuilder = Java.type("javafx.scene.control.cell.CheckBoxTableCellBuilder");
+CheckBoxTreeCell = Java.type("javafx.scene.control.cell.CheckBoxTreeCell");
+CheckBoxTreeCellBuilder = Java.type("javafx.scene.control.cell.CheckBoxTreeCellBuilder");
+CheckBoxTreeTableCell = Java.type("javafx.scene.control.cell.CheckBoxTreeTableCell");
+CheckBoxTreeTableCellBuilder = Java.type("javafx.scene.control.cell.CheckBoxTreeTableCellBuilder");
+ChoiceBoxListCell = Java.type("javafx.scene.control.cell.ChoiceBoxListCell");
+ChoiceBoxListCellBuilder = Java.type("javafx.scene.control.cell.ChoiceBoxListCellBuilder");
+ChoiceBoxTableCell = Java.type("javafx.scene.control.cell.ChoiceBoxTableCell");
+ChoiceBoxTableCellBuilder = Java.type("javafx.scene.control.cell.ChoiceBoxTableCellBuilder");
+ChoiceBoxTreeCell = Java.type("javafx.scene.control.cell.ChoiceBoxTreeCell");
+ChoiceBoxTreeCellBuilder = Java.type("javafx.scene.control.cell.ChoiceBoxTreeCellBuilder");
+ChoiceBoxTreeTableCell = Java.type("javafx.scene.control.cell.ChoiceBoxTreeTableCell");
+ChoiceBoxTreeTableCellBuilder = Java.type("javafx.scene.control.cell.ChoiceBoxTreeTableCellBuilder");
+ComboBoxListCell = Java.type("javafx.scene.control.cell.ComboBoxListCell");
+ComboBoxListCellBuilder = Java.type("javafx.scene.control.cell.ComboBoxListCellBuilder");
+ComboBoxTableCell = Java.type("javafx.scene.control.cell.ComboBoxTableCell");
+ComboBoxTableCellBuilder = Java.type("javafx.scene.control.cell.ComboBoxTableCellBuilder");
+ComboBoxTreeCell = Java.type("javafx.scene.control.cell.ComboBoxTreeCell");
+ComboBoxTreeCellBuilder = Java.type("javafx.scene.control.cell.ComboBoxTreeCellBuilder");
+ComboBoxTreeTableCell = Java.type("javafx.scene.control.cell.ComboBoxTreeTableCell");
+ComboBoxTreeTableCellBuilder = Java.type("javafx.scene.control.cell.ComboBoxTreeTableCellBuilder");
+MapValueFactory = Java.type("javafx.scene.control.cell.MapValueFactory");
+ProgressBarTableCell = Java.type("javafx.scene.control.cell.ProgressBarTableCell");
+ProgressBarTreeTableCell = Java.type("javafx.scene.control.cell.ProgressBarTreeTableCell");
+PropertyValueFactory = Java.type("javafx.scene.control.cell.PropertyValueFactory");
+PropertyValueFactoryBuilder = Java.type("javafx.scene.control.cell.PropertyValueFactoryBuilder");
+TextFieldListCell = Java.type("javafx.scene.control.cell.TextFieldListCell");
+TextFieldListCellBuilder = Java.type("javafx.scene.control.cell.TextFieldListCellBuilder");
+TextFieldTableCell = Java.type("javafx.scene.control.cell.TextFieldTableCell");
+TextFieldTableCellBuilder = Java.type("javafx.scene.control.cell.TextFieldTableCellBuilder");
+TextFieldTreeCell = Java.type("javafx.scene.control.cell.TextFieldTreeCell");
+TextFieldTreeCellBuilder = Java.type("javafx.scene.control.cell.TextFieldTreeCellBuilder");
+TextFieldTreeTableCell = Java.type("javafx.scene.control.cell.TextFieldTreeTableCell");
+TextFieldTreeTableCellBuilder = Java.type("javafx.scene.control.cell.TextFieldTreeTableCellBuilder");
+TreeItemPropertyValueFactory = Java.type("javafx.scene.control.cell.TreeItemPropertyValueFactory");
+TreeItemPropertyValueFactoryBuilder = Java.type("javafx.scene.control.cell.TreeItemPropertyValueFactoryBuilder");
+CellBuilder = Java.type("javafx.scene.control.CellBuilder");
+CheckBox = Java.type("javafx.scene.control.CheckBox");
+CheckBoxBuilder = Java.type("javafx.scene.control.CheckBoxBuilder");
+CheckBoxTreeItem = Java.type("javafx.scene.control.CheckBoxTreeItem");
+CheckBoxTreeItem$TreeModificationEvent = Java.type("javafx.scene.control.CheckBoxTreeItem$TreeModificationEvent");
+CheckBoxTreeItemBuilder = Java.type("javafx.scene.control.CheckBoxTreeItemBuilder");
+CheckMenuItem = Java.type("javafx.scene.control.CheckMenuItem");
+CheckMenuItemBuilder = Java.type("javafx.scene.control.CheckMenuItemBuilder");
+ChoiceBox = Java.type("javafx.scene.control.ChoiceBox");
+ChoiceBoxBuilder = Java.type("javafx.scene.control.ChoiceBoxBuilder");
+ColorPicker = Java.type("javafx.scene.control.ColorPicker");
+ColorPickerBuilder = Java.type("javafx.scene.control.ColorPickerBuilder");
+ComboBox = Java.type("javafx.scene.control.ComboBox");
+ComboBoxBase = Java.type("javafx.scene.control.ComboBoxBase");
+ComboBoxBaseBuilder = Java.type("javafx.scene.control.ComboBoxBaseBuilder");
+ComboBoxBuilder = Java.type("javafx.scene.control.ComboBoxBuilder");
+ContentDisplay = Java.type("javafx.scene.control.ContentDisplay");
+ContextMenu = Java.type("javafx.scene.control.ContextMenu");
+ContextMenuBuilder = Java.type("javafx.scene.control.ContextMenuBuilder");
+Control = Java.type("javafx.scene.control.Control");
+ControlBuilder = Java.type("javafx.scene.control.ControlBuilder");
+CustomMenuItem = Java.type("javafx.scene.control.CustomMenuItem");
+CustomMenuItemBuilder = Java.type("javafx.scene.control.CustomMenuItemBuilder");
+FocusModel = Java.type("javafx.scene.control.FocusModel");
+Hyperlink = Java.type("javafx.scene.control.Hyperlink");
+HyperlinkBuilder = Java.type("javafx.scene.control.HyperlinkBuilder");
+IndexedCell = Java.type("javafx.scene.control.IndexedCell");
+IndexedCellBuilder = Java.type("javafx.scene.control.IndexedCellBuilder");
+IndexRange = Java.type("javafx.scene.control.IndexRange");
+IndexRangeBuilder = Java.type("javafx.scene.control.IndexRangeBuilder");
+Label = Java.type("javafx.scene.control.Label");
+LabelBuilder = Java.type("javafx.scene.control.LabelBuilder");
+Labeled = Java.type("javafx.scene.control.Labeled");
+LabeledBuilder = Java.type("javafx.scene.control.LabeledBuilder");
+ListCell = Java.type("javafx.scene.control.ListCell");
+ListCellBuilder = Java.type("javafx.scene.control.ListCellBuilder");
+ListView = Java.type("javafx.scene.control.ListView");
+ListView$EditEvent = Java.type("javafx.scene.control.ListView$EditEvent");
+ListViewBuilder = Java.type("javafx.scene.control.ListViewBuilder");
+Menu = Java.type("javafx.scene.control.Menu");
+MenuBar = Java.type("javafx.scene.control.MenuBar");
+MenuBarBuilder = Java.type("javafx.scene.control.MenuBarBuilder");
+MenuBuilder = Java.type("javafx.scene.control.MenuBuilder");
+MenuButton = Java.type("javafx.scene.control.MenuButton");
+MenuButtonBuilder = Java.type("javafx.scene.control.MenuButtonBuilder");
+MenuItem = Java.type("javafx.scene.control.MenuItem");
+MenuItemBuilder = Java.type("javafx.scene.control.MenuItemBuilder");
+MultipleSelectionModel = Java.type("javafx.scene.control.MultipleSelectionModel");
+MultipleSelectionModelBuilder = Java.type("javafx.scene.control.MultipleSelectionModelBuilder");
+OverrunStyle = Java.type("javafx.scene.control.OverrunStyle");
+Pagination = Java.type("javafx.scene.control.Pagination");
+PaginationBuilder = Java.type("javafx.scene.control.PaginationBuilder");
+PasswordField = Java.type("javafx.scene.control.PasswordField");
+PasswordFieldBuilder = Java.type("javafx.scene.control.PasswordFieldBuilder");
+PopupControl = Java.type("javafx.scene.control.PopupControl");
+PopupControlBuilder = Java.type("javafx.scene.control.PopupControlBuilder");
+ProgressBar = Java.type("javafx.scene.control.ProgressBar");
+ProgressBarBuilder = Java.type("javafx.scene.control.ProgressBarBuilder");
+ProgressIndicator = Java.type("javafx.scene.control.ProgressIndicator");
+ProgressIndicatorBuilder = Java.type("javafx.scene.control.ProgressIndicatorBuilder");
+RadioButton = Java.type("javafx.scene.control.RadioButton");
+RadioButtonBuilder = Java.type("javafx.scene.control.RadioButtonBuilder");
+RadioMenuItem = Java.type("javafx.scene.control.RadioMenuItem");
+RadioMenuItemBuilder = Java.type("javafx.scene.control.RadioMenuItemBuilder");
+ResizeFeaturesBase = Java.type("javafx.scene.control.ResizeFeaturesBase");
+ResizeFeaturesBaseBuilder = Java.type("javafx.scene.control.ResizeFeaturesBaseBuilder");
+ScrollBar = Java.type("javafx.scene.control.ScrollBar");
+ScrollBarBuilder = Java.type("javafx.scene.control.ScrollBarBuilder");
+ScrollPane = Java.type("javafx.scene.control.ScrollPane");
+ScrollPane$ScrollBarPolicy = Java.type("javafx.scene.control.ScrollPane$ScrollBarPolicy");
+ScrollPaneBuilder = Java.type("javafx.scene.control.ScrollPaneBuilder");
+ScrollToEvent = Java.type("javafx.scene.control.ScrollToEvent");
+SelectionMode = Java.type("javafx.scene.control.SelectionMode");
+SelectionModel = Java.type("javafx.scene.control.SelectionModel");
+Separator = Java.type("javafx.scene.control.Separator");
+SeparatorBuilder = Java.type("javafx.scene.control.SeparatorBuilder");
+SeparatorMenuItem = Java.type("javafx.scene.control.SeparatorMenuItem");
+SeparatorMenuItemBuilder = Java.type("javafx.scene.control.SeparatorMenuItemBuilder");
+SingleSelectionModel = Java.type("javafx.scene.control.SingleSelectionModel");
+Skin = Java.type("javafx.scene.control.Skin");
+SkinBase = Java.type("javafx.scene.control.SkinBase");
+SkinBaseBuilder = Java.type("javafx.scene.control.SkinBaseBuilder");
+Skinnable = Java.type("javafx.scene.control.Skinnable");
+Slider = Java.type("javafx.scene.control.Slider");
+SliderBuilder = Java.type("javafx.scene.control.SliderBuilder");
+SortEvent = Java.type("javafx.scene.control.SortEvent");
+SplitMenuButton = Java.type("javafx.scene.control.SplitMenuButton");
+SplitMenuButtonBuilder = Java.type("javafx.scene.control.SplitMenuButtonBuilder");
+SplitPane = Java.type("javafx.scene.control.SplitPane");
+SplitPane$Divider = Java.type("javafx.scene.control.SplitPane$Divider");
+SplitPaneBuilder = Java.type("javafx.scene.control.SplitPaneBuilder");
+Tab = Java.type("javafx.scene.control.Tab");
+TabBuilder = Java.type("javafx.scene.control.TabBuilder");
+TableCell = Java.type("javafx.scene.control.TableCell");
+TableCellBuilder = Java.type("javafx.scene.control.TableCellBuilder");
+TableColumn = Java.type("javafx.scene.control.TableColumn");
+TableColumn$CellDataFeatures = Java.type("javafx.scene.control.TableColumn$CellDataFeatures");
+TableColumn$CellEditEvent = Java.type("javafx.scene.control.TableColumn$CellEditEvent");
+TableColumn$SortType = Java.type("javafx.scene.control.TableColumn$SortType");
+TableColumnBase = Java.type("javafx.scene.control.TableColumnBase");
+TableColumnBaseBuilder = Java.type("javafx.scene.control.TableColumnBaseBuilder");
+TableColumnBuilder = Java.type("javafx.scene.control.TableColumnBuilder");
+TableFocusModel = Java.type("javafx.scene.control.TableFocusModel");
+TablePosition = Java.type("javafx.scene.control.TablePosition");
+TablePositionBase = Java.type("javafx.scene.control.TablePositionBase");
+TableRow = Java.type("javafx.scene.control.TableRow");
+TableRowBuilder = Java.type("javafx.scene.control.TableRowBuilder");
+TableSelectionModel = Java.type("javafx.scene.control.TableSelectionModel");
+TableSelectionModelBuilder = Java.type("javafx.scene.control.TableSelectionModelBuilder");
+TableView = Java.type("javafx.scene.control.TableView");
+TableView$ResizeFeatures = Java.type("javafx.scene.control.TableView$ResizeFeatures");
+TableView$TableViewFocusModel = Java.type("javafx.scene.control.TableView$TableViewFocusModel");
+TableView$TableViewSelectionModel = Java.type("javafx.scene.control.TableView$TableViewSelectionModel");
+TableViewBuilder = Java.type("javafx.scene.control.TableViewBuilder");
+TabPane = Java.type("javafx.scene.control.TabPane");
+TabPane$TabClosingPolicy = Java.type("javafx.scene.control.TabPane$TabClosingPolicy");
+TabPaneBuilder = Java.type("javafx.scene.control.TabPaneBuilder");
+TextArea = Java.type("javafx.scene.control.TextArea");
+TextAreaBuilder = Java.type("javafx.scene.control.TextAreaBuilder");
+TextField = Java.type("javafx.scene.control.TextField");
+TextFieldBuilder = Java.type("javafx.scene.control.TextFieldBuilder");
+TextInputControl = Java.type("javafx.scene.control.TextInputControl");
+TextInputControl$Content = Java.type("javafx.scene.control.TextInputControl$Content");
+TextInputControlBuilder = Java.type("javafx.scene.control.TextInputControlBuilder");
+TitledPane = Java.type("javafx.scene.control.TitledPane");
+TitledPaneBuilder = Java.type("javafx.scene.control.TitledPaneBuilder");
+Toggle = Java.type("javafx.scene.control.Toggle");
+ToggleButton = Java.type("javafx.scene.control.ToggleButton");
+ToggleButtonBuilder = Java.type("javafx.scene.control.ToggleButtonBuilder");
+ToggleGroup = Java.type("javafx.scene.control.ToggleGroup");
+ToggleGroupBuilder = Java.type("javafx.scene.control.ToggleGroupBuilder");
+ToolBar = Java.type("javafx.scene.control.ToolBar");
+ToolBarBuilder = Java.type("javafx.scene.control.ToolBarBuilder");
+Tooltip = Java.type("javafx.scene.control.Tooltip");
+TooltipBuilder = Java.type("javafx.scene.control.TooltipBuilder");
+TreeCell = Java.type("javafx.scene.control.TreeCell");
+TreeCellBuilder = Java.type("javafx.scene.control.TreeCellBuilder");
+TreeItem = Java.type("javafx.scene.control.TreeItem");
+TreeItem$TreeModificationEvent = Java.type("javafx.scene.control.TreeItem$TreeModificationEvent");
+TreeItemBuilder = Java.type("javafx.scene.control.TreeItemBuilder");
+TreeSortMode = Java.type("javafx.scene.control.TreeSortMode");
+TreeTableCell = Java.type("javafx.scene.control.TreeTableCell");
+TreeTableCellBuilder = Java.type("javafx.scene.control.TreeTableCellBuilder");
+TreeTableColumn = Java.type("javafx.scene.control.TreeTableColumn");
+TreeTableColumn$CellDataFeatures = Java.type("javafx.scene.control.TreeTableColumn$CellDataFeatures");
+TreeTableColumn$CellEditEvent = Java.type("javafx.scene.control.TreeTableColumn$CellEditEvent");
+TreeTableColumn$SortType = Java.type("javafx.scene.control.TreeTableColumn$SortType");
+TreeTableColumnBuilder = Java.type("javafx.scene.control.TreeTableColumnBuilder");
+TreeTablePosition = Java.type("javafx.scene.control.TreeTablePosition");
+TreeTableRow = Java.type("javafx.scene.control.TreeTableRow");
+TreeTableRowBuilder = Java.type("javafx.scene.control.TreeTableRowBuilder");
+TreeTableView = Java.type("javafx.scene.control.TreeTableView");
+TreeTableView$EditEvent = Java.type("javafx.scene.control.TreeTableView$EditEvent");
+TreeTableView$ResizeFeatures = Java.type("javafx.scene.control.TreeTableView$ResizeFeatures");
+TreeTableView$TreeTableViewFocusModel = Java.type("javafx.scene.control.TreeTableView$TreeTableViewFocusModel");
+TreeTableView$TreeTableViewSelectionModel = Java.type("javafx.scene.control.TreeTableView$TreeTableViewSelectionModel");
+TreeTableViewBuilder = Java.type("javafx.scene.control.TreeTableViewBuilder");
+TreeView = Java.type("javafx.scene.control.TreeView");
+TreeView$EditEvent = Java.type("javafx.scene.control.TreeView$EditEvent");
+TreeViewBuilder = Java.type("javafx.scene.control.TreeViewBuilder");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/fx/fxml.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+FXML = Java.type("javafx.fxml.FXML");
+FXMLLoader = Java.type("javafx.fxml.FXMLLoader");
+Initializable = Java.type("javafx.fxml.Initializable");
+JavaFXBuilderFactory = Java.type("javafx.fxml.JavaFXBuilderFactory");
+LoadException = Java.type("javafx.fxml.LoadException");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/fx/graphics.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,431 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+Animation = Java.type("javafx.animation.Animation");
+Animation$Status = Java.type("javafx.animation.Animation$Status");
+AnimationBuilder = Java.type("javafx.animation.AnimationBuilder");
+AnimationTimer = Java.type("javafx.animation.AnimationTimer");
+FadeTransition = Java.type("javafx.animation.FadeTransition");
+FadeTransitionBuilder = Java.type("javafx.animation.FadeTransitionBuilder");
+FillTransition = Java.type("javafx.animation.FillTransition");
+FillTransitionBuilder = Java.type("javafx.animation.FillTransitionBuilder");
+Interpolatable = Java.type("javafx.animation.Interpolatable");
+Interpolator = Java.type("javafx.animation.Interpolator");
+KeyFrame = Java.type("javafx.animation.KeyFrame");
+KeyValue = Java.type("javafx.animation.KeyValue");
+ParallelTransition = Java.type("javafx.animation.ParallelTransition");
+ParallelTransitionBuilder = Java.type("javafx.animation.ParallelTransitionBuilder");
+PathTransition = Java.type("javafx.animation.PathTransition");
+PathTransition$OrientationType = Java.type("javafx.animation.PathTransition$OrientationType");
+PathTransitionBuilder = Java.type("javafx.animation.PathTransitionBuilder");
+PauseTransition = Java.type("javafx.animation.PauseTransition");
+PauseTransitionBuilder = Java.type("javafx.animation.PauseTransitionBuilder");
+RotateTransition = Java.type("javafx.animation.RotateTransition");
+RotateTransitionBuilder = Java.type("javafx.animation.RotateTransitionBuilder");
+ScaleTransition = Java.type("javafx.animation.ScaleTransition");
+ScaleTransitionBuilder = Java.type("javafx.animation.ScaleTransitionBuilder");
+SequentialTransition = Java.type("javafx.animation.SequentialTransition");
+SequentialTransitionBuilder = Java.type("javafx.animation.SequentialTransitionBuilder");
+StrokeTransition = Java.type("javafx.animation.StrokeTransition");
+StrokeTransitionBuilder = Java.type("javafx.animation.StrokeTransitionBuilder");
+Timeline = Java.type("javafx.animation.Timeline");
+TimelineBuilder = Java.type("javafx.animation.TimelineBuilder");
+Transition = Java.type("javafx.animation.Transition");
+TransitionBuilder = Java.type("javafx.animation.TransitionBuilder");
+TranslateTransition = Java.type("javafx.animation.TranslateTransition");
+TranslateTransitionBuilder = Java.type("javafx.animation.TranslateTransitionBuilder");
+Application = Java.type("javafx.application.Application");
+Application$Parameters = Java.type("javafx.application.Application$Parameters");
+ConditionalFeature = Java.type("javafx.application.ConditionalFeature");
+HostServices = Java.type("javafx.application.HostServices");
+Platform = Java.type("javafx.application.Platform");
+Preloader = Java.type("javafx.application.Preloader");
+Preloader$ErrorNotification = Java.type("javafx.application.Preloader$ErrorNotification");
+Preloader$PreloaderNotification = Java.type("javafx.application.Preloader$PreloaderNotification");
+Preloader$ProgressNotification = Java.type("javafx.application.Preloader$ProgressNotification");
+Preloader$StateChangeNotification = Java.type("javafx.application.Preloader$StateChangeNotification");
+Preloader$StateChangeNotification$Type = Java.type("javafx.application.Preloader$StateChangeNotification$Type");
+ScheduledService = Java.type("javafx.concurrent.ScheduledService");
+Service = Java.type("javafx.concurrent.Service");
+Task = Java.type("javafx.concurrent.Task");
+Worker = Java.type("javafx.concurrent.Worker");
+Worker$State = Java.type("javafx.concurrent.Worker$State");
+WorkerStateEvent = Java.type("javafx.concurrent.WorkerStateEvent");
+CssMetaData = Java.type("javafx.css.CssMetaData");
+FontCssMetaData = Java.type("javafx.css.FontCssMetaData");
+ParsedValue = Java.type("javafx.css.ParsedValue");
+PseudoClass = Java.type("javafx.css.PseudoClass");
+SimpleStyleableBooleanProperty = Java.type("javafx.css.SimpleStyleableBooleanProperty");
+SimpleStyleableDoubleProperty = Java.type("javafx.css.SimpleStyleableDoubleProperty");
+SimpleStyleableFloatProperty = Java.type("javafx.css.SimpleStyleableFloatProperty");
+SimpleStyleableIntegerProperty = Java.type("javafx.css.SimpleStyleableIntegerProperty");
+SimpleStyleableLongProperty = Java.type("javafx.css.SimpleStyleableLongProperty");
+SimpleStyleableObjectProperty = Java.type("javafx.css.SimpleStyleableObjectProperty");
+SimpleStyleableStringProperty = Java.type("javafx.css.SimpleStyleableStringProperty");
+Styleable = Java.type("javafx.css.Styleable");
+StyleableBooleanProperty = Java.type("javafx.css.StyleableBooleanProperty");
+StyleableDoubleProperty = Java.type("javafx.css.StyleableDoubleProperty");
+StyleableFloatProperty = Java.type("javafx.css.StyleableFloatProperty");
+StyleableIntegerProperty = Java.type("javafx.css.StyleableIntegerProperty");
+StyleableLongProperty = Java.type("javafx.css.StyleableLongProperty");
+StyleableObjectProperty = Java.type("javafx.css.StyleableObjectProperty");
+StyleableProperty = Java.type("javafx.css.StyleableProperty");
+StyleableStringProperty = Java.type("javafx.css.StyleableStringProperty");
+StyleConverter = Java.type("javafx.css.StyleConverter");
+StyleOrigin = Java.type("javafx.css.StyleOrigin");
+BoundingBox = Java.type("javafx.geometry.BoundingBox");
+BoundingBoxBuilder = Java.type("javafx.geometry.BoundingBoxBuilder");
+Bounds = Java.type("javafx.geometry.Bounds");
+Dimension2D = Java.type("javafx.geometry.Dimension2D");
+Dimension2DBuilder = Java.type("javafx.geometry.Dimension2DBuilder");
+HorizontalDirection = Java.type("javafx.geometry.HorizontalDirection");
+HPos = Java.type("javafx.geometry.HPos");
+Insets = Java.type("javafx.geometry.Insets");
+InsetsBuilder = Java.type("javafx.geometry.InsetsBuilder");
+NodeOrientation = Java.type("javafx.geometry.NodeOrientation");
+Orientation = Java.type("javafx.geometry.Orientation");
+Point2D = Java.type("javafx.geometry.Point2D");
+Point2DBuilder = Java.type("javafx.geometry.Point2DBuilder");
+Point3D = Java.type("javafx.geometry.Point3D");
+Point3DBuilder = Java.type("javafx.geometry.Point3DBuilder");
+Pos = Java.type("javafx.geometry.Pos");
+Rectangle2D = Java.type("javafx.geometry.Rectangle2D");
+Rectangle2DBuilder = Java.type("javafx.geometry.Rectangle2DBuilder");
+Side = Java.type("javafx.geometry.Side");
+VerticalDirection = Java.type("javafx.geometry.VerticalDirection");
+VPos = Java.type("javafx.geometry.VPos");
+Collation = Java.type("javafx.print.Collation");
+JobSettings = Java.type("javafx.print.JobSettings");
+PageLayout = Java.type("javafx.print.PageLayout");
+PageOrientation = Java.type("javafx.print.PageOrientation");
+PageRange = Java.type("javafx.print.PageRange");
+Paper = Java.type("javafx.print.Paper");
+Paper$Units = Java.type("javafx.print.Paper$Units");
+PaperSource = Java.type("javafx.print.PaperSource");
+PrintColor = Java.type("javafx.print.PrintColor");
+Printer = Java.type("javafx.print.Printer");
+Printer$MarginType = Java.type("javafx.print.Printer$MarginType");
+PrinterAttributes = Java.type("javafx.print.PrinterAttributes");
+PrinterJob = Java.type("javafx.print.PrinterJob");
+PrinterJob$JobStatus = Java.type("javafx.print.PrinterJob$JobStatus");
+PrintQuality = Java.type("javafx.print.PrintQuality");
+PrintResolution = Java.type("javafx.print.PrintResolution");
+PrintSides = Java.type("javafx.print.PrintSides");
+AmbientLight = Java.type("javafx.scene.AmbientLight");
+AmbientLightBuilder = Java.type("javafx.scene.AmbientLightBuilder");
+CacheHint = Java.type("javafx.scene.CacheHint");
+Camera = Java.type("javafx.scene.Camera");
+CameraBuilder = Java.type("javafx.scene.CameraBuilder");
+Canvas = Java.type("javafx.scene.canvas.Canvas");
+CanvasBuilder = Java.type("javafx.scene.canvas.CanvasBuilder");
+GraphicsContext = Java.type("javafx.scene.canvas.GraphicsContext");
+Cursor = Java.type("javafx.scene.Cursor");
+DepthTest = Java.type("javafx.scene.DepthTest");
+Blend = Java.type("javafx.scene.effect.Blend");
+BlendBuilder = Java.type("javafx.scene.effect.BlendBuilder");
+BlendMode = Java.type("javafx.scene.effect.BlendMode");
+Bloom = Java.type("javafx.scene.effect.Bloom");
+BloomBuilder = Java.type("javafx.scene.effect.BloomBuilder");
+BlurType = Java.type("javafx.scene.effect.BlurType");
+BoxBlur = Java.type("javafx.scene.effect.BoxBlur");
+BoxBlurBuilder = Java.type("javafx.scene.effect.BoxBlurBuilder");
+ColorAdjust = Java.type("javafx.scene.effect.ColorAdjust");
+ColorAdjustBuilder = Java.type("javafx.scene.effect.ColorAdjustBuilder");
+ColorInput = Java.type("javafx.scene.effect.ColorInput");
+ColorInputBuilder = Java.type("javafx.scene.effect.ColorInputBuilder");
+DisplacementMap = Java.type("javafx.scene.effect.DisplacementMap");
+DisplacementMapBuilder = Java.type("javafx.scene.effect.DisplacementMapBuilder");
+DropShadow = Java.type("javafx.scene.effect.DropShadow");
+DropShadowBuilder = Java.type("javafx.scene.effect.DropShadowBuilder");
+Effect = Java.type("javafx.scene.effect.Effect");
+FloatMap = Java.type("javafx.scene.effect.FloatMap");
+FloatMapBuilder = Java.type("javafx.scene.effect.FloatMapBuilder");
+GaussianBlur = Java.type("javafx.scene.effect.GaussianBlur");
+GaussianBlurBuilder = Java.type("javafx.scene.effect.GaussianBlurBuilder");
+Glow = Java.type("javafx.scene.effect.Glow");
+GlowBuilder = Java.type("javafx.scene.effect.GlowBuilder");
+ImageInput = Java.type("javafx.scene.effect.ImageInput");
+ImageInputBuilder = Java.type("javafx.scene.effect.ImageInputBuilder");
+InnerShadow = Java.type("javafx.scene.effect.InnerShadow");
+InnerShadowBuilder = Java.type("javafx.scene.effect.InnerShadowBuilder");
+Light = Java.type("javafx.scene.effect.Light");
+Light$Distant = Java.type("javafx.scene.effect.Light$Distant");
+Light$Point = Java.type("javafx.scene.effect.Light$Point");
+Light$Spot = Java.type("javafx.scene.effect.Light$Spot");
+LightBuilder = Java.type("javafx.scene.effect.LightBuilder");
+Lighting = Java.type("javafx.scene.effect.Lighting");
+LightingBuilder = Java.type("javafx.scene.effect.LightingBuilder");
+MotionBlur = Java.type("javafx.scene.effect.MotionBlur");
+MotionBlurBuilder = Java.type("javafx.scene.effect.MotionBlurBuilder");
+PerspectiveTransform = Java.type("javafx.scene.effect.PerspectiveTransform");
+PerspectiveTransformBuilder = Java.type("javafx.scene.effect.PerspectiveTransformBuilder");
+Reflection = Java.type("javafx.scene.effect.Reflection");
+ReflectionBuilder = Java.type("javafx.scene.effect.ReflectionBuilder");
+SepiaTone = Java.type("javafx.scene.effect.SepiaTone");
+SepiaToneBuilder = Java.type("javafx.scene.effect.SepiaToneBuilder");
+Shadow = Java.type("javafx.scene.effect.Shadow");
+ShadowBuilder = Java.type("javafx.scene.effect.ShadowBuilder");
+//Group = Java.type("javafx.scene.Group");
+GroupBuilder = Java.type("javafx.scene.GroupBuilder");
+Image = Java.type("javafx.scene.image.Image");
+ImageView = Java.type("javafx.scene.image.ImageView");
+ImageViewBuilder = Java.type("javafx.scene.image.ImageViewBuilder");
+PixelFormat = Java.type("javafx.scene.image.PixelFormat");
+PixelFormat$Type = Java.type("javafx.scene.image.PixelFormat$Type");
+PixelReader = Java.type("javafx.scene.image.PixelReader");
+PixelWriter = Java.type("javafx.scene.image.PixelWriter");
+WritableImage = Java.type("javafx.scene.image.WritableImage");
+WritablePixelFormat = Java.type("javafx.scene.image.WritablePixelFormat");
+ImageCursor = Java.type("javafx.scene.ImageCursor");
+ImageCursorBuilder = Java.type("javafx.scene.ImageCursorBuilder");
+Clipboard = Java.type("javafx.scene.input.Clipboard");
+ClipboardContent = Java.type("javafx.scene.input.ClipboardContent");
+ClipboardContentBuilder = Java.type("javafx.scene.input.ClipboardContentBuilder");
+ContextMenuEvent = Java.type("javafx.scene.input.ContextMenuEvent");
+DataFormat = Java.type("javafx.scene.input.DataFormat");
+Dragboard = Java.type("javafx.scene.input.Dragboard");
+DragEvent = Java.type("javafx.scene.input.DragEvent");
+GestureEvent = Java.type("javafx.scene.input.GestureEvent");
+InputEvent = Java.type("javafx.scene.input.InputEvent");
+InputEventBuilder = Java.type("javafx.scene.input.InputEventBuilder");
+InputMethodEvent = Java.type("javafx.scene.input.InputMethodEvent");
+InputMethodHighlight = Java.type("javafx.scene.input.InputMethodHighlight");
+InputMethodRequests = Java.type("javafx.scene.input.InputMethodRequests");
+InputMethodTextRun = Java.type("javafx.scene.input.InputMethodTextRun");
+InputMethodTextRunBuilder = Java.type("javafx.scene.input.InputMethodTextRunBuilder");
+KeyCharacterCombination = Java.type("javafx.scene.input.KeyCharacterCombination");
+KeyCharacterCombinationBuilder = Java.type("javafx.scene.input.KeyCharacterCombinationBuilder");
+KeyCode = Java.type("javafx.scene.input.KeyCode");
+KeyCodeCombination = Java.type("javafx.scene.input.KeyCodeCombination");
+KeyCodeCombinationBuilder = Java.type("javafx.scene.input.KeyCodeCombinationBuilder");
+KeyCombination = Java.type("javafx.scene.input.KeyCombination");
+KeyCombination$Modifier = Java.type("javafx.scene.input.KeyCombination$Modifier");
+KeyCombination$ModifierValue = Java.type("javafx.scene.input.KeyCombination$ModifierValue");
+KeyEvent = Java.type("javafx.scene.input.KeyEvent");
+Mnemonic = Java.type("javafx.scene.input.Mnemonic");
+MnemonicBuilder = Java.type("javafx.scene.input.MnemonicBuilder");
+MouseButton = Java.type("javafx.scene.input.MouseButton");
+MouseDragEvent = Java.type("javafx.scene.input.MouseDragEvent");
+MouseEvent = Java.type("javafx.scene.input.MouseEvent");
+PickResult = Java.type("javafx.scene.input.PickResult");
+RotateEvent = Java.type("javafx.scene.input.RotateEvent");
+ScrollEvent = Java.type("javafx.scene.input.ScrollEvent");
+ScrollEvent$HorizontalTextScrollUnits = Java.type("javafx.scene.input.ScrollEvent$HorizontalTextScrollUnits");
+ScrollEvent$VerticalTextScrollUnits = Java.type("javafx.scene.input.ScrollEvent$VerticalTextScrollUnits");
+SwipeEvent = Java.type("javafx.scene.input.SwipeEvent");
+TouchEvent = Java.type("javafx.scene.input.TouchEvent");
+TouchPoint = Java.type("javafx.scene.input.TouchPoint");
+TouchPoint$State = Java.type("javafx.scene.input.TouchPoint$State");
+TouchPointBuilder = Java.type("javafx.scene.input.TouchPointBuilder");
+TransferMode = Java.type("javafx.scene.input.TransferMode");
+ZoomEvent = Java.type("javafx.scene.input.ZoomEvent");
+AnchorPane = Java.type("javafx.scene.layout.AnchorPane");
+AnchorPaneBuilder = Java.type("javafx.scene.layout.AnchorPaneBuilder");
+Background = Java.type("javafx.scene.layout.Background");
+BackgroundBuilder = Java.type("javafx.scene.layout.BackgroundBuilder");
+BackgroundFill = Java.type("javafx.scene.layout.BackgroundFill");
+BackgroundFillBuilder = Java.type("javafx.scene.layout.BackgroundFillBuilder");
+BackgroundImage = Java.type("javafx.scene.layout.BackgroundImage");
+BackgroundImageBuilder = Java.type("javafx.scene.layout.BackgroundImageBuilder");
+BackgroundPosition = Java.type("javafx.scene.layout.BackgroundPosition");
+BackgroundPositionBuilder = Java.type("javafx.scene.layout.BackgroundPositionBuilder");
+BackgroundRepeat = Java.type("javafx.scene.layout.BackgroundRepeat");
+BackgroundSize = Java.type("javafx.scene.layout.BackgroundSize");
+BackgroundSizeBuilder = Java.type("javafx.scene.layout.BackgroundSizeBuilder");
+Border = Java.type("javafx.scene.layout.Border");
+BorderBuilder = Java.type("javafx.scene.layout.BorderBuilder");
+BorderImage = Java.type("javafx.scene.layout.BorderImage");
+BorderImageBuilder = Java.type("javafx.scene.layout.BorderImageBuilder");
+BorderPane = Java.type("javafx.scene.layout.BorderPane");
+BorderPaneBuilder = Java.type("javafx.scene.layout.BorderPaneBuilder");
+BorderRepeat = Java.type("javafx.scene.layout.BorderRepeat");
+BorderStroke = Java.type("javafx.scene.layout.BorderStroke");
+BorderStrokeBuilder = Java.type("javafx.scene.layout.BorderStrokeBuilder");
+BorderStrokeStyle = Java.type("javafx.scene.layout.BorderStrokeStyle");
+BorderStrokeStyleBuilder = Java.type("javafx.scene.layout.BorderStrokeStyleBuilder");
+BorderWidths = Java.type("javafx.scene.layout.BorderWidths");
+BorderWidthsBuilder = Java.type("javafx.scene.layout.BorderWidthsBuilder");
+ColumnConstraints = Java.type("javafx.scene.layout.ColumnConstraints");
+ColumnConstraintsBuilder = Java.type("javafx.scene.layout.ColumnConstraintsBuilder");
+ConstraintsBase = Java.type("javafx.scene.layout.ConstraintsBase");
+CornerRadii = Java.type("javafx.scene.layout.CornerRadii");
+FlowPane = Java.type("javafx.scene.layout.FlowPane");
+FlowPaneBuilder = Java.type("javafx.scene.layout.FlowPaneBuilder");
+GridPane = Java.type("javafx.scene.layout.GridPane");
+GridPaneBuilder = Java.type("javafx.scene.layout.GridPaneBuilder");
+HBox = Java.type("javafx.scene.layout.HBox");
+HBoxBuilder = Java.type("javafx.scene.layout.HBoxBuilder");
+Pane = Java.type("javafx.scene.layout.Pane");
+PaneBuilder = Java.type("javafx.scene.layout.PaneBuilder");
+Priority = Java.type("javafx.scene.layout.Priority");
+Region = Java.type("javafx.scene.layout.Region");
+RegionBuilder = Java.type("javafx.scene.layout.RegionBuilder");
+RowConstraints = Java.type("javafx.scene.layout.RowConstraints");
+RowConstraintsBuilder = Java.type("javafx.scene.layout.RowConstraintsBuilder");
+StackPane = Java.type("javafx.scene.layout.StackPane");
+StackPaneBuilder = Java.type("javafx.scene.layout.StackPaneBuilder");
+TilePane = Java.type("javafx.scene.layout.TilePane");
+TilePaneBuilder = Java.type("javafx.scene.layout.TilePaneBuilder");
+VBox = Java.type("javafx.scene.layout.VBox");
+VBoxBuilder = Java.type("javafx.scene.layout.VBoxBuilder");
+LightBase = Java.type("javafx.scene.LightBase");
+LightBaseBuilder = Java.type("javafx.scene.LightBaseBuilder");
+Node = Java.type("javafx.scene.Node");
+NodeBuilder = Java.type("javafx.scene.NodeBuilder");
+Color = Java.type("javafx.scene.paint.Color");
+ColorBuilder = Java.type("javafx.scene.paint.ColorBuilder");
+CycleMethod = Java.type("javafx.scene.paint.CycleMethod");
+ImagePattern = Java.type("javafx.scene.paint.ImagePattern");
+ImagePatternBuilder = Java.type("javafx.scene.paint.ImagePatternBuilder");
+LinearGradient = Java.type("javafx.scene.paint.LinearGradient");
+LinearGradientBuilder = Java.type("javafx.scene.paint.LinearGradientBuilder");
+Material = Java.type("javafx.scene.paint.Material");
+Paint = Java.type("javafx.scene.paint.Paint");
+PhongMaterial = Java.type("javafx.scene.paint.PhongMaterial");
+PhongMaterialBuilder = Java.type("javafx.scene.paint.PhongMaterialBuilder");
+RadialGradient = Java.type("javafx.scene.paint.RadialGradient");
+RadialGradientBuilder = Java.type("javafx.scene.paint.RadialGradientBuilder");
+Stop = Java.type("javafx.scene.paint.Stop");
+StopBuilder = Java.type("javafx.scene.paint.StopBuilder");
+ParallelCamera = Java.type("javafx.scene.ParallelCamera");
+ParallelCameraBuilder = Java.type("javafx.scene.ParallelCameraBuilder");
+Parent = Java.type("javafx.scene.Parent");
+ParentBuilder = Java.type("javafx.scene.ParentBuilder");
+PerspectiveCamera = Java.type("javafx.scene.PerspectiveCamera");
+PerspectiveCameraBuilder = Java.type("javafx.scene.PerspectiveCameraBuilder");
+PointLight = Java.type("javafx.scene.PointLight");
+PointLightBuilder = Java.type("javafx.scene.PointLightBuilder");
+//Scene = Java.type("javafx.scene.Scene");
+SceneBuilder = Java.type("javafx.scene.SceneBuilder");
+Arc = Java.type("javafx.scene.shape.Arc");
+ArcBuilder = Java.type("javafx.scene.shape.ArcBuilder");
+ArcTo = Java.type("javafx.scene.shape.ArcTo");
+ArcToBuilder = Java.type("javafx.scene.shape.ArcToBuilder");
+ArcType = Java.type("javafx.scene.shape.ArcType");
+Box = Java.type("javafx.scene.shape.Box");
+BoxBuilder = Java.type("javafx.scene.shape.BoxBuilder");
+Circle = Java.type("javafx.scene.shape.Circle");
+CircleBuilder = Java.type("javafx.scene.shape.CircleBuilder");
+ClosePath = Java.type("javafx.scene.shape.ClosePath");
+ClosePathBuilder = Java.type("javafx.scene.shape.ClosePathBuilder");
+CubicCurve = Java.type("javafx.scene.shape.CubicCurve");
+CubicCurveBuilder = Java.type("javafx.scene.shape.CubicCurveBuilder");
+CubicCurveTo = Java.type("javafx.scene.shape.CubicCurveTo");
+CubicCurveToBuilder = Java.type("javafx.scene.shape.CubicCurveToBuilder");
+CullFace = Java.type("javafx.scene.shape.CullFace");
+Cylinder = Java.type("javafx.scene.shape.Cylinder");
+CylinderBuilder = Java.type("javafx.scene.shape.CylinderBuilder");
+DrawMode = Java.type("javafx.scene.shape.DrawMode");
+Ellipse = Java.type("javafx.scene.shape.Ellipse");
+EllipseBuilder = Java.type("javafx.scene.shape.EllipseBuilder");
+FillRule = Java.type("javafx.scene.shape.FillRule");
+HLineTo = Java.type("javafx.scene.shape.HLineTo");
+HLineToBuilder = Java.type("javafx.scene.shape.HLineToBuilder");
+Line = Java.type("javafx.scene.shape.Line");
+LineBuilder = Java.type("javafx.scene.shape.LineBuilder");
+LineTo = Java.type("javafx.scene.shape.LineTo");
+LineToBuilder = Java.type("javafx.scene.shape.LineToBuilder");
+Mesh = Java.type("javafx.scene.shape.Mesh");
+MeshView = Java.type("javafx.scene.shape.MeshView");
+MeshViewBuilder = Java.type("javafx.scene.shape.MeshViewBuilder");
+MoveTo = Java.type("javafx.scene.shape.MoveTo");
+MoveToBuilder = Java.type("javafx.scene.shape.MoveToBuilder");
+Path = Java.type("javafx.scene.shape.Path");
+PathBuilder = Java.type("javafx.scene.shape.PathBuilder");
+PathElement = Java.type("javafx.scene.shape.PathElement");
+PathElementBuilder = Java.type("javafx.scene.shape.PathElementBuilder");
+Polygon = Java.type("javafx.scene.shape.Polygon");
+PolygonBuilder = Java.type("javafx.scene.shape.PolygonBuilder");
+Polyline = Java.type("javafx.scene.shape.Polyline");
+PolylineBuilder = Java.type("javafx.scene.shape.PolylineBuilder");
+QuadCurve = Java.type("javafx.scene.shape.QuadCurve");
+QuadCurveBuilder = Java.type("javafx.scene.shape.QuadCurveBuilder");
+QuadCurveTo = Java.type("javafx.scene.shape.QuadCurveTo");
+QuadCurveToBuilder = Java.type("javafx.scene.shape.QuadCurveToBuilder");
+Rectangle = Java.type("javafx.scene.shape.Rectangle");
+RectangleBuilder = Java.type("javafx.scene.shape.RectangleBuilder");
+Shape = Java.type("javafx.scene.shape.Shape");
+Shape3D = Java.type("javafx.scene.shape.Shape3D");
+Shape3DBuilder = Java.type("javafx.scene.shape.Shape3DBuilder");
+ShapeBuilder = Java.type("javafx.scene.shape.ShapeBuilder");
+Sphere = Java.type("javafx.scene.shape.Sphere");
+SphereBuilder = Java.type("javafx.scene.shape.SphereBuilder");
+StrokeLineCap = Java.type("javafx.scene.shape.StrokeLineCap");
+StrokeLineJoin = Java.type("javafx.scene.shape.StrokeLineJoin");
+StrokeType = Java.type("javafx.scene.shape.StrokeType");
+SVGPath = Java.type("javafx.scene.shape.SVGPath");
+SVGPathBuilder = Java.type("javafx.scene.shape.SVGPathBuilder");
+TriangleMesh = Java.type("javafx.scene.shape.TriangleMesh");
+VLineTo = Java.type("javafx.scene.shape.VLineTo");
+VLineToBuilder = Java.type("javafx.scene.shape.VLineToBuilder");
+SnapshotParameters = Java.type("javafx.scene.SnapshotParameters");
+SnapshotParametersBuilder = Java.type("javafx.scene.SnapshotParametersBuilder");
+SnapshotResult = Java.type("javafx.scene.SnapshotResult");
+SubScene = Java.type("javafx.scene.SubScene");
+SubSceneBuilder = Java.type("javafx.scene.SubSceneBuilder");
+Font = Java.type("javafx.scene.text.Font");
+FontBuilder = Java.type("javafx.scene.text.FontBuilder");
+FontPosture = Java.type("javafx.scene.text.FontPosture");
+FontSmoothingType = Java.type("javafx.scene.text.FontSmoothingType");
+FontWeight = Java.type("javafx.scene.text.FontWeight");
+Text = Java.type("javafx.scene.text.Text");
+TextAlignment = Java.type("javafx.scene.text.TextAlignment");
+TextBoundsType = Java.type("javafx.scene.text.TextBoundsType");
+TextBuilder = Java.type("javafx.scene.text.TextBuilder");
+TextFlow = Java.type("javafx.scene.text.TextFlow");
+TextFlowBuilder = Java.type("javafx.scene.text.TextFlowBuilder");
+Affine = Java.type("javafx.scene.transform.Affine");
+AffineBuilder = Java.type("javafx.scene.transform.AffineBuilder");
+MatrixType = Java.type("javafx.scene.transform.MatrixType");
+NonInvertibleTransformException = Java.type("javafx.scene.transform.NonInvertibleTransformException");
+Rotate = Java.type("javafx.scene.transform.Rotate");
+RotateBuilder = Java.type("javafx.scene.transform.RotateBuilder");
+Scale = Java.type("javafx.scene.transform.Scale");
+ScaleBuilder = Java.type("javafx.scene.transform.ScaleBuilder");
+Shear = Java.type("javafx.scene.transform.Shear");
+ShearBuilder = Java.type("javafx.scene.transform.ShearBuilder");
+Transform = Java.type("javafx.scene.transform.Transform");
+TransformBuilder = Java.type("javafx.scene.transform.TransformBuilder");
+TransformChangedEvent = Java.type("javafx.scene.transform.TransformChangedEvent");
+Translate = Java.type("javafx.scene.transform.Translate");
+TranslateBuilder = Java.type("javafx.scene.transform.TranslateBuilder");
+DirectoryChooser = Java.type("javafx.stage.DirectoryChooser");
+DirectoryChooserBuilder = Java.type("javafx.stage.DirectoryChooserBuilder");
+FileChooser = Java.type("javafx.stage.FileChooser");
+FileChooser$ExtensionFilter = Java.type("javafx.stage.FileChooser$ExtensionFilter");
+FileChooserBuilder = Java.type("javafx.stage.FileChooserBuilder");
+Modality = Java.type("javafx.stage.Modality");
+Popup = Java.type("javafx.stage.Popup");
+PopupBuilder = Java.type("javafx.stage.PopupBuilder");
+PopupWindow = Java.type("javafx.stage.PopupWindow");
+PopupWindowBuilder = Java.type("javafx.stage.PopupWindowBuilder");
+Screen = Java.type("javafx.stage.Screen");
+//Stage = Java.type("javafx.stage.Stage");
+StageBuilder = Java.type("javafx.stage.StageBuilder");
+StageStyle = Java.type("javafx.stage.StageStyle");
+Window = Java.type("javafx.stage.Window");
+WindowBuilder = Java.type("javafx.stage.WindowBuilder");
+WindowEvent = Java.type("javafx.stage.WindowEvent");
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/fx/media.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+AudioClip = Java.type("javafx.scene.media.AudioClip");
+AudioClipBuilder = Java.type("javafx.scene.media.AudioClipBuilder");
+AudioEqualizer = Java.type("javafx.scene.media.AudioEqualizer");
+AudioSpectrumListener = Java.type("javafx.scene.media.AudioSpectrumListener");
+AudioTrack = Java.type("javafx.scene.media.AudioTrack");
+EqualizerBand = Java.type("javafx.scene.media.EqualizerBand");
+Media = Java.type("javafx.scene.media.Media");
+MediaBuilder = Java.type("javafx.scene.media.MediaBuilder");
+MediaErrorEvent = Java.type("javafx.scene.media.MediaErrorEvent");
+MediaException = Java.type("javafx.scene.media.MediaException");
+MediaException$Type = Java.type("javafx.scene.media.MediaException$Type");
+MediaMarkerEvent = Java.type("javafx.scene.media.MediaMarkerEvent");
+MediaPlayer = Java.type("javafx.scene.media.MediaPlayer");
+MediaPlayer$Status = Java.type("javafx.scene.media.MediaPlayer$Status");
+MediaPlayerBuilder = Java.type("javafx.scene.media.MediaPlayerBuilder");
+MediaView = Java.type("javafx.scene.media.MediaView");
+MediaViewBuilder = Java.type("javafx.scene.media.MediaViewBuilder");
+SubtitleTrack = Java.type("javafx.scene.media.SubtitleTrack");
+Track = Java.type("javafx.scene.media.Track");
+VideoTrack = Java.type("javafx.scene.media.VideoTrack");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/fx/swing.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+JFXPanel = Java.type("javafx.embed.swing.JFXPanel");
+JFXPanelBuilder = Java.type("javafx.embed.swing.JFXPanelBuilder");
+SwingFXUtils = Java.type("javafx.embed.swing.SwingFXUtils");
+SwingNode = Java.type("javafx.embed.swing.SwingNode");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/fx/swt.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+CustomTransfer = Java.type("javafx.embed.swt.CustomTransfer");
+CustomTransferBuilder = Java.type("javafx.embed.swt.CustomTransferBuilder");
+FXCanvas = Java.type("javafx.embed.swt.FXCanvas");
+SWTFXUtils = Java.type("javafx.embed.swt.SWTFXUtils");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/fx/web.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+HTMLEditor = Java.type("javafx.scene.web.HTMLEditor");
+HTMLEditorBuilder = Java.type("javafx.scene.web.HTMLEditorBuilder");
+PopupFeatures = Java.type("javafx.scene.web.PopupFeatures");
+PromptData = Java.type("javafx.scene.web.PromptData");
+PromptDataBuilder = Java.type("javafx.scene.web.PromptDataBuilder");
+WebEngine = Java.type("javafx.scene.web.WebEngine");
+WebEngineBuilder = Java.type("javafx.scene.web.WebEngineBuilder");
+WebEvent = Java.type("javafx.scene.web.WebEvent");
+WebHistory = Java.type("javafx.scene.web.WebHistory");
+WebView = Java.type("javafx.scene.web.WebView");
+WebViewBuilder = Java.type("javafx.scene.web.WebViewBuilder");
--- a/nashorn/src/jdk/nashorn/tools/Shell.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/src/jdk/nashorn/tools/Shell.java Thu May 16 11:47:51 2013 +0100
@@ -42,9 +42,12 @@
import jdk.nashorn.api.scripting.NashornException;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.debug.ASTWriter;
+import jdk.nashorn.internal.ir.debug.PrintVisitor;
import jdk.nashorn.internal.parser.Parser;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager;
+import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
@@ -168,7 +171,11 @@
return compileScripts(context, global, files);
}
- return runScripts(context, global, files);
+ if (env._fx) {
+ return runFXScripts(context, global, files);
+ } else {
+ return runScripts(context, global, files);
+ }
}
/**
@@ -254,6 +261,14 @@
return COMPILATION_ERROR;
}
+ if (env._print_ast) {
+ context.getErr().println(new ASTWriter(functionNode));
+ }
+
+ if (env._print_parse) {
+ context.getErr().println(new PrintVisitor(functionNode));
+ }
+
//null - pass no code installer - this is compile only
new Compiler(env, functionNode).compile();
}
@@ -318,6 +333,46 @@
}
/**
+ * Runs launches "fx:bootstrap.js" with the given JavaScript files provided
+ * as arguments.
+ *
+ * @param context the nashorn context
+ * @param global the global scope
+ * @param files the list of script files to provide
+ *
+ * @return error code
+ * @throws IOException when any script file read results in I/O error
+ */
+ private int runFXScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
+ final ScriptObject oldGlobal = Context.getGlobal();
+ final boolean globalChanged = (oldGlobal != global);
+ try {
+ if (globalChanged) {
+ Context.setGlobal(global);
+ }
+
+ global.addOwnProperty("$GLOBAL", Property.NOT_ENUMERABLE, global);
+ global.addOwnProperty("$SCRIPTS", Property.NOT_ENUMERABLE, files);
+ context.load(global, "fx:bootstrap.js");
+ } catch (final NashornException e) {
+ context.getErrorManager().error(e.toString());
+ if (context.getEnv()._dump_on_error) {
+ e.printStackTrace(context.getErr());
+ }
+
+ return RUNTIME_ERROR;
+ } finally {
+ context.getOut().flush();
+ context.getErr().flush();
+ if (globalChanged) {
+ Context.setGlobal(oldGlobal);
+ }
+ }
+
+ return SUCCESS;
+ }
+
+ /**
* Hook to ScriptFunction "apply". A performance metering shell may
* introduce enter/exit timing here.
*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/examples/int-micro.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+function bench(name, func) {
+ var start = Date.now();
+ for (var iter = 0; iter < 5000000; iter++) {
+ func();
+ }
+ print(name + "\t" + (Date.now() - start));
+}
+
+function uint32(value) {
+ return function() {
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ };
+}
+
+function int32(value) {
+ return function() {
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ };
+}
+
+print("\nToUint32");
+for (var i = 1; i < 3; i++) {
+ bench("infinity ", uint32(Infinity));
+ bench("infinity neg ", uint32(-Infinity));
+ bench("nan ", uint32(NaN));
+ bench("small ", uint32(1));
+ bench("small neg ", uint32(-1));
+ bench("small frac ", uint32(1.5));
+ bench("small neg frac", uint32(-1.5));
+ bench("large ", uint32(9223372036854775807));
+ bench("large neg ", uint32(-9223372036854775808));
+}
+
+print("\nToInt32");
+for (var i = 1; i < 3; i++) {
+ bench("infinity ", int32(Infinity));
+ bench("infinity neg ", int32(-Infinity));
+ bench("nan ", int32(NaN));
+ bench("small ", int32(1));
+ bench("small neg ", int32(-1));
+ bench("small frac ", int32(1.5));
+ bench("small neg frac", int32(-1.5));
+ bench("large ", int32(9223372036854775807));
+ bench("large neg ", int32(-9223372036854775808));
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8008238.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * JDK-8008238: Labeled break in finally causes stack overflow in Node copy
+ *
+ * @test
+ * @run
+ */
+
+a: try {
+} finally {
+ break a;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8008814-3.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * NASHORN-8008814: it's not a compile time error to have a nested strict function declaration when the outer one is not strict
+ *
+ * @test
+ * @run
+ */
+
+function f() {
+ if(true) {
+ function g() {
+ "use strict";
+ print("g invoked!")
+ }
+ }
+ g()
+}
+f()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8008814-3.js.EXPECTED Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,1 @@
+g invoked!
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8008814-4.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * NASHORN-8008814: it's not a compile time error to have a nested function declaration when warnings are reported
+ *
+ * @option --function-statement-warning
+ * @test
+ * @run/ignore-std-error
+ */
+
+function f() {
+ if(true) {
+ function g() {
+ print("g invoked!")
+ }
+ }
+ g()
+}
+f()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8008814-4.js.EXPECTED Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,1 @@
+g invoked!
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8011578.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * JDK-8011578 : -Dnashorn.unstable.relink.threshold=1 causes tests to fail.
+ *
+ * @test
+ * @option -Dnashorn.unstable.relink.threshold=1
+ * @run
+ */
+
+load(__DIR__ + "NASHORN-296.js");
+load(__DIR__ + "NASHORN-691.js");
+load(__DIR__ + "calllink.js");
+load(__DIR__ + "nosuchproperty.js");
+
+__noSuchProperty__ = function(x) {
+ print(x);
+ return x;
+}
+
+print(this["find"]);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8011578.js.EXPECTED Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,22 @@
+o.foo = 33
+o.foo = 44
+o.foo = 3
+o.foo = hello
+obj1.func called
+obj2.func called
+no such method: func
+obj4's prototype func called
+MyConstructor.prototype.func
+MyConstructor.prototype.func
+obj1.func called
+obj2.func called
+new obj3.func called
+new obj4.func called
+all new MyConstructor.prototype.func
+all new MyConstructor.prototype.func
+obj.__noSuchProperty__ for foo
+new obj.__noSuchProperty__ for foo
+proto.__noSuchProperty__ for foo
+new proto.__noSuchProperty__ for foo
+find
+find
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8012240.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * JDK-8012240: Array.prototype.map.call({length: -1, get 0(){throw 0}}, function(){}).length does not throw error
+ *
+ * @test
+ * @run
+ */
+
+var in_getter_for_0 = false;
+
+try {
+ Array.prototype.map.call(
+ {
+ length: -1,
+ get 0() {
+ in_getter_for_0 = true;
+ throw 0;
+ }
+ },
+ function(){}).length;
+} catch (e) {
+ if (e !== 0 || !in_getter_for_0) {
+ fail("should have thrown error from getter for '0'th element");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8012334.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * JDK-8012334: ToUint32, ToInt32, and ToUint16 don't conform to spec
+ *
+ * @test
+ * @run
+ */
+
+
+function test(val) {
+ print(val | 0);
+ print(val >> 0);
+ print(val >>> 0);
+ print(1 >>> val);
+ print(parseInt("10", val));
+}
+
+test(0);
+test(-0);
+test('Infinity');
+test('+Infinity');
+test('-Infinity');
+test(Number.POSITIVE_INFINITY);
+test(Number.NEGATIVE_INFINITY);
+test(Number.NaN);
+test(Number.MIN_VALUE);
+test(-Number.MIN_VALUE);
+test(1);
+test(-1);
+test(0.1);
+test(-0.1);
+test(1.1);
+test(-1.1);
+test(9223372036854775807);
+test(-9223372036854775808);
+test('9223372036854775807');
+test('-9223372036854775808');
+test(2147483647);
+test(2147483648);
+test(2147483649);
+test(-2147483647);
+test(-2147483648);
+test(-2147483649);
+test(4294967295);
+test(4294967296);
+test(4294967297);
+test(-4294967295);
+test(-4294967296);
+test(-4294967297);
+test(1e23);
+test(-1e23);
+test(1e24);
+test(-1e24);
+test(1e25);
+test(-1e25);
+test(1e26);
+test(-1e26);
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8012334.js.EXPECTED Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,200 @@
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+1
+1
+1
+0
+NaN
+-1
+-1
+4294967295
+0
+NaN
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+1
+1
+1
+0
+NaN
+-1
+-1
+4294967295
+0
+NaN
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+2147483647
+2147483647
+2147483647
+0
+NaN
+-2147483648
+-2147483648
+2147483648
+1
+NaN
+-2147483647
+-2147483647
+2147483649
+0
+NaN
+-2147483647
+-2147483647
+2147483649
+0
+NaN
+-2147483648
+-2147483648
+2147483648
+1
+NaN
+2147483647
+2147483647
+2147483647
+0
+NaN
+-1
+-1
+4294967295
+0
+NaN
+0
+0
+0
+1
+10
+1
+1
+1
+0
+NaN
+1
+1
+1
+0
+NaN
+0
+0
+0
+1
+10
+-1
+-1
+4294967295
+0
+NaN
+-167772160
+-167772160
+4127195136
+1
+NaN
+167772160
+167772160
+167772160
+1
+NaN
+-1610612736
+-1610612736
+2684354560
+1
+NaN
+1610612736
+1610612736
+1610612736
+1
+NaN
+-2147483648
+-2147483648
+2147483648
+1
+NaN
+-2147483648
+-2147483648
+2147483648
+1
+NaN
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8012457.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * JDK-8012457: Function.prototype.apply should accept any array-like argument for function arguments
+ *
+ * @test
+ * @run
+ */
+
+// no exception for these
+Function().apply(null, {length: null})
+Function().apply(null, {length: 0.1})
+
+// getter should be called
+var getter_0_called = false;
+
+Function().apply(null,
+ Object.defineProperty([],"0",
+ { get: function(){ getter_0_called = true; return 0 }
+ })
+);
+
+if (! getter_0_called) {
+ fail("getter for '0' of arguments array not called");
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8012460.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * JDK-8012460: RegExp regression
+ *
+ * @test
+ * @run
+ */
+
+
+var semver = "\\s*[v=]*\\s*([0-9]+)" // major
+ + "\\.([0-9]+)" // minor
+ + "\\.([0-9]+)" // patch
+ + "(-[0-9]+-?)?" // build
+ + "([a-zA-Z-+][a-zA-Z0-9-\.:]*)?" // tag
+ , exprComparator = "^((<|>)?=?)\s*("+semver+")$|^$";
+var validComparator = new RegExp("^"+exprComparator+"$");
+
+
+print(exprComparator);
+print(">=0.6.0-".match(validComparator));
+print("=0.6.0-".match(validComparator));
+print("0.6.0-".match(validComparator));
+print("<=0.6.0-".match(validComparator));
+print(">=0.6.0-a:b-c.d".match(validComparator));
+print("=0.6.0-a:b-c.d".match(validComparator));
+print("0.6.0+a:b-c.d".match(validComparator));
+print("<=0.6.0+a:b-c.d".match(validComparator));
+
+print(/[a-zA-Z-+]/.exec("a"));
+print(/[a-zA-Z-+]/.exec("b"));
+print(/[a-zA-Z-+]/.exec("y"));
+print(/[a-zA-Z-+]/.exec("z"));
+print(/[a-zA-Z-+]/.exec("B"));
+print(/[a-zA-Z-+]/.exec("Y"));
+print(/[a-zA-Z-+]/.exec("Z"));
+print(/[a-zA-Z-+]/.exec("-"));
+print(/[a-zA-Z-+]/.exec("+"));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8012460.js.EXPECTED Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,18 @@
+^((<|>)?=?)s*(\s*[v=]*\s*([0-9]+)\.([0-9]+)\.([0-9]+)(-[0-9]+-?)?([a-zA-Z-+][a-zA-Z0-9-.:]*)?)$|^$
+>=0.6.0-,>=,>,0.6.0-,0,6,0,,-
+=0.6.0-,=,,0.6.0-,0,6,0,,-
+0.6.0-,,,0.6.0-,0,6,0,,-
+<=0.6.0-,<=,<,0.6.0-,0,6,0,,-
+>=0.6.0-a:b-c.d,>=,>,0.6.0-a:b-c.d,0,6,0,,-a:b-c.d
+=0.6.0-a:b-c.d,=,,0.6.0-a:b-c.d,0,6,0,,-a:b-c.d
+0.6.0+a:b-c.d,,,0.6.0+a:b-c.d,0,6,0,,+a:b-c.d
+<=0.6.0+a:b-c.d,<=,<,0.6.0+a:b-c.d,0,6,0,,+a:b-c.d
+a
+b
+y
+z
+B
+Y
+Z
+-
++
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8012462.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * JDK-8012462: Date.prototype.toJSON does not handle non-Date 'this' as per the spec.
+ *
+ * @test
+ * @run
+ */
+
+var toJSON = Date.prototype.toJSON;
+
+function checkJSON(value, expected) {
+ var res = toJSON.call({
+ valueOf: function() { return value; },
+ toISOString: function() { return value; }
+ });
+
+ if (res !== expected) {
+ fail("Date.prototype.toJSON does not work for non-Date 'this'");
+ }
+}
+
+checkJSON(NaN, null);
+checkJSON(-Infinity, null);
+checkJSON(Infinity, null);
+checkJSON("foo", "foo");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8012931.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * JDK-8012931: NativeDate.safeToString() throws RangeError for invalid date
+ *
+ * @test
+ * @run
+ */
+
+var d = new Date(NaN);
+d.toString = Number.prototype.toString;
+
+try {
+ d.toString();
+} catch(e) {
+ print(e);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8012931.js.EXPECTED Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,1 @@
+TypeError: [Date Invalid Date] is not a Number
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8013131.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * JDK-8013131: Various compatibility issues in String.prototype.split()
+ *
+ * @test
+ * @run
+ */
+
+
+// Make sure limit is honored with undefined/empty separator
+print(JSON.stringify("aa".split(undefined, 0)));
+print(JSON.stringify("abc".split("", 1)));
+
+// Make sure limit is honored with capture groups
+print(JSON.stringify("aa".split(/(a)/, 1)));
+print(JSON.stringify("aa".split(/(a)/, 2)));
+print(JSON.stringify("aa".split(/((a))/, 1)));
+print(JSON.stringify("aa".split(/((a))/, 2)));
+
+// Empty capture group at end of string should be ignored
+print(JSON.stringify("aaa".split(/((?:))/)));
+
+// Tests below are to make sure that split does not read or write lastIndex property
+var r = /a/;
+r.lastIndex = {
+ valueOf: function(){throw 2}
+};
+print(JSON.stringify("aa".split(r)));
+
+r = /a/g;
+r.lastIndex = 100;
+print(JSON.stringify("aa".split(r)));
+print(r.lastIndex);
+
+r = /((?:))/g;
+r.lastIndex = 100;
+print(JSON.stringify("aaa".split(r)));
+print(r.lastIndex);
+
+// Make sure lastIndex is not updated on non-global regexp
+r = /a/;
+r.lastIndex = 100;
+print(JSON.stringify(r.exec("aaa")));
+print(r.lastIndex);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8013131.js.EXPECTED Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,14 @@
+[]
+["a"]
+[""]
+["","a"]
+[""]
+["","a"]
+["a","","a","","a"]
+["","",""]
+["","",""]
+100
+["a","","a","","a"]
+100
+["a"]
+100
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8013167.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * JDK-8013167: Vararg constructor was not found
+ *
+ * @test
+ * @run
+ */
+
+var x = new Packages.jdk.nashorn.test.models.VarArgConstructor(1, false, "a", "b", "c")
+print(x.indicator)
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8013167.js.EXPECTED Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,1 @@
+vararg
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8013325.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * JDK-8013325: function named 'arguments' should still access arguments object within itself.
+ * Its parent should however see the function and not an arguments object.
+ *
+ * @test
+ * @run
+ */
+
+function x() {
+ // x doesn't see an arguments object as it has a nested function with that name
+ // so it'll invoke the function.
+ arguments("a", "b", "c");
+
+ function arguments(x, y, z) {
+ // The function 'arguments' OTOH can't see itself; if it uses the
+ // identifier 'arguments', it'll see its own arguments object.
+ print(arguments)
+ print(x + " " + y + " " + z)
+ }
+}
+x()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8013325.js.EXPECTED Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,2 @@
+[object Arguments]
+a b c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8013337.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * JDK-8013337: Issues with Date.prototype's get, set functions
+ *
+ * @test
+ * @option -timezone=Asia/Calcutta
+ * @run
+ */
+
+function check(str) {
+ print(str + " = " + eval(str));
+}
+
+check('new Date(NaN).setFullYear(NaN)');
+check('new Date(0).setYear(70)');
+check('new Date(0).setYear(NaN)');
+check('new Date(NaN).setYear(70)');
+check('new Date(NaN).getTimezoneOffset()');
+
+function checkGetterCalled(func) {
+ var getterCalled = false;
+
+ new Date(NaN)[func]( { valueOf: function() { getterCalled = true } } );
+
+ if (getterCalled) {
+ print("Date.prototype." + func + " calls valueOf on arg");
+ }
+}
+
+checkGetterCalled("setMilliseconds");
+checkGetterCalled("setUTCMilliseconds");
+checkGetterCalled("setSeconds");
+checkGetterCalled("setUTCSeconds");
+checkGetterCalled("setMinutes");
+checkGetterCalled("setUTCMinutes");
+checkGetterCalled("setHours");
+checkGetterCalled("setUTCHours");
+checkGetterCalled("setDate");
+checkGetterCalled("setUTCDate");
+checkGetterCalled("setMonth");
+checkGetterCalled("setUTCMonth");
+
+try {
+ Date.prototype.setTime.call({}, { valueOf: function() { throw "err" } })
+} catch (e) {
+ if (! (e instanceof TypeError)) {
+ fail("TypeError expected, got " + e);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8013337.js.EXPECTED Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,17 @@
+new Date(NaN).setFullYear(NaN) = NaN
+new Date(0).setYear(70) = 0
+new Date(0).setYear(NaN) = NaN
+new Date(NaN).setYear(70) = -19800000
+new Date(NaN).getTimezoneOffset() = NaN
+Date.prototype.setMilliseconds calls valueOf on arg
+Date.prototype.setUTCMilliseconds calls valueOf on arg
+Date.prototype.setSeconds calls valueOf on arg
+Date.prototype.setUTCSeconds calls valueOf on arg
+Date.prototype.setMinutes calls valueOf on arg
+Date.prototype.setUTCMinutes calls valueOf on arg
+Date.prototype.setHours calls valueOf on arg
+Date.prototype.setUTCHours calls valueOf on arg
+Date.prototype.setDate calls valueOf on arg
+Date.prototype.setUTCDate calls valueOf on arg
+Date.prototype.setMonth calls valueOf on arg
+Date.prototype.setUTCMonth calls valueOf on arg
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8013444.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * JDK-8013444: JSON.parse does not invoke "reviver" callback as per spec.
+ *
+ * @test
+ * @run
+ */
+
+
+var type = typeof JSON.parse('{}',function(){})
+print("type is " + type);
+
+var obj = JSON.parse('{"name": "nashorn"}',
+ function(k, v) {
+ if (k === "") return v;
+ return v.toUpperCase();
+ });
+print(JSON.stringify(obj))
+
+var array =
+ JSON.parse("[1, 3, 5, 7, 9, 11]",
+ function(k, v) {
+ if (k === "") return v;
+ return v*2;
+ }
+ );
+print(array)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8013444.js.EXPECTED Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,3 @@
+type is undefined
+{"name":"NASHORN"}
+2,6,10,14,18,22
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/try2.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * Try throw test - nest finally
+ *
+ * @test
+ * @run
+ */
+
+function f() {
+ print("a");
+ try {
+ print("b");
+ } finally {
+ print("c");
+ try {
+ print("d");
+ } finally {
+ print("e");
+ }
+ print("f");
+ }
+ print("g");
+}
+
+f();
+
+print("done");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/try2.js.EXPECTED Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,8 @@
+a
+b
+c
+d
+e
+f
+g
+done
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/error/JDK-8008814-1.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * NASHORN-8008814: it's a compile time error to have a nested function declaration when there's an option to treat it as an error
+ *
+ * @option --function-statement-error
+ * @test/compile-error
+ */
+
+if(true) {
+ function g() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/error/JDK-8008814-1.js.EXPECTED Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,3 @@
+test/script/error/JDK-8008814-1.js:32:2 Function declarations can only occur at program or function body level. You should use a function expression here instead.
+ function g() {
+ ^
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/error/JDK-8008814-2.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * NASHORN-8008814: it's a compile time error to have a nested function declaration in strict mode
+ *
+ * @test/compile-error
+ */
+
+"use strict";
+if(true) {
+ function g() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/error/JDK-8008814-2.js.EXPECTED Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,3 @@
+test/script/error/JDK-8008814-2.js:32:2 In strict mode, function declarations can only occur at program or function body level. You should use a function expression here instead.
+ function g() {
+ ^
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/trusted/logcoverage.js Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * mh_coverage.js
+ * Screen scrape various logs to ensure that we cover enough functionality,
+ * e.g. method handle instrumentation
+ *
+ * @test
+ * @run
+ */
+
+/*
+ * creates new script engine initialized with given options and
+ * runs given code on it. Returns standard output captured.
+ */
+
+function runScriptEngine(opts, name) {
+ var imports = new JavaImporter(
+ Packages.jdk.nashorn.api.scripting,
+ java.io, java.lang, java.util);
+
+ with(imports) {
+ var fac = new NashornScriptEngineFactory();
+ // get current System.err
+ var oldErr = System.err;
+ var oldOut = System.out;
+ var baosErr = new ByteArrayOutputStream();
+ var newErr = new PrintStream(baosErr);
+ var baosOut = new ByteArrayOutputStream();
+ var newOut = new PrintStream(baosOut);
+ try {
+ // set new standard err
+ System.setErr(newErr);
+ System.setOut(newOut);
+ var strType = Java.type("java.lang.String");
+ var engine = fac.getScriptEngine(Java.toJavaArray(opts, strType));
+ var reader = new java.io.FileReader(name);
+ engine.eval(reader);
+ newErr.flush();
+ newOut.flush();
+ return new java.lang.String(baosErr.toByteArray());
+ } finally {
+ // restore System.err to old value
+ System.setErr(oldErr);
+ System.setOut(oldOut);
+ }
+ }
+}
+
+var str;
+
+var methodsCalled = [
+ 'asCollector',
+ 'asType',
+ 'bindTo',
+ 'dropArguments',
+ 'explicitCastArguments',
+ 'filterArguments',
+ 'filterReturnValue',
+ 'findStatic',
+ 'findVirtual',
+ 'foldArguments',
+ 'getter',
+ 'guardWithTest',
+ 'insertArguments',
+ 'methodType',
+ 'setter'
+];
+
+function check(str, strs) {
+ for each (s in strs) {
+ if (s.indexOf(str) !== -1) {
+ continue;
+ }
+ print(method + "not found");
+ return;
+ }
+ print("check ok!");
+}
+
+str = runScriptEngine([ "--log=codegen,compiler=finest,methodhandles=finest,fields=finest" ], __DIR__ + "../basic/NASHORN-19.js");
+
+check(str, methodsCalled);
+check(str, ['return', 'get', 'set', '[fields]']);
+check(str, ['codegen']);
+
+print("hello, world!");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/trusted/logcoverage.js.EXPECTED Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,4 @@
+check ok!
+check ok!
+check ok!
+hello, world!
--- a/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java Thu May 16 11:47:51 2013 +0100
@@ -55,7 +55,7 @@
* Tests for JSR-223 script engine for Nashorn.
*
* @test
- * @build jdk.nashorn.api.scripting.Window jdk.nashorn.api.scripting.WindowEventHandler jdk.nashorn.api.scripting.ScriptEngineTest
+ * @build jdk.nashorn.api.scripting.Window jdk.nashorn.api.scripting.WindowEventHandler jdk.nashorn.api.scripting.VariableArityTestInterface jdk.nashorn.api.scripting.ScriptEngineTest
* @run testng jdk.nashorn.api.scripting.ScriptEngineTest
*/
public class ScriptEngineTest {
@@ -906,4 +906,26 @@
fail(se.getMessage());
}
}
+
+ @Test
+ /**
+ * Tests whether invocation of a JavaScript method through a variable arity Java method will pass the vararg array.
+ * Both non-vararg and vararg JavaScript methods are tested.
+ * @throws ScriptException
+ */
+ public void variableArityInterfaceTest() throws ScriptException {
+ final ScriptEngineManager m = new ScriptEngineManager();
+ final ScriptEngine e = m.getEngineByName("nashorn");
+ e.eval(
+ "function test1(i, strings) {" +
+ " return 'i == ' + i + ', strings instanceof java.lang.String[] == ' + (strings instanceof Java.type('java.lang.String[]')) + ', strings == ' + java.util.Arrays.toString(strings)" +
+ "}" +
+ "function test2() {" +
+ " return 'arguments[0] == ' + arguments[0] + ', arguments[1] instanceof java.lang.String[] == ' + (arguments[1] instanceof Java.type('java.lang.String[]')) + ', arguments[1] == ' + java.util.Arrays.toString(arguments[1])" +
+ "}"
+ );
+ final VariableArityTestInterface itf = ((Invocable)e).getInterface(VariableArityTestInterface.class);
+ Assert.assertEquals(itf.test1(42, "a", "b"), "i == 42, strings instanceof java.lang.String[] == true, strings == [a, b]");
+ Assert.assertEquals(itf.test2(44, "c", "d", "e"), "arguments[0] == 44, arguments[1] instanceof java.lang.String[] == true, arguments[1] == [c, d, e]");
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/src/jdk/nashorn/api/scripting/VariableArityTestInterface.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2010, 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 jdk.nashorn.api.scripting;
+
+public interface VariableArityTestInterface {
+ public String test1(int i, String... strings);
+ public String test2(int i, String... strings);
+}
--- a/nashorn/test/src/jdk/nashorn/internal/runtime/JSTypeTest.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/test/src/jdk/nashorn/internal/runtime/JSTypeTest.java Thu May 16 11:47:51 2013 +0100
@@ -105,4 +105,89 @@
// FIXME: add more number-to-string test cases
// FIXME: add case for Object type (JSObject with getDefaultValue)
}
+
+ /**
+ * Test of JSType.toUint32(double)
+ */
+ @Test
+ public void testToUint32() {
+ assertEquals(JSType.toUint32(+0.0), 0);
+ assertEquals(JSType.toUint32(-0.0), 0);
+ assertEquals(JSType.toUint32(Double.NaN), 0);
+ assertEquals(JSType.toUint32(Double.POSITIVE_INFINITY), 0);
+ assertEquals(JSType.toUint32(Double.NEGATIVE_INFINITY), 0);
+ assertEquals(JSType.toUint32(9223372036854775807.0d), 0);
+ assertEquals(JSType.toUint32(-9223372036854775807.0d), 0);
+ assertEquals(JSType.toUint32(1099511627776.0d), 0);
+ assertEquals(JSType.toUint32(-1099511627776.0d), 0);
+ assertEquals(JSType.toUint32(4294967295.0d), 4294967295l);
+ assertEquals(JSType.toUint32(4294967296.0d), 0);
+ assertEquals(JSType.toUint32(4294967297.0d), 1);
+ assertEquals(JSType.toUint32(-4294967295.0d), 1);
+ assertEquals(JSType.toUint32(-4294967296.0d), 0);
+ assertEquals(JSType.toUint32(-4294967297.0d), 4294967295l);
+ assertEquals(JSType.toUint32(4294967295.6d), 4294967295l);
+ assertEquals(JSType.toUint32(4294967296.6d), 0);
+ assertEquals(JSType.toUint32(4294967297.6d), 1);
+ assertEquals(JSType.toUint32(-4294967295.6d), 1);
+ assertEquals(JSType.toUint32(-4294967296.6d), 0);
+ assertEquals(JSType.toUint32(-4294967297.6d), 4294967295l);
+ }
+
+ /**
+ * Test of JSType.toInt32(double)
+ */
+ @Test
+ public void testToInt32() {
+ assertEquals(JSType.toInt32(+0.0), 0);
+ assertEquals(JSType.toInt32(-0.0), 0);
+ assertEquals(JSType.toInt32(Double.NaN), 0);
+ assertEquals(JSType.toInt32(Double.POSITIVE_INFINITY), 0);
+ assertEquals(JSType.toInt32(Double.NEGATIVE_INFINITY), 0);
+ assertEquals(JSType.toInt32(9223372036854775807.0d), 0);
+ assertEquals(JSType.toInt32(-9223372036854775807.0d), 0);
+ assertEquals(JSType.toInt32(1099511627776.0d), 0);
+ assertEquals(JSType.toInt32(-1099511627776.0d), 0);
+ assertEquals(JSType.toInt32(4294967295.0d), -1);
+ assertEquals(JSType.toInt32(4294967296.0d), 0);
+ assertEquals(JSType.toInt32(4294967297.0d), 1);
+ assertEquals(JSType.toInt32(-4294967295.0d), 1);
+ assertEquals(JSType.toInt32(-4294967296.0d), 0);
+ assertEquals(JSType.toInt32(-4294967297.d), -1);
+ assertEquals(JSType.toInt32(4294967295.6d), -1);
+ assertEquals(JSType.toInt32(4294967296.6d), 0);
+ assertEquals(JSType.toInt32(4294967297.6d), 1);
+ assertEquals(JSType.toInt32(-4294967295.6d), 1);
+ assertEquals(JSType.toInt32(-4294967296.6d), 0);
+ assertEquals(JSType.toInt32(-4294967297.6d), -1);
+ }
+
+ /**
+ * Test of JSType.toUint16(double)
+ */
+ @Test
+ public void testToUint16() {
+ assertEquals(JSType.toUint16(+0.0), 0);
+ assertEquals(JSType.toUint16(-0.0), 0);
+ assertEquals(JSType.toUint16(Double.NaN), 0);
+ assertEquals(JSType.toUint16(Double.POSITIVE_INFINITY), 0);
+ assertEquals(JSType.toUint16(Double.NEGATIVE_INFINITY), 0);
+ assertEquals(JSType.toUint16(9223372036854775807.0d), 0);
+ assertEquals(JSType.toUint16(-9223372036854775807.0d), 0);
+ assertEquals(JSType.toUint16(1099511627776.0d), 0);
+ assertEquals(JSType.toUint16(-1099511627776.0d), 0);
+ assertEquals(JSType.toUint16(4294967295.0d), 65535);
+ assertEquals(JSType.toUint16(4294967296.0d), 0);
+ assertEquals(JSType.toUint16(4294967297.0d), 1);
+ assertEquals(JSType.toUint16(-4294967295.0d), 1);
+ assertEquals(JSType.toUint16(-4294967296.0d), 0);
+ assertEquals(JSType.toUint16(-4294967297.0d), 65535);
+ assertEquals(JSType.toUint16(4294967295.6d), 65535);
+ assertEquals(JSType.toUint16(4294967296.6d), 0);
+ assertEquals(JSType.toUint16(4294967297.6d), 1);
+ assertEquals(JSType.toUint16(-4294967295.6d), 1);
+ assertEquals(JSType.toUint16(-4294967296.6d), 0);
+ assertEquals(JSType.toUint16(-4294967297.6d), 65535);
+ }
+
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/src/jdk/nashorn/test/models/VarArgConstructor.java Thu May 16 11:47:51 2013 +0100
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2010, 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 jdk.nashorn.test.models;
+
+import java.util.List;
+
+public class VarArgConstructor {
+ private final String indicator;
+
+ public VarArgConstructor(int x, boolean y, List<String> z) {
+ indicator = "non-vararg";
+ }
+
+ public VarArgConstructor(int x, boolean y, String... z) {
+ indicator = "vararg";
+ }
+
+ public String getIndicator() {
+ return indicator;
+ }
+}
--- a/nashorn/tools/fxshell/jdk/nashorn/tools/FXShell.java Wed May 08 11:22:25 2013 +0100
+++ b/nashorn/tools/fxshell/jdk/nashorn/tools/FXShell.java Thu May 16 11:47:51 2013 +0100
@@ -178,7 +178,7 @@
*
* @param path Path to UTF-8 encoded JavaScript file.
*
- * @return Last evalulation result (discarded.)
+ * @return Last evaluation result (discarded.)
*/
private Object load(String path) {
try {