();
+ String[] searchForJavaString = new String[]{
+ System.getenv("JAVA_HOME") + "\\..",
+ System.getenv("PROGRAMW6432") + "\\JAVA",
+ System.getenv("PROGRAMFILES") + "\\JAVA",
+ System.getenv("SYSTEMDRIVE") + "\\JAVA"};
+
+ for (String fileString : searchForJavaString) {
+ File javaDir = new File(fileString);
+
+ if (javaDir.exists() && javaDir.isDirectory()) {
+ for (File f : javaDir.listFiles()) {
+ if (f.getName().startsWith("jdk") || f.getName().startsWith("jre")) {
+ try {
+ Installation i = new Installation(f, this);
+ if (!installations.contains(i)) {
+ installations.add(i);
+ }
+ } catch (InstallerException ex) {
+ }
+ }
+ }
+ }
+ }
+ return installations;
+ }
+
+ @Override
+ public boolean isJDK(File directory) {
+ if (directory.isDirectory() && directory.getName().startsWith("jdk")) {
+ File jreDir = new File(directory, "jre");
+ return isJRE(jreDir);
+ }
+ return false;
+ }
+}
diff -r f5603a6e5042 hotswapinstaller/Installer/src/at/ssw/hotswap/installer/splash.png
Binary file hotswapinstaller/Installer/src/at/ssw/hotswap/installer/splash.png has changed
diff -r f5603a6e5042 hotswaptest/HotSwapTests/build.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/build.xml Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+ Builds, tests, and runs the project HotSwapTests.
+
+
+
diff -r f5603a6e5042 hotswaptest/HotSwapTests/manifest.mf
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/manifest.mf Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
+
diff -r f5603a6e5042 hotswaptest/HotSwapTests/nbproject/build-impl.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/nbproject/build-impl.xml Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,1056 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set platform.home
+ Must set platform.bootcp
+ Must set platform.java
+ Must set platform.javac
+
+ The J2SE Platform is not correctly set up.
+ Your active platform is: ${platform.active}, but the corresponding property "platforms.${platform.active}.home" is not found in the project's properties files.
+ Either open the project in the IDE and setup the Platform with the same name or add it manually.
+ For example like this:
+ ant -Duser.properties.file=<path_to_property_file> jar (where you put the property "platforms.${platform.active}.home" in a .properties file)
+ or ant -Dplatforms.${platform.active}.home=<path_to_JDK_home> jar (where no properties file is used)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set src.dir
+ Must set build.dir
+ Must set dist.dir
+ Must set build.classes.dir
+ Must set dist.javadoc.dir
+ Must set build.test.classes.dir
+ Must set build.test.results.dir
+ Must set build.classes.excludes
+ Must set dist.jar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set JVM to use for profiling in profiler.info.jvm
+ Must set profiler agent JVM arguments in profiler.info.jvmargs.agent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+
+
+
+
+
+ ${platform.java} -cp "${run.classpath.with.dist.jar}" ${main.class}
+
+
+
+
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+ ${platform.java} -jar "${dist.jar.resolved}"
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+ ${platform.java} -jar "${dist.jar.resolved}"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must set fix.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set profile.class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set test.includes
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+ Must select one file in the IDE or set test.class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r f5603a6e5042 hotswaptest/HotSwapTests/nbproject/configs/Run_All_Tests.properties
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/nbproject/configs/Run_All_Tests.properties Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,2 @@
+$label=Run All Tests
+run.jvmargs=\ -Xdebug -XX:TraceRedefineClasses=4 -Xrunjdwp:transport=dt_socket,server=y,address=4000,suspend=n
diff -r f5603a6e5042 hotswaptest/HotSwapTests/nbproject/configs/Run_Natives_Tests.properties
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/nbproject/configs/Run_Natives_Tests.properties Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,2 @@
+$label=Run Natives Tests
+run.jvmargs=\ -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=4000,suspend=n
diff -r f5603a6e5042 hotswaptest/HotSwapTests/nbproject/genfiles.properties
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/nbproject/genfiles.properties Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,8 @@
+nbbuild.xml.data.CRC32=c8ab650e
+nbbuild.xml.script.CRC32=2ea92a55
+nbbuild.xml.stylesheet.CRC32=958a1d3e@1.26.2.45
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=c46425e2
+nbproject/build-impl.xml.script.CRC32=2aba14d0
+nbproject/build-impl.xml.stylesheet.CRC32=fce8b508@1.40.0.45
diff -r f5603a6e5042 hotswaptest/HotSwapTests/nbproject/project.properties
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/nbproject/project.properties Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,76 @@
+application.title=HotSwapTests
+application.vendor=Thomas
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+build.generated.sources.dir=${build.dir}/generated-sources
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+buildfile=build.xml
+# Uncomment to specify the preferred debugger connection transport:
+#debug.transport=dt_socket
+debug.classpath=\
+ ${run.classpath}
+debug.test.classpath=\
+ ${run.test.classpath}
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/HotSwapTests.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+endorsed.classpath=
+excludes=
+file.reference.asm-all-3.2.jar=../lib/asm-all-3.2.jar
+file.reference.HotSwapTests-src=src
+file.reference.tools.jar=../lib/tools.jar
+includes=**
+jar.compress=false
+javac.classpath=\
+ ${libs.junit_4.classpath}:\
+ ${reference.HotSwapTool.jar}:\
+ ${file.reference.asm-all-3.2.jar}:\
+ ${file.reference.tools.jar}
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.source=1.6
+javac.target=1.6
+javac.test.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}:\
+ ${libs.junit.classpath}:\
+ ${libs.junit_4.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api"
+jnlp.codebase.type=local
+jnlp.descriptor=application
+jnlp.enabled=false
+jnlp.offline-allowed=false
+jnlp.signed=false
+main.class=at.ssw.hotswap.test.Main
+manifest.file=manifest.mf
+meta.inf.dir=${src.dir}/META-INF
+platform.active=JDK_1.7_HotSwap
+project.HotSwapTool=../HotSwapTool
+reference.HotSwapTool.jar=${project.HotSwapTool}/dist/HotSwapTool.jar
+run.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+run.test.classpath=\
+ ${javac.test.classpath}:\
+ ${build.test.classes.dir}
+source.encoding=UTF-8
+src.dir=${file.reference.HotSwapTests-src}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/nbproject/project.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/nbproject/project.xml Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,25 @@
+
+
+ org.netbeans.modules.java.j2seproject
+
+
+ HotSwapTests
+ 1.6.5
+
+
+
+
+
+
+
+
+ HotSwapTool
+ jar
+
+ jar
+ clean
+ jar
+
+
+
+
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/CompleteTestSuite.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/CompleteTestSuite.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test;
+
+import at.ssw.hotswap.test.accesstests.AccessTestSuite;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+import at.ssw.hotswap.test.body.BodyTestSuite;
+import at.ssw.hotswap.test.eval.EvalTestSuite;
+import at.ssw.hotswap.test.fields.FieldsTestSuite;
+import at.ssw.hotswap.test.methods.MethodsTestSuite;
+import at.ssw.hotswap.test.natives.NativesTestSuite;
+import at.ssw.hotswap.test.transformer.TransformerTestSuite;
+import at.ssw.hotswap.test.structural.StructuralTestSuite;
+
+/**
+ * Summarizes all available test suites.
+ *
+ * @author Thomas Wuerthinger
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ BodyTestSuite.class,
+ EvalTestSuite.class,
+ MethodsTestSuite.class,
+ FieldsTestSuite.class,
+ StructuralTestSuite.class,
+ TransformerTestSuite.class,
+ NativesTestSuite.class,
+ AccessTestSuite.class
+})
+public class CompleteTestSuite {
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/Main.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/Main.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test;
+
+import at.ssw.hotswap.HotSwapTool;
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.runner.Description;
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Request;
+import org.junit.runner.manipulation.Filter;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+
+/**
+ * Main class for running class redefinition tests. Make sure that the execution directory is set such that this class file
+ * can be reached via "at/ssw/hotswap/test/Main.class".
+ *
+ * There are different levels of redefinition:
+ * Swap method bodies < add/remove methods < add/remove fields < add/remove super type
+ *
+ * Make sure that the application is started with a Java debug agent on port 4000. If you specify an argument, only tests
+ * containing the specified string are executed.
+ *
+ * Example usage:
+ * java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=4000,suspend=n at.ssw.hotswap.test.Main SimpleTest
+ *
+ * @author Thomas Wuerthinger
+ *
+ * Usage:
+ * If a first parameter is given, then only tests with a name containing this parameter are executed.
+ *
+ * Default values of system properties that can be overwritten:
+ * -Dhotswap.trace=0
+ *
+ */
+public class Main {
+
+ private static int failedCount;
+ private static int finishedCount;
+ private static String failureString = "";
+
+ public static void main(final String[] args) {
+
+ boolean runNativeTests = true;
+ String nativeLibraryName = new File("../../../HotSwapTestsNatives/dist/" + System.mapLibraryName("libHotSwapTestsNatives")).getAbsolutePath();
+ try {
+ System.out.println("Load native library: ");
+ System.load(nativeLibraryName);
+ } catch(UnsatisfiedLinkError e) {
+ System.out.println("WARNING: Could not load native library from path " + nativeLibraryName);
+ System.out.println("Disabling native tests");
+ runNativeTests = false;
+ }
+
+ System.out.println("Running JUnit tests: ");
+
+ JUnitCore core = new JUnitCore();
+ core.addListener(runListener);
+ Request request = Request.classes(CompleteTestSuite.class);
+ HotSwapTool.setTraceLevel(Integer.parseInt(System.getProperty("hotswap.trace", "0")));
+
+ // Filter the request?
+ if (args.length > 0) {
+
+ System.out.println("Only run tests containing \"" + args[0] + "\"");
+
+ request = request.filterWith(new Filter() {
+
+ @Override
+ public String describe() {
+ return "Filter";
+ }
+
+ private Set childrenToRun = new HashSet();
+
+ @Override
+ public boolean shouldRun(Description d) {
+ System.out.println(d.getDisplayName());
+
+ if (d.getDisplayName().contains(args[0]) || childrenToRun.contains(d)) {
+ childrenToRun.addAll(d.getChildren());
+ return true;
+ }
+
+ // explicitly check if any children want to run
+ for (Description each : d.getChildren()) {
+ if (shouldRun(each)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ });
+ }
+
+ long startTime = System.currentTimeMillis();
+ core.run(request);
+ long time = System.currentTimeMillis() - startTime;
+
+ System.out.println("" + (finishedCount - failedCount) + " of " + finishedCount + " tests are OK!");
+ System.out.println("Time: " + ((double) time) / 1000);
+ if (failedCount == 0) {
+ System.out.println("ALL OK");
+ } else {
+ System.out.println(failedCount + " FAILURES: " + failureString);
+ }
+ }
+
+ private static RunListener runListener = new RunListener() {
+
+ @Override
+ public void testStarted(Description description) throws Exception {
+ System.out.println("============================================================");
+ System.out.println("Test started: " + description.getDisplayName());
+ }
+
+ @Override
+ public void testFailure(Failure failure) throws Exception {
+ System.out.println("Test failure: " + failure.getMessage());
+ failure.getException().printStackTrace();
+ failedCount++;
+ failureString += failure.getDescription().getDisplayName() + " ";
+ }
+
+ @Override
+ public void testFinished(Description description) throws Exception {
+ System.out.println("Test finished: " + description.getDisplayName());
+ finishedCount++;
+ }
+ };
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/TestUtil.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/TestUtil.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test;
+
+import junit.framework.Assert;
+
+/**
+ *
+ * Utility methods for unit testing.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class TestUtil {
+
+ public static int assertException(Class exceptionClass, Runnable run) {
+
+ try {
+ run.run();
+ } catch(Throwable t) {
+ if (t.getClass().equals(exceptionClass)) {
+ return t.getStackTrace()[0].getLineNumber();
+ }
+ Assert.assertTrue("An exception of type " + t.getClass().getSimpleName() + " instead of " + exceptionClass.getSimpleName() + " has been thrown!", false);
+ }
+
+ Assert.assertTrue("No exception has been thrown!", false);
+ return -1;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/ClassAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/ClassAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access;
+
+import java.util.List;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public interface ClassAccess {
+
+ public String getName();
+
+ public MethodAccess findMethod(String method);
+
+ public List getMethods();
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/MethodAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/MethodAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access;
+
+import java.util.List;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public interface MethodAccess {
+
+ public String getName();
+
+ public String getSignature();
+
+ public boolean canCheckObsoletness();
+
+ public boolean isObsolete();
+
+ public Object invoke(Object[] o, Object instance);
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/StackFrameAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/StackFrameAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public interface StackFrameAccess {
+
+ public MethodAccess getMethod() throws ClassNotFoundException;
+
+ public ClassAccess getClazz();
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/VMAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/VMAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access;
+
+import java.util.List;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public interface VMAccess {
+
+ public boolean canGetFrames();
+
+ public List getFrames(final String threadName);
+
+ public List getThreadNames();
+
+ public ClassAccess findClass(String clazz);
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jdi/JDIClassAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jdi/JDIClassAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access.jdi;
+
+import at.ssw.hotswap.test.access.ClassAccess;
+import at.ssw.hotswap.test.access.MethodAccess;
+import com.sun.jdi.Method;
+import com.sun.jdi.ReferenceType;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class JDIClassAccess implements ClassAccess {
+
+ private ReferenceType clazz;
+
+ public JDIClassAccess(ReferenceType clazz) {
+ this.clazz = clazz;
+ }
+
+ @Override
+ public String getName() {
+ return clazz.name();
+ }
+
+ @Override
+ public MethodAccess findMethod(String method) {
+ List methods = clazz.methodsByName(method);
+ List newMethods = new ArrayList();
+ for (Method m : methods) {
+ if (!m.isObsolete()) {
+ newMethods.add(m);
+ }
+ }
+ if (newMethods.size() > 1) {
+ throw new RuntimeException("ambiguous methods: " + newMethods.get(0) + ", " + newMethods.get(1));
+ }
+ if (newMethods.isEmpty()) {
+ return null;
+ }
+ return new JDIMethodAccess(newMethods.get(0), clazz);
+ }
+
+ @Override
+ public List getMethods() {
+ List methodAccesses = new ArrayList();
+ for (Method m : clazz.methods()) {
+ if (!m.name().equals("")) {
+ methodAccesses.add(new JDIMethodAccess(m, clazz));
+ }
+ }
+ return methodAccesses;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jdi/JDIMethodAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jdi/JDIMethodAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access.jdi;
+
+import at.ssw.hotswap.JDIProxy;
+import at.ssw.hotswap.test.access.MethodAccess;
+import com.sun.jdi.ClassNotLoadedException;
+import com.sun.jdi.ClassType;
+import com.sun.jdi.IncompatibleThreadStateException;
+import com.sun.jdi.InvalidTypeException;
+import com.sun.jdi.InvocationException;
+import com.sun.jdi.Method;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.Type;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class JDIMethodAccess implements MethodAccess {
+
+ private Method method;
+
+ public JDIMethodAccess(Method m, ReferenceType clazz) {
+ method = m;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString();
+ }
+
+ @Override
+ public String getName() {
+ return method.name();
+ }
+
+ @Override
+ public String getSignature() {
+ String methodString = Modifier.toString(method.modifiers()) + " " + method.returnTypeName() + " " + method.name() + "(";
+ boolean paramFound = false;
+ try {
+ for (Type type : method.argumentTypes()) {
+ if (paramFound) {
+ methodString += ", ";
+ }
+ paramFound = true;
+ methodString += type.name();
+ }
+ } catch (ClassNotLoadedException ex) {
+ throw new RuntimeException(ex);
+ }
+ return methodString + ")";
+ }
+
+ @Override
+ public boolean canCheckObsoletness() {
+ return true;
+ }
+
+ @Override
+ public boolean isObsolete() {
+ return method.isObsolete();
+ }
+
+ @Override
+ public Object invoke(Object[] o, Object instance) {
+ Thread tempthread = new Thread("invokeThread") {
+
+ @Override
+ public void run() {
+ try {
+ sleep(2000);
+ } catch (InterruptedException ex) {
+ Logger.getLogger(JDIMethodAccess.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ };
+ tempthread.start();
+
+ // get Main-Thread
+ final JDIProxy jdi = JDIProxy.getJDI();
+ ThreadReference reference = null;
+ for (ThreadReference t : jdi.getVM().allThreads()) {
+ if (t.name().equals("invokeThread")) {
+ reference = t;
+ break;
+ }
+ }
+ // reference.suspend();
+
+ Object obj = null;
+ try {
+ obj = ((ClassType) method.declaringType()).invokeMethod(reference, method, new ArrayList(), ClassType.INVOKE_SINGLE_THREADED);
+ } catch (InvalidTypeException ex) {
+ Logger.getLogger(JDIMethodAccess.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (ClassNotLoadedException ex) {
+ Logger.getLogger(JDIMethodAccess.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (IncompatibleThreadStateException ex) {
+ Logger.getLogger(JDIMethodAccess.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (InvocationException ex) {
+ Logger.getLogger(JDIMethodAccess.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ // reference.resume();
+ return obj;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jdi/JDIStackFrameAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jdi/JDIStackFrameAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access.jdi;
+
+import at.ssw.hotswap.test.access.ClassAccess;
+import at.ssw.hotswap.test.access.MethodAccess;
+import at.ssw.hotswap.test.access.StackFrameAccess;
+import com.sun.jdi.Method;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.StackFrame;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class JDIStackFrameAccess implements StackFrameAccess {
+
+ private ReferenceType clazz;
+ private Method method;
+ private MethodAccess methodAccess;
+ private ClassAccess classAccess;
+
+ JDIStackFrameAccess(StackFrame s) {
+ method = s.location().method();
+ clazz = s.location().method().declaringType();
+ }
+
+ @Override
+ public MethodAccess getMethod() {
+ if (methodAccess == null) {
+ methodAccess = new JDIMethodAccess(method, clazz);
+ }
+ return methodAccess;
+ }
+
+ @Override
+ public ClassAccess getClazz() {
+ if (classAccess == null) {
+ classAccess = new JDIClassAccess(clazz);
+ }
+ return classAccess;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString();
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jdi/JDIVMAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jdi/JDIVMAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access.jdi;
+
+import at.ssw.hotswap.JDIProxy;
+import at.ssw.hotswap.test.access.ClassAccess;
+import at.ssw.hotswap.test.access.StackFrameAccess;
+import at.ssw.hotswap.test.access.VMAccess;
+import com.sun.jdi.IncompatibleThreadStateException;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.StackFrame;
+import com.sun.jdi.ThreadReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class JDIVMAccess implements VMAccess {
+
+ private List getThreads() {
+ final JDIProxy jdi = JDIProxy.getJDI();
+ return jdi.getVM().allThreads();
+ }
+
+ @Override
+ public boolean canGetFrames() {
+ return true;
+ }
+
+ @Override
+ public List getFrames(final String threadName) {
+ final JDIProxy jdi = JDIProxy.getJDI();
+ final Exception[] exception = new Exception[1];
+ final Object[] object = new Object[1];
+ object[0] = null;
+ exception[0] = null;
+
+ Runnable r = new Runnable() {
+
+ public void run() {
+ List stackAccesses = new ArrayList();
+ for (ThreadReference t : getThreads()) {
+ if (t.name().equals(threadName)) {
+ try {
+ for (StackFrame stack : t.frames()) {
+ StackFrameAccess stackAccess = new JDIStackFrameAccess(stack);
+ stackAccesses.add(stackAccess);
+ }
+ } catch (IncompatibleThreadStateException ex) {
+ exception[0] = ex;
+ }
+ object[0] = stackAccesses;
+ return;
+ }
+ }
+ }
+ };
+
+ jdi.executeSuspended(r);
+
+ if (exception[0] != null) {
+ throw new RuntimeException(exception[0]);
+ }
+ return (List) object[0];
+ }
+
+ @Override
+ public List getThreadNames() {
+ List threadNames = new ArrayList();
+
+ for (ThreadReference t : getThreads()) {
+ threadNames.add(t.name());
+ }
+ return threadNames;
+ }
+
+ @Override
+ public ClassAccess findClass(String clazz) {
+ final JDIProxy jdi = JDIProxy.getJDI();
+ ReferenceType found = null;
+ // ensures, that all classes are retrieved
+ jdi.refreshAllClasses();
+ for (ReferenceType referenceType : jdi.getVM().allClasses()) {
+ if (referenceType.name().equals(clazz)) {
+ if (found != null) {
+ throw new RuntimeException("ambiguous class name");
+ }
+ found = referenceType;
+ }
+ }
+ if (found == null) {
+ return null;
+ }
+ return new JDIClassAccess(found);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jni/JNIClassAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jni/JNIClassAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access.jni;
+
+import at.ssw.hotswap.test.access.ClassAccess;
+import at.ssw.hotswap.test.access.MethodAccess;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class JNIClassAccess implements ClassAccess {
+
+ Class clazz;
+
+ public JNIClassAccess(Class clazz) {
+ this.clazz = clazz;
+ }
+
+ @Override
+ public String getName() {
+ return clazz.getName();
+ }
+
+ public static native Method findMethodNative(Class clazz, String methodName);
+
+ @Override
+ public MethodAccess findMethod(String methodName) {
+ Method m;
+ try {
+ m = findMethodNative(clazz, methodName);
+ } catch (NoSuchMethodError ex) {
+ return null;
+ }
+ return new JNIMethodAccess(m);
+ }
+
+ public static native Method[] getMethodsNative(Class clazz);
+
+ @Override
+ public List getMethods() {
+ Method[] array = getMethodsNative(clazz);
+ List mAccesses = new ArrayList();
+ for (int i = 0; i < array.length; i++) {
+ mAccesses.add(new JNIMethodAccess(array[i]));
+ }
+ return mAccesses;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jni/JNIMethodAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jni/JNIMethodAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access.jni;
+
+import at.ssw.hotswap.test.access.MethodAccess;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import org.objectweb.asm.Type;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class JNIMethodAccess implements MethodAccess {
+
+ Method method;
+
+ public JNIMethodAccess(Method m) {
+ method = m;
+ }
+
+ @Override
+ public String getName() {
+ return method.getName();
+ }
+
+ @Override
+ public String getSignature() {
+ String methodString = Modifier.toString(method.getModifiers()) + " " + method.getReturnType().getName() + " " + method.getName() + "(";
+ boolean paramFound = false;
+ for (Class c : method.getParameterTypes()) {
+ if (paramFound) {
+ methodString += ", ";
+ }
+ paramFound = true;
+ methodString += c.getName();
+ }
+ return methodString + ")";
+ }
+
+ @Override
+ public boolean canCheckObsoletness() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public boolean isObsolete() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public static native Object invokeMethodNative(Class clazz, Object obj, String methodName, String retValue, boolean staticValue, String descriptor, Object[] params);
+
+ @Override
+ public Object invoke(Object[] o, Object instance) {
+ boolean staticValue = java.lang.reflect.Modifier.isStatic(method.getModifiers());
+ String retValue = method.getReturnType().getName();
+ String descriptor = new org.objectweb.asm.commons.Method(method.getName(), Type.getReturnType(method), Type.getArgumentTypes(method)).getDescriptor();
+
+ return invokeMethodNative(method.getDeclaringClass(), instance, method.getName(), retValue, staticValue, descriptor, o);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jni/JNIStackFrameAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jni/JNIStackFrameAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access.jni;
+
+import at.ssw.hotswap.test.access.ClassAccess;
+import at.ssw.hotswap.test.access.MethodAccess;
+import at.ssw.hotswap.test.access.StackFrameAccess;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class JNIStackFrameAccess implements StackFrameAccess {
+
+ @Override
+ public MethodAccess getMethod() throws ClassNotFoundException {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public ClassAccess getClazz() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jni/JNIVMAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/jni/JNIVMAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access.jni;
+
+import at.ssw.hotswap.test.access.ClassAccess;
+import at.ssw.hotswap.test.access.StackFrameAccess;
+import at.ssw.hotswap.test.access.VMAccess;
+import java.io.File;
+import java.util.List;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class JNIVMAccess implements VMAccess {
+
+ @Override
+ public boolean canGetFrames() {
+ return false;
+ }
+
+ @Override
+ public List getFrames(String threadName) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public List getThreadNames() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ public static native Class findClassNative(String clazz) throws ClassNotFoundException, NoClassDefFoundError;
+
+ @Override
+ public ClassAccess findClass(String clazz) {
+ try {
+ return new JNIClassAccess(findClassNative(clazz.replace('.', '/')));
+ } catch (ClassNotFoundException ex) {
+ return null;
+ } catch (NoClassDefFoundError ex) {
+ return null;
+ }
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/reflection/ReflectionClassAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/reflection/ReflectionClassAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access.reflection;
+
+import at.ssw.hotswap.test.access.ClassAccess;
+import at.ssw.hotswap.test.access.MethodAccess;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class ReflectionClassAccess implements ClassAccess {
+
+ private Class clazz;
+
+ public ReflectionClassAccess() {
+ }
+ public ReflectionClassAccess(String name) throws ClassNotFoundException {
+ clazz = Class.forName(name);
+ }
+
+ @Override
+ public String getName() {
+ return clazz.getName();
+ }
+
+ @Override
+ public MethodAccess findMethod(String method) {
+ try {
+ return new ReflectionMethodAccess(method, clazz.getName());
+ } catch (ClassNotFoundException ex) {
+ return null;
+ } catch (NoSuchMethodError ex) {
+ return null;
+ }
+ }
+
+ @Override
+ public List getMethods() {
+ List methodAccesses = new ArrayList();
+ for (Method m : clazz.getDeclaredMethods()) {
+ methodAccesses.add(new ReflectionMethodAccess(m, clazz));
+ }
+ return methodAccesses;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/reflection/ReflectionMethodAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/reflection/ReflectionMethodAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access.reflection;
+
+import at.ssw.hotswap.test.access.MethodAccess;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class ReflectionMethodAccess implements MethodAccess {
+
+ private Method method;
+
+ public ReflectionMethodAccess(String methodName, String className) throws ClassNotFoundException, NoSuchMethodError {
+
+ Class clazz = Class.forName(className);
+
+ Method found = null;
+ for (Method m : clazz.getDeclaredMethods()) {
+ if (m.getName().equals(methodName)) {
+ if (found != null) {
+ throw new RuntimeException("ambiguous method name");
+ }
+ found = m;
+ }
+ }
+ if (found == null) {
+ throw new NoSuchMethodError(methodName);
+ }
+ this.method = found;
+ }
+
+ public ReflectionMethodAccess(Method m, Class clazz) {
+ this.method = m;
+ }
+
+ @Override
+ public String getSignature() {
+ String methodString = Modifier.toString(method.getModifiers()) + " " + method.getReturnType().getName() + " " + method.getName() + "(";
+ boolean paramFound = false;
+ for (Class c : method.getParameterTypes()) {
+ if (paramFound) {
+ methodString += ", ";
+ }
+ paramFound = true;
+ methodString += c.getName();
+ }
+ return methodString + ")";
+ }
+
+ @Override
+ public String getName() {
+ return method.getName();
+ }
+
+ @Override
+ public boolean canCheckObsoletness() {
+ return false;
+ }
+
+ @Override
+ public boolean isObsolete() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public Object invoke(Object[] o, Object instance) {
+ try {
+ boolean acc = method.isAccessible();
+ method.setAccessible(true);
+ Object obj = method.invoke(instance, o);
+ method.setAccessible(acc);
+ return obj;
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex);
+ } catch (IllegalArgumentException ex) {
+ throw new RuntimeException(ex);
+ } catch (InvocationTargetException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/reflection/ReflectionStackFrameAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/reflection/ReflectionStackFrameAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access.reflection;
+
+import at.ssw.hotswap.test.access.ClassAccess;
+import at.ssw.hotswap.test.access.MethodAccess;
+import at.ssw.hotswap.test.access.StackFrameAccess;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class ReflectionStackFrameAccess implements StackFrameAccess {
+
+ private String methodName;
+ private String className;
+ private MethodAccess methodAccess;
+ private ClassAccess classAccess;
+
+ ReflectionStackFrameAccess(StackTraceElement element) {
+ methodName = element.getMethodName();
+ className = element.getClassName();
+ }
+
+ @Override
+ public MethodAccess getMethod() throws ClassNotFoundException {
+ if (methodAccess == null) {
+ methodAccess = new ReflectionMethodAccess(methodName, className);
+ }
+ return methodAccess;
+ }
+
+ @Override
+ public ClassAccess getClazz() {
+ if (classAccess == null) {
+ try {
+ classAccess = new ReflectionClassAccess(className);
+ } catch (ClassNotFoundException ex) {
+ return null;
+ }
+ }
+ return classAccess;
+ }
+
+ @Override
+ public String toString() {
+ return methodName;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/reflection/ReflectionVMAccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/access/reflection/ReflectionVMAccess.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.access.reflection;
+
+import at.ssw.hotswap.test.access.ClassAccess;
+import at.ssw.hotswap.test.access.VMAccess;
+import at.ssw.hotswap.test.access.StackFrameAccess;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class ReflectionVMAccess implements VMAccess {
+
+ private List getThreads() {
+ ThreadGroup root = Thread.currentThread().getThreadGroup();
+ while (root.getParent() != null) {
+ root = root.getParent();
+ }
+
+ Thread[] threads;
+ int cnt;
+ int estsize = root.activeCount();
+
+ do {
+ estsize *= 2;
+ threads = new Thread[estsize];
+
+ cnt = root.enumerate(threads, true);
+ } while (cnt == estsize);
+
+ List ret = new ArrayList();
+ for (int i = 0; i < cnt; i++) {
+ ret.add(threads[i]);
+ }
+ return ret;
+ }
+
+ @Override
+ public List getThreadNames() {
+ List threadNames = new ArrayList();
+ List threads = getThreads();
+ for (Thread t : threads) {
+ threadNames.add(t.getName());
+ }
+ return threadNames;
+ }
+
+ @Override
+ public boolean canGetFrames() {
+ return true;
+ }
+
+ @Override
+ public List getFrames(String threadName) {
+
+ List threads = getThreads();
+ List stackAccesses = new ArrayList();
+ for (Thread t : threads) {
+ if (t.getName().equals(threadName)) {
+ for (StackTraceElement stackElement : t.getStackTrace()) {
+ StackFrameAccess stackAccess = new ReflectionStackFrameAccess(stackElement);
+ stackAccesses.add(stackAccess);
+ }
+ }
+ }
+ return stackAccesses;
+ }
+
+ @Override
+ public ClassAccess findClass(String clazz) {
+ try {
+ return new ReflectionClassAccess(clazz);
+ } catch (ClassNotFoundException ex) {
+ return null;
+ }
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/accesstests/AccessTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/accesstests/AccessTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.accesstests;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+import at.ssw.hotswap.HotSwapTool;
+import at.ssw.hotswap.test.access.ClassAccess;
+import at.ssw.hotswap.test.access.MethodAccess;
+import at.ssw.hotswap.test.access.StackFrameAccess;
+import at.ssw.hotswap.test.access.VMAccess;
+import at.ssw.hotswap.test.access.jdi.JDIVMAccess;
+import at.ssw.hotswap.test.access.jni.JNIVMAccess;
+import at.ssw.hotswap.test.access.reflection.ReflectionVMAccess;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+@RunWith(Parameterized.class)
+public class AccessTest {
+
+ private static VMAccess vma;
+
+ public AccessTest(VMAccess vma) {
+ this.vma = vma;
+ }
+
+ // Version 0
+ public static class A {
+
+ //needed for StackTraceTest
+ public static String stackTraceHelper() {
+ return getParentMethodSignature();
+ }
+
+ //needed for FindMethodTest
+ public static String method_Version0() {
+ return "Version0";
+ }
+
+ //needed for SignatureTest
+ public static String signatureHelper(String y, int x) {
+ return "Version0";
+ }
+
+ //needed for invokeMethodIntTest()
+ private int testIntRetValue() {
+ return 2;
+ }
+
+ //needed for invokeMethodObjectTest
+ private Object testObjectRetValue() {
+ return new Integer(2);
+ }
+
+ //needed for invokeMethodVoidTest
+ private static void testVoidRetValue() {
+ System.out.println("Version 0");
+ }
+
+ //needed for invokeMethodParameterTest()
+ private static int testMethodParameter(Integer x) {
+ return x;
+ }
+ }
+
+ // Version 1
+ public static class A___1 {
+
+ //needed for StackTraceTest
+ protected static String stackTraceHelper() {
+ return getParentMethodSignature();
+ }
+
+ //needed for FindMethodTest, SignatureTest
+ public static String method_Version1() {
+ return "Version1";
+ }
+
+ //needed for SignatureTest
+ private static String signatureHelper(int y, int x) {
+ return "Version1";
+ }
+
+ //needed for invokeMethodIntTest
+ private int testIntRetValue() {
+ return 3;
+ }
+
+ //needed for invokeMethodObjectTest
+ private Object testObjectRetValue() {
+ return new Integer(3);
+ }
+
+ //needed for invokeMethodVoidTest
+ private static void testVoidRetValue() {
+ System.out.println("Version 1");
+ }
+
+ //needed for invokeMethodParameterTest()
+ private static int testMethodParameter(Integer x) {
+ return x + 1;
+ }
+ }
+
+ //needed for StackTraceTest
+ private static String getParentMethodSignature() {
+
+ try {
+ int i = 0;
+ for (StackFrameAccess stack : vma.getFrames("main")) {
+ if (stack.getMethod().getName().equals("access$000")) {
+ break;
+ } else {
+ i++;
+ }
+ }
+ return vma.getFrames("main").get(++i).getMethod().getSignature();
+ } catch (ClassNotFoundException ex) {
+ return null;
+ }
+ }
+
+ @Parameters
+ public static Collection accessValues() {
+ return Arrays.asList(new Object[][]{{new JDIVMAccess()}, {new JNIVMAccess()}, {new ReflectionVMAccess()}});
+ //return Arrays.asList(new Object[][]{{new JDIVMAccess()}});
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(AccessTest.class, 0);
+ }
+
+ @Test
+ public void FindClassTest() {
+
+
+ ClassAccess classAccess = vma.findClass("at.ssw.hotswap.test.accesstests.AccessTest$XYZ");
+ assertNull(classAccess);
+
+ classAccess = vma.findClass("at.ssw.hotswap.test.accesstests.AccessTest$A");
+ assertNotNull(classAccess);
+
+ HotSwapTool.toVersion(AccessTest.class, 1);
+
+ classAccess = vma.findClass("at.ssw.hotswap.test.accesstests.AccessTest$A");
+ assertNotNull(classAccess);
+ }
+
+ @Test
+ public void FindMethodTest() {
+
+ ClassAccess classAccess = vma.findClass("at.ssw.hotswap.test.accesstests.AccessTest$A");
+ assertNotNull(classAccess);
+
+ assertNotNull(classAccess.findMethod("method_Version0"));
+ assertNull(classAccess.findMethod("method_Version1"));
+
+ HotSwapTool.toVersion(AccessTest.class, 1);
+
+ assertNull(classAccess.findMethod("method_Version0"));
+ assertNotNull(classAccess.findMethod("method_Version1"));
+ }
+
+ @Test
+ public void StackTraceTest() {
+ if (vma.canGetFrames()) {
+ assertEquals("public static java.lang.String stackTraceHelper()", A.stackTraceHelper());
+
+ HotSwapTool.toVersion(AccessTest.class, 1);
+
+ assertEquals("protected static java.lang.String stackTraceHelper()", A.stackTraceHelper());
+ }
+ }
+
+ @Test
+ public void SignatureTest() {
+ MethodAccess mAccess = vma.findClass("at.ssw.hotswap.test.accesstests.AccessTest$A").findMethod("signatureHelper");
+ assertEquals("public static java.lang.String signatureHelper(java.lang.String, int)", mAccess.getSignature());
+
+ HotSwapTool.toVersion(AccessTest.class, 1);
+
+ mAccess = vma.findClass("at.ssw.hotswap.test.accesstests.AccessTest$A").findMethod("signatureHelper");
+ assertEquals("private static java.lang.String signatureHelper(int, int)", mAccess.getSignature());
+ }
+
+ @Test
+ public void invokeMethodIntTest() {
+ if (vma.getClass().getName().equals("at.ssw.hotswap.test.access.jdi.JDIVMAccess")) {
+ return;
+ }
+
+ ClassAccess classAccess = vma.findClass("at.ssw.hotswap.test.accesstests.AccessTest$A");
+ assertEquals(2, classAccess.findMethod("testIntRetValue").invoke(new Object[0], new A()));
+
+ HotSwapTool.toVersion(AccessTest.class, 1);
+
+ assertEquals(3, classAccess.findMethod("testIntRetValue").invoke(new Object[0], new A()));
+ }
+
+ @Test
+ public void invokeMethodObjectTest() {
+ if (vma.getClass().getName().equals("at.ssw.hotswap.test.access.jdi.JDIVMAccess")) {
+ return;
+ }
+
+ ClassAccess classAccess = vma.findClass("at.ssw.hotswap.test.accesstests.AccessTest$A");
+ assertEquals(2, classAccess.findMethod("testObjectRetValue").invoke(new Object[0], new A()));
+
+ HotSwapTool.toVersion(AccessTest.class, 1);
+
+ assertEquals(3, classAccess.findMethod("testObjectRetValue").invoke(new Object[0], new A()));
+ }
+
+ @Test
+ public void invokeMethodVoidTest() {
+ if (vma.getClass().getName().equals("at.ssw.hotswap.test.access.jdi.JDIVMAccess")) {
+ return;
+ }
+
+ ClassAccess classAccess = vma.findClass("at.ssw.hotswap.test.accesstests.AccessTest$A");
+ assertNull(classAccess.findMethod("testVoidRetValue").invoke(new Object[0], new A()));
+
+ HotSwapTool.toVersion(AccessTest.class, 1);
+
+ assertNull(classAccess.findMethod("testVoidRetValue").invoke(new Object[0], new A()));
+ }
+
+ @Test
+ public void invokeMethodParameterTest() {
+ if (vma.getClass().getName().equals("at.ssw.hotswap.test.access.jdi.JDIVMAccess")) {
+ return;
+ }
+
+ ClassAccess classAccess = vma.findClass("at.ssw.hotswap.test.accesstests.AccessTest$A");
+ assertEquals(6, classAccess.findMethod("testMethodParameter").invoke(new Object[]{6}, new A()));
+
+ HotSwapTool.toVersion(AccessTest.class, 1);
+
+ assertEquals(7, classAccess.findMethod("testMethodParameter").invoke(new Object[]{6}, new A()));
+
+ }
+
+ @Test
+ public void getMethodsTest() {
+
+ ClassAccess cAccess = vma.findClass("at.ssw.hotswap.test.accesstests.AccessTest$A");
+ List list = cAccess.getMethods();
+
+ assertEquals(7, list.size());
+
+ Object[] mAccesses = list.toArray();
+
+ Arrays.sort(mAccesses, new Comparator() {
+
+ @Override
+ public int compare(Object o1, Object o2) {
+ return ((MethodAccess) o1).getName().compareTo(((MethodAccess) o2).getName());
+ }
+ });
+
+ assertEquals("method_Version0", ((MethodAccess) mAccesses[0]).getName());
+ assertEquals("signatureHelper", ((MethodAccess) mAccesses[1]).getName());
+ assertEquals("stackTraceHelper", ((MethodAccess) mAccesses[2]).getName());
+ assertEquals("testIntRetValue", ((MethodAccess) mAccesses[3]).getName());
+ assertEquals("testMethodParameter", ((MethodAccess) mAccesses[4]).getName());
+ assertEquals("testObjectRetValue", ((MethodAccess) mAccesses[5]).getName());
+ assertEquals("testVoidRetValue", ((MethodAccess) mAccesses[6]).getName());
+
+ HotSwapTool.toVersion(AccessTest.class, 1);
+
+ list = cAccess.getMethods();
+
+ assertEquals(7, list.size());
+
+ mAccesses = list.toArray();
+ Arrays.sort(mAccesses, new Comparator() {
+
+ @Override
+ public int compare(Object o1, Object o2) {
+ return ((MethodAccess) o1).getName().compareTo(((MethodAccess) o2).getName());
+ }
+ });
+
+ assertEquals("method_Version1", ((MethodAccess) mAccesses[0]).getName());
+ assertEquals("signatureHelper", ((MethodAccess) mAccesses[1]).getName());
+ assertEquals("stackTraceHelper", ((MethodAccess) mAccesses[2]).getName());
+ assertEquals("testIntRetValue", ((MethodAccess) mAccesses[3]).getName());
+ assertEquals("testMethodParameter", ((MethodAccess) mAccesses[4]).getName());
+ assertEquals("testObjectRetValue", ((MethodAccess) mAccesses[5]).getName());
+ assertEquals("testVoidRetValue", ((MethodAccess) mAccesses[6]).getName());
+
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/accesstests/AccessTestSuite.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/accesstests/AccessTestSuite.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.accesstests;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ AccessTest.class
+})
+public class AccessTestSuite {
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/BodyTestSuite.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/BodyTestSuite.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.body;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ *
+ * Class redefinition tests that swap only method bodies and change nothing else. This test cases should also
+ * run with the current version of HotSpot.
+ *
+ * @author Thomas Wuerthinger
+ *
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses(
+{
+ StaticTest.class,
+ SimpleStaticTest.class,
+ MultipleThreadsTest.class,
+ OldActivationTest.class,
+ RefactorActiveMethodTest.class,
+ StressTest.class,
+ FacTest.class,
+ FibTest.class,
+ RedefinePrivateMethodTest.class,
+ ClassRenamingTestCase.class,
+ EMCPTest.class
+})
+public class BodyTestSuite {
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/ClassRenamingTestCase.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/ClassRenamingTestCase.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.body;
+
+import at.ssw.hotswap.ClassRedefinitionPolicy;
+import at.ssw.hotswap.HotSwapTool;
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+/**
+ *
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ *
+ */
+public class ClassRenamingTestCase {
+
+ public static class B {
+
+ public int a() {
+ return 1;
+ }
+ }
+
+ @ClassRedefinitionPolicy(alias = B.class)
+ public static class A___1 {
+
+ public int a() {
+ return 2;
+ }
+ }
+
+ @Test
+ public void testRenaming() {
+ HotSwapTool.toVersion(ClassRenamingTestCase.class, 0);
+
+ B b = new B();
+ assertEquals(1, b.a());
+
+ HotSwapTool.toVersion(ClassRenamingTestCase.class, 1);
+
+ assertEquals(2, b.a());
+
+ HotSwapTool.toVersion(ClassRenamingTestCase.class, 0);
+
+ assertEquals(1, b.a());
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/EMCPTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/EMCPTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.body;
+
+import at.ssw.hotswap.HotSwapTool;
+import at.ssw.hotswap.test.TestUtil;
+import java.io.PrintStream;
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+/**
+ *
+ * EMCP (Equivalent modulo Constant Pool) tests.
+ *
+ * @author Thomas Wuerthinger
+ *
+ */
+public class EMCPTest {
+
+ public static class A {
+
+ public static int EMCPReturn() {
+ change();
+ PrintStream s = System.out;
+ return 1;
+ }
+ }
+
+ public static class B {
+
+ public static int b() {
+ change();
+ throw new RuntimeException();
+ }
+ }
+
+ public static class C {
+
+ public static int c() {
+ changeAndThrow();
+ return 0;
+ }
+ }
+
+ public static class D {
+
+ private static int value = 1;
+
+ public static int EMCPReturn() {
+ change3();
+ return value;
+ }
+ }
+
+ public static class A___1 {
+
+ public static int EMCPReturn() {
+ change();
+ PrintStream s = System.out;
+ return 1;
+ }
+ }
+
+ public static class B___1 {
+
+ public static int b() {
+ change();
+ throw new RuntimeException();
+ }
+ }
+
+ public static class C___1 {
+
+ public static int c() {
+ changeAndThrow();
+ return 0;
+ }
+ }
+
+ public static class D___1 {
+ private static int value = 1;
+
+ public static int EMCPReturn() {
+ change3();
+ return value;
+ }
+ }
+
+ public static class D___2 {
+ private static int value = 1;
+
+ public static int EMCPReturn() {
+ change3();
+ return value;
+ }
+ }
+
+ public static class D___3 {
+ private static int value = 1;
+
+ public static int EMCPReturn() {
+ change3();
+ return value;
+ }
+ }
+
+ public static void change() {
+
+ HotSwapTool.toVersion(EMCPTest.class, 1);
+ }
+
+ public static void change3() {
+
+ HotSwapTool.toVersion(EMCPTest.class, 1);
+ HotSwapTool.toVersion(EMCPTest.class, 2);
+ HotSwapTool.toVersion(EMCPTest.class, 3);
+ }
+
+ public static void changeAndThrow() {
+
+ HotSwapTool.toVersion(EMCPTest.class, 1);
+
+ throw new RuntimeException();
+ }
+
+
+ @Test
+ public void testEMCPReturn() {
+ HotSwapTool.toVersion(EMCPTest.class, 0);
+
+ assertEquals(1, A.EMCPReturn());
+
+ HotSwapTool.toVersion(EMCPTest.class, 0);
+
+ assertEquals(1, A.EMCPReturn());
+
+ HotSwapTool.toVersion(EMCPTest.class, 0);
+ }
+
+ @Test
+ public void testEMCPMultiChangeReturn() {
+ HotSwapTool.toVersion(EMCPTest.class, 0);
+
+ assertEquals(1, D.EMCPReturn());
+
+ HotSwapTool.toVersion(EMCPTest.class, 0);
+
+ assertEquals(1, D.EMCPReturn());
+
+ HotSwapTool.toVersion(EMCPTest.class, 0);
+ }
+
+ @Test
+ public void testEMCPException() {
+ HotSwapTool.toVersion(EMCPTest.class, 0);
+
+ TestUtil.assertException(RuntimeException.class, new Runnable(){
+ @Override
+ public void run() {
+ B.b();
+ }
+ });
+
+ HotSwapTool.toVersion(EMCPTest.class, 0);
+
+ TestUtil.assertException(RuntimeException.class, new Runnable(){
+ @Override
+ public void run() {
+ B.b();
+ }
+ });
+
+ HotSwapTool.toVersion(EMCPTest.class, 0);
+ }
+
+ @Test
+ public void testEMCPExceptionInCallee() {
+ HotSwapTool.toVersion(EMCPTest.class, 0);
+
+ TestUtil.assertException(RuntimeException.class, new Runnable(){
+ @Override
+ public void run() {
+ C.c();
+ }
+ });
+
+ HotSwapTool.toVersion(EMCPTest.class, 0);
+
+ TestUtil.assertException(RuntimeException.class, new Runnable(){
+ @Override
+ public void run() {
+ C.c();
+ }
+ });
+
+ HotSwapTool.toVersion(EMCPTest.class, 0);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/FacTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/FacTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.body;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Recursive implementation of the factorial function using class redefinition.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class FacTest {
+
+ public static abstract class Base {
+
+ protected int calc() {
+ return calc(HotSwapTool.getCurrentVersion(FacTest.class));
+ }
+
+ public int calcAt(int version) {
+ HotSwapTool.toVersion(FacTest.class, version);
+ int result = calc();
+ HotSwapTool.toVersion(FacTest.class, 0);
+ return result;
+ }
+
+ protected int calc(int version) {
+ return calc();
+ }
+ }
+
+ public static class Factorial extends Base {
+
+ @Override
+ protected int calc(int n) {
+ return n * calcAt(n - 1);
+ }
+ }
+
+ public static class Factorial___1 extends Base {
+
+ @Override
+ protected int calc() {
+ return 1;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(FacTest.class, 0);
+ }
+
+ @Test
+ public void testFac() {
+
+ assert HotSwapTool.getCurrentVersion(FacTest.class) == 0;
+ Factorial f = new Factorial();
+
+ assertEquals(1, f.calcAt(1));
+
+ assert HotSwapTool.getCurrentVersion(FacTest.class) == 0;
+ assertEquals(2, f.calcAt(2));
+
+ assert HotSwapTool.getCurrentVersion(FacTest.class) == 0;
+ assertEquals(6, f.calcAt(3));
+
+ assert HotSwapTool.getCurrentVersion(FacTest.class) == 0;
+ assertEquals(24, f.calcAt(4));
+
+ assert HotSwapTool.getCurrentVersion(FacTest.class) == 0;
+ assertEquals(120, f.calcAt(5));
+
+ assert HotSwapTool.getCurrentVersion(FacTest.class) == 0;
+ assertEquals(479001600, f.calcAt(12));
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/FibTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/FibTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.body;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Recursive implementation of the fibonacci function using class redefinition.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class FibTest {
+
+ public static abstract class Base {
+
+ protected int calc() {
+ return calc(HotSwapTool.getCurrentVersion(FibTest.class));
+ }
+
+ public int calcAt(int version) {
+ HotSwapTool.toVersion(FibTest.class, version);
+ int result = calc();
+ HotSwapTool.toVersion(FibTest.class, 0);
+ return result;
+ }
+
+ protected int calc(int version) {
+ return calc();
+ }
+ }
+
+ public static class Fib extends Base {
+
+ @Override
+ protected int calc(int n) {
+ return calcAt(n - 1) + calcAt(n - 2);
+ }
+ }
+
+ public static class Fib___1 extends Base {
+
+ @Override
+ protected int calc() {
+ return 1;
+ }
+ }
+
+ public static class Fib___2 extends Base {
+
+ @Override
+ protected int calc() {
+ return 2;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(FibTest.class, 0);
+ }
+
+ @Test
+ public void testFib() {
+
+ // 0 1 2 3 4 5
+ // 1 1 2 3 5 8
+ assert HotSwapTool.getCurrentVersion(FibTest.class) == 0;
+ Fib f = new Fib();
+
+ assertEquals(1, f.calcAt(1));
+
+ assert HotSwapTool.getCurrentVersion(FibTest.class) == 0;
+ assertEquals(2, f.calcAt(2));
+
+ assert HotSwapTool.getCurrentVersion(FibTest.class) == 0;
+ assertEquals(3, f.calcAt(3));
+
+ assert HotSwapTool.getCurrentVersion(FibTest.class) == 0;
+ assertEquals(5, f.calcAt(4));
+
+ assert HotSwapTool.getCurrentVersion(FibTest.class) == 0;
+ assertEquals(8, f.calcAt(5));
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/MultipleThreadsTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/MultipleThreadsTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.body;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+import at.ssw.hotswap.test.methods.OverrideMethodTest;
+
+/**
+ * Class for testing redefining methods of classes that extend the Thread class. In the test setup the run method
+ * calls the doit method in a loop until this methods returns false.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class MultipleThreadsTest {
+
+ public static final int COUNT = 10;
+
+ // Version 0
+ public static class A extends Thread {
+
+ private int value;
+ private int value2;
+ private boolean flag = false;
+
+ @Override
+ public void run() {
+ while (doit()) {
+ flag = false;
+ }
+ }
+
+ public boolean doit() {
+ if (flag) {
+ throw new RuntimeException("Must not reach here");
+ }
+ flag = true;
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e) {
+ }
+
+ value++;
+ return true;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public int getValue2() {
+ return value2;
+ }
+ }
+
+ // Version 1
+ public static class A___1 extends Thread {
+
+ private int value;
+ private int value2;
+ private boolean flag = false;
+
+ @Override
+ public void run() {
+ while (doit()) {
+ flag = false;
+ }
+ }
+
+ public boolean doit() {
+ if (flag) {
+ throw new RuntimeException("Must not reach here");
+ }
+ flag = true;
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e) {
+ }
+
+ value2++;
+ return true;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public int getValue2() {
+ return value2;
+ }
+ }
+
+ // Version 2
+ public static class A___2 extends Thread {
+
+ private int value;
+ private int value2;
+ private boolean flag = false;
+
+ @Override
+ public void run() {
+ while (doit()) {
+ flag = false;
+ }
+ }
+
+ public boolean doit() {
+ return false;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public int getValue2() {
+ return value2;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(MultipleThreadsTest.class, 0);
+ }
+
+ @Test
+ public void testOneThread() {
+ test(1);
+ }
+
+ @Test
+ public void testThreads() {
+ test(COUNT);
+ }
+
+ private void test(int count) {
+
+ assert HotSwapTool.getCurrentVersion(MultipleThreadsTest.class) == 0;
+
+ A[] arr = new A[count];
+ for (int i = 0; i < count; i++) {
+ arr[i] = new A();
+ arr[i].start();
+ }
+
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ }
+
+ for (int i = 0; i < count; i++) {
+ //assertTrue(arr[i].getValue() > 0);
+ }
+
+ HotSwapTool.toVersion(MultipleThreadsTest.class, 1);
+
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ }
+
+ for (int i = 0; i < count; i++) {
+ assertTrue(arr[i].getValue2() > 0);
+ }
+
+ HotSwapTool.toVersion(MultipleThreadsTest.class, 2);
+
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ }
+
+
+ for (int i = 0; i < count; i++) {
+ assertFalse(arr[i].isAlive());
+ }
+
+ HotSwapTool.toVersion(OverrideMethodTest.class, 0);
+
+
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/OldActivationTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/OldActivationTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.body;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Stress test for the number of old activations on the stack. In the test setup 10 different versions of the method A.value will be on the stack.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class OldActivationTest {
+
+ // Version 0
+ public static class A {
+
+ public int value() {
+ HotSwapTool.toVersion(OldActivationTest.class, 1);
+ return 1 + this.value();
+ }
+ }
+
+ // Version 1
+ public static class A___1 {
+
+ public int value() {
+ HotSwapTool.toVersion(OldActivationTest.class, 2);
+ return 2 + this.value();
+ }
+ }
+
+ // Version 2
+ public static class A___2 {
+
+ public int value() {
+ HotSwapTool.toVersion(OldActivationTest.class, 3);
+ return 3 + this.value();
+ }
+ }
+
+ // Version 3
+ public static class A___3 {
+
+ public int value() {
+ HotSwapTool.toVersion(OldActivationTest.class, 4);
+ return 4 + this.value();
+ }
+ }
+
+ // Version 4
+ public static class A___4 {
+
+ public int value() {
+ HotSwapTool.toVersion(OldActivationTest.class, 5);
+ return 5 + this.value();
+ }
+ }
+
+ // Version 5
+ public static class A___5 {
+
+ public int value() {
+ HotSwapTool.toVersion(OldActivationTest.class, 6);
+ return 6 + this.value();
+ }
+ }
+
+ // Version 6
+ public static class A___6 {
+
+ public int value() {
+ HotSwapTool.toVersion(OldActivationTest.class, 7);
+ return 7 + this.value();
+ }
+ }
+
+ // Version 7
+ public static class A___7 {
+
+ public int value() {
+ HotSwapTool.toVersion(OldActivationTest.class, 8);
+ return 8 + this.value();
+ }
+ }
+
+ // Version 8
+ public static class A___8 {
+
+ public int value() {
+ HotSwapTool.toVersion(OldActivationTest.class, 9);
+ return 9 + this.value();
+ }
+ }
+
+ // Version 9
+ public static class A___9 {
+
+ public int value() {
+ HotSwapTool.toVersion(OldActivationTest.class, 0);
+ return 10;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(OldActivationTest.class, 0);
+ }
+
+ @Test
+ public void testOldActivationTest() {
+
+ assert HotSwapTool.getCurrentVersion(OldActivationTest.class) == 0;
+
+ A a = new A();
+
+ assertEquals(1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10, a.value());
+ assert HotSwapTool.getCurrentVersion(OldActivationTest.class) == 0;
+
+ HotSwapTool.toVersion(OldActivationTest.class, 1);
+ assertEquals(2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10, a.value());
+ assert HotSwapTool.getCurrentVersion(OldActivationTest.class) == 0;
+
+ HotSwapTool.toVersion(OldActivationTest.class, 8);
+ assertEquals(9 + 10, a.value());
+ assert HotSwapTool.getCurrentVersion(OldActivationTest.class) == 0;
+
+ HotSwapTool.toVersion(OldActivationTest.class, 4);
+ assertEquals(5 + 6 + 7 + 8 + 9 + 10, a.value());
+ assert HotSwapTool.getCurrentVersion(OldActivationTest.class) == 0;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/RedefinePrivateMethodTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/RedefinePrivateMethodTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.body;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Tests redefinition of a class such that old code still accesses a redefined private method.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class RedefinePrivateMethodTest {
+
+ // Version 0
+ public static class A {
+
+ public int foo() {
+ int result = bar();
+ HotSwapTool.toVersion(RedefinePrivateMethodTest.class, 1);
+ result += bar();
+ return result;
+ }
+
+ private int bar() {
+ return 1;
+ }
+ }
+
+ // Version 1
+ public static class A___1 {
+
+ public int foo() {
+ return -1;
+ }
+
+ private int bar() {
+ return 2;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(RedefinePrivateMethodTest.class, 0);
+ }
+
+ @Test
+ public void testRedefinePrivateMethod() {
+
+ assert HotSwapTool.getCurrentVersion(RedefinePrivateMethodTest.class) == 0;
+
+ A a = new A();
+
+ assertEquals(3, a.foo());
+
+ assert HotSwapTool.getCurrentVersion(RedefinePrivateMethodTest.class) == 1;
+
+ assertEquals(-1, a.foo());
+
+ HotSwapTool.toVersion(RedefinePrivateMethodTest.class, 0);
+
+ assertEquals(3, a.foo());
+
+ assert HotSwapTool.getCurrentVersion(RedefinePrivateMethodTest.class) == 1;
+
+ assertEquals(-1, a.foo());
+
+ HotSwapTool.toVersion(RedefinePrivateMethodTest.class, 0);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/RefactorActiveMethodTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/RefactorActiveMethodTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.body;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * @author Thomas Wuerthinger
+ */
+public class RefactorActiveMethodTest {
+
+ // Version 0
+ public static class A {
+
+ public int value() {
+ HotSwapTool.toVersion(RefactorActiveMethodTest.class, 1);
+ return 5;
+ }
+
+ public int secondValue() {
+ return 1;
+ }
+ }
+
+ // Version 1
+ public static class A___1 {
+
+ public int value() {
+ return secondValue() * 2;
+ }
+
+ public int secondValue() {
+ return 2;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(RefactorActiveMethodTest.class, 0);
+ }
+
+ @Test
+ public void testActiveMethodReplacement() {
+
+ assert HotSwapTool.getCurrentVersion(RefactorActiveMethodTest.class) == 0;
+
+ A a = new A();
+
+ assertEquals(5, a.value());
+
+ assert HotSwapTool.getCurrentVersion(RefactorActiveMethodTest.class) == 1;
+
+ assertEquals(2, a.secondValue());
+ assertEquals(4, a.value());
+ assertEquals(2, a.secondValue());
+
+ assert HotSwapTool.getCurrentVersion(RefactorActiveMethodTest.class) == 1;
+
+ HotSwapTool.toVersion(RefactorActiveMethodTest.class, 0);
+
+ assertEquals(1, a.secondValue());
+ assertEquals(5, a.value());
+ assertEquals(4, a.value());
+
+ HotSwapTool.toVersion(RefactorActiveMethodTest.class, 0);
+
+ assertEquals(1, a.secondValue());
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/SimpleStaticTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/SimpleStaticTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.body;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+import at.ssw.hotswap.test.TestUtil;
+
+/**
+ * @author Thomas Wuerthinger
+ */
+public class SimpleStaticTest {
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(SimpleStaticTest.class, 0);
+
+ // E and Helper must be loaded and initialized
+ E e = new E();
+ Helper h = new Helper();
+ }
+
+ // Version 0
+
+ public static class Helper {
+ public static int getIntegerField() {
+ return E.integerField;
+ }
+
+ public static void setIntegerField(int x) {
+ E.integerField = x;
+ }
+
+ public static int getFinalIntegerField() {
+ return E.finalIntegerField;
+ }
+ }
+
+ public static class E {
+ public static int integerField = 10;
+
+ // javac will generate "ConstantValue" attribute for this field!
+ public static final int finalIntegerField = 7;
+ }
+
+ public static class E___1 {
+ }
+
+ // Version 1
+ public static class E___2 {
+ public static int integerField = 10;
+
+ // javac will generate "ConstantValue" attribute for this field!
+ public static final int finalIntegerField = 7;
+ }
+
+ @Test
+ public void testSimpleNewStaticField() {
+
+ assert HotSwapTool.getCurrentVersion(SimpleStaticTest.class) == 0;
+
+ HotSwapTool.toVersion(SimpleStaticTest.class, 1);
+
+ TestUtil.assertException(NoSuchFieldError.class, new Runnable(){
+ @Override
+ public void run() {
+ Helper.getIntegerField();
+ }
+ });
+
+ HotSwapTool.toVersion(SimpleStaticTest.class, 2);
+
+ assertEquals(0, Helper.getIntegerField());
+ assertEquals(7, Helper.getFinalIntegerField());
+ Helper.setIntegerField(1000);
+ assertEquals(1000, Helper.getIntegerField());
+
+ HotSwapTool.toVersion(SimpleStaticTest.class, 1);
+
+ TestUtil.assertException(NoSuchFieldError.class, new Runnable(){
+ @Override
+ public void run() {
+ Helper.getIntegerField();
+ }
+ });
+
+ HotSwapTool.toVersion(SimpleStaticTest.class, 2);
+
+ assertEquals(0, Helper.getIntegerField());
+ assertEquals(7, Helper.getFinalIntegerField());
+ Helper.setIntegerField(1000);
+ assertEquals(1000, Helper.getIntegerField());
+
+ HotSwapTool.toVersion(SimpleStaticTest.class, 0);
+
+ assertEquals(7, Helper.getFinalIntegerField());
+ assertEquals(1000, Helper.getIntegerField());
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/StaticTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/StaticTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.body;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Thomas Wuerthinger
+ */
+public class StaticTest {
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(StaticTest.class, 0);
+ }
+
+ // Version 0
+
+
+ public static class Helper {
+ public static int getAdditionalField() {
+ return -1;
+ }
+
+ public static void setAdditionalField(int x) {
+
+ }
+ }
+
+ public static class A {
+
+ public static int value() {
+ return 1;
+ }
+ }
+
+ public static class B {
+
+ public static int value() {
+ return 2;
+ }
+ }
+
+ public static class C {
+ static {
+ System.out.println("Static initializer of C");
+ }
+
+ public static int value = 5;
+ }
+
+ public static class D {
+ public static List objectField = new ArrayList();
+ public static int[] arrayField = new int[10];
+ public static int integerField = 5;
+ public static char characterField = 6;
+ public static short shortField = 7;
+ public static double doubleField = 1.0;
+ public static float floatField = 2.0f;
+ public static long longField = 8;
+ public static boolean booleanField = true;
+ }
+
+ // Version 1
+ public static class A___1 {
+
+ public static int value() {
+ return B.value() * 2;
+ }
+ }
+
+ // Version 2
+ public static class B___2 {
+
+ public static int value() {
+ return 3;
+ }
+ }
+
+ // Version 3
+ public static class A___3 {
+
+ public static int value() {
+ return 5;
+ }
+ }
+
+ public static class B___3 {
+
+ public static int value() {
+ return A.value() * 2;
+ }
+ }
+
+ // Version 4
+ public static class C___4 {
+
+ static {
+ System.out.println("Static initializer of C-4");
+ }
+
+ public static int value = 6;
+ }
+
+ public static class Helper___5 {
+ public static int getAdditionalField() {
+ return D___5.additionalField;
+ }
+
+ public static void setAdditionalField(int x) {
+ D___5.additionalField = x;
+ }
+ }
+
+ public static class D___5 {
+ public static int additionalField;
+
+ public static List objectField;
+ public static long longField;
+ public static short shortField = 10;
+ public static float floatField;
+ public static int[] arrayField;
+ public static int integerField;
+ public static char characterField;
+ public static double doubleField;
+ public static boolean booleanField;
+ }
+
+ @Test
+ public void testBase() {
+
+ assert HotSwapTool.getCurrentVersion(StaticTest.class) == 0;
+
+
+ assertEquals(1, A.value());
+ assertEquals(2, B.value());
+
+ HotSwapTool.toVersion(StaticTest.class, 1);
+
+ assertEquals(4, A.value());
+ assertEquals(2, B.value());
+
+ HotSwapTool.toVersion(StaticTest.class, 2);
+
+ assertEquals(6, A.value());
+ assertEquals(3, B.value());
+
+ HotSwapTool.toVersion(StaticTest.class, 3);
+
+ assertEquals(5, A.value());
+ assertEquals(10, B.value());
+
+ HotSwapTool.toVersion(StaticTest.class, 0);
+
+ assertEquals(1, A.value());
+ assertEquals(2, B.value());
+ }
+
+ @Test
+ public void testStaticField() {
+
+ assert HotSwapTool.getCurrentVersion(StaticTest.class) == 0;
+ assertEquals(5, C.value);
+
+ HotSwapTool.toVersion(StaticTest.class, 4);
+ assertEquals(5, C.value);
+
+ HotSwapTool.toVersion(StaticTest.class, 0);
+ assertEquals(5, C.value);
+ }
+
+
+ @Test
+ public void testManyStaticFields() {
+
+ assert HotSwapTool.getCurrentVersion(StaticTest.class) == 0;
+ assertTrue(D.objectField != null);
+ assertTrue(D.arrayField != null);
+ assertEquals(5, D.integerField);
+ assertEquals(6, D.characterField);
+ assertEquals(7, D.shortField);
+ assertEquals(1.0, D.doubleField, 0.0);
+ assertEquals(2.0f, D.floatField, 0.0);
+ assertEquals(8, D.longField);
+ assertEquals(true, D.booleanField);
+
+ HotSwapTool.toVersion(StaticTest.class, 5);
+ assertTrue(D.objectField != null);
+ assertTrue(D.arrayField != null);
+ assertEquals(5, D.integerField);
+ assertEquals(6, D.characterField);
+ assertEquals(7, D.shortField);
+ assertEquals(1.0, D.doubleField, 0.0);
+ assertEquals(2.0f, D.floatField, 0.0);
+ assertEquals(8, D.longField);
+ assertEquals(true, D.booleanField);
+
+ assertEquals(0, Helper.getAdditionalField());
+ Helper.setAdditionalField(1000);
+ assertEquals(1000, Helper.getAdditionalField());
+
+
+ HotSwapTool.toVersion(StaticTest.class, 0);
+
+ assertTrue(D.objectField != null);
+ assertTrue(D.arrayField != null);
+ assertEquals(5, D.integerField);
+ assertEquals(6, D.characterField);
+ assertEquals(7, D.shortField);
+ assertEquals(1.0, D.doubleField, 0.0);
+ assertEquals(2.0f, D.floatField, 0.0);
+ assertEquals(8, D.longField);
+ assertEquals(true, D.booleanField);
+
+ HotSwapTool.toVersion(StaticTest.class, 5);
+ assertTrue(D.objectField != null);
+ assertTrue(D.arrayField != null);
+ assertEquals(5, D.integerField);
+ assertEquals(6, D.characterField);
+ assertEquals(7, D.shortField);
+ assertEquals(1.0, D.doubleField, 0.0);
+ assertEquals(2.0f, D.floatField, 0.0);
+ assertEquals(8, D.longField);
+ assertEquals(true, D.booleanField);
+
+ assertEquals(0, Helper.getAdditionalField());
+
+ HotSwapTool.toVersion(StaticTest.class, 0);
+ assertTrue(D.objectField != null);
+ assertTrue(D.arrayField != null);
+ assertEquals(5, D.integerField);
+ assertEquals(6, D.characterField);
+ assertEquals(7, D.shortField);
+ assertEquals(1.0, D.doubleField, 0.0);
+ assertEquals(2.0f, D.floatField, 0.0);
+ assertEquals(8, D.longField);
+ assertEquals(true, D.booleanField);
+
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/StressTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/body/StressTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.body;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * @author Thomas Wuerthinger
+ */
+public class StressTest {
+
+ public final static int COUNT = 10;
+
+ // Version 0
+ public static class A {
+
+ public int value() {
+ return 1;
+ }
+ }
+
+ // Version 1
+ public static class A___1 {
+
+ public int value() {
+ return 2;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(StressTest.class, 0);
+ }
+
+ @Test
+ public void testStressSwap() {
+
+ assert HotSwapTool.getCurrentVersion(StressTest.class) == 0;
+
+ A a = new A();
+
+ for (int i = 0; i < COUNT; i++) {
+
+ assertEquals(1, a.value());
+
+ HotSwapTool.toVersion(StressTest.class, 1);
+
+ assertEquals(2, a.value());
+
+ HotSwapTool.toVersion(StressTest.class, 0);
+ }
+
+ assertEquals(1, a.value());
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/eval/AddingInterfaceTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/eval/AddingInterfaceTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.eval;
+
+import at.ssw.hotswap.HotSwapTool;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Adds an implemented interface to a class and tests whether an instance of this class can then really be treated as an instance of the interface.
+ * Additionally, performs performance measurements of a call to this interface compared to a proxy object.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class AddingInterfaceTest {
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(AddingInterfaceTest.class, 0);
+ assert HotSwapTool.getCurrentVersion(AddingInterfaceTest.class) == 0;
+ }
+
+ public static class A {
+
+ public int getValue() {
+ return 1;
+ }
+ }
+
+ public static interface I {
+
+ public int getValue();
+ }
+
+ public static class A___1 implements I {
+
+ public int getValue() {
+ return 1;
+ }
+ }
+
+ public static class Proxy implements I {
+
+ private A a;
+
+ public Proxy(A a) {
+ this.a = a;
+ }
+
+ public int getValue() {
+ return a.getValue();
+ }
+ }
+
+ @Test
+ public void testAddInterface() {
+
+ A a = new A();
+ Proxy p = new Proxy(a);
+
+ final int N = 100000;
+ final int Z = 1;
+
+
+
+ HotSwapTool.toVersion(AddingInterfaceTest.class, 1);
+ I i = (I) a;
+
+ long startTime = System.currentTimeMillis();
+ for (int j = 0; j < Z; j++) {
+ calculateSum(N, i);
+ }
+ long time = System.currentTimeMillis() - startTime;
+ System.out.println(time);
+
+ // Must set to null, otherwise local variable i would violate type safety
+ i = null;
+ HotSwapTool.toVersion(AddingInterfaceTest.class, 0);
+
+ startTime = System.currentTimeMillis();
+ for (int j = 0; j < Z; j++) {
+ calculateSum(N, p);
+ }
+ time = System.currentTimeMillis() - startTime;
+ System.out.println(time);
+ }
+
+ public int calculateSum(int n, I i) {
+ int sum = 0;
+ for (int j = 0; j < n; j++) {
+ sum += i.getValue();
+ }
+ return sum;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/eval/EvalTestSuite.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/eval/EvalTestSuite.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.eval;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ *
+ * Tests used for evaluation purposes (especially performance measurements).
+ *
+ * @author Thomas Wuerthinger
+ *
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ FractionTest.class,
+ AddingInterfaceTest.class
+})
+public class EvalTestSuite {
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/eval/FractionTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/eval/FractionTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.eval;
+
+import java.lang.Runnable;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * @author Thomas Wuerthinger
+ */
+public class FractionTest {
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(FractionTest.class, 0);
+ assert HotSwapTool.getCurrentVersion(FractionTest.class) == 0;
+ }
+
+ // Version 0
+ public static class NoChange {
+
+ int i1;
+ int i2;
+ int i3;
+ Object o1;
+ Object o2;
+ Object o3;
+ }
+
+ public static class Change {
+
+ int i1;
+ int i2;
+ int i3;
+ Object o1;
+ Object o2;
+ Object o3;
+ }
+
+ // Version 1
+ public static class Change___1 {
+
+ int i1;
+ int i2;
+ int i3;
+ Object o1;
+ Object o2;
+ Object o3;
+ Object o4;
+ }
+
+ // Version 2
+ public static class Change___2 {
+
+ int i1;
+ int i2;
+ int i3;
+ Object o1;
+ }
+
+ // Version 3
+ public static class Change___3 {
+
+ int i3;
+ int i1;
+ int i2;
+ Object o3;
+ Object o1;
+ Object o2;
+ }
+
+ // Version 3
+ public static class Change___4 {
+
+ int i1;
+ int i2;
+ int i3;
+ Object o1;
+ Object o2;
+ Object o3;
+ }
+ private static List measurements = new ArrayList();
+ private final int BASE = 10;
+ private Object[] objects;
+
+ private void clear() {
+ objects = null;
+ System.gc();
+ System.gc();
+ HotSwapTool.toVersion(FractionTest.class, 0);
+ System.gc();
+ System.gc();
+
+ }
+
+ private void init(int count, int percent) {
+ objects = new Object[count];
+ int changed = 0;
+ int unchanged = 0;
+ for (int k = 0; k < count; k++) {
+ if ((count / BASE) * percent <= k/* && k >= 200000*/) {
+ objects[k] = new NoChange();
+ unchanged++;
+ } else {
+ objects[k] = new Change();
+ changed++;
+ }
+ }
+
+ System.gc();
+
+ System.out.println(changed + " changed objects allocated");
+ }
+
+ @Test
+ public void testBase() {
+
+ assert HotSwapTool.getCurrentVersion(FractionTest.class) == 0;
+
+ final int N = 1;
+ final int INC = 4;
+
+ int[] benchmarking = new int[]{40000};
+ int base = BASE;
+ int start = 0;
+
+ MicroBenchmark[] benchmarks = new MicroBenchmark[]{new GCMicroBenchmark(), new IncreaseMicroBenchmark(), new DecreaseMicroBenchmark(), new ReorderMicroBenchmark(), new NoRealChangeMicroBenchmark()};
+
+ clear();
+ for (int k = 0; k < N; k++) {
+ for (MicroBenchmark m : benchmarks) {
+ for (int i : benchmarking) {
+ System.out.println(m.getClass().getName() + " with " + i + " objects");
+ for (int j = start; j <= base; j += INC) {
+ System.out.println(j);
+ m.init(i);
+ init(i, j);
+ m.doit(i, measurements);
+ clear();
+ }
+ }
+ }
+ }
+
+ System.out.println("Results:");
+ for (long l : measurements) {
+ System.out.println(l);
+ }
+ measurements.clear();
+ }
+}
+
+abstract class MicroBenchmark {
+
+ public void init(int count) {
+ }
+
+ public abstract void doit(int count, List measurements);
+}
+
+class GCMicroBenchmark extends MicroBenchmark {
+
+ public void doit(int count, List measurements) {
+ long startTime = System.currentTimeMillis();
+ System.gc();
+ long curTime = System.currentTimeMillis() - startTime;
+ measurements.add(curTime);
+ }
+}
+
+class IncreaseMicroBenchmark extends MicroBenchmark {
+
+ public void doit(int count, List measurements) {
+ HotSwapTool.resetTimings();
+ HotSwapTool.toVersion(FractionTest.class, 1);
+ measurements.add(HotSwapTool.getTotalTime());
+ }
+}
+
+class DecreaseMicroBenchmark extends MicroBenchmark {
+
+ public void doit(int count, List measurements) {
+ HotSwapTool.resetTimings();
+ HotSwapTool.toVersion(FractionTest.class, 2);
+ measurements.add(HotSwapTool.getTotalTime());
+ }
+}
+
+class ReorderMicroBenchmark extends MicroBenchmark {
+
+ public void doit(int count, List measurements) {
+ HotSwapTool.resetTimings();
+ HotSwapTool.toVersion(FractionTest.class, 3);
+ measurements.add(HotSwapTool.getTotalTime());
+ }
+}
+
+class NoRealChangeMicroBenchmark extends MicroBenchmark {
+
+ public void doit(int count, List measurements) {
+ HotSwapTool.resetTimings();
+ HotSwapTool.toVersion(FractionTest.class, 4);
+ measurements.add(HotSwapTool.getTotalTime());
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/AccessDeletedFieldTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/AccessDeletedFieldTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.fields;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+import at.ssw.hotswap.test.TestUtil;
+
+/**
+ * Tests for accessing a deleted field. In the first scenario, the field is deleted from the class.
+ * In the second scenario, it is deleted because of a changed subtype relationship.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class AccessDeletedFieldTest {
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(AccessDeletedFieldTest.class, 0);
+ }
+
+ // Version 0
+ public static class A {
+
+ public int x;
+
+ int getFieldInOldCode() {
+
+ HotSwapTool.toVersion(AccessDeletedFieldTest.class, 1);
+
+ // This field does no longer exist
+ return x;
+ }
+ }
+
+ public static class B extends A {
+ }
+
+ // Version 1
+ public static class A___1 {
+ }
+
+ // Version 2
+ public static class B___2 {
+ }
+
+ // Method to enforce cast (otherwise bytecodes become invalid in version 2)
+ public static A convertBtoA(Object b) {
+ return (A) b;
+ }
+
+ @Test
+ public void testOldCodeAccessesDeletedField() {
+
+ assert HotSwapTool.getCurrentVersion(AccessDeletedFieldTest.class) == 0;
+
+ final A a = new A();
+ a.x = 1;
+
+ TestUtil.assertException(NoSuchFieldError.class, new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(0, a.getFieldInOldCode());
+ }
+ });
+
+ assert HotSwapTool.getCurrentVersion(AccessDeletedFieldTest.class) == 1;
+ HotSwapTool.toVersion(AccessDeletedFieldTest.class, 0);
+ assertEquals(0, a.x);
+ }
+
+ @Test
+ public void testAccessDeletedField() {
+
+ assert HotSwapTool.getCurrentVersion(AccessDeletedFieldTest.class) == 0;
+
+ final A a = new A();
+ a.x = 1;
+
+ assertEquals(1, a.x);
+
+ HotSwapTool.toVersion(AccessDeletedFieldTest.class, 1);
+
+ TestUtil.assertException(NoSuchFieldError.class, new Runnable() {
+ @Override
+ public void run() {
+ System.out.println(a.x);
+ }
+ });
+
+ HotSwapTool.toVersion(AccessDeletedFieldTest.class, 0);
+ assertEquals(0, a.x);
+ }
+
+ @Test
+ public void testAccessDeleteBaseClassFieldNormal() {
+
+ HotSwapTool.toVersion(AccessDeletedFieldTest.class, 0);
+ assert HotSwapTool.getCurrentVersion(AccessDeletedFieldTest.class) == 0;
+ final B b = new B();
+ b.x = 1;
+ final A a = new A();
+ a.x = 2;
+
+ assertEquals(1, b.x);
+ assertEquals(2, a.x);
+
+ HotSwapTool.toVersion(AccessDeletedFieldTest.class, 2);
+
+ TestUtil.assertException(NoSuchFieldError.class, new Runnable() {
+
+ @Override
+ public void run() {
+ System.out.println(b.x);
+ }
+ });
+
+ assertEquals(2, a.x);
+
+ HotSwapTool.toVersion(AccessDeletedFieldTest.class, 0);
+ assertEquals(0, b.x);
+ }
+
+ @Test
+ public void testAccessDeleteBaseClassFieldInvalid() {
+
+ HotSwapTool.toVersion(AccessDeletedFieldTest.class, 0);
+ assert HotSwapTool.getCurrentVersion(AccessDeletedFieldTest.class) == 0;
+ final B b = new B();
+ final A a1 = new A();
+ a1.x = 1;
+ b.x = 1;
+
+ HotSwapTool.toVersion(AccessDeletedFieldTest.class, 2);
+
+ TestUtil.assertException(NoSuchFieldError.class, new Runnable() {
+
+ @Override
+ public void run() {
+ System.out.println(b.x);
+ }
+ });
+
+ assertEquals(1, a1.x);
+
+ HotSwapTool.toVersion(AccessDeletedFieldTest.class, 0);
+ assertEquals(0, b.x);
+ assertEquals(1, a1.x);
+
+ A a = convertBtoA(b);
+
+ assertEquals(0, b.x);
+
+ // Must fail, because now an instance of B is in a local variable of type A!
+ TestUtil.assertException(UnsupportedOperationException.class, new Runnable() {
+
+ @Override
+ public void run() {
+ HotSwapTool.toVersion(AccessDeletedFieldTest.class, 2);
+ }
+ });
+
+ assertEquals(0, a.x);
+
+ // Still at version 0
+ assert HotSwapTool.getCurrentVersion(AccessDeletedFieldTest.class) == 0;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/AccessDeletedStaticFieldTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/AccessDeletedStaticFieldTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.fields;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+import at.ssw.hotswap.test.TestUtil;
+
+/**
+ * Tests for accessing a deleted static field.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class AccessDeletedStaticFieldTest {
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(AccessDeletedStaticFieldTest.class, 0);
+ }
+
+ // Version 0
+ public static class A {
+
+ public static int x;
+
+ static int getFieldInOldCode() {
+
+ HotSwapTool.toVersion(AccessDeletedStaticFieldTest.class, 1);
+
+ newMethodFromOldCode();
+
+ // This field does no longer exist
+ return x;
+ }
+
+ static int getFieldEMCPMethod() {
+ HotSwapTool.toVersion(AccessDeletedStaticFieldTest.class, 2);
+ return A.x;
+ }
+ }
+
+ // Version 1
+ public static class A___1 {
+ }
+
+ // Version 2
+
+ public static class A___2 {
+
+ // EMCP to method in version 0
+ static int getFieldEMCPMethod() {
+ HotSwapTool.toVersion(AccessDeletedStaticFieldTest.class, 2);
+ return A.x;
+ }
+ }
+
+ private static void newMethodFromOldCode() {
+ TestUtil.assertException(NoSuchFieldError.class, new Runnable() {
+ @Override
+ public void run() {
+ System.out.println(A.x);
+ }
+ });
+ }
+
+ @Test
+ public void testAccessDeletedStaticField() {
+
+ assert HotSwapTool.getCurrentVersion(AccessDeletedStaticFieldTest.class) == 0;
+
+ A.x = 1;
+ assertEquals(1, A.getFieldInOldCode());
+
+ assert HotSwapTool.getCurrentVersion(AccessDeletedStaticFieldTest.class) == 1;
+ HotSwapTool.toVersion(AccessDeletedStaticFieldTest.class, 0);
+ assertEquals(0, A.x);
+
+ assert HotSwapTool.getCurrentVersion(AccessDeletedStaticFieldTest.class) == 0;
+ }
+
+
+ @Test
+ public void testAccessDeletedStaticFieldFromEMCPMethod() {
+
+ assert HotSwapTool.getCurrentVersion(AccessDeletedStaticFieldTest.class) == 0;
+ TestUtil.assertException(NoSuchFieldError.class, new Runnable() {
+ @Override
+ public void run() {
+ System.out.println(A.getFieldEMCPMethod());
+ }
+ });
+
+ HotSwapTool.toVersion(AccessDeletedStaticFieldTest.class, 0);
+ assertEquals(0, A.x);
+
+ assert HotSwapTool.getCurrentVersion(AccessDeletedStaticFieldTest.class) == 0;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/ComplexFieldTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/ComplexFieldTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.fields;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Complex field test.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class ComplexFieldTest {
+
+ // Version 0
+ public static class A {
+ public byte byteFld = 10;
+ public short shortFld = 20;
+ public int intFld = 30;
+ public long longFld = 40L;
+ public float floatFld = 50.2F;
+ public double doubleFld = 60.3D;
+ public char charFld = 'b';
+ public boolean booleanFld = true;
+ public String stringFld = "OLD";
+ }
+
+ // Version 1
+ public static class A___1 {
+ public byte byteFld = 11;
+ public short shortFld = 22;
+ public int intFld = 33;
+ public long longFld = 44L;
+ public float floatFld = 55.5F;
+ public double doubleFld = 66.6D;
+ public char charFld = 'c';
+ public boolean booleanFld = false;
+ public String stringFld = "NEW";
+
+ // completely new instance fields are below
+ public int intComplNewFld = 333;
+ public long longComplNewFld = 444L;
+ public String stringComplNewFld = "completely new String field";
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(ComplexFieldTest.class, 0);
+ }
+
+ /**
+ * Checks that the given object is unmodified (i.e. the values of the fields are correct)
+ * @param a the object to be checked
+ */
+ private void assertObjectOK(A a) {
+ assertEquals(10, a.byteFld);
+ assertEquals(20, a.shortFld);
+ assertEquals(30, a.intFld);
+ assertEquals(40L, a.longFld);
+ assertEquals(50.2F, a.floatFld, 0.01);
+ assertEquals(60.3D, a.doubleFld, 0.01);
+ assertEquals('b', a.charFld);
+ assertEquals(true, a.booleanFld);
+ assertEquals("OLD", a.stringFld);
+ }
+
+ @Test
+ public void testComplexFieldChange() {
+ assert HotSwapTool.getCurrentVersion(ComplexFieldTest.class) == 0;
+ A a = new A();
+ assertObjectOK(a);
+ HotSwapTool.toVersion(ComplexFieldTest.class, 1);
+ assertObjectOK(a);
+ HotSwapTool.toVersion(ComplexFieldTest.class, 0);
+ assertObjectOK(a);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/FieldChangedOrderTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/FieldChangedOrderTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.fields;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Test that changes the order of two int fields.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class FieldChangedOrderTest {
+
+ // Version 0
+ public static class A {
+
+ public int value1;
+ public int value2;
+
+ public A() {
+ value1 = 1;
+ value2 = 2;
+ }
+
+ public int getValue1() {
+ return value1;
+ }
+
+ public int getValue2() {
+ return value2;
+ }
+ }
+
+ public static class B {
+
+ public static int getStaticValue1(A a) {
+ return a.value1;
+ }
+
+ public static int getStaticValue2(A a) {
+ return a.value2;
+ }
+ }
+
+ // Version 1
+ public static class A___1 {
+
+ public int value2;
+ public int value1;
+
+ public int getValue1() {
+ return value1;
+ }
+
+ public int getValue2() {
+ return value2;
+ }
+ }
+
+ public static class B___1 {
+
+ public static int getStaticValue1(A a) {
+ return a.value1;
+ }
+
+ public static int getStaticValue2(A a) {
+ return a.value2;
+ }
+ }
+
+ // Version 2
+ public static class A___2 {
+
+ public int tmp1;
+ public int value2;
+ public int tmp2;
+ public int value1;
+ public int tmp3;
+
+ public int getValue1() {
+ return value1;
+ }
+
+ public int getValue2() {
+ return value2;
+ }
+ }
+
+ // Version 3
+ public static class A___3 {
+
+ public int tmp1;
+ public int value2;
+
+ public int getValue1() {
+ return tmp1;
+ }
+
+ public int getValue2() {
+ return value2;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(FieldChangedOrderTest.class, 0);
+ }
+
+ @Test
+ public void testRenameField() {
+ assert HotSwapTool.getCurrentVersion(FieldChangedOrderTest.class) == 0;
+ A a = new A();
+ assertObjectOK(a);
+ HotSwapTool.toVersion(FieldChangedOrderTest.class, 3);
+ assertEquals(0, a.getValue1());
+ assertEquals(2, a.getValue2());
+ HotSwapTool.toVersion(FieldChangedOrderTest.class, 0);
+ assertEquals(0, a.getValue1());
+ assertEquals(2, a.getValue2());
+ }
+
+ @Test
+ public void testSimpleOrderChange() {
+ assert HotSwapTool.getCurrentVersion(FieldChangedOrderTest.class) == 0;
+ A a = new A();
+ assertObjectOK(a);
+ HotSwapTool.toVersion(FieldChangedOrderTest.class, 1);
+ assertObjectOK(a);
+ HotSwapTool.toVersion(FieldChangedOrderTest.class, 0);
+ assertObjectOK(a);
+ }
+
+ /**
+ * Checks that the given object is unmodified (i.e. the values of the fields are correct)
+ * @param a the object to be checked
+ */
+ private void assertObjectOK(A a) {
+ assertEquals(1, a.getValue1());
+ assertEquals(2, a.getValue2());
+ assertEquals(1, B.getStaticValue1(a));
+ assertEquals(2, B.getStaticValue2(a));
+ assertEquals(1, a.value1);
+ assertEquals(2, a.value2);
+ }
+
+ @Test
+ public void testSimpleOrderChangeWithNewTempFields() {
+ assert HotSwapTool.getCurrentVersion(FieldChangedOrderTest.class) == 0;
+ A a = new A();
+ assertObjectOK(a);
+ HotSwapTool.toVersion(FieldChangedOrderTest.class, 2);
+ assertObjectOK(a);
+ HotSwapTool.toVersion(FieldChangedOrderTest.class, 0);
+ assertObjectOK(a);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/FieldModificationTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/FieldModificationTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.fields;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * @author Thomas Wuerthinger
+ */
+public class FieldModificationTest {
+
+ // Version 0
+ public static class A {
+
+ public int val0;
+ public int val1;
+ public int val2;
+ public int val3;
+ public int val4;
+ public int val5;
+ public int val6;
+ public int val7;
+
+ public void increaseAllByOne() {
+ val0++;
+ val1++;
+ val2++;
+ val3++;
+ val4++;
+ val5++;
+ val6++;
+ val7++;
+ }
+
+ public int sum() {
+ return val0 + val1 + val2 + val3 + val4 + val5 + val6 + val7;
+ }
+ }
+
+ // Version 1
+ public static class A___1 {
+
+ public int val0;
+
+ public void increaseAllByOne() {
+ val0++;
+ }
+
+ public int sum() {
+ return val0;
+ }
+ }
+
+ // Version 2
+ public static class A___2 {
+
+ public int val0;
+ public int val1;
+ public int val2;
+ public int val3;
+ public int val4;
+ public int val5;
+ public int val6;
+ public int val7;
+ public int val8;
+ public int val9;
+ public int val10;
+ public int val11;
+ public int val12;
+ public int val13;
+ public int val14;
+ public int val15;
+
+ public int sum() {
+ return val0 + val1 + val2 + val3 + val4 + val5 + val6 + val7 + val8 + val9 + val10 + val11 + val12 + val13 + val14 + val15;
+ }
+
+ public void increaseAllByOne() {
+ val0++;
+ val1++;
+ val2++;
+ val3++;
+ val4++;
+ val5++;
+ val6++;
+ val7++;
+ val8++;
+ val9++;
+ val10++;
+ val11++;
+ val12++;
+ val13++;
+ val14++;
+ val15++;
+ }
+ }
+
+ // Version 3
+ public static class A___3 {
+
+ public int val6;
+ public int val0;
+ public int val7;
+ public int val1;
+ public int val2;
+ public int val5;
+ public int val3;
+ public int val4;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(FieldModificationTest.class, 0);
+ }
+
+ @Test
+ public void testReorder() {
+
+ A a = new A();
+
+ a.val0 = 0;
+ a.val1 = 1;
+ a.val2 = 2;
+ a.val3 = 3;
+ a.val4 = 4;
+ a.val5 = 5;
+ a.val6 = 6;
+ a.val7 = 7;
+ }
+
+ @Test
+ public void testIncreaseFirst() {
+
+ A a = new A();
+
+ a.val0 = 0;
+ a.val1 = 1;
+ a.val2 = 2;
+ a.val3 = 3;
+ a.val4 = 4;
+ a.val5 = 5;
+ a.val6 = 6;
+ a.val7 = 7;
+
+ assertEquals(0, a.val0);
+ assertEquals(1, a.val1);
+ assertEquals(2, a.val2);
+ assertEquals(3, a.val3);
+ assertEquals(4, a.val4);
+ assertEquals(5, a.val5);
+ assertEquals(6, a.val6);
+ assertEquals(7, a.val7);
+ assertEquals(0 + 1 + 2 + 3 + 4 + 5 + 6 + 7, a.sum());
+
+ HotSwapTool.toVersion(FieldModificationTest.class, 2);
+
+ assertEquals(0, a.val0);
+ assertEquals(1, a.val1);
+ assertEquals(2, a.val2);
+ assertEquals(3, a.val3);
+ assertEquals(4, a.val4);
+ assertEquals(5, a.val5);
+ assertEquals(6, a.val6);
+ assertEquals(7, a.val7);
+ assertEquals(0 + 1 + 2 + 3 + 4 + 5 + 6 + 7, a.sum());
+
+ a.increaseAllByOne();
+ assertEquals(0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 16, a.sum());
+
+ HotSwapTool.toVersion(FieldModificationTest.class, 0);
+
+ assertEquals(0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8, a.sum());
+ assertEquals(1, a.val0);
+ assertEquals(2, a.val1);
+ assertEquals(3, a.val2);
+ assertEquals(4, a.val3);
+ assertEquals(5, a.val4);
+ assertEquals(6, a.val5);
+ assertEquals(7, a.val6);
+ assertEquals(8, a.val7);
+
+ HotSwapTool.toVersion(FieldModificationTest.class, 2);
+
+ assertEquals(0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8, a.sum());
+ assertEquals(1, a.val0);
+ assertEquals(2, a.val1);
+ assertEquals(3, a.val2);
+ assertEquals(4, a.val3);
+ assertEquals(5, a.val4);
+ assertEquals(6, a.val5);
+ assertEquals(7, a.val6);
+ assertEquals(8, a.val7);
+
+ a.increaseAllByOne();
+
+ assertEquals(0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 16, a.sum());
+ assertEquals(2, a.val0);
+ assertEquals(3, a.val1);
+ assertEquals(4, a.val2);
+ assertEquals(5, a.val3);
+ assertEquals(6, a.val4);
+ assertEquals(7, a.val5);
+ assertEquals(8, a.val6);
+ assertEquals(9, a.val7);
+ HotSwapTool.toVersion(FieldModificationTest.class, 0);
+
+ assertEquals(0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 16, a.sum());
+ assertEquals(2, a.val0);
+ assertEquals(3, a.val1);
+ assertEquals(4, a.val2);
+ assertEquals(5, a.val3);
+ assertEquals(6, a.val4);
+ assertEquals(7, a.val5);
+ assertEquals(8, a.val6);
+ assertEquals(9, a.val7);
+ }
+
+ @Test
+ public void testAddRemoveField() {
+
+ assert HotSwapTool.getCurrentVersion(FieldModificationTest.class) == 0;
+
+ A a = new A();
+
+ assertEquals(0, a.val0);
+ assertEquals(0, a.val1);
+
+ HotSwapTool.toVersion(FieldModificationTest.class, 1);
+
+ a.val0 = 1234;
+
+ HotSwapTool.toVersion(FieldModificationTest.class, 0);
+
+ assertEquals(1234, a.val0);
+ assertEquals(0, a.val1);
+
+ a.val1 = 1234;
+
+ assertEquals(1234, a.val0);
+ assertEquals(1234, a.val1);
+
+ HotSwapTool.toVersion(FieldModificationTest.class, 1);
+
+ assertEquals(1234, a.val0);
+
+ HotSwapTool.toVersion(FieldModificationTest.class, 0);
+
+ assertEquals(1234, a.val0);
+ assertEquals(0, a.val1);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/FieldsTestSuite.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/FieldsTestSuite.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.fields;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * Class redefinition tests that may change the methods and fields of class, but do not change the superklass or the implemented
+ * interface.
+ *
+ * @author Thomas Wuerthinger
+ *
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ AccessDeletedFieldTest.class,
+ FieldChangedOrderTest.class,
+ FieldModificationTest.class,
+ ObjectStressTest.class,
+ YieldTest.class,
+ ComplexFieldTest.class,
+ StringFieldTest.class,
+ RedefinePrivateFieldTest.class,
+ AccessDeletedStaticFieldTest.class
+})
+public class FieldsTestSuite {
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/ObjectStressTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/ObjectStressTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.fields;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * @author Thomas Wuerthinger
+ */
+public class ObjectStressTest {
+
+ private final int COUNT = 10000;
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(ObjectStressTest.class, 0);
+ }
+
+ // Version 0
+ public static class A {
+
+ public A thisPointer;
+ public int i1;
+ public int i2;
+ public int i3;
+ public int i4;
+ public int i5;
+ public int i6;
+ public int i7;
+ public int i8;
+ public int i9;
+ public int i10;
+
+ public int sum() {
+ return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10;
+ }
+ }
+
+ // Version 1
+ public static class A___1 {
+
+ public int i1;
+ public int i2;
+ public int i8;
+ public int i3;
+ public int i4;
+ public int i10;
+ public int i5;
+ public int i6;
+ public int i7;
+ public int i9;
+ public A thisPointer;
+
+ public int sum() {
+ return i1 * i2 * i3 * i4 * i5 * i6 * i7 * i8 * i9 * i10;
+ }
+ }
+
+ @Test
+ public void testLotsOfObjects() {
+
+ assert HotSwapTool.getCurrentVersion(ObjectStressTest.class) == 0;
+
+ A[] arr = new A[COUNT];
+ for (int i = 0; i < arr.length; i++) {
+ arr[i] = new A();
+ arr[i].thisPointer = arr[i];
+ arr[i].i1 = 1;
+ arr[i].i2 = 2;
+ arr[i].i3 = 3;
+ arr[i].i4 = 4;
+ arr[i].i5 = 5;
+ arr[i].i6 = 6;
+ arr[i].i7 = 7;
+ arr[i].i8 = 8;
+ arr[i].i9 = 9;
+ arr[i].i10 = 10;
+ }
+
+
+ HotSwapTool.toVersion(ObjectStressTest.class, 1);
+
+ for (int i = 0; i < arr.length; i++) {
+ assertEquals(1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10, arr[i].sum());
+ assertEquals(arr[i].thisPointer, arr[i]);
+ }
+
+ HotSwapTool.toVersion(ObjectStressTest.class, 0);
+
+ for (int i = 0; i < arr.length; i++) {
+ assertEquals(1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10, arr[i].sum());
+ assertEquals(arr[i].thisPointer, arr[i]);
+ }
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/RedefinePrivateFieldTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/RedefinePrivateFieldTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.fields;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Tests redefinition of a class such that old code still accesses a redefined private field.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class RedefinePrivateFieldTest {
+
+ // Version 0
+ public static class A {
+
+ private int f1;
+
+ public A() {
+ f1 = 5;
+ }
+
+ public int foo() {
+ int result = f1;
+ HotSwapTool.toVersion(RedefinePrivateFieldTest.class, 1);
+ result += f1;
+ return result;
+ }
+ }
+
+ // Version 1
+ public static class A___1 {
+
+ int f0;
+ int f1;
+
+ public int foo() {
+ return -1;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(RedefinePrivateFieldTest.class, 0);
+ }
+
+ @Test
+ public void testRedefinePrivateField() {
+
+ assert HotSwapTool.getCurrentVersion(RedefinePrivateFieldTest.class) == 0;
+
+ A a = new A();
+
+ assertEquals(10, a.foo());
+
+ assert HotSwapTool.getCurrentVersion(RedefinePrivateFieldTest.class) == 1;
+
+ assertEquals(-1, a.foo());
+
+ HotSwapTool.toVersion(RedefinePrivateFieldTest.class, 0);
+
+ assertEquals(10, a.foo());
+
+ assert HotSwapTool.getCurrentVersion(RedefinePrivateFieldTest.class) == 1;
+
+ assertEquals(-1, a.foo());
+
+ HotSwapTool.toVersion(RedefinePrivateFieldTest.class, 0);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/StringFieldTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/StringFieldTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.fields;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Complex field test.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class StringFieldTest {
+
+ // Version 0
+ public static class A {
+ public String stringFld = "OLD";
+ }
+
+ // Version 1
+ public static class A___1 {
+ public String stringFld = "NEW";
+ public int intComplNewFld = 333;
+ public long longComplNewFld = 444L;
+ public String stringComplNewFld = "completely new String field";
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(StringFieldTest.class, 0);
+ }
+
+ /**
+ * Checks that the given object is unmodified (i.e. the values of the fields are correct)
+ * @param a the object to be checked
+ */
+ private void assertObjectOK(A a) {
+ assertEquals("OLD", a.stringFld);
+ }
+
+ @Test
+ public void testComplexFieldChange() {
+ assert HotSwapTool.getCurrentVersion(StringFieldTest.class) == 0;
+ A a = new A();
+ assertObjectOK(a);
+ HotSwapTool.toVersion(StringFieldTest.class, 1);
+ assertObjectOK(a);
+ HotSwapTool.toVersion(StringFieldTest.class, 0);
+ assertObjectOK(a);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/YieldTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/fields/YieldTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.fields;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Test case that produces a list of integer values recursively.
+ * The recursive function does not contain a conditional statement.
+ * The recursion is stopped by swapping the recursive method with a different non-recursive implementation.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class YieldTest {
+
+ // Version 0
+ public static class Base {
+
+ protected List arr = new ArrayList();
+
+ public void reset() {
+ HotSwapTool.toVersion(YieldTest.class, 0);
+ }
+
+ public void next() {
+ HotSwapTool.toVersion(YieldTest.class, HotSwapTool.getCurrentVersion(YieldTest.class) + 1);
+ }
+ }
+
+ public static abstract class A extends Base {
+
+ public List gen() {
+ arr.add(produce());
+ next();
+ return gen();
+ }
+
+ public abstract int produce();
+ }
+
+ public static class B extends A {
+
+ public int produce() {
+ return 1;
+ }
+ }
+
+ public static class B___10 extends A {
+
+ public int produce() {
+ return 2;
+ }
+ }
+
+ public static class B___20 extends A {
+
+ private int x;
+
+ public int produce() {
+ return ++x;
+ }
+ }
+
+ public static class A___30 extends Base {
+
+ public List gen() {
+ reset();
+ return arr;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(YieldTest.class, 0);
+ }
+
+ @Test
+ public void testYield() {
+
+ assert HotSwapTool.getCurrentVersion(YieldTest.class) == 0;
+
+ B b = new B();
+ assertEquals(Arrays.asList(
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), b.gen());
+ assert HotSwapTool.getCurrentVersion(YieldTest.class) == 0;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/AddMethodTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/AddMethodTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.methods;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+import at.ssw.hotswap.test.TestUtil;
+
+/**
+ * Tests for adding / removing methods in a single class.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class AddMethodTest {
+
+ // Version 0
+ public static class A {
+ public int value(int newVersion) {
+ return newVersion;
+ }
+ }
+
+ // Version 1
+ public static class A___1 {
+
+ public int value(int newVersion) {
+
+ int x = 1;
+ try {
+ x = 2;
+ } catch (NumberFormatException e) {
+ x = 3;
+ } catch (Exception e) {
+ x = 4;
+ } finally {
+ x = x * 2;
+ }
+ HotSwapTool.toVersion(AddMethodTest.class, newVersion);
+ throw new IllegalArgumentException();
+ }
+ }
+
+ // Version 2
+ public static class A___2 {
+
+ public int value2() {
+ return 2;
+ }
+
+ public int value(int newVersion) {
+
+ int x = 1;
+ try {
+ x = 2;
+ } catch (NumberFormatException e) {
+ x = 3;
+ } catch (Exception e) {
+ x = 4;
+ } finally {
+ x = x * 2;
+ }
+ HotSwapTool.toVersion(AddMethodTest.class, newVersion);
+ throw new IllegalArgumentException();
+ }
+
+ public int value3() {
+ return 3;
+ }
+
+ public int value4() {
+ return 4;
+ }
+
+ public int value5() {
+ return 5;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(AddMethodTest.class, 0);
+ }
+
+ @Test
+ public void testAddMethodToKlassWithEMCPExceptionMethod() {
+
+ assert HotSwapTool.getCurrentVersion(AddMethodTest.class) == 0;
+
+ final A a = new A();
+
+ assertEquals(1, a.value(1));
+
+ HotSwapTool.toVersion(AddMethodTest.class, 1);
+
+ int firstLineNumber = TestUtil.assertException(IllegalArgumentException.class, new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(4, a.value(1));
+ }
+ });
+
+ int secondLineNumber = TestUtil.assertException(IllegalArgumentException.class, new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(4, a.value(2));
+ }
+ });
+
+ System.out.println("Exception line numbers: " + firstLineNumber + " and " + secondLineNumber);
+
+ assertTrue("Must have different line numbers (A.value is an EMCP method and therefore execution has to be transferred)", firstLineNumber != secondLineNumber);
+
+ assert HotSwapTool.getCurrentVersion(AddMethodTest.class) == 2;
+
+ int newFirstLineNumber = TestUtil.assertException(IllegalArgumentException.class, new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(4, a.value(2));
+ }
+ });
+
+ assertEquals(secondLineNumber, newFirstLineNumber);
+
+ int newSecondLineNumber = TestUtil.assertException(IllegalArgumentException.class, new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(4, a.value(1));
+ }
+ });
+
+ assertEquals(newSecondLineNumber, firstLineNumber);
+
+ assertTrue("Must have different line numbers (A.value is an EMCP method and therefore execution has to be transferred)", firstLineNumber != secondLineNumber);
+
+ HotSwapTool.toVersion(AddMethodTest.class, 0);
+
+ assertEquals(1, a.value(1));
+
+ assert HotSwapTool.getCurrentVersion(AddMethodTest.class) == 0;
+
+
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/CallDeletedInterfaceMethodTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/CallDeletedInterfaceMethodTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.methods;
+
+import static org.junit.Assert.assertEquals;
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Test case that calls an interface method that was deleted through class redefinition.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class CallDeletedInterfaceMethodTest {
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(CallDeletedInterfaceMethodTest.class, 0);
+ }
+
+ // Version 0
+ public static interface I {
+ public int foo();
+ }
+
+ public static class A implements I {
+ @Override
+ public int foo() {
+ return 1;
+ }
+ }
+
+ public static class Helper {
+ public static int process(I i) {
+ HotSwapTool.toVersion(CallDeletedInterfaceMethodTest.class, 1);
+ return i.foo();
+ }
+ }
+
+ // Version 1
+ public static interface I___1 {
+
+ }
+
+ public static class Helper___1 {
+ public static int process(I i) {
+ return 2;
+ }
+ }
+
+ @Test
+ public void testOldCodeCallsDeletedInterfaceMethod() {
+
+ assert HotSwapTool.getCurrentVersion(CallDeletedInterfaceMethodTest.class) == 0;
+ A a = new A();
+
+ assertEquals(1, Helper.process(a));
+ assert HotSwapTool.getCurrentVersion(CallDeletedInterfaceMethodTest.class) == 1;
+ assertEquals(2, Helper.process(a));
+
+ HotSwapTool.toVersion(CallDeletedInterfaceMethodTest.class, 0);
+
+ assertEquals(1, Helper.process(a));
+ assert HotSwapTool.getCurrentVersion(CallDeletedInterfaceMethodTest.class) == 1;
+ assertEquals(2, Helper.process(a));
+
+ HotSwapTool.toVersion(CallDeletedInterfaceMethodTest.class, 0);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/CallDeletedMethodTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/CallDeletedMethodTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.methods;
+
+import at.ssw.hotswap.MethodRedefinitionPolicy;
+import static org.junit.Assert.assertEquals;
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+import at.ssw.hotswap.RedefinitionPolicy;
+
+/**
+ * Test case that calls a virtual method that was deleted through class redefinition.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class CallDeletedMethodTest {
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(CallDeletedMethodTest.class, 0);
+ }
+
+ // Version 0
+ public static class A {
+
+ public int value() {
+ return 5;
+ }
+
+ public int oldMethod() {
+ HotSwapTool.toVersion(CallDeletedMethodTest.class, 1);
+ return deletedMethod();
+ }
+
+ public int deletedMethod() {
+ return 1;
+ }
+ }
+
+ // Version 1
+ @MethodRedefinitionPolicy(RedefinitionPolicy.AccessDeletedMembers)
+ public static class A___1 {
+
+ public int oldMethod() {
+ return 2;
+ }
+ }
+
+ @Test
+ public void testOldCodeCallsDeletedMethod() {
+
+ assert HotSwapTool.getCurrentVersion(CallDeletedMethodTest.class) == 0;
+ A a = new A();
+
+ assertEquals(1, a.oldMethod());
+ assert HotSwapTool.getCurrentVersion(CallDeletedMethodTest.class) == 1;
+ assertEquals(2, a.oldMethod());
+
+ HotSwapTool.toVersion(CallDeletedMethodTest.class, 0);
+
+ assertEquals(1, a.oldMethod());
+ assert HotSwapTool.getCurrentVersion(CallDeletedMethodTest.class) == 1;
+ assertEquals(2, a.oldMethod());
+
+ HotSwapTool.toVersion(CallDeletedMethodTest.class, 0);
+ }
+
+ @Test
+ public void testNewCodeCallsDeletedMethod() {
+
+ assert HotSwapTool.getCurrentVersion(CallDeletedMethodTest.class) == 0;
+
+ A a = new A();
+ assertEquals(5, a.value());
+
+ HotSwapTool.toVersion(CallDeletedMethodTest.class, 1);
+
+ try {
+ a.value();
+ Assert.fail("NoSuchMethodError exception must be thrown!");
+ } catch (NoSuchMethodError e) {
+ // Expected exception
+ }
+
+ HotSwapTool.toVersion(CallDeletedMethodTest.class, 0);
+ assertEquals(5, a.value());
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/DeleteActiveMethodTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/DeleteActiveMethodTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.methods;
+
+import at.ssw.hotswap.MethodRedefinitionPolicy;
+import static org.junit.Assert.assertEquals;
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+import at.ssw.hotswap.RedefinitionPolicy;
+import at.ssw.hotswap.test.TestUtil;
+
+/**
+ * Test cases that delete a method that is currently active on the stack.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class DeleteActiveMethodTest {
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(DeleteActiveMethodTest.class, 0);
+ }
+
+ // Version 0
+ public static class A {
+
+ boolean firstCall;
+
+ public int value() {
+ firstCall = true;
+ return helperValue();
+ }
+
+ public int helperValue() {
+
+ if (!firstCall) {
+ return -1;
+ }
+ firstCall = false;
+
+ Thread t = new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ HotSwapTool.toVersion(DeleteActiveMethodTest.class, 1);
+ }
+ });
+ t.start();
+
+ try {
+ while (t.isAlive()) {
+ try {
+ this.helperValue();
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ }
+ }
+ Assert.fail("Exception expected!");
+ } catch (NoSuchMethodError e) {
+ }
+
+ try {
+ t.join();
+ } catch (InterruptedException e) {
+ }
+
+ return 1;
+ }
+ }
+
+ public static class B {
+
+ public int fac(int x) {
+ if (x == 0) {
+ HotSwapTool.toVersion(DeleteActiveMethodTest.class, 1);
+ }
+
+ return x * fac(x - 1);
+ }
+ }
+
+ // Version 1
+ @MethodRedefinitionPolicy(RedefinitionPolicy.DynamicCheck)
+ public static class A___1 {
+
+ boolean firstCall;
+
+ public int value() {
+ HotSwapTool.toVersion(DeleteActiveMethodTest.class, 0);
+ return 2;
+ }
+ }
+
+ @MethodRedefinitionPolicy(RedefinitionPolicy.DynamicCheck)
+ public static class B___1 {
+ }
+
+ @Test
+ public void testDeleteActiveMethodSimple() {
+ assert HotSwapTool.getCurrentVersion(DeleteActiveMethodTest.class) == 0;
+
+ final B b = new B();
+ TestUtil.assertException(NoSuchMethodError.class, new Runnable() {
+ @Override
+ public void run() {
+ b.fac(5);
+ }
+ });
+
+ assert HotSwapTool.getCurrentVersion(DeleteActiveMethodTest.class) == 1;
+
+ HotSwapTool.toVersion(DeleteActiveMethodTest.class, 0);
+ assert HotSwapTool.getCurrentVersion(DeleteActiveMethodTest.class) == 0;
+ }
+
+ @Test
+ public void testDeleteActiveMethod() {
+ assert HotSwapTool.getCurrentVersion(DeleteActiveMethodTest.class) == 0;
+
+ A a = new A();
+
+ assertEquals(1, a.value());
+ assert HotSwapTool.getCurrentVersion(DeleteActiveMethodTest.class) == 1;
+
+ assertEquals(2, a.value());
+ assert HotSwapTool.getCurrentVersion(DeleteActiveMethodTest.class) == 0;
+
+ assertEquals(1, a.value());
+ assert HotSwapTool.getCurrentVersion(DeleteActiveMethodTest.class) == 1;
+
+ assertEquals(2, a.value());
+ assert HotSwapTool.getCurrentVersion(DeleteActiveMethodTest.class) == 0;
+
+ assertEquals(1, a.value());
+ assert HotSwapTool.getCurrentVersion(DeleteActiveMethodTest.class) == 1;
+
+ assertEquals(2, a.value());
+ assert HotSwapTool.getCurrentVersion(DeleteActiveMethodTest.class) == 0;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/MethodsTestSuite.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/MethodsTestSuite.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.methods;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * Class redefinition tests that perform adding/removing/changing the methods of a class.
+ *
+ * @author Thomas Wuerthinger
+ *
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ AddMethodTest.class,
+ CallDeletedMethodTest.class,
+ DeleteActiveMethodTest.class,
+ ReflectionTest.class,
+ OverrideMethodTest.class,
+ SingleClassTest.class,
+ SingleClassReflectionTest.class,
+ OldCodeNonOSRTest.class,
+ CallDeletedInterfaceMethodTest.class
+})
+public class MethodsTestSuite {
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/OldCodeNonOSRTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/OldCodeNonOSRTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.methods;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Test case that makes sure that old code does not get on-stack-replaced.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class OldCodeNonOSRTest {
+
+ // Chose high enough to make sure method could get OSR (usually the OSR flag in the VM is set to about 15000)
+ private static final int N = 100000;
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(OldCodeNonOSRTest.class, 0);
+ }
+
+ // Version 0
+ public static class A {
+
+ public int value() {
+ return 5;
+ }
+
+ public int oldMethod() {
+ HotSwapTool.toVersion(OldCodeNonOSRTest.class, 1);
+ int sum = 0;
+ for (int i=0; i c, String methodName) {
+ boolean found = false;
+ for (Method m : c.getDeclaredMethods()) {
+ if (m.getName().equals(methodName)) {
+ found = true;
+ break;
+ }
+ }
+
+ Assert.assertTrue(found);
+ }
+
+ private void assertDoesNotContainMethod(Class> c, String methodName) {
+ boolean found = false;
+ for (Method m : c.getDeclaredMethods()) {
+ if (m.getName().equals(methodName)) {
+ found = true;
+ break;
+ }
+ }
+
+ Assert.assertFalse(found);
+ }
+
+ private void assertIsSuper(Class> s, Class> c) {
+ assertEquals(s, c.getSuperclass());
+ }
+
+ private void assertIsNotSuper(Class> s, Class> c) {
+ Assert.assertFalse(s.equals(c.getSuperclass()));
+ }
+
+ @Test
+ public void testClassReflection() {
+
+ assert HotSwapTool.getCurrentVersion(ReflectionTest.class) == 0;
+
+ A a = new A();
+ B b = new B();
+ C c = new C();
+
+ assertIsSuper(A.class, B.class);
+ assertIsSuper(B.class, C.class);
+ assertIsNotSuper(A.class, C.class);
+
+ assertEquals(1, a.value());
+ assertEquals(2, b.value());
+ assertEquals(3, c.value());
+
+ HotSwapTool.toVersion(ReflectionTest.class, 2);
+
+ assertIsSuper(A.class, B.class);
+ assertIsNotSuper(B.class, C.class);
+ assertIsSuper(A.class, C.class);
+
+ assertEquals(1, a.value());
+ assertEquals(2, b.value());
+ assertEquals(1, c.value());
+
+ HotSwapTool.toVersion(ReflectionTest.class, 0);
+
+ assertIsSuper(A.class, B.class);
+ assertIsNotSuper(A.class, C.class);
+ assertIsSuper(B.class, C.class);
+
+ assertEquals(1, a.value());
+ assertEquals(2, b.value());
+ assertEquals(3, c.value());
+ }
+
+ @Test
+ public void testWeakReferenceOnClass() {
+ A a = new A();
+ Class strongRef = a.getClass();
+ SoftReference softRef = new SoftReference(a.getClass());
+
+
+ assertEquals(1, a.value());
+ HotSwapTool.toVersion(ReflectionTest.class, 1);
+ assertEquals(1, a.value());
+ Assert.assertTrue(strongRef == softRef.get());
+
+ HotSwapTool.toVersion(ReflectionTest.class, 0);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/SingleClassReflectionTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/SingleClassReflectionTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.methods;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Testing correct behaviour of the class object after class redefinition (with respect to the identity hash code and synchronization).
+ *
+ * @author Thomas Wuerthinger
+ */
+public class SingleClassReflectionTest {
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(SingleClassReflectionTest.class, 0);
+ }
+
+ // Version 0
+ public static class A {
+
+ public static synchronized void staticSynchronized() {
+ HotSwapTool.toVersion(SingleClassReflectionTest.class, 1);
+ }
+ }
+
+
+ // Version 1
+ public static class A___1 {
+ public static synchronized void staticSynchronized() {
+ }
+ }
+
+
+ @Test
+ public void testHashcode() {
+
+ assert HotSwapTool.getCurrentVersion(SingleClassReflectionTest.class) == 0;
+
+ A a = new A();
+
+ int hashcode = a.getClass().hashCode();
+
+ HotSwapTool.toVersion(SingleClassReflectionTest.class, 1);
+
+ assertEquals(hashcode, a.getClass().hashCode());
+
+ HotSwapTool.toVersion(SingleClassReflectionTest.class, 0);
+ }
+
+ @Test
+ public void testStaticSynchronized() {
+
+ A.staticSynchronized();
+
+ assertEquals(1, HotSwapTool.getCurrentVersion(SingleClassReflectionTest.class));
+
+ HotSwapTool.toVersion(SingleClassReflectionTest.class, 0);
+ }
+
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/SingleClassTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/methods/SingleClassTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.methods;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Tests for adding / removing methods in a single class.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class SingleClassTest {
+
+ // Version 0
+ public static class A {
+
+ public int value() {
+ return 5;
+ }
+ }
+
+ // Version 3
+ public static class A___3 {
+
+ public int value() {
+ return 5;
+ }
+ }
+
+ // Version 1
+ public static class A___1 {
+
+ public int value() {
+ return 6;
+ }
+
+ public int testValue() {
+ return 1;
+
+ }
+ }
+
+ // Version 2
+ public static class A___2 {
+
+ public int value() {
+ return baseValue() * 2;
+ }
+
+ public int baseValue() {
+ return 10;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(SingleClassTest.class, 0);
+ }
+
+ @Test
+ public void testSimpleReplacement() {
+
+ assert HotSwapTool.getCurrentVersion(SingleClassTest.class) == 0;
+
+ A a = new A();
+
+ assertEquals(5, a.value());
+
+ HotSwapTool.toVersion(SingleClassTest.class, 1);
+
+ assertEquals(6, a.value());
+
+ HotSwapTool.toVersion(SingleClassTest.class, 3);
+
+ assertEquals(5, a.value());
+
+ HotSwapTool.toVersion(SingleClassTest.class, 0);
+
+ assertEquals(5, a.value());
+ }
+
+ @Test
+ public void testAddMethod() {
+
+ assert HotSwapTool.getCurrentVersion(SingleClassTest.class) == 0;
+
+ A a = new A();
+ assertEquals(a.value(), 5);
+
+ HotSwapTool.toVersion(SingleClassTest.class, 2);
+ assertEquals(a.value(), 20);
+
+ HotSwapTool.toVersion(SingleClassTest.class, 0);
+ assertEquals(a.value(), 5);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/natives/NativesTestSuite.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/natives/NativesTestSuite.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.natives;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * Class redefinition tests that include native code.
+ *
+ * @author Thomas Wuerthinger
+ *
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ SimpleNativeTest.class
+})
+public class NativesTestSuite {
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/natives/SimpleNativeTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/natives/SimpleNativeTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.natives;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Testing correct resolving of a native method after class redefinition.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class SimpleNativeTest {
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(SimpleNativeTest.class, 0);
+ }
+
+ // Version 0
+ public static class A {
+
+ public static int get() {
+ return value();
+ }
+
+ public static native int value();
+ }
+
+ // Version 1
+ public static class A___1 {
+
+ public static int get() {
+ return value() + value2();
+ }
+
+ public static native int value();
+
+ public static native int value2();
+ }
+
+ @Test
+ public void testSimpleNativeCalls() {
+
+ assert HotSwapTool.getCurrentVersion(SimpleNativeTest.class) == 0;
+
+
+ assertEquals(1, A.get());
+
+ HotSwapTool.toVersion(SimpleNativeTest.class, 1);
+
+ assertEquals(3, A.get());
+
+ HotSwapTool.toVersion(SimpleNativeTest.class, 0);
+
+ assertEquals(1, A.get());
+
+ }
+
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/GeometryScenario.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/GeometryScenario.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.structural;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * A small geometry example application including a Point and a Rectangle class.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class GeometryScenario {
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(GeometryScenario.class, 0);
+ }
+
+ // Version 0
+ public static class Point {
+
+ private int x;
+ private int y;
+
+ public Point(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public boolean isBottomRightOf(Point p) {
+ return p.x >= x && p.y >= y;
+ }
+
+ public boolean isTopLeftOf(Point p) {
+ return p.x <= x && p.y <= y;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public int getY() {
+ return y;
+ }
+ }
+
+ public static interface IFigure {
+
+ public boolean isHitAt(Point p);
+ }
+
+ public static class Rectangle {
+
+ private Point topLeft;
+ private Point bottomRight;
+
+ public Rectangle(Point p1, Point p2) {
+ topLeft = p1;
+ bottomRight = p2;
+ }
+
+ public boolean isHitAt(Point p) {
+ return p.isBottomRightOf(topLeft) && !p.isTopLeftOf(bottomRight);
+ }
+
+ public Point getTopLeft() {
+ return topLeft;
+ }
+
+ public Point getBottomRight() {
+ return bottomRight;
+ }
+
+ public static Rectangle create(Point p) {
+ return (Rectangle) (Object) (new Rectangle___1(p));
+ }
+ }
+
+ // Version 1
+ public static class Rectangle___1 implements IFigure {
+
+ private Point topLeft;
+ private Point center;
+ private Point bottomRight;
+
+ public Point getCenter() {
+ return center;
+ }
+
+ public Rectangle___1(Point p) {
+ topLeft = p;
+ bottomRight = p;
+ }
+
+ public boolean isHitAt(Point p) {
+ return p.isBottomRightOf(topLeft) && !p.isTopLeftOf(bottomRight);
+ }
+
+ public Point getTopLeft() {
+ return topLeft;
+ }
+
+ public Point getBottomRight() {
+ return bottomRight;
+ }
+
+ public static Rectangle create(Point p) {
+ return (Rectangle) (Object) (new Rectangle___1(p));
+ }
+ }
+
+ public static class Point___1 {
+
+ private char x1;
+ private int y;
+ private char x2;
+
+ public boolean isBottomRightOf(Point p) {
+ return p.x >= x1 && p.y >= y;
+ }
+
+ public boolean isTopLeftOf(Point p) {
+ return p.x <= x1 && p.y <= y;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public int getX() {
+ return x1;
+ }
+
+ public char getX2() {
+ return x2;
+ }
+ }
+
+ @Test
+ public void testContructorChange() {
+
+ assert HotSwapTool.getCurrentVersion(GeometryScenario.class) == 0;
+
+ Point p1 = new Point(1, 2);
+ Point p2 = new Point(3, 4);
+ Rectangle r1 = new Rectangle(p1, p2);
+
+ assertEquals(1, p1.getX());
+ assertEquals(2, p1.getY());
+ assertEquals(3, p2.getX());
+ assertEquals(4, p2.getY());
+ assertEquals(p1, r1.getTopLeft());
+ assertEquals(p2, r1.getBottomRight());
+
+ HotSwapTool.toVersion(GeometryScenario.class, 1);
+
+ r1 = Rectangle.create(p1);
+ assertEquals(0, p1.getX());
+ assertEquals(2, p1.getY());
+ assertEquals(0, p2.getX());
+ assertEquals(4, p2.getY());
+ assertEquals(p1, r1.getTopLeft());
+ assertEquals(p1, r1.getBottomRight());
+
+ HotSwapTool.toVersion(GeometryScenario.class, 0);
+
+ assertEquals(0, p1.getX());
+ assertEquals(2, p1.getY());
+ assertEquals(0, p2.getX());
+ assertEquals(4, p2.getY());
+ assertEquals(p1, r1.getTopLeft());
+ assertEquals(p1, r1.getBottomRight());
+ }
+
+ @Test
+ public void testSingleGeometry() {
+
+ assert HotSwapTool.getCurrentVersion(GeometryScenario.class) == 0;
+
+ Point p1 = new Point(1, 2);
+ Point p2 = new Point(3, 4);
+ Rectangle r1 = new Rectangle(p1, p2);
+
+ assertEquals(1, p1.getX());
+ assertEquals(2, p1.getY());
+ assertEquals(3, p2.getX());
+ assertEquals(4, p2.getY());
+ assertEquals(p1, r1.getTopLeft());
+ assertEquals(p2, r1.getBottomRight());
+
+ HotSwapTool.toVersion(GeometryScenario.class, 1);
+
+ System.out.println("after redef");
+ assertEquals(0, p1.getX());
+ System.out.println("1");
+ assertEquals(2, p1.getY());
+ System.out.println("2");
+ assertEquals(0, p2.getX());
+ System.out.println("3");
+ assertEquals(4, p2.getY());
+ System.out.println("4");
+ assertEquals(p1, r1.getTopLeft());
+ System.out.println("5");
+ assertEquals(p2, r1.getBottomRight());
+ System.out.println("6");
+
+ HotSwapTool.toVersion(GeometryScenario.class, 0);
+
+ assertEquals(0, p1.getX());
+ assertEquals(2, p1.getY());
+ assertEquals(0, p2.getX());
+ assertEquals(4, p2.getY());
+ assertEquals(p1, r1.getTopLeft());
+ assertEquals(p2, r1.getBottomRight());
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/HierarchySwapTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/HierarchySwapTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.structural;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Smallest test case for a hierarchy swap. A getClass___();
+
+ public native int hashCode();
+
+ public boolean equals(Object obj) {
+ return (this == obj);
+ }
+
+ public static int x;
+ public static int x1;
+ public static int x2;
+ public static int x3;
+ public static int x4;
+ public static int x5;
+
+ protected native Object clone() throws CloneNotSupportedException;
+
+ public String toString() {
+ System.out.println("x=" + (x++));
+ return getClass().getName() + "@" + Integer.toHexString(hashCode());// myTestFunction___();
+ }
+
+ public final String myTestFunction___() {
+ return "test";
+ }
+
+ public final native void notify___();
+
+ public final native void notifyAll___();
+
+ public final native void wait___(long timeout) throws InterruptedException;
+
+ public final void wait___(long timeout, int nanos) throws InterruptedException {
+
+
+ if (timeout < 0) {
+ throw new IllegalArgumentException("timeout value is negative");
+ }
+
+ if (nanos < 0 || nanos > 999999) {
+ throw new IllegalArgumentException(
+ "nanosecond timeout value out of range");
+ }
+
+ if (nanos >= 500000 || (nanos != 0 && timeout == 0)) {
+ timeout++;
+ }
+
+ wait(timeout);
+ }
+
+ public final void wait___() throws InterruptedException {
+ wait(0);
+ }
+
+ protected void finalize() throws Throwable {
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(RedefineObjectClassTest.class, 0);
+ }
+
+ @Test
+ public void testRedefineObject() {
+
+ assert HotSwapTool.getCurrentVersion(RedefineObjectClassTest.class) == 0;
+
+ Object o = new Object();
+ HotSwapTool.toVersion(RedefineObjectClassTest.class, 1);
+
+ System.out.println(this.toString());
+ System.out.println(o.toString());
+ System.out.println(this.toString());
+
+
+ //assertEquals("test", o.toString());
+ assertEquals("test", Helper.access(o));
+ HotSwapTool.toVersion(RedefineObjectClassTest.class, 0);
+ HotSwapTool.toVersion(RedefineObjectClassTest.class, 1);
+ HotSwapTool.toVersion(RedefineObjectClassTest.class, 0);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/StructuralTestSuite.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/StructuralTestSuite.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.structural;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * Class redefinition tests that do arbitrary structural changes.
+ *
+ * TODO: Add a test where redefinition triggers classloading (e.g. because a super type is not yet loaded).
+ *
+ * @author Thomas Wuerthinger
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ RedefineClassClassTest.class,
+ RedefineObjectClassTest.class,
+ GeometryScenario.class,
+ HierarchySwapTest.class,
+ InterfaceTest.class,
+ LargeHierarchyTest.class,
+ ThisTypeChange.class,
+ TypeNarrowingHeapTest.class,
+ TypeNarrowingMethodTest.class,
+ TypeNarrowingMethodTest2.class,
+ TypeNarrowingMethodTest3.class
+})
+public class StructuralTestSuite {
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/ThisTypeChange.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/ThisTypeChange.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.structural;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+import at.ssw.hotswap.test.TestUtil;
+
+/**
+ * Tests that change the type of the object references by the Java this pointer.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class ThisTypeChange {
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(ThisTypeChange.class, 0);
+ }
+
+ // Version 0
+ public static class A {
+
+ public int valueOK() {
+ return 1;
+ }
+
+ public int value() {
+ HotSwapTool.toVersion(ThisTypeChange.class, 1);
+ return 1;
+ }
+ }
+
+ public static class B extends A {
+
+ @Override
+ public int value() {
+ return super.value();
+ }
+
+
+ @Override
+ public int valueOK() {
+ HotSwapTool.toVersion(ThisTypeChange.class, 1);
+ return super.valueOK();
+ }
+ }
+
+ // Version 1
+ public static class A___1 {
+
+ public int valueOK() {
+ return 2;
+ }
+ }
+
+ // Version 1
+ public static class B___1 {
+ }
+
+ // Method to enforce cast (otherwise bytecodes become invalid in version 2)
+ public static A convertBtoA(Object b) {
+ return (A)b;
+ }
+
+ @Test
+ public void testThisTypeChange() {
+
+ assert HotSwapTool.getCurrentVersion(ThisTypeChange.class) == 0;
+
+ final B b = new B();
+ TestUtil.assertException(UnsupportedOperationException.class, new Runnable() {
+ @Override
+ public void run() {
+ b.value();
+ }
+ });
+
+ assert HotSwapTool.getCurrentVersion(ThisTypeChange.class) == 0;
+
+ TestUtil.assertException(UnsupportedOperationException.class, new Runnable() {
+ @Override
+ public void run() {
+ b.valueOK();
+ }
+ });
+
+ assert HotSwapTool.getCurrentVersion(ThisTypeChange.class) == 0;
+
+ TestUtil.assertException(UnsupportedOperationException.class, new Runnable() {
+ @Override
+ public void run() {
+ b.valueOK();
+ }
+ });
+
+ assert HotSwapTool.getCurrentVersion(ThisTypeChange.class) == 0;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/TypeNarrowingHeapTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/TypeNarrowingHeapTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.structural;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+import at.ssw.hotswap.test.TestUtil;
+
+/**
+ * Test case for type narrowing.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class TypeNarrowingHeapTest {
+
+ // Version 0
+ public static class A {
+
+ int x = 1;
+ int y = 2;
+ int z = 3;
+
+ public int value() {
+ return x;
+ }
+ }
+
+ public static class C {
+ private A a;
+
+ public C(A a) {
+ this.a = a;
+ }
+ }
+
+ public static class B extends A {
+
+ }
+
+
+ // Version 1
+ public static class B___1 {
+ }
+
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(TypeNarrowingHeapTest.class, 0);
+ A a = new A();
+ B b = new B();
+ }
+
+ @Test
+ public void testSimpleTypeNarrowing() {
+
+ assert HotSwapTool.getCurrentVersion(TypeNarrowingHeapTest.class) == 0;
+
+ A a = convertBtoA(new B());
+
+ assertEquals(1, a.value());
+
+ // Cannot do conversion if A object is on the stack!
+ a = null;
+
+ HotSwapTool.toVersion(TypeNarrowingHeapTest.class, 1);
+
+ TestUtil.assertException(NoSuchMethodError.class, new Runnable() {
+ @Override
+ public void run() {
+ B b = new B();
+ b.value();
+ }
+ });
+
+ HotSwapTool.toVersion(TypeNarrowingHeapTest.class, 0);
+ assert HotSwapTool.getCurrentVersion(TypeNarrowingHeapTest.class) == 0;
+ }
+
+ @Test
+ public void testTypeNarrowingWithField() {
+ C c = new C(new A());
+
+ HotSwapTool.toVersion(TypeNarrowingHeapTest.class, 1);
+
+ HotSwapTool.toVersion(TypeNarrowingHeapTest.class, 0);
+
+ c = new C(convertBtoA(new B()));
+
+ TestUtil.assertException(UnsupportedOperationException.class, new Runnable() {
+ @Override
+ public void run() {
+ HotSwapTool.toVersion(TypeNarrowingHeapTest.class, 1);
+ }
+ });
+
+ assert HotSwapTool.getCurrentVersion(TypeNarrowingHeapTest.class) == 0;
+
+ c.a = null;
+
+ HotSwapTool.toVersion(TypeNarrowingHeapTest.class, 1);
+
+ HotSwapTool.toVersion(TypeNarrowingHeapTest.class, 0);
+ }
+
+ // Method to enforce cast (otherwise bytecodes become invalid in version 2)
+ public static A convertBtoA(Object b) {
+ return (A)b;
+ }
+
+ @Test
+ public void testTypeNarrowingWithArray() {
+ final B b = new B();
+ final A[] arr = new A[3];
+ arr[0] = new A();
+
+ assert b instanceof A;
+
+ HotSwapTool.toVersion(TypeNarrowingHeapTest.class, 1);
+
+ assert !(b instanceof A);
+
+ TestUtil.assertException(ArrayStoreException.class, new Runnable() {
+ @Override
+ public void run() {
+ arr[1] = b;
+ }
+ });
+
+ HotSwapTool.toVersion(TypeNarrowingHeapTest.class, 0);
+
+ arr[1] = new B();
+
+ TestUtil.assertException(UnsupportedOperationException.class, new Runnable() {
+ @Override
+ public void run() {
+ HotSwapTool.toVersion(TypeNarrowingHeapTest.class, 1);
+ }
+ });
+
+ assert HotSwapTool.getCurrentVersion(TypeNarrowingHeapTest.class) == 0;
+
+ assert b instanceof A;
+
+ arr[1] = new A();
+
+ HotSwapTool.toVersion(TypeNarrowingHeapTest.class, 1);
+
+ assert !(b instanceof A);
+
+ HotSwapTool.toVersion(TypeNarrowingHeapTest.class, 0);
+
+ assert b instanceof A;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/TypeNarrowingMethodTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/TypeNarrowingMethodTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.structural;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+import at.ssw.hotswap.test.TestUtil;
+
+/**
+ * Test case for type narrowing where a non-active method fails verification because of the new hierarchy.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class TypeNarrowingMethodTest {
+
+ // Version 0
+ public static class A {
+
+ int x = 1;
+ int y = 2;
+ int z = 3;
+
+ public int value() {
+ return x;
+ }
+
+ public static int badMethod(B b) {
+ A a = b;
+ return a.y;
+ }
+ }
+
+ public static class B extends A {
+
+ }
+
+
+ // Version 1
+ public static class B___1 {
+ }
+
+ // Version 2
+ public static class A___2 {
+
+ int x = 1;
+ int y = 2;
+ int z = 3;
+
+ public int value() {
+ return x;
+ }
+
+ public static int badMethod(B b) {
+ return 5;
+ }
+ }
+
+ public static class B___2 {
+ }
+
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(TypeNarrowingMethodTest.class, 0);
+ A a = new A();
+ B b = new B();
+ }
+
+
+ @Test
+ public void testTypeNarrowingWithViolatingMethod() {
+
+ TestUtil.assertException(UnsupportedOperationException.class, new Runnable() {
+ @Override
+ public void run() {
+ HotSwapTool.toVersion(TypeNarrowingMethodTest.class, 1);
+ }
+ });
+
+ assert HotSwapTool.getCurrentVersion(TypeNarrowingMethodTest.class) == 0;
+
+ HotSwapTool.toVersion(TypeNarrowingMethodTest.class, 2);
+
+ HotSwapTool.toVersion(TypeNarrowingMethodTest.class, 0);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/TypeNarrowingMethodTest2.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/TypeNarrowingMethodTest2.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.structural;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+import at.ssw.hotswap.test.TestUtil;
+
+/**
+ * Test case for type narrowing where a non-active method fails verification because of the new hierarchy.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class TypeNarrowingMethodTest2 {
+
+ // Version 0
+ public static class A {
+
+ int x = 1;
+ int y = 2;
+ int z = 3;
+
+ public int value() {
+ return x;
+ }
+
+ public static int badMethod() {
+ A a = indirectionMethod();
+ return a.y;
+ }
+ }
+
+ public static class B extends A {
+
+ }
+
+
+ // Version 1
+ public static class B___1 {
+ }
+
+ // Version 2
+ public static class A___2 {
+
+ int x = 1;
+ int y = 2;
+ int z = 3;
+
+ public int value() {
+ return x;
+ }
+
+ public static int badMethod(B b) {
+ return 5;
+ }
+ }
+
+ public static class B___2 {
+ }
+
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(TypeNarrowingMethodTest2.class, 0);
+ A a = new A();
+ B b = new B();
+ }
+
+ public static B indirectionMethod() {
+ return new B();
+ }
+
+ @Test
+ public void testTypeNarrowingWithViolatingMethod() {
+ final A a = new A();
+
+ TestUtil.assertException(UnsupportedOperationException.class, new Runnable() {
+ @Override
+ public void run() {
+ HotSwapTool.toVersion(TypeNarrowingMethodTest2.class, 1);
+ System.out.println(a.badMethod());
+ }
+ });
+
+ assert HotSwapTool.getCurrentVersion(TypeNarrowingMethodTest2.class) == 0;
+
+ HotSwapTool.toVersion(TypeNarrowingMethodTest2.class, 2);
+
+ HotSwapTool.toVersion(TypeNarrowingMethodTest2.class, 0);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/TypeNarrowingMethodTest3.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/structural/TypeNarrowingMethodTest3.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.structural;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+import at.ssw.hotswap.test.TestUtil;
+
+/**
+ * Test case for type narrowing where a non-active method fails verification because of the new hierarchy.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class TypeNarrowingMethodTest3 {
+
+ // Version 0
+ public static class A {
+
+ int x = 1;
+ int y = 2;
+ int z = 3;
+
+ public int value() {
+ return x;
+ }
+
+ public static int badMethod() {
+ A a = indirectionMethod()[0];
+ return a.y;
+ }
+ }
+
+ public static class B extends A {
+
+ }
+
+ // Version 1
+ public static class B___1 {
+ }
+
+ // Version 2
+ public static class A___2 {
+
+ int x = 1;
+ int y = 2;
+ int z = 3;
+
+ public int value() {
+ return x;
+ }
+
+ public static int badMethod(B b) {
+ return 5;
+ }
+ }
+
+ public static class B___2 {
+ }
+
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(TypeNarrowingMethodTest3.class, 0);
+ A a = new A();
+ B b = new B();
+ }
+
+ public static B[] indirectionMethod() {
+ return new B[]{ new B() };
+ }
+
+ @Test
+ public void testTypeNarrowingWithViolatingMethod() {
+ final A a = new A();
+
+ TestUtil.assertException(UnsupportedOperationException.class, new Runnable() {
+ @Override
+ public void run() {
+ HotSwapTool.toVersion(TypeNarrowingMethodTest3.class, 1);
+ System.out.println(a.badMethod());
+ }
+ });
+
+ assert HotSwapTool.getCurrentVersion(TypeNarrowingMethodTest3.class) == 0;
+
+ HotSwapTool.toVersion(TypeNarrowingMethodTest3.class, 2);
+
+ HotSwapTool.toVersion(TypeNarrowingMethodTest3.class, 0);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/transformer/BaseClassTransformerTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/transformer/BaseClassTransformerTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.transformer;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+class BaseClass {
+ public void $transformer() {
+ transformerExecuted();
+ }
+
+ public void transformerExecuted() {
+
+ }
+}
+
+/**
+ * Tests for executing the transformer of a base class.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class BaseClassTransformerTest {
+
+ // Version 0
+ public static class A {
+
+ public int x = 2;
+ }
+
+ // Version 3
+ public static class A___1 extends BaseClass {
+
+ public int x;
+
+ @Override
+ public void transformerExecuted() {
+ System.out.println("Transformer of A executing...");
+ x = x * 2;
+ }
+ }
+
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(BaseClassTransformerTest.class, 0);
+ }
+
+ @Test
+ public void testSimpleTransformer() {
+
+ assert HotSwapTool.getCurrentVersion(BaseClassTransformerTest.class) == 0;
+
+ A a = new A();
+
+ assertEquals(2, a.x);
+
+ HotSwapTool.toVersion(BaseClassTransformerTest.class, 1);
+
+ assertEquals(4, a.x);
+
+ HotSwapTool.toVersion(BaseClassTransformerTest.class, 0);
+
+ assertEquals(4, a.x);
+
+ HotSwapTool.toVersion(BaseClassTransformerTest.class, 1);
+
+ assertEquals(8, a.x);
+
+ HotSwapTool.toVersion(BaseClassTransformerTest.class, 0);
+
+ assertEquals(8, a.x);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/transformer/SimpleTransformerTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/transformer/SimpleTransformerTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.transformer;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Tests for executing the transformer of a class.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class SimpleTransformerTest {
+
+ // Version 0
+ public static class A {
+
+ public int x = 2;
+ }
+
+ // Version 3
+ public static class A___1 {
+
+ public int x;
+
+ public void $transformer() {
+ System.out.println("Transformer of A executing...");
+ x = x * 2;
+ }
+ }
+
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(SimpleTransformerTest.class, 0);
+ }
+
+ @Test
+ public void testSimpleTransformer() {
+
+ assert HotSwapTool.getCurrentVersion(SimpleTransformerTest.class) == 0;
+
+ A a = new A();
+
+ assertEquals(2, a.x);
+
+ HotSwapTool.toVersion(SimpleTransformerTest.class, 1);
+
+ assertEquals(4, a.x);
+
+ HotSwapTool.toVersion(SimpleTransformerTest.class, 0);
+
+ assertEquals(4, a.x);
+
+ HotSwapTool.toVersion(SimpleTransformerTest.class, 1);
+
+ assertEquals(8, a.x);
+
+ HotSwapTool.toVersion(SimpleTransformerTest.class, 0);
+
+ assertEquals(8, a.x);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/transformer/StaticConstructorTransformerTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/transformer/StaticConstructorTransformerTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.transformer;
+
+import at.ssw.hotswap.HotSwapTool;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class StaticConstructorTransformerTest {
+
+ //Version 0
+ public static class Static_TestClass {
+
+ // remove static --> no fatal error occurs
+ public static int x = 0;
+ //public int x = 0;
+
+ static {
+ System.out.println("Start Static_TestClass Version 0");
+
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ex) {
+ }
+ System.out.println("End Static_TestClass Version 0");
+ }
+ }
+
+ //Version 1
+ public static class Static_TestClass___1 {
+
+ public int version = 1;
+
+ static {
+ System.out.println("Static_TestClass Version 1");
+ }
+
+ public void $transformer() {
+ System.out.println(":::::::::transformerExecuted:::::::::::");
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(StaticConstructorTransformerTest.class, 0);
+ }
+
+ @Test
+ public void testStaticConstructorTransformerTest() {
+
+ assert HotSwapTool.getCurrentVersion(StaticConstructorTransformerTest.class) == 0;
+ try {
+ Class.forName("at.ssw.hotswap.test.transformer.StaticConstructorTransformerTest$Static_TestClass");
+ } catch (ClassNotFoundException ex) {
+ ex.printStackTrace();
+ }
+ Static_TestClass clazz = new Static_TestClass();
+
+ HotSwapTool.toVersion(StaticConstructorTransformerTest.class, 1);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/transformer/StaticTransformerTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/transformer/StaticTransformerTest.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.transformer;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import at.ssw.hotswap.HotSwapTool;
+
+/**
+ * Tests for executing the transformer of a class.
+ *
+ * @author Thomas Wuerthinger
+ */
+public class StaticTransformerTest {
+
+ // Version 0
+ public static class A {
+
+ public static int x = 2;
+ }
+
+ // Version 3
+ public static class A___1 {
+
+ public static int x;
+
+ public static void $staticTransformer() {
+ System.out.println("Static transformer of A executing...");
+ x = x * 2;
+ }
+ }
+
+
+ @Before
+ public void setUp() throws Exception {
+ HotSwapTool.toVersion(StaticTransformerTest.class, 0);
+ }
+
+ @Test
+ public void testStaticTransformer() {
+
+ assert HotSwapTool.getCurrentVersion(StaticTransformerTest.class) == 0;
+
+ assertEquals(2, A.x);
+
+ HotSwapTool.toVersion(StaticTransformerTest.class, 1);
+
+ assertEquals(4, A.x);
+
+ HotSwapTool.toVersion(StaticTransformerTest.class, 0);
+
+ assertEquals(4, A.x);
+
+ HotSwapTool.toVersion(StaticTransformerTest.class, 1);
+
+ assertEquals(8, A.x);
+
+ HotSwapTool.toVersion(StaticTransformerTest.class, 0);
+
+ assertEquals(8, A.x);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/transformer/TransformerTestSuite.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/transformer/TransformerTestSuite.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.transformer;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * Class redefinition tests that test the correct execution of transformer methods
+ *
+ * @author Thomas Wuerthinger
+ *
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ SimpleTransformerTest.class,
+ BaseClassTransformerTest.class,
+ StaticTransformerTest.class,
+ StaticConstructorTransformerTest.class
+})
+public class TransformerTestSuite {
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/util/CallingMethodGetter.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/util/CallingMethodGetter.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.util;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public interface CallingMethodGetter {
+
+ public String getCallingMethod();
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/util/CallingMethodGetterFactory.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/util/CallingMethodGetterFactory.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.util;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class CallingMethodGetterFactory {
+
+ public enum CallingMethodType {
+
+ REFLECTION,
+ JDI
+ }
+
+ public static CallingMethodGetter create(CallingMethodType type) {
+ if (type == CallingMethodType.REFLECTION) {
+ return new ReflectionCallingMethodGetter();
+ }
+ if (type == CallingMethodType.JDI) {
+ return new JDICallingMethodGetter();
+ }
+ return null;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/util/JDICallingMethodGetter.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/util/JDICallingMethodGetter.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.util;
+
+import at.ssw.hotswap.JDIProxy;
+import com.sun.jdi.ClassNotLoadedException;
+import com.sun.jdi.IncompatibleThreadStateException;
+import com.sun.jdi.Method;
+import com.sun.jdi.StackFrame;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.Type;
+import java.lang.reflect.Modifier;
+import java.util.List;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class JDICallingMethodGetter implements CallingMethodGetter {
+
+ @Override
+ public String getCallingMethod() {
+
+ final StringBuffer methodBuffer = new StringBuffer();
+ final JDIProxy jdi = JDIProxy.getJDI();
+ final Exception[] exception = new Exception[1];
+ exception[0] = null;
+
+ Runnable r = new Runnable() {
+
+ public void run() {
+
+ final List threads = jdi.getVM().allThreads();
+ for (ThreadReference t : threads) {
+ if (t.name().equals("main")) {
+ StackFrame stackFrame = null;
+ boolean found = false;
+ try {
+ for (StackFrame s : t.frames()) {
+ if (found) {
+ stackFrame = s;
+ break;
+ }
+ if (s.toString().contains("JDICallingMethodGetter")) {
+ found = true;
+ }
+ }
+ } catch (IncompatibleThreadStateException ex) {
+ exception[0] = ex;
+ }
+
+ Method method = stackFrame.location().method();
+ String methodString = Modifier.toString(method.modifiers()) + " " + method.name() + "(";
+ boolean paramFound = false;
+ try {
+ for (Type type : method.argumentTypes()) {
+ if (paramFound) {
+ methodString += ", ";
+ }
+ paramFound = true;
+ methodString += type.name();
+ }
+ } catch (ClassNotLoadedException ex) {
+ exception[0] = ex;
+ }
+ methodString += ")";
+ methodBuffer.append(methodString);
+ }
+ }
+ }
+ };
+
+ jdi.executeSuspended(r);
+ if (exception[0] != null) {
+ throw new RuntimeException(exception[0]);
+ }
+
+ return methodBuffer.toString();
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/util/ReflectionCallingMethodGetter.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTests/src/at/ssw/hotswap/test/util/ReflectionCallingMethodGetter.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap.test.util;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class ReflectionCallingMethodGetter implements CallingMethodGetter {
+
+ @Override
+ public String getCallingMethod() {
+ StackTraceElement stack = Thread.currentThread().getStackTrace()[2];
+ Class clazz = null;
+
+ try {
+ clazz = Class.forName(stack.getClassName());
+ } catch (ClassNotFoundException ex) {
+ throw new RuntimeException(ex);
+ }
+
+ Method found = null;
+ for (Method method : clazz.getDeclaredMethods()) {
+ if (method.getName().equals(stack.getMethodName())) {
+ if (found != null) {
+ throw new RuntimeException("ambiguous method name");
+ }
+ found = method;
+ }
+ }
+
+ String methodString = Modifier.toString(found.getModifiers()) + " " + found.getName() + "(";
+ boolean paramFound = false;
+ for (Class c : found.getParameterTypes()) {
+ if (paramFound) {
+ methodString += ", ";
+ }
+ paramFound = true;
+ methodString += c.getName();
+ }
+ methodString += ")";
+
+ return methodString;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTestsNatives/.dep.inc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTestsNatives/.dep.inc Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,5 @@
+# This code depends on make tool being used
+DEPFILES=$(wildcard $(addsuffix .d, ${OBJECTFILES}))
+ifneq (${DEPFILES},)
+include ${DEPFILES}
+endif
diff -r f5603a6e5042 hotswaptest/HotSwapTestsNatives/Makefile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTestsNatives/Makefile Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,109 @@
+#
+# There exist several targets which are by default empty and which can be
+# used for execution of your targets. These targets are usually executed
+# before and after some main targets. They are:
+#
+# .build-pre: called before 'build' target
+# .build-post: called after 'build' target
+# .clean-pre: called before 'clean' target
+# .clean-post: called after 'clean' target
+# .clobber-pre: called before 'clobber' target
+# .clobber-post: called after 'clobber' target
+# .all-pre: called before 'all' target
+# .all-post: called after 'all' target
+# .help-pre: called before 'help' target
+# .help-post: called after 'help' target
+#
+# Targets beginning with '.' are not intended to be called on their own.
+#
+# Main targets can be executed directly, and they are:
+#
+# build build a specific configuration
+# clean remove built files from a configuration
+# clobber remove all built files
+# all build all configurations
+# help print help mesage
+#
+# Targets .build-impl, .clean-impl, .clobber-impl, .all-impl, and
+# .help-impl are implemented in nbproject/makefile-impl.mk.
+#
+# Available make variables:
+#
+# CND_BASEDIR base directory for relative paths
+# CND_DISTDIR default top distribution directory (build artifacts)
+# CND_BUILDDIR default top build directory (object files, ...)
+# CONF name of current configuration
+# CND_PLATFORM_${CONF} platform name (current configuration)
+# CND_ARTIFACT_DIR_${CONF} directory of build artifact (current configuration)
+# CND_ARTIFACT_NAME_${CONF} name of build artifact (current configuration)
+# CND_ARTIFACT_PATH_${CONF} path to build artifact (current configuration)
+# CND_PACKAGE_DIR_${CONF} directory of package (current configuration)
+# CND_PACKAGE_NAME_${CONF} name of package (current configuration)
+# CND_PACKAGE_PATH_${CONF} path to package (current configuration)
+#
+# NOCDDL
+
+
+# Environment
+MKDIR=mkdir
+CP=cp
+CCADMIN=CCadmin
+RANLIB=ranlib
+
+
+# build
+build: .build-post
+
+.build-pre:
+# Add your pre 'build' code here...
+
+.build-post: .build-impl
+# Add your post 'build' code here...
+
+
+# clean
+clean: .clean-post
+
+.clean-pre:
+# Add your pre 'clean' code here...
+
+.clean-post: .clean-impl
+# Add your post 'clean' code here...
+
+
+# clobber
+clobber: .clobber-post
+
+.clobber-pre:
+# Add your pre 'clobber' code here...
+
+.clobber-post: .clobber-impl
+# Add your post 'clobber' code here...
+
+
+# all
+all: .all-post
+
+.all-pre:
+# Add your pre 'all' code here...
+
+.all-post: .all-impl
+# Add your post 'all' code here...
+
+
+# help
+help: .help-post
+
+.help-pre:
+# Add your pre 'help' code here...
+
+.help-post: .help-impl
+# Add your post 'help' code here...
+
+
+
+# include project implementation makefile
+include nbproject/Makefile-impl.mk
+
+# include project make variables
+include nbproject/Makefile-variables.mk
diff -r f5603a6e5042 hotswaptest/HotSwapTestsNatives/natives.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTestsNatives/natives.c Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include "natives.h"
+
+JNIEXPORT jint JNICALL Java_at_ssw_hotswap_test_natives_SimpleNativeTest_00024A_value(JNIEnv *env, jclass c) {
+ return 1;
+}
+
+JNIEXPORT jint JNICALL Java_at_ssw_hotswap_test_natives_SimpleNativeTest_00024A_value2(JNIEnv *env, jclass c) {
+ return 2;
+}
+
+JNIEXPORT jclass JNICALL Java_at_ssw_hotswap_test_access_jni_JNIVMAccess_findClassNative(JNIEnv *env, jclass c, jstring s) {
+ const char* name = (*env)->GetStringUTFChars(env, s, 0);
+ jclass clazz = (*env)->FindClass(env, name);
+ (*env)->ReleaseStringUTFChars(env, s, name);
+ return clazz;
+}
+
+JNIEXPORT jobject JNICALL Java_at_ssw_hotswap_test_access_jni_JNIClassAccess_findMethodNative(JNIEnv *env, jclass c, jclass cls, jstring methodName) {
+ const char *methodstr = (*env)->GetStringUTFChars(env, methodName, 0);
+
+ jclass jCls = (*env)->GetObjectClass(env, cls);
+
+ // Get Method ID of getMethods()
+ jmethodID midGetFields = (*env)->GetMethodID(env, jCls, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;");
+ (*env)->ExceptionDescribe(env);
+ jobjectArray jobjArray = (jobjectArray) (*env)->CallObjectMethod(env, cls, midGetFields);
+
+ jsize len = (*env)->GetArrayLength(env, jobjArray);
+ jsize i = 0;
+
+ for (i = 0; i < len; i++) {
+ jobject _strMethod = (*env)->GetObjectArrayElement(env, jobjArray, i);
+ jclass _methodClazz = (*env)->GetObjectClass(env, _strMethod);
+ jmethodID mid = (*env)->GetMethodID(env, _methodClazz, "getName", "()Ljava/lang/String;");
+ jstring _name = (jstring) (*env)->CallObjectMethod(env, _strMethod, mid);
+
+ const char *str = (*env)->GetStringUTFChars(env, _name, 0);
+
+ if (strcmp(str, methodstr) == 0) {
+ (*env)->ReleaseStringUTFChars(env, methodName, methodstr);
+ (*env)->ReleaseStringUTFChars(env, _name, str);
+ return _strMethod;
+ }
+ (*env)->ReleaseStringUTFChars(env, _name, str);
+ }
+
+ jclass exc = (*env)->FindClass(env, "java/lang/NoSuchMethodError");
+ (*env)->ThrowNew(env, exc, methodstr);
+ (*env)->ReleaseStringUTFChars(env, methodName, methodstr);
+
+}
+
+JNIEXPORT jobjectArray JNICALL Java_at_ssw_hotswap_test_access_jni_JNIClassAccess_getMethodsNative(JNIEnv *env, jclass c, jclass cls) {
+ jobjectArray array;
+
+ jclass jCls = (*env)->GetObjectClass(env, cls);
+
+ // Get Method ID of getMethods()
+ jmethodID midGetFields = (*env)->GetMethodID(env, jCls, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;");
+ (*env)->ExceptionDescribe(env);
+ jobjectArray jobjArray = (jobjectArray) (*env)->CallObjectMethod(env, cls, midGetFields);
+
+ jsize len = (*env)->GetArrayLength(env, jobjArray);
+ jsize i = 0;
+
+ array = (*env)->NewObjectArray(env, len, (*env)->FindClass(env, "java/lang/reflect/Method"), 0);
+
+ for (i = 0; i < len; i++) {
+ jobject _strMethod = (*env)->GetObjectArrayElement(env, jobjArray, i);
+ (*env)->SetObjectArrayElement(env, array, i, _strMethod);
+ }
+
+ return array;
+}
+
+jobject callVoidMethod(JNIEnv *env, jobject obj, jboolean staticValue, jmethodID methodID, jvalue *params) {
+ if (staticValue) {
+ (*env)->CallStaticVoidMethodA(env, obj, methodID, params);
+ } else {
+ (*env)->CallVoidMethodA(env, obj, methodID, params);
+ }
+ return (*env)->NewGlobalRef(env, NULL);
+}
+
+jobject callIntMethod(JNIEnv *env, jobject obj, jboolean staticValue, jmethodID methodID, jvalue *params) {
+ jint intValue;
+ if (staticValue) {
+ intValue = (*env)->CallStaticIntMethodA(env, obj, methodID, params);
+ } else {
+ intValue = (*env)->CallIntMethodA(env, obj, methodID, params);
+ }
+ jclass clazz = (*env)->FindClass(env, "Ljava/lang/Integer;");
+ jmethodID methodIDInteger = (*env)->GetMethodID(env, clazz, "", "(I)V");
+ return (*env)->NewObject(env, clazz, methodIDInteger, intValue);
+}
+
+jobject callObjectMethod(JNIEnv *env, jobject obj, jboolean staticValue, jmethodID methodID, jvalue *params) {
+ if (staticValue) {
+ return (*env)->CallStaticObjectMethodA(env, obj, methodID, params);
+ } else {
+ return (*env)->CallObjectMethodA(env, obj, methodID, params);
+ }
+}
+
+JNIEXPORT jobject JNICALL Java_at_ssw_hotswap_test_access_jni_JNIMethodAccess_invokeMethodNative(JNIEnv *env, jclass c, jclass cls, jobject obj, jstring methodName, jstring retValue, jboolean staticValue, jstring descriptor, jobjectArray params) {
+ const char *methodstr = (*env)->GetStringUTFChars(env, methodName, 0);
+ const char *descriptorstr = (*env)->GetStringUTFChars(env, descriptor, 0);
+ const char *retValuestr = (*env)->GetStringUTFChars(env, retValue, 0);
+
+ jmethodID methodID;
+ if (staticValue) {
+ methodID = (*env)->GetStaticMethodID(env, cls, methodstr, descriptorstr);
+ } else {
+ methodID = (*env)->GetMethodID(env, cls, methodstr, descriptorstr);
+ }
+
+ jsize len = (*env)->GetArrayLength(env, params);
+ jvalue *m = (jvalue*) malloc(sizeof (jvalue) * len);
+
+ jvalue *mm = m;
+ int i = 0;
+ for (i; i < len; i++) {
+ *mm = (jvalue)(*env)->GetObjectArrayElement(env, params, i);
+ mm += 1;
+ }
+
+ jobject object = (*env)->NewGlobalRef(env, NULL);
+
+ if (strcmp(retValuestr, "void") == 0) {
+ object = callVoidMethod(env, obj, staticValue, methodID, m);
+ } else if (strcmp(retValuestr, "int") == 0) {
+ object = callIntMethod(env, obj, staticValue, methodID, m);
+ } else if (strcmp(retValuestr, "java.lang.Object") == 0) {
+ object = callObjectMethod(env, obj, staticValue, methodID, m);
+ } else {
+ jclass exc = (*env)->FindClass(env, "java.lang.NotImplementedException");
+ (*env)->ThrowNew(env, exc, "required retValue: bool/int/object");
+ }
+
+ (*env)->ReleaseStringUTFChars(env, methodName, methodstr);
+ (*env)->ReleaseStringUTFChars(env, descriptor, descriptorstr);
+ (*env)->ReleaseStringUTFChars(env, retValue, retValuestr);
+
+ return object;
+}
+
diff -r f5603a6e5042 hotswaptest/HotSwapTestsNatives/natives.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTestsNatives/natives.h Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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
+
+#ifndef NATIVES
+#define NATIVES
+
+/*
+ * Class: at_ssw_hotswap_test_natives_SimpleNativeTest_A
+ * Method: value
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_at_ssw_hotswap_test_natives_SimpleNativeTest_00024A_value(JNIEnv *, jclass);
+
+/*
+ * Class: at_ssw_hotswap_test_natives_SimpleNativeTest_A
+ * Method: value2
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_at_ssw_hotswap_test_natives_SimpleNativeTest_00024A_value2(JNIEnv *, jclass);
+
+/*
+ * Class: at_ssw_hotswap_test_access_jni_JNIVMAccess
+ * Method: findClass
+ * Signature: (String)Class
+ */
+JNIEXPORT jclass JNICALL Java_at_ssw_hotswap_test_access_jni_JNIVMAccess_findClassNative(JNIEnv *, jclass, jstring);
+
+/*
+ * Class: at_ssw_hotswap_test_access_jni_JNIClassAccess
+ * Method: findMethod
+ * Signature: (Class, String)Object
+ */
+JNIEXPORT jobject JNICALL Java_at_ssw_hotswap_test_access_jni_JNIClassAccess_findMethodNative(JNIEnv *, jclass, jclass, jstring);
+
+/*
+ * Class: at_ssw_hotswap_test_access_jni_JNIClassAccess
+ * Method: getMethods
+ * Signature: (Class)Object[]
+ */
+JNIEXPORT jobjectArray JNICALL Java_at_ssw_hotswap_test_access_jni_JNIClassAccess_getMethodsNative(JNIEnv *, jclass, jclass);
+
+/*
+ * Class: at_ssw_hotswap_test_access_jni_JNIMethodAccess
+ * Method: invokeMethod
+ * Signature: (Class, String, Object)Value
+ */
+JNIEXPORT jobject JNICALL Java_at_ssw_hotswap_test_access_jni_JNIMethodAccess_invokeMethod(JNIEnv *, jclass, jclass, jobject, jstring, jstring, jboolean, jstring);
+
+#endif
diff -r f5603a6e5042 hotswaptest/HotSwapTestsNatives/nbproject/Makefile-Debug.mk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTestsNatives/nbproject/Makefile-Debug.mk Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,79 @@
+#
+# Generated Makefile - do not edit!
+#
+# Edit the Makefile in the project folder instead (../Makefile). Each target
+# has a -pre and a -post target defined where you can add customized code.
+#
+# This makefile implements configuration specific macros and targets.
+
+
+# Environment
+MKDIR=mkdir
+CP=cp
+CCADMIN=CCadmin
+RANLIB=ranlib
+CC=gcc.exe
+CCC=g++.exe
+CXX=g++.exe
+FC=
+AS=as.exe
+
+# Macros
+CND_PLATFORM=MinGW-Windows
+CND_CONF=Debug
+CND_DISTDIR=dist
+
+# Include project Makefile
+include Makefile
+
+# Object Directory
+OBJECTDIR=build/${CND_CONF}/${CND_PLATFORM}
+
+# Object Files
+OBJECTFILES= \
+ ${OBJECTDIR}/natives.o
+
+# C Compiler Flags
+CFLAGS=
+
+# CC Compiler Flags
+CCFLAGS=
+CXXFLAGS=
+
+# Fortran Compiler Flags
+FFLAGS=
+
+# Assembler Flags
+ASFLAGS=
+
+# Link Libraries and Options
+LDLIBSOPTIONS=
+
+# Build Targets
+.build-conf: ${BUILD_SUBPROJECTS}
+ ${MAKE} -f nbproject/Makefile-Debug.mk dist/libHotSwapTestsNatives.dll
+
+dist/libHotSwapTestsNatives.dll: ${OBJECTFILES}
+ ${MKDIR} -p dist
+ ${LINK.c} -Wl,--add-stdcall-alias -shared -o ${CND_DISTDIR}/libHotSwapTestsNatives.dll ${OBJECTFILES} ${LDLIBSOPTIONS}
+
+${OBJECTDIR}/natives.o: nbproject/Makefile-${CND_CONF}.mk natives.c
+ ${MKDIR} -p ${OBJECTDIR}
+ ${RM} $@.d
+ $(COMPILE.c) -g -MMD -MP -MF $@.d -o ${OBJECTDIR}/natives.o natives.c
+
+# Subprojects
+.build-subprojects:
+
+# Clean Targets
+.clean-conf: ${CLEAN_SUBPROJECTS}
+ ${RM} -r build/Debug
+ ${RM} dist/libHotSwapTestsNatives.dll
+
+# Subprojects
+.clean-subprojects:
+
+# Enable dependency checking
+.dep.inc: .depcheck-impl
+
+include .dep.inc
diff -r f5603a6e5042 hotswaptest/HotSwapTestsNatives/nbproject/Makefile-Release.mk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTestsNatives/nbproject/Makefile-Release.mk Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,79 @@
+#
+# Generated Makefile - do not edit!
+#
+# Edit the Makefile in the project folder instead (../Makefile). Each target
+# has a -pre and a -post target defined where you can add customized code.
+#
+# This makefile implements configuration specific macros and targets.
+
+
+# Environment
+MKDIR=mkdir
+CP=cp
+CCADMIN=CCadmin
+RANLIB=ranlib
+CC=gcc.exe
+CCC=g++.exe
+CXX=g++.exe
+FC=
+AS=as.exe
+
+# Macros
+CND_PLATFORM=MinGW-Windows
+CND_CONF=Release
+CND_DISTDIR=dist
+
+# Include project Makefile
+include Makefile
+
+# Object Directory
+OBJECTDIR=build/${CND_CONF}/${CND_PLATFORM}
+
+# Object Files
+OBJECTFILES= \
+ ${OBJECTDIR}/natives.o
+
+# C Compiler Flags
+CFLAGS=
+
+# CC Compiler Flags
+CCFLAGS=
+CXXFLAGS=
+
+# Fortran Compiler Flags
+FFLAGS=
+
+# Assembler Flags
+ASFLAGS=
+
+# Link Libraries and Options
+LDLIBSOPTIONS=
+
+# Build Targets
+.build-conf: ${BUILD_SUBPROJECTS}
+ ${MAKE} -f nbproject/Makefile-Release.mk dist/libHotSwapTestsNatives.dll
+
+dist/libHotSwapTestsNatives.dll: ${OBJECTFILES}
+ ${MKDIR} -p dist
+ ${LINK.c} -Wl,--add-stdcall-alias -shared -o ${CND_DISTDIR}/libHotSwapTestsNatives.dll -s ${OBJECTFILES} ${LDLIBSOPTIONS}
+
+${OBJECTDIR}/natives.o: nbproject/Makefile-${CND_CONF}.mk natives.c
+ ${MKDIR} -p ${OBJECTDIR}
+ ${RM} $@.d
+ $(COMPILE.c) -O2 -MMD -MP -MF $@.d -o ${OBJECTDIR}/natives.o natives.c
+
+# Subprojects
+.build-subprojects:
+
+# Clean Targets
+.clean-conf: ${CLEAN_SUBPROJECTS}
+ ${RM} -r build/Release
+ ${RM} dist/libHotSwapTestsNatives.dll
+
+# Subprojects
+.clean-subprojects:
+
+# Enable dependency checking
+.dep.inc: .depcheck-impl
+
+include .dep.inc
diff -r f5603a6e5042 hotswaptest/HotSwapTestsNatives/nbproject/Makefile-impl.mk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTestsNatives/nbproject/Makefile-impl.mk Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,123 @@
+#
+# Generated Makefile - do not edit!
+#
+# Edit the Makefile in the project folder instead (../Makefile). Each target
+# has a pre- and a post- target defined where you can add customization code.
+#
+# This makefile implements macros and targets common to all configurations.
+#
+# NOCDDL
+
+
+# Building and Cleaning subprojects are done by default, but can be controlled with the SUB
+# macro. If SUB=no, subprojects will not be built or cleaned. The following macro
+# statements set BUILD_SUB-CONF and CLEAN_SUB-CONF to .build-reqprojects-conf
+# and .clean-reqprojects-conf unless SUB has the value 'no'
+SUB_no=NO
+SUBPROJECTS=${SUB_${SUB}}
+BUILD_SUBPROJECTS_=.build-subprojects
+BUILD_SUBPROJECTS_NO=
+BUILD_SUBPROJECTS=${BUILD_SUBPROJECTS_${SUBPROJECTS}}
+CLEAN_SUBPROJECTS_=.clean-subprojects
+CLEAN_SUBPROJECTS_NO=
+CLEAN_SUBPROJECTS=${CLEAN_SUBPROJECTS_${SUBPROJECTS}}
+
+
+# Project Name
+PROJECTNAME=HotSwapTestsNatives
+
+# Active Configuration
+DEFAULTCONF=Debug
+CONF=${DEFAULTCONF}
+
+# All Configurations
+ALLCONFS=Debug Release
+
+
+# build
+.build-impl: .build-pre .validate-impl .depcheck-impl
+ @#echo "=> Running $@... Configuration=$(CONF)"
+ ${MAKE} -f nbproject/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .build-conf
+
+
+# clean
+.clean-impl: .clean-pre .validate-impl .depcheck-impl
+ @#echo "=> Running $@... Configuration=$(CONF)"
+ ${MAKE} -f nbproject/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .clean-conf
+
+
+# clobber
+.clobber-impl: .clobber-pre .depcheck-impl
+ @#echo "=> Running $@..."
+ for CONF in ${ALLCONFS}; \
+ do \
+ ${MAKE} -f nbproject/Makefile-$${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .clean-conf; \
+ done
+
+# all
+.all-impl: .all-pre .depcheck-impl
+ @#echo "=> Running $@..."
+ for CONF in ${ALLCONFS}; \
+ do \
+ ${MAKE} -f nbproject/Makefile-$${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .build-conf; \
+ done
+
+# dependency checking support
+.depcheck-impl:
+ @echo "# This code depends on make tool being used" >.dep.inc
+ @if [ -n "${MAKE_VERSION}" ]; then \
+ echo "DEPFILES=\$$(wildcard \$$(addsuffix .d, \$${OBJECTFILES}))" >>.dep.inc; \
+ echo "ifneq (\$${DEPFILES},)" >>.dep.inc; \
+ echo "include \$${DEPFILES}" >>.dep.inc; \
+ echo "endif" >>.dep.inc; \
+ else \
+ echo ".KEEP_STATE:" >>.dep.inc; \
+ echo ".KEEP_STATE_FILE:.make.state.\$${CONF}" >>.dep.inc; \
+ fi
+
+# configuration validation
+.validate-impl:
+ @if [ ! -f nbproject/Makefile-${CONF}.mk ]; \
+ then \
+ echo ""; \
+ echo "Error: can not find the makefile for configuration '${CONF}' in project ${PROJECTNAME}"; \
+ echo "See 'make help' for details."; \
+ echo "Current directory: " `pwd`; \
+ echo ""; \
+ fi
+ @if [ ! -f nbproject/Makefile-${CONF}.mk ]; \
+ then \
+ exit 1; \
+ fi
+
+
+# help
+.help-impl: .help-pre
+ @echo "This makefile supports the following configurations:"
+ @echo " ${ALLCONFS}"
+ @echo ""
+ @echo "and the following targets:"
+ @echo " build (default target)"
+ @echo " clean"
+ @echo " clobber"
+ @echo " all"
+ @echo " help"
+ @echo ""
+ @echo "Makefile Usage:"
+ @echo " make [CONF=] [SUB=no] build"
+ @echo " make [CONF=] [SUB=no] clean"
+ @echo " make [SUB=no] clobber"
+ @echo " make [SUB=no] all"
+ @echo " make help"
+ @echo ""
+ @echo "Target 'build' will build a specific configuration and, unless 'SUB=no',"
+ @echo " also build subprojects."
+ @echo "Target 'clean' will clean a specific configuration and, unless 'SUB=no',"
+ @echo " also clean subprojects."
+ @echo "Target 'clobber' will remove all built files from all configurations and,"
+ @echo " unless 'SUB=no', also from subprojects."
+ @echo "Target 'all' will will build all configurations and, unless 'SUB=no',"
+ @echo " also build subprojects."
+ @echo "Target 'help' prints this message."
+ @echo ""
+
diff -r f5603a6e5042 hotswaptest/HotSwapTestsNatives/nbproject/Makefile-variables.mk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTestsNatives/nbproject/Makefile-variables.mk Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,24 @@
+#
+# Generated - do not edit!
+#
+# NOCDDL
+#
+CND_BASEDIR=`pwd`
+CND_BUILDDIR=build
+CND_DISTDIR=dist
+# Debug configuration
+CND_PLATFORM_Debug=MinGW-Windows
+CND_ARTIFACT_DIR_Debug=dist
+CND_ARTIFACT_NAME_Debug=libHotSwapTestsNatives.dll
+CND_ARTIFACT_PATH_Debug=dist/libHotSwapTestsNatives.dll
+CND_PACKAGE_DIR_Debug=dist/Debug/MinGW-Windows/package
+CND_PACKAGE_NAME_Debug=libHotSwapTestsNatives.dll.tar
+CND_PACKAGE_PATH_Debug=dist/Debug/MinGW-Windows/package/libHotSwapTestsNatives.dll.tar
+# Release configuration
+CND_PLATFORM_Release=MinGW-Windows
+CND_ARTIFACT_DIR_Release=dist
+CND_ARTIFACT_NAME_Release=libHotSwapTestsNatives.dll
+CND_ARTIFACT_PATH_Release=dist/libHotSwapTestsNatives.dll
+CND_PACKAGE_DIR_Release=dist/Release/MinGW-Windows/package
+CND_PACKAGE_NAME_Release=libHotSwapTestsNatives.dll.tar
+CND_PACKAGE_PATH_Release=dist/Release/MinGW-Windows/package/libHotSwapTestsNatives.dll.tar
diff -r f5603a6e5042 hotswaptest/HotSwapTestsNatives/nbproject/Package-Debug.bash
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTestsNatives/nbproject/Package-Debug.bash Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,74 @@
+#!/bin/bash -x
+
+#
+# Generated - do not edit!
+#
+
+# Macros
+TOP=`pwd`
+CND_PLATFORM=MinGW-Windows
+CND_CONF=Debug
+CND_DISTDIR=dist
+TMPDIR=build/${CND_CONF}/${CND_PLATFORM}/tmp-packaging
+TMPDIRNAME=tmp-packaging
+OUTPUT_PATH=${CND_DISTDIR}/libHotSwapTestsNatives.dll
+OUTPUT_BASENAME=libHotSwapTestsNatives.dll
+PACKAGE_TOP_DIR=libHotSwapTestsNatives.dll/
+
+# Functions
+function checkReturnCode
+{
+ rc=$?
+ if [ $rc != 0 ]
+ then
+ exit $rc
+ fi
+}
+function makeDirectory
+# $1 directory path
+# $2 permission (optional)
+{
+ mkdir -p "$1"
+ checkReturnCode
+ if [ "$2" != "" ]
+ then
+ chmod $2 "$1"
+ checkReturnCode
+ fi
+}
+function copyFileToTmpDir
+# $1 from-file path
+# $2 to-file path
+# $3 permission
+{
+ cp "$1" "$2"
+ checkReturnCode
+ if [ "$3" != "" ]
+ then
+ chmod $3 "$2"
+ checkReturnCode
+ fi
+}
+
+# Setup
+cd "${TOP}"
+mkdir -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package
+rm -rf ${TMPDIR}
+mkdir -p ${TMPDIR}
+
+# Copy files and create directories and links
+cd "${TOP}"
+makeDirectory ${TMPDIR}/libHotSwapTestsNatives.dll/lib
+copyFileToTmpDir "${OUTPUT_PATH}" "${TMPDIR}/${PACKAGE_TOP_DIR}lib/${OUTPUT_BASENAME}" 0644
+
+
+# Generate tar file
+cd "${TOP}"
+rm -f ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/libHotSwapTestsNatives.dll.tar
+cd ${TMPDIR}
+tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/libHotSwapTestsNatives.dll.tar *
+checkReturnCode
+
+# Cleanup
+cd "${TOP}"
+rm -rf ${TMPDIR}
diff -r f5603a6e5042 hotswaptest/HotSwapTestsNatives/nbproject/Package-Release.bash
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTestsNatives/nbproject/Package-Release.bash Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,74 @@
+#!/bin/bash -x
+
+#
+# Generated - do not edit!
+#
+
+# Macros
+TOP=`pwd`
+CND_PLATFORM=MinGW-Windows
+CND_CONF=Release
+CND_DISTDIR=dist
+TMPDIR=build/${CND_CONF}/${CND_PLATFORM}/tmp-packaging
+TMPDIRNAME=tmp-packaging
+OUTPUT_PATH=${CND_DISTDIR}/libHotSwapTestsNatives.dll
+OUTPUT_BASENAME=libHotSwapTestsNatives.dll
+PACKAGE_TOP_DIR=libHotSwapTestsNatives.dll/
+
+# Functions
+function checkReturnCode
+{
+ rc=$?
+ if [ $rc != 0 ]
+ then
+ exit $rc
+ fi
+}
+function makeDirectory
+# $1 directory path
+# $2 permission (optional)
+{
+ mkdir -p "$1"
+ checkReturnCode
+ if [ "$2" != "" ]
+ then
+ chmod $2 "$1"
+ checkReturnCode
+ fi
+}
+function copyFileToTmpDir
+# $1 from-file path
+# $2 to-file path
+# $3 permission
+{
+ cp "$1" "$2"
+ checkReturnCode
+ if [ "$3" != "" ]
+ then
+ chmod $3 "$2"
+ checkReturnCode
+ fi
+}
+
+# Setup
+cd "${TOP}"
+mkdir -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package
+rm -rf ${TMPDIR}
+mkdir -p ${TMPDIR}
+
+# Copy files and create directories and links
+cd "${TOP}"
+makeDirectory ${TMPDIR}/libHotSwapTestsNatives.dll/lib
+copyFileToTmpDir "${OUTPUT_PATH}" "${TMPDIR}/${PACKAGE_TOP_DIR}lib/${OUTPUT_BASENAME}" 0644
+
+
+# Generate tar file
+cd "${TOP}"
+rm -f ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/libHotSwapTestsNatives.dll.tar
+cd ${TMPDIR}
+tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/libHotSwapTestsNatives.dll.tar *
+checkReturnCode
+
+# Cleanup
+cd "${TOP}"
+rm -rf ${TMPDIR}
diff -r f5603a6e5042 hotswaptest/HotSwapTestsNatives/nbproject/configurations.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTestsNatives/nbproject/configurations.xml Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,67 @@
+
+
+
+
+ natives.h
+
+
+
+
+ natives.c
+
+
+ Makefile
+
+
+ Makefile
+
+
+
+ localhost
+ MinGW|MinGW
+ 3
+
+
+
+
+
+
+ -Wl,--add-stdcall-alias
+
+
+
+
+
+ localhost
+ MinGW|MinGW
+ 3
+
+
+
+ 5
+
+
+ 5
+
+
+ 5
+
+
+
+ true
+
+
+ -Wl,--add-stdcall-alias
+
+
+
+
+
diff -r f5603a6e5042 hotswaptest/HotSwapTestsNatives/nbproject/project.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTestsNatives/nbproject/project.xml Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,15 @@
+
+
+ org.netbeans.modules.cnd.makeproject
+
+
+ HotSwapTestsNatives
+ 0
+ c
+
+ h
+ UTF-8
+
+
+
+
diff -r f5603a6e5042 hotswaptest/HotSwapTool/build.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/build.xml Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+ Builds, tests, and runs the project HotSwapTool.
+
+
+
diff -r f5603a6e5042 hotswaptest/HotSwapTool/manifest.mf
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/manifest.mf Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
+
diff -r f5603a6e5042 hotswaptest/HotSwapTool/nbproject/build-impl.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/nbproject/build-impl.xml Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,880 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set src.dir
+ Must set build.dir
+ Must set dist.dir
+ Must set build.classes.dir
+ Must set dist.javadoc.dir
+ Must set build.test.classes.dir
+ Must set build.test.results.dir
+ Must set build.classes.excludes
+ Must set dist.jar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+
+
+
+
+
+ java -cp "${run.classpath.with.dist.jar}" ${main.class}
+
+
+
+
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+ java -jar "${dist.jar.resolved}"
+
+
+
+
+
+
+
+ To run this application from the command line without Ant, try:
+
+ java -jar "${dist.jar.resolved}"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+ Must select one file in the IDE or set run.class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must select one file in the IDE or set debug.class
+
+
+
+
+ Must set fix.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set javac.includes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+
+
+
+
+ Must select some files in the IDE or set test.includes
+
+
+
+ Some tests failed; see details above.
+
+
+
+
+ Must select one file in the IDE or set test.class
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+ Must select one file in the IDE or set applet.url
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r f5603a6e5042 hotswaptest/HotSwapTool/nbproject/genfiles.properties
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/nbproject/genfiles.properties Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=e63f7c25
+build.xml.script.CRC32=0269d054
+build.xml.stylesheet.CRC32=28e38971@1.40.0.45
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=e63f7c25
+nbproject/build-impl.xml.script.CRC32=57906c39
+nbproject/build-impl.xml.stylesheet.CRC32=5c621a33@1.26.2.45
diff -r f5603a6e5042 hotswaptest/HotSwapTool/nbproject/project.properties
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/nbproject/project.properties Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,70 @@
+application.title=HotSwapTool
+application.vendor=Thomas
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+build.generated.sources.dir=${build.dir}/generated-sources
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+# Uncomment to specify the preferred debugger connection transport:
+#debug.transport=dt_socket
+debug.classpath=\
+ ${run.classpath}
+debug.test.classpath=\
+ ${run.test.classpath}
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/HotSwapTool.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+endorsed.classpath=
+excludes=
+file.reference.asm-all-3.2.jar=../lib/asm-all-3.2.jar
+file.reference.HotSwapTool-src=src
+file.reference.tools.jar=../lib/tools.jar
+includes=**
+jar.compress=false
+javac.classpath=\
+ ${file.reference.tools.jar}:\
+ ${file.reference.asm-all-3.2.jar}
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.source=1.5
+javac.target=1.5
+javac.test.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}:\
+ ${libs.junit.classpath}:\
+ ${libs.junit_4.classpath}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api"
+main.class=
+manifest.file=manifest.mf
+meta.inf.dir=${src.dir}/META-INF
+platform.active=default_platform
+run.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+# Space-separated list of JVM arguments used when running the project
+# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
+# or test-sys-prop.name=value to set system properties for unit tests):
+run.jvmargs=
+run.test.classpath=\
+ ${javac.test.classpath}:\
+ ${build.test.classes.dir}
+source.encoding=UTF-8
+src.dir=${file.reference.HotSwapTool-src}
diff -r f5603a6e5042 hotswaptest/HotSwapTool/nbproject/project.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/nbproject/project.xml Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,14 @@
+
+
+ org.netbeans.modules.java.j2seproject
+
+
+ HotSwapTool
+ 1.6.5
+
+
+
+
+
+
+
diff -r f5603a6e5042 hotswaptest/HotSwapTool/src/at/ssw/hotswap/ClassIdentifier.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/src/at/ssw/hotswap/ClassIdentifier.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class ClassIdentifier {
+
+ private String methodSignature = null;
+ private String typeSignature = null;
+ private String slashedTypeName = null;
+ private String dottedTypeName = null;
+
+ public ClassIdentifier(String name) {
+ if (name != null) {
+ if (name.indexOf('(') >= 0) {
+ this.methodSignature = name;
+ } else if (name.endsWith(";")) {
+ this.typeSignature = name;
+ } else if (name.indexOf('.') >= 0) {
+ this.dottedTypeName = name;
+ } else {
+ this.slashedTypeName = name;
+ }
+ }
+ }
+
+ public String getOriginal() {
+ if (this.typeSignature != null) {
+ return this.typeSignature;
+ }
+ if (this.methodSignature != null) {
+ return this.methodSignature;
+ }
+ if (this.dottedTypeName != null) {
+ return this.dottedTypeName;
+ }
+ return this.slashedTypeName;
+ }
+
+ public String getDescriptor() {
+ if (this.dottedTypeName != null) {
+ return this.dottedTypeName;
+ }
+ if (this.typeSignature != null) {
+ return this.typeSignature.substring(1, this.typeSignature.length() - 1).replace('/', '.');
+ }
+ if (this.methodSignature != null) {
+ return this.methodSignature.substring(1).replace('/', '.');
+ }
+ if (this.slashedTypeName != null) {
+ return this.slashedTypeName.replace('/', '.');
+ }
+
+ return null;
+ }
+
+ public void setDescriptor(String ts) {
+
+ if (this.dottedTypeName != null) {
+ this.dottedTypeName = ts;
+ } else if (this.typeSignature != null) {
+ this.typeSignature = "L" + ts.replace('.', '/') + ";";
+ } else if (this.methodSignature != null) {
+ this.methodSignature = "(" + ts.replace('.', '/');
+ } else if (this.slashedTypeName != null) {
+ this.slashedTypeName = ts.replace('.', '/');
+ } else {
+ assert false : "something went wrong in ClassIdentifier";
+ }
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTool/src/at/ssw/hotswap/ClassRedefinitionPolicy.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/src/at/ssw/hotswap/ClassRedefinitionPolicy.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @author Kerstin Breiteneder
+ */
+@Retention(value = RetentionPolicy.RUNTIME)
+public @interface ClassRedefinitionPolicy {
+
+ // Default value if no alias is set.
+ public final static class NoClass {
+ }
+
+ Class alias() default NoClass.class;
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTool/src/at/ssw/hotswap/FieldRedefinitionPolicy.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/src/at/ssw/hotswap/FieldRedefinitionPolicy.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ *
+ * @author Thomas Wuerthinger
+ */
+@Retention(RetentionPolicy.CLASS)
+public @interface FieldRedefinitionPolicy {
+
+ RedefinitionPolicy value();
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTool/src/at/ssw/hotswap/HotSwapException.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/src/at/ssw/hotswap/HotSwapException.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap;
+
+/**
+ * @author Thomas Wuerthinger
+ */
+public class HotSwapException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public HotSwapException() {
+ super("Exception during hotswapping");
+ }
+
+ public HotSwapException(Throwable t) {
+ super(t);
+ }
+
+ public HotSwapException(String s) {
+ super(s);
+ }
+
+ public HotSwapException(String s, Throwable t) {
+ super(s, t);
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTool/src/at/ssw/hotswap/HotSwapTool.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/src/at/ssw/hotswap/HotSwapTool.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,542 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap;
+
+import com.sun.jdi.IncompatibleThreadStateException;
+import com.sun.jdi.Location;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.StackFrame;
+import com.sun.jdi.ThreadReference;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
+/**
+ *
+ * @author Thomas Wuerthinger
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ *
+ */
+public class HotSwapTool {
+
+ /** Prefix for the version number in the class name. The class bytes are modified that this string including
+ * the following number is removed. This means that e.g. A___2 is treated as A anywhere in the source code. This is introduced
+ * to make the IDE not complain about multiple defined classes.
+ */
+ public static final String IDENTIFIER = "___";
+ /** Level at which the program prints console output. Use 0 to disable the console output. **/
+ private static int TRACE_LEVEL;
+ private static final String CLASS_FILE_SUFFIX = ".class";
+ private static Map, Integer> currentVersion = new Hashtable, Integer>();
+ private static int redefinitionCount;
+ private static long totalTime;
+
+ public static void setTraceLevel(int level) {
+ TRACE_LEVEL = level;
+ }
+
+ /**
+ * Utility method for dumping the current call stack. The parameter identifies the class version the caller method corresponds to.
+ * @param version the manually specified class version of the caller method
+ */
+ public static void dumpEnter(int version) {
+
+ System.out.print("Method enter version " + version + " of ");
+
+ StackTraceElement[] trace = Thread.currentThread().getStackTrace();
+ if (trace.length < 3) {
+ return;
+ }
+
+ boolean first = true;
+ for (int i = 2; i <= 3; i++) {
+ StackTraceElement elem = trace[i];
+ if (elem.getClassName().startsWith("at.ssw")) {
+ String className = elem.getClassName();
+ className = className.substring(className.lastIndexOf('.') + 1);
+ if (first) {
+ first = false;
+ System.out.print(className + "." + elem.getMethodName() + ": ");
+ } else {
+ System.out.print(className + "." + elem.getMethodName() + "[" + elem.getLineNumber() + "] ");
+ }
+ }
+ }
+ System.out.println();
+ }
+
+ /**
+ * Returns the current version of the inner classes of a specified outer class.
+ * @param baseClass the outer class whose version is queried
+ * @return the version of the inner classes of the specified outer class
+ */
+ public static int getCurrentVersion(Class> baseClass) {
+ if (!currentVersion.containsKey(baseClass)) {
+ currentVersion.put(baseClass, 0);
+ }
+ return currentVersion.get(baseClass).intValue();
+ }
+
+ /**
+ * Performs an explit shutdown and disconnects from the VM.
+ */
+ public static void shutdown() {
+ final JDIProxy jdi = JDIProxy.getJDI();
+
+ try {
+ jdi.disconnect();
+ } catch (HotSwapException e) {
+ throw new Error(e);
+ }
+ }
+
+ /**
+ * Redefines the Java class specified by the array of class files.
+ * @param files the class files with the byte codes of the new version of the java classes
+ * @throws HotSwapException if redefining the classes failed
+ * @throws ClassNotFoundException if a class that is not yet loaded in the target VM should be redefined
+ */
+ public static void redefine(List files, final Map replacements) throws HotSwapException, ClassNotFoundException {
+
+ final JDIProxy jdi = JDIProxy.getJDI();
+ assert jdi.isConnected();
+
+ // Be sure to be in sync with the VM.
+ jdi.refreshAllClasses();
+ List klasses = jdi.allClasses();
+ if (TRACE_LEVEL >= 2) {
+ System.out.println("Number of classes loaded before redefinition: " + klasses.size());
+ }
+
+ Map klassesMap = new HashMap();
+ for (ReferenceType t : klasses) {
+ if (!t.name().endsWith("[]")) {
+ assert !klassesMap.containsKey(t.name()) : "Must not contain two classes with same name " + t.name() + "!";
+ klassesMap.put(t.name(), t);
+ }
+ }
+
+ // Print loaded types
+ if (TRACE_LEVEL >= 4) {
+ for (Entry entry : klassesMap.entrySet()) {
+ System.out.println("Loaded reference type: " + entry.getValue());
+ }
+ }
+
+ final Set redefinedClasses = new HashSet();
+ Map map = new HashMap();
+
+ for (File f : files) {
+ try {
+
+ InputStream s = new FileInputStream(f);
+ ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+
+ TestCaseClassAdapter adapter = new TestCaseClassAdapter(writer, new TypeNameAdapter() {
+
+ public String adapt(String typeName) {
+ String newClass = getReplacementClassName(typeName, replacements);
+ return newClass;
+ }
+ });
+
+ ClassReader cr = new ClassReader(s);
+ cr.accept(adapter, 0);
+ s.close();
+ byte[] bytes = writer.toByteArray();
+
+ String className = adapter.name.replace('/', '.');
+ ReferenceType t = klassesMap.get(className);
+
+ assert t != null : "Class must be loaded in target VM (name=" + className + ")!";
+
+ // TODO: Check if the hack to put in java/lang/Object as the name for every class is apropriate.
+ // This could be used to make redefinition of not-yet-loaded classes possible.
+ // Problem: Which class loaded to choose from? Currently a ReferenceType object is a combination of classname/classloader.
+ map.put(t, bytes);
+ redefinedClasses.add(t.name());
+ } catch (FileNotFoundException e) {
+ throw new HotSwapException(
+ "Could not find specified class file", e);
+ } catch (IOException e) {
+ throw new HotSwapException(
+ "IO exception while reading class file", e);
+ }
+ }
+
+ if (TRACE_LEVEL >= 2) {
+ System.out.print("Redefining the classes:");
+ for (ReferenceType t : map.keySet()) {
+ System.out.print(" " + t.name());
+ }
+ System.out.println();
+ }
+
+ long startTime = System.currentTimeMillis();
+ jdi.getVM().redefineClasses(map);
+
+ long curTime = System.currentTimeMillis() - startTime;
+ totalTime += curTime;
+ redefinitionCount++;
+
+ }
+
+ private static void getAndTouchMainStackFrames() {
+ try {
+ List frames = HotSwapTool.getStackFramesOfMainThread();
+ for (int i = 0; i < frames.size(); i++) {
+ StackFrame frame = frames.get(i);
+
+ // Access the parts of the stack frame one-by-one.
+ Field f = null;
+ try {
+ f = frame.getClass().getDeclaredField("location");
+ } catch (NoSuchFieldException ex) {
+ throw new HotSwapException(ex);
+ } catch (SecurityException ex) {
+ throw new HotSwapException(ex);
+ }
+ f.setAccessible(true);
+ Location l = null;
+ try {
+ l = (Location) f.get(frame);
+ } catch (IllegalArgumentException ex) {
+ throw new HotSwapException(ex);
+ } catch (IllegalAccessException ex) {
+ throw new HotSwapException(ex);
+ }
+ if (TRACE_LEVEL >= 3) {
+ System.out.println("Frame number " + i);
+
+ }
+ com.sun.jdi.Method method = l.method();
+ if (TRACE_LEVEL >= 3) {
+ System.out.println("Method: " + method);
+ }
+ int lineNumber = l.lineNumber();
+ if (TRACE_LEVEL >= 3) {
+ System.out.println(lineNumber + " ");
+ }
+ if (l.lineNumber() == -1) {
+ String methodString = l.method().toString();
+ if (TRACE_LEVEL >= 3) {
+ System.out.print(methodString);
+ System.out.print("+");
+ System.out.println(l.codeIndex());
+ }
+ } else if (TRACE_LEVEL >= 3) {
+ System.out.print(l.declaringType().name());
+ System.out.print(":");
+ System.out.println(l.lineNumber());
+ }
+ }
+ } catch (HotSwapException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * Redefines all inner classes of a outer class to a specified version. Inner classes who do not have a particular
+ * representation for a version remain unchanged.
+ * @param outerClass the outer class whose inner classes should be redefined
+ * @param versionNumber the target version number
+ */
+ public static void toVersion(Class> outerClass, int versionNumber) {
+ assert versionNumber >= 0;
+
+ if (TRACE_LEVEL >= 2) {
+ System.out.println("Entering toVersion");
+ }
+
+ // Touch stack frames to make sure everything is OK.
+ getAndTouchMainStackFrames();
+
+ if (TRACE_LEVEL >= 1) {
+ System.out.println("Changing class " + outerClass.getSimpleName() + " to version number " + versionNumber + " from version " + getCurrentVersion(outerClass));
+ }
+
+ if (versionNumber == getCurrentVersion(outerClass)) {
+ // Nothing to do!
+ return;
+ }
+
+ List result = new ArrayList();
+ getClassFilesWithVersion(result, new File("."), versionNumber, outerClass);
+
+ // Make sure all classes are loaded in the VM, before they are redefined
+ List> classes = new ArrayList>();
+ for (File curFile : result) {
+ String curFileName = fileToClassName(curFile.getPath());
+
+ try {
+ classes.add(Class.forName(curFileName));
+ if (TRACE_LEVEL >= 2) {
+ System.out.println("Class added: " + classes.get(classes.size() - 1));
+ }
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ assert false;
+ return;
+ }
+ }
+ Map replacements = new HashMap();
+
+ for(Class c : classes) {
+ Annotation a = c.getAnnotation(ClassRedefinitionPolicy.class);
+ if(a!=null && a instanceof ClassRedefinitionPolicy) {
+ Class rep = ((ClassRedefinitionPolicy)a).alias();
+
+ if(rep != ClassRedefinitionPolicy.NoClass.class) {
+ String oldClassName = c.getName();
+ String newClassName = rep.getName();
+ replacements.put(oldClassName, newClassName);
+ }
+ else {
+ replacements.put(c.getName(), stripVersion(c.getName()));
+ }
+ }
+ else {
+ replacements.put(c.getName(), stripVersion(c.getName()));
+ }
+ }
+
+ for (File curFile : result) {
+ String curFileName = curFile.getPath();
+ String curClassName = getReplacementClassName(fileToClassName(curFileName), replacements);
+
+ try {
+ classes.add(Class.forName(curClassName));
+ if (TRACE_LEVEL >= 2) {
+ System.out.println("Class added: " + classes.get(classes.size() - 1));
+ }
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ assert false;
+ return;
+ }
+ }
+ try {
+ redefine(result, replacements);
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ assert false;
+ return;
+ } catch (HotSwapException e) {
+ e.printStackTrace();
+ assert false;
+ return;
+ }
+
+ // Touch stack frames again for testing purposes.
+ getAndTouchMainStackFrames();
+
+ if (TRACE_LEVEL >= 2) {
+ for (Class> c : classes) {
+ System.out.println("Classes were: " + c.getName());
+ assert isClassLoaded(c.getName());
+ }
+ }
+ setCurrentVersion(outerClass, versionNumber);
+ if (TRACE_LEVEL >= 2) {
+ System.out.println("Version successfully changed to " + versionNumber);
+ }
+ assert getCurrentVersion(outerClass) == versionNumber;
+ }
+
+ private static void getClassFilesWithVersion(List files, File f, int versionNumber,
+ Class> baseClass) {
+ if (f.isFile()) {
+ String name = f.getName();
+ if (isInnerClass(name, baseClass)) {
+ int curVersionNumber = getVersionNumber(name);
+ if (curVersionNumber == versionNumber) {
+ files.add(f);
+ }
+ }
+ } else if (f.isDirectory()) {
+ for (File subFile : f.listFiles()) {
+ getClassFilesWithVersion(files, subFile, versionNumber, baseClass);
+ }
+ } else {
+ assert false;
+ }
+ }
+
+ private static String fileToClassName(String s) {
+ s = s.substring(0, s.length() - ".class".length());
+ s = s.replace(".", "");
+ s = s.replace(File.separator, ".");
+ return s.substring(1);
+ }
+
+ private static int getVersionNumber(String name) {
+
+ if (!name.endsWith(CLASS_FILE_SUFFIX)) {
+ return -1;
+ }
+
+ int index = name.indexOf(IDENTIFIER);
+ if (index == -1) {
+ return 0;
+ }
+
+ int result = 0;
+ int curIndex = index + IDENTIFIER.length();
+ while (curIndex < name.length()) {
+
+ char c = name.charAt(curIndex);
+ if (!Character.isDigit(c)) {
+ break;
+ }
+ curIndex++;
+ result *= 10;
+ result += c - '0';
+ }
+
+ return result;
+ }
+
+ private static boolean isInnerClass(String name, Class> baseClass) {
+ return name.contains("." + baseClass.getSimpleName() + "$") || name.startsWith(baseClass.getSimpleName() + "$");
+ }
+
+ private static void setCurrentVersion(Class> baseClass, int value) {
+ currentVersion.put(baseClass, value);
+ }
+
+ private static String getReplacementClassName(String clazz, Map replacements) {
+ if (clazz == null) {
+ return null;
+ }
+ ClassIdentifier classIdentifier = new ClassIdentifier(clazz);
+
+
+ for(String key: replacements.keySet()){
+ String newClazz = classIdentifier.getDescriptor();
+ if(newClazz.startsWith(key)){
+ newClazz = newClazz.substring(key.length());
+ classIdentifier.setDescriptor(replacements.get(key)+newClazz);
+ return classIdentifier.getOriginal();
+ }
+ }
+ return clazz;
+ }
+
+ public static String decodeDescriptor(String s) {
+ assert s.charAt(0) == 'L' && s.charAt(s.length() - 1) == ';' : "argument must be type descriptor LCLASSNAME;";
+ String typeName = s.substring(1, s.length() - 1);
+ typeName = typeName.replace('/', '.');
+ return typeName;
+ }
+
+ private static String stripVersion(String className) {
+ if (className == null) {
+ return null;
+ }
+ int index = className.indexOf(IDENTIFIER);
+ if (index != -1) {
+
+ int curIndex = index + IDENTIFIER.length();
+ while (curIndex < className.length()) {
+ if (Character.isDigit(className.charAt(curIndex))) {
+ curIndex++;
+ } else {
+ break;
+ }
+ }
+
+ className = className.substring(0, index) + className.substring(curIndex);
+ }
+ return className;
+ }
+
+ public static List getStackFramesOfMainThread() {
+ final JDIProxy jdi = JDIProxy.getJDI();
+ final List resultList = new ArrayList();
+ Runnable r = new Runnable() {
+
+ public void run() {
+ final List threads = jdi.getVM().allThreads();
+ for (ThreadReference t : threads) {
+ if (t.name().equals("main")) {
+ try {
+ if (TRACE_LEVEL >= 3) {
+ System.out.println("Before accessing stack frames");
+ }
+ resultList.addAll(t.frames());
+ if (TRACE_LEVEL >= 3) {
+ System.out.println("Number of stack frames: " + resultList.size());
+ }
+ } catch (IncompatibleThreadStateException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+ }
+ }
+ };
+
+ jdi.executeSuspended(r);
+ return resultList;
+ }
+ static int cnt;
+
+ private static boolean isClassLoaded(String className) {
+
+ List klasses = JDIProxy.getJDI().allClasses();
+ for (ReferenceType klass : klasses) {
+ if (klass.name().equals(className)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static void resetTimings() {
+ redefinitionCount = 0;
+ totalTime = 0;
+ }
+
+ public static int getRedefinitionCount() {
+ return redefinitionCount;
+ }
+
+ public static long getTotalTime() {
+ return totalTime;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTool/src/at/ssw/hotswap/JDIProxy.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/src/at/ssw/hotswap/JDIProxy.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+
+import com.sun.jdi.Bootstrap;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.VirtualMachine;
+import com.sun.jdi.VirtualMachineManager;
+import com.sun.jdi.connect.AttachingConnector;
+import com.sun.jdi.connect.IllegalConnectorArgumentsException;
+import com.sun.jdi.connect.Connector.Argument;
+import java.lang.reflect.Field;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Utility class for performing hotswapping. Two main use cases:
+ *
+ * - Redefine a set of classes using the {@link #redefine(File[]) redefine} method.
+ * - Redefine all inner classes of a specified outer class to a certain version using the
+ * {@link #toVersion(Class, int) toVersion} method.
+ * The magic prefix to identify version numbers in classes is specified by the {@link #IDENTIFIER IDENTIFIER} field.
+ *
+ *
+ *
+ * @author Thomas Wuerthinger
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ *
+ */
+public class JDIProxy {
+
+ private static final String PORT_ARGUMENT_NAME = "port";
+ private static final String TRANSPORT_NAME = "dt_socket";
+
+ private VirtualMachine vm;
+
+ private static JDIProxy jdi;
+
+ /** Port at which to connect to the agent of the VM. **/
+ public static final int PORT = 4000;
+
+ private JDIProxy() {
+ }
+
+ public static JDIProxy getJDI() {
+ if (jdi == null) {
+
+ jdi = new JDIProxy();
+ try {
+ jdi.connect(PORT);
+ } catch (HotSwapException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ return jdi;
+ }
+
+ public void connect(int port) throws HotSwapException {
+
+ if (isConnected()) {
+ throw new HotSwapException("Already connected");
+ }
+ VirtualMachineManager manager = Bootstrap.virtualMachineManager();
+
+ // Find appropiate connector
+ List connectors = manager.attachingConnectors();
+ AttachingConnector chosenConnector = null;
+ for (AttachingConnector c : connectors) {
+ if (c.transport().name().equals(TRANSPORT_NAME)) {
+ chosenConnector = c;
+ break;
+ }
+ }
+ if (chosenConnector == null) {
+ throw new HotSwapException("Could not find socket connector");
+ }
+
+ // Set port argument
+ AttachingConnector connector = chosenConnector;
+ Map defaults = connector.defaultArguments();
+ Argument a = defaults.get(PORT_ARGUMENT_NAME);
+ if (a == null) {
+ throw new HotSwapException("Could not find port argument");
+ }
+ a.setValue(Integer.toString(port));
+
+ // Attach
+ try {
+ System.out.println("Connector arguments: " + defaults);
+ vm = connector.attach(defaults);
+ // vm.setDebugTraceMode(VirtualMachine.TRACE_EVENTS);
+ } catch (IOException e) {
+ throw new HotSwapException("IO exception during attach", e);
+ } catch (IllegalConnectorArgumentsException e) {
+ throw new HotSwapException("Illegal connector arguments", e);
+ }
+
+ assert isConnected();
+ }
+
+ public void disconnect() throws HotSwapException {
+ assert isConnected();
+ vm.dispose();
+ vm = null;
+ assert !isConnected();
+ }
+
+ // Checks if the tool is currently connected
+ public boolean isConnected() {
+ return vm != null;
+ }
+
+ public VirtualMachine getVM() {
+ return vm;
+ }
+
+ /**
+ * Call this method before calling allClasses() in order to refresh the JDI state of loaded classes.
+ * This is necessary because the JDI map of all loaded classes is only updated based on events received over JDWP (network connection)
+ * and therefore it is not necessarily up-to-date with the real state within the VM.
+ */
+ public void refreshAllClasses() {
+ try {
+ Field f = vm.getClass().getDeclaredField("retrievedAllTypes");
+ f.setAccessible(true);
+ f.set(vm, false);
+ } catch (IllegalArgumentException ex) {
+ Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (IllegalAccessException ex) {
+ Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (NoSuchFieldException ex) {
+ Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (SecurityException ex) {
+ Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+
+ public void executeSuspended(final Runnable r) {
+
+ assert isConnected();
+
+ Thread backup = new Thread(new Runnable() {
+
+ public void run() {
+ List threadsToSuspend = new ArrayList();
+
+ for (ThreadReference t : vm.allThreads()) {
+ if (t.name().equals("main")) {
+ threadsToSuspend.add(t);
+ }
+ }
+
+ for (ThreadReference t : threadsToSuspend) {
+ t.suspend();
+ }
+ r.run();
+ for (ThreadReference t : threadsToSuspend) {
+ t.resume();
+ }
+ }
+ });
+ backup.start();
+ try {
+ backup.join();
+ } catch (InterruptedException ex) {
+ ex.getStackTrace();
+ }
+ }
+
+ List allClasses() {
+ // System.out.println("enter all classes");
+ List result = vm.allClasses();
+ //System.out.println("exit all classes");
+ return result;
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTool/src/at/ssw/hotswap/Main.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/src/at/ssw/hotswap/Main.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Small program for using class HotSwapTool from the command line.
+ * Example:
+ * java at.ssw.hotswap.Main File1.class File2.class
+ *
+ * @author Thomas Wuerthinger
+ */
+public class Main {
+
+ public static final int PORT = 4000;
+ public static final int TOOL_PORT = 1234;
+
+ public static void main(String[] args) {
+ System.out.println("Starting HotSwapTool");
+ if (args.length == 0) {
+ System.out.println("Usage: Specify class file names you want to hotswap.");
+ return;
+ }
+
+ try {
+ System.out.println("Using port " + PORT + " to connect to target VM");
+ System.out.println("Redefining classes");
+
+ List klasses = new ArrayList();
+ for(int i=0; i());
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ } finally {
+ HotSwapTool.shutdown();
+ }
+
+ } catch(HotSwapException e) {
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff -r f5603a6e5042 hotswaptest/HotSwapTool/src/at/ssw/hotswap/MethodRedefinitionPolicy.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/src/at/ssw/hotswap/MethodRedefinitionPolicy.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ *
+ * @author Thomas Wuerthinger
+ */
+@Retention(RetentionPolicy.CLASS)
+public @interface MethodRedefinitionPolicy {
+ RedefinitionPolicy value();
+
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTool/src/at/ssw/hotswap/RedefinitionPolicy.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/src/at/ssw/hotswap/RedefinitionPolicy.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap;
+
+/**
+ *
+ * @author Thomas Wuerthinger
+ */
+public enum RedefinitionPolicy {
+ StaticCheck,
+ DynamicCheck,
+ AccessDeletedMembers,
+ AccessOldMembers
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTool/src/at/ssw/hotswap/TestCaseClassAdapter.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/src/at/ssw/hotswap/TestCaseClassAdapter.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ByteVector;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.tree.AnnotationNode;
+
+/**
+ *
+ * @author Thomas Wuerthinger
+ */
+public class TestCaseClassAdapter extends ClassAdapter {
+
+ public String name;
+
+ private TypeNameAdapter adapter;
+
+ private Set innerClassEntries = new HashSet();
+
+ private static class InnerClassEntry {
+ String name;
+ String outerName;
+ String innerName;
+ int access;
+
+ public InnerClassEntry(String name, String outerName, String innerName, int access) {
+ this.name = name;
+ this.outerName = outerName;
+ this.innerName = innerName;
+ this.access = access;
+ }
+
+ @Override
+ public int hashCode() {
+ return ((name == null) ? 0 : name.hashCode()) * 27 + ((outerName == null) ? 0 : outerName.hashCode()) * 13 + ((innerName == null) ? 0 : innerName.hashCode()) * 7 + access;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o != null && o instanceof InnerClassEntry) {
+ final InnerClassEntry other = (InnerClassEntry)o;
+ return equals(name, other.name) && equals(outerName, other.outerName) && equals(innerName, other.innerName) && access == other.access;
+ }
+
+ return false;
+ }
+
+ private boolean equals(Object o1, Object o2) {
+ if (o1 == null) return o2 == null;
+ if (o2 == null) return o1 == null;
+ return o1.equals(o2);
+ }
+ }
+
+ public TestCaseClassAdapter(ClassVisitor cv, TypeNameAdapter adapter) {
+ super(cv);
+ this.adapter = adapter;
+ }
+
+ private static String[] adapt(TypeNameAdapter adapter, String[] values) {
+ if (values == null) return null;
+ final String[] result = new String[values.length];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = adapter.adapt(values[i]);
+ }
+ return result;
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+ this.name = adapter.adapt(name);
+
+ if (this.name.equals("java/lang/Object")) {
+ superName = null;
+ }
+
+ cv.visit(version, access, this.name, adapter.adapt(signature), adapter.adapt(superName), adapt(adapter, interfaces));
+ }
+
+ @Override
+ public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
+ return cv.visitField(access, name, desc, adapter.adapt(signature), value);
+ }
+
+ @Override
+ public void visitInnerClass(String name, String outerName, String innerName, int access) {
+
+ // Inner class entries may only occur once.
+ InnerClassEntry entry = new InnerClassEntry(adapter.adapt(name), adapter.adapt(outerName), adapter.adapt(innerName), access);
+ if (innerClassEntries.contains(entry)) {
+ return;
+ }
+ innerClassEntries.add(entry);
+
+ cv.visitInnerClass(entry.name, entry.outerName, entry.innerName, entry.access);
+ }
+
+ @Override
+ public void visitOuterClass(String owner, String name, String desc) {
+ cv.visitOuterClass(adapter.adapt(owner), adapter.adapt(name), desc);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+
+ if (name.endsWith("___")) {
+ name = name.substring(0, name.length() - 3);
+ }
+
+ MethodVisitor mv = cv.visitMethod(access, name, adapter.adapt(desc), adapter.adapt(signature), adapt(adapter, exceptions));
+ if (mv != null) {
+ mv = new MethodAdapter(mv) {
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+ if (name.equals("") && TestCaseClassAdapter.this.name.equals("java/lang/Object") && owner.equals("java/lang/Object")) {
+ return;
+ }
+
+ if (name.endsWith("___")) {
+ name = name.substring(0, name.length() - 3);
+ }
+
+ mv.visitMethodInsn(opcode, adapter.adapt(owner), name, adapter.adapt(desc));
+ }
+
+ @Override
+ public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+ super.visitFieldInsn(opcode, adapter.adapt(owner), name, adapter.adapt(desc));
+ }
+
+ @Override
+ public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
+ super.visitLocalVariable(name, adapter.adapt(desc), adapter.adapt(signature), start, end, index);
+ }
+
+ @Override
+ public void visitTypeInsn(int opcode, String type) {
+ super.visitTypeInsn(opcode, adapter.adapt(type));
+ }
+
+ @Override
+ public void visitMultiANewArrayInsn(String desc, int dims) {
+ super.visitMultiANewArrayInsn(adapter.adapt(desc), dims);
+ }
+
+ @Override
+ public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+ super.visitTryCatchBlock(start, end, handler, adapter.adapt(type));
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return super.visitAnnotation(adapter.adapt(desc), visible);
+ }
+ };
+ }
+
+ return mv;
+ }
+
+
+ @Override
+ public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
+
+ return new AnnotationNode(adapter.adapt(desc)) {
+
+ @Override
+ public void visitEnum(String name, String enumDesc, String value) {
+
+
+ class SingleByteAttribute extends Attribute {
+ private byte value;
+
+ public SingleByteAttribute(String name, byte value) {
+ super(name);
+ this.value = value;
+ }
+
+ @Override
+ protected ByteVector write(ClassWriter writer, byte[] code, int len, int maxStack, int maxLocals) {
+ return new ByteVector().putByte(value);
+ }
+
+ }
+
+ if (HotSwapTool.decodeDescriptor(enumDesc).equals(RedefinitionPolicy.class.getName())) {
+ RedefinitionPolicy valueAsEnum = RedefinitionPolicy.valueOf(value);
+ if (HotSwapTool.decodeDescriptor(desc).equals(FieldRedefinitionPolicy.class.getName())) {
+ cv.visitAttribute(new SingleByteAttribute(FieldRedefinitionPolicy.class.getSimpleName(), (byte) valueAsEnum.ordinal()));
+ }
+ if (HotSwapTool.decodeDescriptor(desc).equals(MethodRedefinitionPolicy.class.getName())) {
+ cv.visitAttribute(new SingleByteAttribute(MethodRedefinitionPolicy.class.getSimpleName(), (byte) valueAsEnum.ordinal()));
+ }
+ }
+
+ super.visitEnum(name, desc, value);
+ }
+
+ @Override
+ public void visitEnd() {
+ accept(cv.visitAnnotation(desc, visible));
+ }
+ };
+
+ }
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTool/src/at/ssw/hotswap/TypeNameAdapter.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/src/at/ssw/hotswap/TypeNameAdapter.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please 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 at.ssw.hotswap;
+
+/**
+ *
+ * @author Thomas Wuerthinger
+ */
+public interface TypeNameAdapter {
+
+ String adapt(String typeName);
+}
diff -r f5603a6e5042 hotswaptest/HotSwapTool/src/sun/misc/ClassRedefinition.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotswaptest/HotSwapTool/src/sun/misc/ClassRedefinition.java Fri Dec 17 13:23:04 2010 +0100
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+package sun.misc;
+
+/**
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ */
+public class ClassRedefinition {
+ //TODO: change parameters to allow multiple redefinitions
+ public static native int redefineClasses(Class target, byte[] newBytes);
+}
diff -r f5603a6e5042 hotswaptest/lib/asm-all-3.2.jar
Binary file hotswaptest/lib/asm-all-3.2.jar has changed
diff -r f5603a6e5042 hotswaptest/lib/tools.jar
Binary file hotswaptest/lib/tools.jar has changed
diff -r f5603a6e5042 make/windows/build_vm_def.sh
--- a/make/windows/build_vm_def.sh Wed Nov 17 22:42:08 2010 -0800
+++ b/make/windows/build_vm_def.sh Fri Dec 17 13:23:04 2010 +0100
@@ -51,6 +51,7 @@
CAT="$MKS_HOME/cat.exe"
RM="$MKS_HOME/rm.exe"
DUMPBIN="link.exe /dump"
+export VS_UNICODE_OUTPUT=
# When called from IDE the first param should contain the link version, otherwise may be nill
if [ "x$1" != "x" ]; then
diff -r f5603a6e5042 make/windows/create.bat
--- a/make/windows/create.bat Wed Nov 17 22:42:08 2010 -0800
+++ b/make/windows/create.bat Fri Dec 17 13:23:04 2010 +0100
@@ -39,6 +39,8 @@
REM Note: Running this batch file from the Windows command shell requires
REM that "grep" be accessible on the PATH. An MKS install does this.
REM
+cl 2>&1 | grep "x64" >NUL
+if %errorlevel% == 0 goto amd64
cl 2>&1 | grep "IA-64" >NUL
if %errorlevel% == 0 goto isia64
cl 2>&1 | grep "AMD64" >NUL
diff -r f5603a6e5042 src/cpu/x86/vm/interp_masm_x86_32.cpp
--- a/src/cpu/x86/vm/interp_masm_x86_32.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/cpu/x86/vm/interp_masm_x86_32.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -1352,7 +1352,7 @@
}
// RedefineClasses() tracing support for obsolete method entry
- if (RC_TRACE_IN_RANGE(0x00001000, 0x00002000)) {
+ IF_TRACE_RC4 {
get_thread(rcx);
get_method(rbx);
call_VM_leaf(
diff -r f5603a6e5042 src/cpu/x86/vm/interp_masm_x86_64.cpp
--- a/src/cpu/x86/vm/interp_masm_x86_64.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/cpu/x86/vm/interp_masm_x86_64.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -1438,7 +1438,7 @@
}
// RedefineClasses() tracing support for obsolete method entry
- if (RC_TRACE_IN_RANGE(0x00001000, 0x00002000)) {
+ IF_TRACE_RC4 {
get_method(c_rarg1);
call_VM_leaf(
CAST_FROM_FN_PTR(address, SharedRuntime::rc_trace_method_entry),
diff -r f5603a6e5042 src/cpu/x86/vm/sharedRuntime_x86_32.cpp
--- a/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -1494,7 +1494,7 @@
}
// RedefineClasses() tracing support for obsolete method entry
- if (RC_TRACE_IN_RANGE(0x00001000, 0x00002000)) {
+ IF_TRACE_RC4 {
__ movoop(rax, JNIHandles::make_local(method()));
__ call_VM_leaf(
CAST_FROM_FN_PTR(address, SharedRuntime::rc_trace_method_entry),
diff -r f5603a6e5042 src/cpu/x86/vm/sharedRuntime_x86_64.cpp
--- a/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -1490,7 +1490,7 @@
}
// RedefineClasses() tracing support for obsolete method entry
- if (RC_TRACE_IN_RANGE(0x00001000, 0x00002000)) {
+ IF_TRACE_RC4 {
// protect the args we've loaded
save_args(masm, total_c_args, c_arg, out_regs);
__ movoop(c_rarg1, JNIHandles::make_local(method()));
diff -r f5603a6e5042 src/cpu/x86/vm/templateTable_x86_32.cpp
--- a/src/cpu/x86/vm/templateTable_x86_32.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/cpu/x86/vm/templateTable_x86_32.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -2110,6 +2110,22 @@
// resolve first time through
address entry;
switch (bytecode()) {
+ case Bytecodes::_fast_agetfield : // fall through
+ case Bytecodes::_fast_bgetfield : // fall through
+ case Bytecodes::_fast_cgetfield : // fall through
+ case Bytecodes::_fast_dgetfield : // fall through
+ case Bytecodes::_fast_fgetfield : // fall through
+ case Bytecodes::_fast_igetfield : // fall through
+ case Bytecodes::_fast_lgetfield : // fall through
+ case Bytecodes::_fast_sgetfield : // fall through
+ case Bytecodes::_fast_aputfield : // fall through
+ case Bytecodes::_fast_bputfield : // fall through
+ case Bytecodes::_fast_cputfield : // fall through
+ case Bytecodes::_fast_dputfield : // fall through
+ case Bytecodes::_fast_fputfield : // fall through
+ case Bytecodes::_fast_iputfield : // fall through
+ case Bytecodes::_fast_lputfield : // fall through
+ case Bytecodes::_fast_sputfield : // fall through
case Bytecodes::_getstatic : // fall through
case Bytecodes::_putstatic : // fall through
case Bytecodes::_getfield : // fall through
@@ -2204,6 +2220,7 @@
// Correct values of the cache and index registers are preserved.
void TemplateTable::jvmti_post_field_access(Register cache,
Register index,
+ int byte_no,
bool is_static,
bool has_tos) {
if (JvmtiExport::can_post_field_access()) {
@@ -2230,7 +2247,11 @@
// cache: cache entry pointer
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_access),
rax, cache);
- __ get_cache_and_index_at_bcp(cache, index, 1);
+
+ // (tw) Redefinition might have occured => reresolve the cp entry.
+ __ restore_bcp();
+ resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2));
+
__ bind(L1);
}
}
@@ -2251,7 +2272,7 @@
const Register flags = rax;
resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2));
- jvmti_post_field_access(cache, index, is_static, false);
+ jvmti_post_field_access(cache, index, byte_no, is_static, false);
load_field_cp_cache_entry(obj, cache, index, off, flags, is_static);
if (!is_static) pop_and_check_object(obj);
@@ -2386,7 +2407,7 @@
// The registers cache and index expected to be set before call.
// The function may destroy various registers, just not the cache and index registers.
-void TemplateTable::jvmti_post_field_mod(Register cache, Register index, bool is_static) {
+void TemplateTable::jvmti_post_field_mod(Register cache, Register index, int byte_no, bool is_static) {
ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset();
@@ -2444,7 +2465,11 @@
// rcx: jvalue object on the stack
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification),
rbx, rax, rcx);
- __ get_cache_and_index_at_bcp(cache, index, 1);
+
+ // (tw) Redefinition might have occured => reresolve the cp entry.
+ __ restore_bcp();
+ resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2));
+
__ bind(L1);
}
}
@@ -2460,7 +2485,7 @@
const Register flags = rax;
resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2));
- jvmti_post_field_mod(cache, index, is_static);
+ jvmti_post_field_mod(cache, index, byte_no, is_static);
load_field_cp_cache_entry(obj, cache, index, off, flags, is_static);
// Doug Lea believes this is not needed with current Sparcs (TSO) and Intel (PSO).
@@ -2683,6 +2708,11 @@
// rax,: cache entry pointer
// rcx: jvalue object on the stack
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), rbx, rax, rcx);
+
+ // (tw) Redefinition might have occured => reresolve the cp entry.
+ __ restore_bcp();
+ resolve_cache_and_index(2, noreg, rax, rcx, sizeof(u2));
+
if (bytecode() == Bytecodes::_fast_lputfield) __ pop(rdx); // restore high value
__ pop(rax); // restore lower value
__ addptr(rsp, sizeof(jvalue)); // release jvalue object space
@@ -2803,6 +2833,11 @@
// rcx: cache entry pointer
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_access), rax, rcx);
__ pop_ptr(rax); // restore object pointer
+
+ // (tw) Redefinition might have occured => reresolve the cp entry.
+ __ restore_bcp();
+ resolve_cache_and_index(1, noreg, rax, rcx, sizeof(u2));
+
__ bind(L1);
}
@@ -2973,6 +3008,29 @@
__ bind(notFinal);
+ Label notOld;
+
+ // Check if we are calling an old method (and have to go slow path)
+ __ movl(rax, flags);
+ __ andl(rax, (1 << ConstantPoolCacheEntry::oldMethodBit));
+ __ jcc(Assembler::zero, notOld);
+
+ // Need a null check here!
+ __ null_check(recv);
+
+ // Call out to VM to do look up based on correct vTable version (has to iterate back over the class history of the receiver class)
+ // (tw) TODO: Check if we can improve performance by inlining.
+ // (tw) TODO: Check if this additional branch affects normal execution time.
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::find_correct_method), recv, index);
+ // Method is now in rax
+ __ mov(method, rax);
+
+ // profile this call
+ __ profile_final_call(rax);
+ __ jump_from_interpreted(method, rdx);
+
+ __ bind(notOld);
+
// get receiver klass
__ null_check(recv, oopDesc::klass_offset_in_bytes());
// Keep recv in rcx for callee expects it there
@@ -3054,6 +3112,36 @@
invokevirtual_helper(rbx, rcx, rdx);
__ bind(notMethod);
+
+ Label notOld;
+
+ // Check if we are calling an old method (and have to go slow path)
+ //__ movl(rax, rdx);
+ __ andl(rdx, (1 << ConstantPoolCacheEntry::oldMethodBit));
+ __ jcc(Assembler::zero, notOld);
+
+ // Get receiver klass into rdx - also a null check
+ __ movptr(rdx, Address(rcx, oopDesc::klass_offset_in_bytes()));
+ __ verify_oop(rdx);
+
+ // Call out to VM to do look up based on correct vTable version (has to iterate back over the class history of the receiver class)
+ // (tw) TODO: Check if we can improve performance by inlining.
+ // (tw) TODO: Check if this additional branch affects normal execution time.
+ // (tw) TODO: Check the exact semantic (with respect to destoying registers) of call_VM
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::find_correct_interface_method), rcx, rax, rbx);
+
+ // Method is now in rax
+ __ movptr(rbx, rax);
+
+ // (tw) TODO: Check if resolved method could be null.
+
+ // profile this call
+ __ profile_virtual_call(rdx, rsi, rdi);
+
+ __ jump_from_interpreted(rbx, rdx);
+
+ __ bind(notOld);
+
// Get receiver klass into rdx - also a null check
__ restore_locals(); // restore rdi
__ movptr(rdx, Address(rcx, oopDesc::klass_offset_in_bytes()));
diff -r f5603a6e5042 src/cpu/x86/vm/templateTable_x86_64.cpp
--- a/src/cpu/x86/vm/templateTable_x86_64.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/cpu/x86/vm/templateTable_x86_64.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -2113,6 +2113,22 @@
// resolve first time through
address entry;
switch (bytecode()) {
+ case Bytecodes::_fast_agetfield : // fall through
+ case Bytecodes::_fast_bgetfield : // fall through
+ case Bytecodes::_fast_cgetfield : // fall through
+ case Bytecodes::_fast_dgetfield : // fall through
+ case Bytecodes::_fast_fgetfield : // fall through
+ case Bytecodes::_fast_igetfield : // fall through
+ case Bytecodes::_fast_lgetfield : // fall through
+ case Bytecodes::_fast_sgetfield : // fall through
+ case Bytecodes::_fast_aputfield : // fall through
+ case Bytecodes::_fast_bputfield : // fall through
+ case Bytecodes::_fast_cputfield : // fall through
+ case Bytecodes::_fast_dputfield : // fall through
+ case Bytecodes::_fast_fputfield : // fall through
+ case Bytecodes::_fast_iputfield : // fall through
+ case Bytecodes::_fast_lputfield : // fall through
+ case Bytecodes::_fast_sputfield : // fall through
case Bytecodes::_getstatic:
case Bytecodes::_putstatic:
case Bytecodes::_getfield:
@@ -2219,7 +2235,7 @@
// The registers cache and index expected to be set before call.
// Correct values of the cache and index registers are preserved.
void TemplateTable::jvmti_post_field_access(Register cache, Register index,
- bool is_static, bool has_tos) {
+ int byte_no, bool is_static, bool has_tos) {
// do the JVMTI work here to avoid disturbing the register state below
// We use c_rarg registers here because we want to use the register used in
// the call to the VM
@@ -2250,7 +2266,11 @@
__ call_VM(noreg, CAST_FROM_FN_PTR(address,
InterpreterRuntime::post_field_access),
c_rarg1, c_rarg2, c_rarg3);
- __ get_cache_and_index_at_bcp(cache, index, 1);
+
+ // (tw) Redefinition might have occured => reresolve the cp entry.
+ __ restore_bcp();
+ resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2));
+
__ bind(L1);
}
}
@@ -2272,7 +2292,7 @@
const Register bc = c_rarg3; // uses same reg as obj, so don't mix them
resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2));
- jvmti_post_field_access(cache, index, is_static, false);
+ jvmti_post_field_access(cache, index, byte_no, is_static, false);
load_field_cp_cache_entry(obj, cache, index, off, flags, is_static);
if (!is_static) {
@@ -2406,7 +2426,7 @@
// The registers cache and index expected to be set before call.
// The function may destroy various registers, just not the cache and index registers.
-void TemplateTable::jvmti_post_field_mod(Register cache, Register index, bool is_static) {
+void TemplateTable::jvmti_post_field_mod(Register cache, Register index, int byte_no, bool is_static) {
transition(vtos, vtos);
ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset();
@@ -2459,7 +2479,11 @@
CAST_FROM_FN_PTR(address,
InterpreterRuntime::post_field_modification),
c_rarg1, c_rarg2, c_rarg3);
- __ get_cache_and_index_at_bcp(cache, index, 1);
+
+ // (tw) Redefinition might have occured => reresolve the cp entry.
+ __ restore_bcp();
+ resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2));
+
__ bind(L1);
}
}
@@ -2475,7 +2499,7 @@
const Register bc = c_rarg3;
resolve_cache_and_index(byte_no, noreg, cache, index, sizeof(u2));
- jvmti_post_field_mod(cache, index, is_static);
+ jvmti_post_field_mod(cache, index, byte_no, is_static);
load_field_cp_cache_entry(obj, cache, index, off, flags, is_static);
// [jk] not needed currently
@@ -2661,6 +2685,11 @@
CAST_FROM_FN_PTR(address,
InterpreterRuntime::post_field_modification),
rbx, c_rarg2, c_rarg3);
+
+ // (tw) Redefinition might have occured => reresolve the cp entry.
+ __ restore_bcp();
+ resolve_cache_and_index(2, noreg, rax, rcx, sizeof(u2));
+
__ pop(rax); // restore lower value
__ addptr(rsp, sizeof(jvalue)); // release jvalue object space
__ bind(L2);
@@ -2762,6 +2791,11 @@
c_rarg1, c_rarg2);
__ mov(rax, r12); // restore object pointer
__ reinit_heapbase();
+
+ // (tw) Redefinition might have occured => reresolve the cp entry.
+ __ restore_bcp();
+ resolve_cache_and_index(1, noreg, rax, rcx, sizeof(u2));
+
__ bind(L1);
}
@@ -2977,6 +3011,29 @@
__ bind(notFinal);
+ Label notOld;
+
+ // Check if we are calling an old method (and have to go slow path)
+ __ movl(rax, flags);
+ __ andl(rax, (1 << ConstantPoolCacheEntry::oldMethodBit));
+ __ jcc(Assembler::zero, notOld);
+
+ // Need a null check here!
+ __ null_check(recv);
+
+ // Call out to VM to do look up based on correct vTable version (has to iterate back over the class history of the receiver class)
+ // (tw) TODO: Check if we can improve performance by inlining.
+ // (tw) TODO: Check if this additional branch affects normal execution time.
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::find_correct_method), recv, index);
+ // Method is now in rax
+ __ mov(method, rax);
+
+ // profile this call
+ __ profile_final_call(rax);
+ __ jump_from_interpreted(method, rdx);
+
+ __ bind(notOld);
+
// get receiver klass
__ null_check(recv, oopDesc::klass_offset_in_bytes());
__ load_klass(rax, recv);
@@ -3060,6 +3117,35 @@
invokevirtual_helper(rbx, rcx, rdx);
__ bind(notMethod);
+ Label notOld;
+
+ // Check if we are calling an old method (and have to go slow path)
+ //__ movl(rax, rdx);
+ __ andl(rdx, (1 << ConstantPoolCacheEntry::oldMethodBit));
+ __ jcc(Assembler::zero, notOld);
+
+ // Get receiver klass into rdx - also a null check
+ __ movptr(rdx, Address(rcx, oopDesc::klass_offset_in_bytes()));
+ __ verify_oop(rdx);
+
+ // Call out to VM to do look up based on correct vTable version (has to iterate back over the class history of the receiver class)
+ // (tw) TODO: Check if we can improve performance by inlining.
+ // (tw) TODO: Check if this additional branch affects normal execution time.
+ // (tw) TODO: Check the exact semantic (with respect to destoying registers) of call_VM
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::find_correct_interface_method), rcx, rax, rbx);
+
+ // Method is now in rax
+ __ movptr(rbx, rax);
+
+ // (tw) TODO: Check if resolved method could be null.
+
+ // profile this call
+ __ profile_virtual_call(rdx, rsi, rdi);
+
+ __ jump_from_interpreted(rbx, rdx);
+
+ __ bind(notOld);
+
// Get receiver klass into rdx - also a null check
__ restore_locals(); // restore r14
__ load_klass(rdx, rcx);
diff -r f5603a6e5042 src/share/tools/MakeDeps/BuildConfig.java
--- a/src/share/tools/MakeDeps/BuildConfig.java Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/tools/MakeDeps/BuildConfig.java Fri Dec 17 13:23:04 2010 +0100
@@ -242,7 +242,20 @@
void initDefaultDefines(Vector defines) {
Vector sysDefines = new Vector();
- sysDefines.add("WIN32");
+
+ if( Util.os().equals("Win32")) {
+ sysDefines.add("WIN32");
+ sysDefines.add("HOTSPOT_LIB_ARCH=\\\"i386\\\"");
+ } else {
+ sysDefines.add("_AMD64_");
+ sysDefines.add("AMD64");
+ sysDefines.add("_WIN64");
+ sysDefines.add("_LP64");
+ if (System.getenv("MSC_VER") != null)
+ sysDefines.add("MSC_VER=" + System.getenv("MSC_VER"));
+ sysDefines.add("HOTSPOT_LIB_ARCH=\\\"amd64\\\"");
+ }
+
sysDefines.add("_WINDOWS");
sysDefines.add("HOTSPOT_BUILD_USER="+System.getProperty("user.name"));
sysDefines.add("HOTSPOT_BUILD_TARGET=\\\""+get("Build")+"\\\"");
diff -r f5603a6e5042 src/share/tools/MakeDeps/Util.java
--- a/src/share/tools/MakeDeps/Util.java Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/tools/MakeDeps/Util.java Fri Dec 17 13:23:04 2010 +0100
@@ -84,5 +84,26 @@
}
static String sep = File.separator;
- static String os = "Win32"; //System.getProperty("os.name");
+
+ private static String _os;
+
+ static String os() {
+ if( _os==null) {
+
+ for(Map.Entry entry: System.getenv().entrySet())
+ if("PLATFORM_ARCH_MODEL".equals(entry.getKey().toUpperCase())) {
+ String archModel = entry.getValue();
+ if("x86_32".equals(archModel))
+ _os = "Win32";
+ else if("x86_64".equals(archModel))
+ _os = "x64";
+ else
+ throw new RuntimeException("Unsupported PLATFORM_ARCH_MODEL " + archModel);
+ return _os;
+ }
+ throw new RuntimeException("PLATFORM_ARCH_MODEL not specified");
+ }
+ return _os;
+ }
+
}
diff -r f5603a6e5042 src/share/tools/MakeDeps/WinGammaPlatformVC6.java
--- a/src/share/tools/MakeDeps/WinGammaPlatformVC6.java Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/tools/MakeDeps/WinGammaPlatformVC6.java Fri Dec 17 13:23:04 2010 +0100
@@ -236,7 +236,7 @@
" /nologo /base:\"0x8000000\" /subsystem:windows /dll" +
" /export:JNI_GetDefaultJavaVMInitArgs /export:JNI_CreateJavaVM /export:JNI_GetCreatedJavaVMs "+
" /export:jio_snprintf /export:jio_printf /export:jio_fprintf /export:jio_vfprintf "+
- " /export:jio_vsnprintf ");
+ " /export:jio_vsnprintf /export:JVM_GetVersionInfo /export:JVM_GetThreadStateNames /export:JVM_GetThreadStateValues /export:JVM_InitAgentProperties /export:JVM_FindClassFromBootLoader");
rv.add("SUBTRACT LINK32 /pdb:none /map");
return rv;
@@ -286,6 +286,6 @@
}
String makeCfgName(String flavourBuild) {
- return "vm - "+ Util.os + " " + flavourBuild;
+ return "vm - "+ "Win32" + " " + flavourBuild;
}
}
diff -r f5603a6e5042 src/share/tools/MakeDeps/WinGammaPlatformVC7.java
--- a/src/share/tools/MakeDeps/WinGammaPlatformVC7.java Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/tools/MakeDeps/WinGammaPlatformVC7.java Fri Dec 17 13:23:04 2010 +0100
@@ -51,7 +51,7 @@
);
startTag("Platforms", null);
- tag("Platform", new String[] {"Name", Util.os});
+ tag("Platform", new String[] {"Name", Util.os()});
endTag("Platforms");
startTag("Configurations", null);
@@ -467,7 +467,7 @@
"PreprocessorDefinitions", "NDEBUG",
"MkTypLibCompatible", "TRUE",
"SuppressStartupBanner", "TRUE",
- "TargetEnvironment", "1",
+ "TargetEnvironment", Util.os().equals("Win32") ? "1" : "3",
"TypeLibraryName", cfg.get("OutputDir") + Util.sep + "vm.tlb",
"HeaderFileName", ""
}
@@ -596,7 +596,7 @@
"/export:JNI_GetCreatedJavaVMs "+
"/export:jio_snprintf /export:jio_printf "+
"/export:jio_fprintf /export:jio_vfprintf "+
- "/export:jio_vsnprintf ");
+ "/export:jio_vsnprintf /export:JVM_GetVersionInfo /export:JVM_GetThreadStateNames /export:JVM_GetThreadStateValues /export:JVM_InitAgentProperties /export:JVM_FindClassFromBootLoader");
addAttr(rv, "AdditionalDependencies", "Wsock32.lib winmm.lib");
addAttr(rv, "OutputFile", outDll);
// Set /INCREMENTAL option. 1 is linkIncrementalNo
@@ -609,7 +609,7 @@
addAttr(rv, "BaseAddress", "0x8000000");
addAttr(rv, "ImportLibrary", outDir+Util.sep+"jvm.lib");
// Set /MACHINE option. 1 is machineX86
- addAttr(rv, "TargetMachine", "1");
+ addAttr(rv, "TargetMachine", Util.os().equals("Win32") ? "1" : "17");
return rv;
}
@@ -687,6 +687,6 @@
}
String makeCfgName(String flavourBuild) {
- return flavourBuild + "|" + Util.os;
+ return flavourBuild + "|" + Util.os();
}
}
diff -r f5603a6e5042 src/share/tools/MakeDeps/WinGammaPlatformVC8.java
--- a/src/share/tools/MakeDeps/WinGammaPlatformVC8.java Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/tools/MakeDeps/WinGammaPlatformVC8.java Fri Dec 17 13:23:04 2010 +0100
@@ -41,6 +41,9 @@
addAttr(rv, "UsePrecompiledHeader", "2");
// Set /EHsc- option. 0 is cppExceptionHandlingNo
addAttr(rv, "ExceptionHandling", "0");
+ // Parallel compilation
+ addAttr(rv, "AdditionalOptions", "/MP");
+
return rv;
}
diff -r f5603a6e5042 src/share/vm/c1/c1_Compilation.hpp
--- a/src/share/vm/c1/c1_Compilation.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/c1/c1_Compilation.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -232,8 +232,8 @@
#define BAILOUT(msg) { bailout(msg); return; }
#define BAILOUT_(msg, res) { bailout(msg); return res; }
-#define CHECK_BAILOUT() { if (bailed_out()) return; }
-#define CHECK_BAILOUT_(res) { if (bailed_out()) return res; }
+#define CHECK_BAILOUT() { if (((CompilerThread *)Thread::current())->should_bailout()) bailout("Aborted externally"); if (bailed_out()) return; }
+#define CHECK_BAILOUT_(res) { if (((CompilerThread *)Thread::current())->should_bailout()) bailout("Aborted externally"); if (bailed_out()) return res; }
class InstructionMark: public StackObj {
diff -r f5603a6e5042 src/share/vm/c1/c1_LIRGenerator.cpp
--- a/src/share/vm/c1/c1_LIRGenerator.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/c1/c1_LIRGenerator.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -2498,7 +2498,7 @@
// Load CallSite object from constant pool cache.
__ oop2reg(cpcache->constant_encoding(), tmp);
- __ load(new LIR_Address(tmp, call_site_offset, T_OBJECT), tmp);
+ __ load(new LIR_Address(tmp, (int)call_site_offset, T_OBJECT), tmp);
// Load target MethodHandle from CallSite object.
__ load(new LIR_Address(tmp, java_dyn_CallSite::target_offset_in_bytes(), T_OBJECT), receiver);
diff -r f5603a6e5042 src/share/vm/ci/ciEnv.cpp
--- a/src/share/vm/ci/ciEnv.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/ci/ciEnv.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -1075,3 +1075,11 @@
// If memory is low, we stop compiling methods.
record_method_not_compilable("out of memory");
}
+
+// (tw) Called after class redefinition to clean up possibly invalidated state.
+void ciEnv::cleanup_after_redefinition() {
+
+ if (_factory != NULL) {
+ _factory->cleanup_after_redefinition();
+ }
+}
\ No newline at end of file
diff -r f5603a6e5042 src/share/vm/ci/ciEnv.hpp
--- a/src/share/vm/ci/ciEnv.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/ci/ciEnv.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -383,4 +383,6 @@
void record_failure(const char* reason);
void record_method_not_compilable(const char* reason, bool all_tiers = true);
void record_out_of_memory_failure();
+
+ void cleanup_after_redefinition();
};
diff -r f5603a6e5042 src/share/vm/ci/ciObjectFactory.cpp
--- a/src/share/vm/ci/ciObjectFactory.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/ci/ciObjectFactory.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -255,6 +255,11 @@
// into the table. We need to recompute our index.
index = find(keyHandle(), _ci_objects);
}
+
+ if (is_found_at(index, keyHandle(), _ci_objects)) {
+ // (tw) Check if this is an error? Can occur when redefining classes.
+ return _ci_objects->at(index);
+ }
assert(!is_found_at(index, keyHandle(), _ci_objects), "no double insert");
insert(index, new_object, _ci_objects);
return new_object;
@@ -711,3 +716,50 @@
_unloaded_instances->length(),
_unloaded_klasses->length());
}
+
+// (tw) Resoring the ciObject arrays after class redefinition
+void ciObjectFactory::sort_ci_objects(GrowableArray* objects) {
+
+ // Resort the _ci_objects array. The order of two class pointers can be changed during class redefinition.
+ oop last = NULL;
+ for (int j = 0; j< objects->length(); j++) {
+ oop o = objects->at(j)->get_oop();
+ if (last >= o) {
+ int cur_last_index = j - 1;
+ oop cur_last = last;
+ while (cur_last >= o) {
+
+ // Swap the two objects to guarantee ordering
+ ciObject *tmp = objects->at(cur_last_index);
+ objects->at_put(cur_last_index, objects->at(cur_last_index + 1));
+ objects->at_put(cur_last_index + 1, tmp);
+
+ // Decrement index to move one step to the left
+ cur_last_index--;
+ if (cur_last_index < 0) {
+ break;
+ }
+ cur_last =objects->at(cur_last_index)->get_oop();
+ }
+ } else {
+ assert(last < o, "out of order");
+ last = o;
+ }
+ }
+
+#ifdef ASSERT
+ if (CIObjectFactoryVerify) {
+ oop last = NULL;
+ for (int j = 0; j< objects->length(); j++) {
+ oop o = objects->at(j)->get_oop();
+ assert(last < o, "out of order");
+ last = o;
+ }
+ }
+#endif // ASSERT
+}
+
+// (tw) Called after class redefinition to clean up possibly invalidated state.
+void ciObjectFactory::cleanup_after_redefinition() {
+ sort_ci_objects(_ci_objects);
+}
\ No newline at end of file
diff -r f5603a6e5042 src/share/vm/ci/ciObjectFactory.hpp
--- a/src/share/vm/ci/ciObjectFactory.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/ci/ciObjectFactory.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -29,6 +29,10 @@
// which ensures that for each oop, at most one ciObject is created.
// This invariant allows efficient implementation of ciObject.
class ciObjectFactory : public ResourceObj {
+
+ friend class ciEnv;
+ friend class CompileBroker;
+
private:
static volatile bool _initialized;
static GrowableArray* _shared_ci_objects;
@@ -122,4 +126,9 @@
void print_contents();
void print();
+
+private:
+
+ static void sort_ci_objects(GrowableArray* objects);
+ void cleanup_after_redefinition();
};
diff -r f5603a6e5042 src/share/vm/classfile/classFileParser.cpp
--- a/src/share/vm/classfile/classFileParser.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/classfile/classFileParser.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -833,6 +833,7 @@
Handle class_loader,
Handle protection_domain,
symbolHandle class_name,
+ KlassHandle old_klass,
TRAPS) {
ClassFileStream* cfs = stream();
assert(length > 0, "only called for length>0");
@@ -851,6 +852,9 @@
interface_index, CHECK_(nullHandle));
if (cp->tag_at(interface_index).is_klass()) {
interf = KlassHandle(THREAD, cp->resolved_klass_at(interface_index));
+ if (!old_klass.is_null() && !interf->is_newest_version()) {
+ interf = KlassHandle(THREAD, interf->newest_version());
+ }
} else {
symbolHandle unresolved_klass (THREAD, cp->klass_name_at(interface_index));
@@ -863,6 +867,9 @@
klassOop k = SystemDictionary::resolve_super_or_fail(class_name,
unresolved_klass, class_loader, protection_domain,
false, CHECK_(nullHandle));
+ if (!old_klass.is_null()) {
+ k = k->klass_part()->newest_version();
+ }
interf = KlassHandle(THREAD, k);
if (LinkWellKnownClasses) // my super type is well known to me
@@ -1706,6 +1713,8 @@
int runtime_invisible_parameter_annotations_length = 0;
u1* annotation_default = NULL;
int annotation_default_length = 0;
+ u2 code_section_table_length;
+ typeArrayHandle code_section_table;
// Parse code and exceptions attribute
u2 method_attributes_count = cfs->get_u2_fast();
@@ -1805,7 +1814,6 @@
// Parse and compress line number table
parse_linenumber_table(code_attribute_length, code_length,
&linenumber_table, CHECK_(nullHandle));
-
} else if (LoadLocalVariableTables &&
cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_local_variable_table()) {
// Parse local variable table
@@ -1875,6 +1883,24 @@
parse_stackmap_table(code_attribute_length, CHECK_(nullHandle));
stackmap_data = typeArrayHandle(THREAD, sm);
parsed_stackmap_attribute = true;
+ } else if (UseMethodForwardPoints && cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_code_sections()) {
+ int length = code_attribute_length;
+ int value_count = length / sizeof(u2);
+ int line_count = length / 3;
+ if (TraceRedefineClasses >= 3) {
+ tty->print_cr("Found code section attribute when loading class with %d entries", value_count, line_count);
+ }
+ code_section_table_length = value_count;
+ code_section_table = oopFactory::new_permanent_shortArray(value_count, CHECK_NULL);
+ code_section_table->set_length(value_count);
+
+ for (int i = 0; i < value_count; ++i) {
+ u2 value = cfs->get_u2(CHECK_(nullHandle));
+ code_section_table->short_at_put(i, value);
+ if (TraceRedefineClasses >= 4) {
+ tty->print_cr("Code section table at %d: %d", i, value);
+ }
+ }
} else {
// Skip unknown attributes
cfs->skip_u1(code_attribute_length, CHECK_(nullHandle));
@@ -1996,6 +2022,18 @@
}
#endif
+ // (tw) TODO: Get a different solution for the problem of method forward
+ // points and variable sized interpreter frames.
+ if (UseMethodForwardPoints) {
+ if (max_stack > MethodForwardPointsMaxStack) {
+ fatal(err_msg("Method has too large stack (%d), increase the value of MethodForwardPointsMaxStack (%d)", max_stack, MethodForwardPointsMaxStack));
+ }
+ if (max_locals > MethodForwardPointsMaxLocals) {
+ fatal(err_msg("Method has too many locals (%d), increase the value of MethodForwardPointsMaxLocals (%d)", max_stack, MethodForwardPointsMaxStack));
+ }
+ max_stack = MethodForwardPointsMaxStack;
+ max_locals = MethodForwardPointsMaxLocals;
+ }
// Fill in code attribute information
m->set_max_stack(max_stack);
m->set_max_locals(max_locals);
@@ -2010,6 +2048,8 @@
*/
m->set_exception_table(exception_handlers());
+ m->constMethod()->set_code_section_table(code_section_table());
+
// Copy byte codes
m->set_code(code_start);
@@ -2469,6 +2509,24 @@
"Invalid Deprecated classfile attribute length %u in class file %s",
attribute_length, CHECK);
}
+ // (tw) Check for deleted field attribute
+ } else if (tag == vmSymbols::tag_field_redefinition_policy()) {
+
+ char field_redefinition_policy = cfs->get_u1_fast();
+ k->set_field_redefinition_policy(field_redefinition_policy);
+
+ // (tw) Check for deleted static field attribute
+ } else if (tag == vmSymbols::tag_static_field_redefinition_policy()) {
+
+ char static_field_redefinition_policy = cfs->get_u1_fast();
+ k->set_static_field_redefinition_policy(static_field_redefinition_policy);
+
+ // (tw) Check for deleted method attribute
+ } else if (tag == vmSymbols::tag_method_redefinition_policy()) {
+
+ char method_redefinition_policy = cfs->get_u1_fast();
+ k->set_method_redefinition_policy(method_redefinition_policy);
+
} else if (_major_version >= JAVA_1_5_VERSION) {
if (tag == vmSymbols::tag_signature()) {
if (attribute_length != 2) {
@@ -2549,7 +2607,7 @@
}
-static void initialize_static_field(fieldDescriptor* fd, TRAPS) {
+void ClassFileParser::initialize_static_field(fieldDescriptor* fd, TRAPS) {
KlassHandle h_k (THREAD, fd->field_holder());
assert(h_k.not_null() && fd->is_static(), "just checking");
if (fd->has_initial_value()) {
@@ -2746,6 +2804,123 @@
(*next_nonstatic_oop_offset_ptr) += (extra * heapOopSize);
}
+// (tw) Finds the super symbols by reading the bytes of the class and returns
+// them in a growable array.
+void ClassFileParser::findSuperSymbols(symbolHandle name,
+ Handle class_loader,
+ Handle protection_domain,
+ KlassHandle old_klass,
+ GrowableArray &handles,
+ TRAPS) {
+
+ _cp_patches = NULL;
+ // So that JVMTI can cache class file in the state before retransformable agents
+ // have modified it
+ unsigned char *cached_class_file_bytes = NULL;
+
+ ClassFileStream* cfs = stream();
+
+ _has_finalizer = _has_empty_finalizer = _has_vanilla_constructor = false;
+
+ instanceKlassHandle nullHandle;
+
+ // Save the class file name for easier error message printing.
+ _class_name = name.not_null()? name : vmSymbolHandles::unknown_class_name();
+
+ cfs->guarantee_more(8, CHECK); // magic, major, minor
+ // Magic value
+ u4 magic = cfs->get_u4_fast();
+ if (magic != JAVA_CLASSFILE_MAGIC) {
+ // Invalid class file!
+ return;
+ }
+
+ // Version numbers
+ u2 minor_version = cfs->get_u2_fast();
+ u2 major_version = cfs->get_u2_fast();
+
+ // Check version numbers - we check this even with verifier off
+ if (!is_supported_version(major_version, minor_version)) {
+
+ // Unsupported version!
+ return;
+ }
+
+ _major_version = major_version;
+ _minor_version = minor_version;
+
+
+ // Check if verification needs to be relaxed for this class file
+ // Do not restrict it to jdk1.0 or jdk1.1 to maintain backward compatibility (4982376)
+ _relax_verify = Verifier::relax_verify_for(class_loader());
+ _need_verify = false;
+
+ // Constant pool
+ constantPoolHandle cp = parse_constant_pool(CHECK);
+ int cp_size = cp->length();
+
+ cfs->guarantee_more(8, CHECK); // flags, this_class, super_class, infs_len
+
+ // Access flags
+ AccessFlags access_flags;
+ jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_CLASS_MODIFIERS;
+
+ if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) {
+ // Set abstract bit for old class files for backward compatibility
+ flags |= JVM_ACC_ABSTRACT;
+ }
+ access_flags.set_flags(flags);
+
+ // This class and superclass
+ instanceKlassHandle super_klass;
+ u2 this_class_index = cfs->get_u2_fast();
+ check_property(
+ valid_cp_range(this_class_index, cp_size) &&
+ cp->tag_at(this_class_index).is_unresolved_klass(),
+ "Invalid this class index %u in constant pool in class file %s",
+ this_class_index, CHECK);
+
+ symbolHandle class_name (THREAD, cp->unresolved_klass_at(this_class_index));
+ assert(class_name.not_null(), "class_name can't be null");
+
+ // Update _class_name which could be null previously to be class_name
+ _class_name = class_name;
+
+ // (tw) DO NOT release all handles when parsing is done
+ {// HandleMark hm(THREAD);
+
+ // Checks if name in class file matches requested name
+ if (name.not_null() && class_name() != name()) {
+ return;
+ }
+
+ u2 super_class_index = cfs->get_u2_fast();
+
+ if (super_class_index != 0) {
+ symbolHandle super_class (THREAD, cp->klass_name_at(super_class_index));
+ handles.append(super_class);
+ } else {
+ // (tw) This redefinition must be for the Object class.
+ }
+
+ // Interfaces
+ u2 itfs_len = cfs->get_u2_fast();
+ objArrayHandle local_interfaces;
+ if (itfs_len == 0) {
+ local_interfaces = objArrayHandle(THREAD, Universe::the_empty_system_obj_array());
+ } else {
+ local_interfaces = parse_interfaces(cp, itfs_len, class_loader, protection_domain, _class_name, old_klass, CHECK);
+ }
+
+ for (int i=0; ilength(); i++) {
+ oop o = local_interfaces->obj_at(i);
+ symbolHandle interface_handle (THREAD, ((klassOop)o)->klass_part()->name());
+ handles.append(interface_handle);
+ }
+ }
+}
+
+
// Force MethodHandle.vmentry to be an unmanaged pointer.
// There is no way for a classfile to express this, so we must help it.
@@ -2819,6 +2994,7 @@
instanceKlassHandle ClassFileParser::parseClassFile(symbolHandle name,
Handle class_loader,
Handle protection_domain,
+ KlassHandle old_klass,
KlassHandle host_klass,
GrowableArray* cp_patches,
symbolHandle& parsed_name,
@@ -2847,10 +3023,13 @@
unsigned char* ptr = cfs->buffer();
unsigned char* end_ptr = cfs->buffer() + cfs->length();
+ bool pretend_new_universe = Thread::current()->pretend_new_universe();
+ Thread::current()->set_pretend_new_universe(false);
JvmtiExport::post_class_file_load_hook(name, class_loader, protection_domain,
&ptr, &end_ptr,
&cached_class_file_bytes,
&cached_class_file_length);
+ Thread::current()->set_pretend_new_universe(pretend_new_universe);
if (ptr != cfs->buffer()) {
// JVMTI agent has modified class file data.
@@ -3001,7 +3180,11 @@
// However, make sure it is not an array type.
bool is_array = false;
if (cp->tag_at(super_class_index).is_klass()) {
- super_klass = instanceKlassHandle(THREAD, cp->resolved_klass_at(super_class_index));
+ klassOop resolved_klass = cp->resolved_klass_at(super_class_index);
+ if (!old_klass.is_null()) {
+ resolved_klass = resolved_klass->klass_part()->newest_version();
+ }
+ super_klass = instanceKlassHandle(THREAD, resolved_klass);
if (_need_verify)
is_array = super_klass->oop_is_array();
} else if (_need_verify) {
@@ -3019,7 +3202,7 @@
if (itfs_len == 0) {
local_interfaces = objArrayHandle(THREAD, Universe::the_empty_system_obj_array());
} else {
- local_interfaces = parse_interfaces(cp, itfs_len, class_loader, protection_domain, _class_name, CHECK_(nullHandle));
+ local_interfaces = parse_interfaces(cp, itfs_len, class_loader, protection_domain, _class_name, old_klass, CHECK_(nullHandle));
}
// Fields (offsets are filled in later)
@@ -3063,7 +3246,9 @@
protection_domain,
true,
CHECK_(nullHandle));
-
+ if (!old_klass.is_null()) {
+ k = k->klass_part()->newest_version();
+ }
KlassHandle kh (THREAD, k);
super_klass = instanceKlassHandle(THREAD, kh());
if (LinkWellKnownClasses) // my super class is well known to me
@@ -3490,6 +3675,19 @@
rt = REF_NONE;
} else {
rt = super_klass->reference_type();
+
+ // (tw) With class redefinition, it can also happen that special classes are loaded.
+ if (name() == vmSymbols::java_lang_ref_Reference()) {
+ rt = REF_OTHER;
+ } else if (name() == vmSymbols::java_lang_ref_SoftReference()) {
+ rt = REF_SOFT;
+ } else if (name() == vmSymbols::java_lang_ref_WeakReference()) {
+ rt = REF_WEAK;
+ } else if (name() == vmSymbols::java_lang_ref_FinalReference()) {
+ rt = REF_FINAL;
+ } else if (name() == vmSymbols::java_lang_ref_PhantomReference()) {
+ rt = REF_PHANTOM;
+ }
}
// We can now create the basic klassOop for this klass
@@ -3591,7 +3789,7 @@
// Do final class setup
fill_oop_maps(this_klass, nonstatic_oop_map_count, nonstatic_oop_offsets, nonstatic_oop_counts);
- set_precomputed_flags(this_klass);
+ set_precomputed_flags(this_klass, old_klass);
// reinitialize modifiers, using the InnerClasses attribute
int computed_modifiers = this_klass->compute_modifier_flags(CHECK_(nullHandle));
@@ -3611,6 +3809,10 @@
check_illegal_static_method(this_klass, CHECK_(nullHandle));
}
+ if (rt == REF_OTHER) {
+ instanceRefKlass::update_nonstatic_oop_maps(ik);
+ }
+
ClassLoadingService::notify_class_loaded(instanceKlass::cast(this_klass()),
false /* not shared class */);
@@ -3753,7 +3955,7 @@
}
-void ClassFileParser::set_precomputed_flags(instanceKlassHandle k) {
+void ClassFileParser::set_precomputed_flags(instanceKlassHandle k, KlassHandle old_klass) {
klassOop super = k->super();
// Check if this klass has an empty finalize method (i.e. one with return bytecode only),
@@ -3761,7 +3963,9 @@
if (!_has_empty_finalizer) {
if (_has_finalizer ||
(super != NULL && super->klass_part()->has_finalizer())) {
- k->set_has_finalizer();
+ if (old_klass.is_null() || old_klass->has_finalizer()) {
+ k->set_has_finalizer();
+ }
}
}
@@ -3777,7 +3981,7 @@
// Check if this klass supports the java.lang.Cloneable interface
if (SystemDictionary::Cloneable_klass_loaded()) {
- if (k->is_subtype_of(SystemDictionary::Cloneable_klass())) {
+ if (k->is_subtype_of(SystemDictionary::Cloneable_klass()) || k->is_subtype_of(SystemDictionary::Cloneable_klass()->klass_part()->newest_version())) {
k->set_is_cloneable();
}
}
diff -r f5603a6e5042 src/share/vm/classfile/classFileParser.hpp
--- a/src/share/vm/classfile/classFileParser.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/classfile/classFileParser.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -65,6 +65,7 @@
Handle class_loader,
Handle protection_domain,
symbolHandle class_name,
+ KlassHandle old_klass,
TRAPS);
// Field parsing
@@ -135,7 +136,7 @@
unsigned int nonstatic_oop_map_count,
int* nonstatic_oop_offsets,
unsigned int* nonstatic_oop_counts);
- void set_precomputed_flags(instanceKlassHandle k);
+ void set_precomputed_flags(instanceKlassHandle k, KlassHandle old_klass);
objArrayHandle compute_transitive_interfaces(instanceKlassHandle super,
objArrayHandle local_ifs, TRAPS);
@@ -265,21 +266,33 @@
instanceKlassHandle parseClassFile(symbolHandle name,
Handle class_loader,
Handle protection_domain,
+ KlassHandle old_klass,
symbolHandle& parsed_name,
bool verify,
TRAPS) {
KlassHandle no_host_klass;
- return parseClassFile(name, class_loader, protection_domain, no_host_klass, NULL, parsed_name, verify, THREAD);
+ return parseClassFile(name, class_loader, protection_domain, old_klass, no_host_klass, NULL, parsed_name, verify, THREAD);
}
instanceKlassHandle parseClassFile(symbolHandle name,
Handle class_loader,
Handle protection_domain,
+ KlassHandle old_klass,
KlassHandle host_klass,
GrowableArray* cp_patches,
symbolHandle& parsed_name,
bool verify,
TRAPS);
+ static void initialize_static_field(fieldDescriptor* fd, TRAPS);
+
+ // (tw) Creates symbol handles for the super class and the interfaces
+ void findSuperSymbols(symbolHandle name,
+ Handle class_loader,
+ Handle protection_domain,
+ KlassHandle old_klass,
+ GrowableArray &handles,
+ TRAPS);
+
// Verifier checks
static void check_super_class_access(instanceKlassHandle this_klass, TRAPS);
static void check_super_interface_access(instanceKlassHandle this_klass, TRAPS);
diff -r f5603a6e5042 src/share/vm/classfile/classLoader.cpp
--- a/src/share/vm/classfile/classLoader.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/classfile/classLoader.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -164,7 +164,7 @@
if (file_handle != -1) {
// read contents into resource array
u1* buffer = NEW_RESOURCE_ARRAY(u1, st.st_size);
- size_t num_read = os::read(file_handle, (char*) buffer, st.st_size);
+ size_t num_read = hpi::read(file_handle, (char*) buffer, st.st_size);
// close file
hpi::close(file_handle);
// construct ClassFileStream
@@ -872,6 +872,7 @@
instanceKlassHandle result = parser.parseClassFile(h_name,
class_loader,
protection_domain,
+ KlassHandle(),
parsed_name,
false,
CHECK_(h));
diff -r f5603a6e5042 src/share/vm/classfile/dictionary.cpp
--- a/src/share/vm/classfile/dictionary.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/classfile/dictionary.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -149,8 +149,8 @@
int live_count = 0;
// RC_TRACE macro has an embedded ResourceMark
- RC_TRACE(0x00000200, ("unload: %s: previous version length=%d",
- ik->external_name(), ik->previous_versions()->length()));
+ TRACE_RC4("unload: %s: previous version length=%d",
+ ik->external_name(), ik->previous_versions()->length());
for (int i = ik->previous_versions()->length() - 1; i >= 0; i--) {
// check the previous versions array for GC'ed weak refs
@@ -175,7 +175,7 @@
gc_count++;
continue;
} else {
- RC_TRACE(0x00000200, ("unload: previous version @%d is alive", i));
+ TRACE_RC4("unload: previous version @%d is alive", i);
if (is_alive->do_object_b(pvcp)) {
live_count++;
} else {
@@ -185,8 +185,7 @@
GrowableArray* method_refs = pv_node->prev_EMCP_methods();
if (method_refs != NULL) {
- RC_TRACE(0x00000200, ("unload: previous methods length=%d",
- method_refs->length()));
+ TRACE_RC4("unload: previous methods length=%d", method_refs->length());
for (int j = method_refs->length() - 1; j >= 0; j--) {
jweak method_ref = method_refs->at(j);
assert(method_ref != NULL, "weak method ref was unexpectedly cleared");
@@ -203,19 +202,14 @@
JNIHandles::destroy_weak_global(method_ref);
method_refs->remove_at(j);
} else {
- // RC_TRACE macro has an embedded ResourceMark
- RC_TRACE(0x00000200,
- ("unload: %s(%s): prev method @%d in version @%d is alive",
- method->name()->as_C_string(),
- method->signature()->as_C_string(), j, i));
+ TRACE_RC4("unload: %s(%s): prev method @%d in version @%d is alive", method->name()->as_C_string(),
+ method->signature()->as_C_string(), j, i);
}
}
}
}
assert(ik->previous_versions()->length() == live_count, "sanity check");
- RC_TRACE(0x00000200,
- ("unload: previous version stats: live=%d, GC'ed=%d", live_count,
- gc_count));
+ TRACE_RC4("unload: previous version stats: live=%d, GC'ed=%d", live_count, gc_count);
}
// Non-unloadable classes were handled in always_strong_oops_do
@@ -321,6 +315,21 @@
}
}
+
+// (tw) Just the classes from defining class loaders
+void Dictionary::classes_do(ObjectClosure *closure) {
+ for (int index = 0; index < table_size(); index++) {
+ for (DictionaryEntry* probe = bucket(index);
+ probe != NULL;
+ probe = probe->next()) {
+ klassOop k = probe->klass();
+ if (probe->loader() == instanceKlass::cast(k)->class_loader()) {
+ closure->do_object(k);
+ }
+ }
+ }
+}
+
// Added for initialize_itable_for_klass to handle exceptions
// Just the classes from defining class loaders
void Dictionary::classes_do(void f(klassOop, TRAPS), TRAPS) {
@@ -428,6 +437,33 @@
add_entry(index, entry);
}
+// (tw) Updates the klass entry to point to the new klassOop. Necessary only for class redefinition.
+bool Dictionary::update_klass(int index, unsigned int hash, symbolHandle name, Handle loader, KlassHandle k, KlassHandle old_class) {
+
+ // There are several entries for the same class in the dictionary: One extra entry for each parent classloader of the classloader of the class.
+ bool found = false;
+ for (int index = 0; index < table_size(); index++) {
+ for (DictionaryEntry* entry = bucket(index); entry != NULL; entry = entry->next()) {
+ if (entry->klass() == old_class()) {
+ entry->set_literal(k());
+ found = true;
+ }
+ }
+ }
+
+ return found;
+}
+
+// (tw) Undo previous updates to the system dictionary
+void Dictionary::rollback_redefinition() {
+ for (int index = 0; index < table_size(); index++) {
+ for (DictionaryEntry* entry = bucket(index); entry != NULL; entry = entry->next()) {
+ if (entry->klass()->klass_part()->is_redefining()) {
+ entry->set_literal(entry->klass()->klass_part()->old_version());
+ }
+ }
+ }
+}
// This routine does not lock the system dictionary.
//
@@ -455,12 +491,21 @@
return NULL;
}
+klassOop Dictionary::intercept_for_version(klassOop k) {
+ if (k == NULL) return k;
+
+ if (k->klass_part()->is_redefining() && !Thread::current()->pretend_new_universe()) {
+ return k->klass_part()->old_version();
+ }
+
+ return k;
+}
klassOop Dictionary::find(int index, unsigned int hash, symbolHandle name,
Handle loader, Handle protection_domain, TRAPS) {
DictionaryEntry* entry = get_entry(index, hash, name, loader);
if (entry != NULL && entry->is_valid_protection_domain(protection_domain)) {
- return entry->klass();
+ return intercept_for_version(entry->klass());
} else {
return NULL;
}
@@ -473,7 +518,7 @@
assert (index == index_for(name, loader), "incorrect index?");
DictionaryEntry* entry = get_entry(index, hash, name, loader);
- return (entry != NULL) ? entry->klass() : (klassOop)NULL;
+ return intercept_for_version((entry != NULL) ? entry->klass() : (klassOop)NULL);
}
@@ -485,7 +530,7 @@
assert (index == index_for(name, Handle()), "incorrect index?");
DictionaryEntry* entry = get_entry(index, hash, name, Handle());
- return (entry != NULL) ? entry->klass() : (klassOop)NULL;
+ return intercept_for_version((entry != NULL) ? entry->klass() : (klassOop)NULL);
}
diff -r f5603a6e5042 src/share/vm/classfile/dictionary.hpp
--- a/src/share/vm/classfile/dictionary.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/classfile/dictionary.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -65,6 +65,10 @@
void add_klass(symbolHandle class_name, Handle class_loader,KlassHandle obj);
+ bool update_klass(int index, unsigned int hash, symbolHandle name, Handle loader, KlassHandle k, KlassHandle old_class);
+
+ void rollback_redefinition();
+
klassOop find_class(int index, unsigned int hash,
symbolHandle name, Handle loader);
@@ -81,6 +85,7 @@
void classes_do(void f(klassOop, TRAPS), TRAPS);
void classes_do(void f(klassOop, oop));
void classes_do(void f(klassOop, oop, TRAPS), TRAPS);
+ void classes_do(ObjectClosure *closure);
void methods_do(void f(methodOop));
@@ -97,6 +102,7 @@
bool do_unloading(BoolObjectClosure* is_alive);
// Protection domains
+ static klassOop intercept_for_version(klassOop k);
klassOop find(int index, unsigned int hash, symbolHandle name,
Handle loader, Handle protection_domain, TRAPS);
bool is_valid_protection_domain(int index, unsigned int hash,
diff -r f5603a6e5042 src/share/vm/classfile/javaClasses.cpp
--- a/src/share/vm/classfile/javaClasses.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/classfile/javaClasses.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -1537,7 +1537,7 @@
klassOop klass = SystemDictionary::reflect_Method_klass();
// This class is eagerly initialized during VM initialization, since we keep a refence
// to one of the methods
- assert(instanceKlass::cast(klass)->is_initialized(), "must be initialized");
+ assert(instanceKlass::cast(klass)->is_initialized() || klass->klass_part()->old_version() != NULL, "must be initialized");
return instanceKlass::cast(klass)->allocate_instance_handle(CHECK_NH);
}
diff -r f5603a6e5042 src/share/vm/classfile/javaClasses.hpp
--- a/src/share/vm/classfile/javaClasses.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/classfile/javaClasses.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -124,6 +124,7 @@
class java_lang_Class : AllStatic {
friend class VMStructs;
+ friend class VM_RedefineClasses;
private:
// The fake offsets are added by the class loader when java.lang.Class is loaded
diff -r f5603a6e5042 src/share/vm/classfile/loaderConstraints.cpp
--- a/src/share/vm/classfile/loaderConstraints.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/classfile/loaderConstraints.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -449,7 +449,7 @@
if (k != NULL) {
// We found the class in the system dictionary, so we should
// make sure that the klassOop matches what we already have.
- guarantee(k == probe->klass(), "klass should be in dictionary");
+ guarantee(k == probe->klass()->klass_part()->newest_version(), "klass should be in dictionary");
} else {
// If we don't find the class in the system dictionary, it
// has to be in the placeholders table.
diff -r f5603a6e5042 src/share/vm/classfile/loaderConstraints.hpp
--- a/src/share/vm/classfile/loaderConstraints.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/classfile/loaderConstraints.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -99,7 +99,7 @@
klassOop klass() { return (klassOop)literal(); }
klassOop* klass_addr() { return (klassOop*)literal_addr(); }
- void set_klass(klassOop k) { set_literal(k); }
+ void set_klass(klassOop k) { set_literal(k); assert(k == NULL || !k->klass_part()->is_redefining(), "just checking"); }
LoaderConstraintEntry* next() {
return (LoaderConstraintEntry*)HashtableEntry::next();
diff -r f5603a6e5042 src/share/vm/classfile/systemDictionary.cpp
--- a/src/share/vm/classfile/systemDictionary.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/classfile/systemDictionary.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -119,6 +119,7 @@
// can return a null klass
klass = handle_resolution_exception(class_name, class_loader, protection_domain, throw_error, k_h, THREAD);
}
+ assert(klass == NULL || klass->klass_part()->is_newest_version() || klass->klass_part()->newest_version()->klass_part()->is_redefining(), "must be");
return klass;
}
@@ -161,7 +162,8 @@
// Forwards to resolve_instance_class_or_null
klassOop SystemDictionary::resolve_or_null(symbolHandle class_name, Handle class_loader, Handle protection_domain, TRAPS) {
- assert(!THREAD->is_Compiler_thread(), "Can not load classes with the Compiler thread");
+ // (tw) Check if this relaxing of the condition is correct? Test case hs203t004 failing otherwise.
+ assert(!THREAD->is_Compiler_thread() || JvmtiThreadState::state_for(JavaThread::current())->get_class_being_redefined() != NULL, "Can not load classes with the Compiler thread");
if (FieldType::is_array(class_name())) {
return resolve_array_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
} else {
@@ -976,6 +978,7 @@
instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name,
class_loader,
protection_domain,
+ KlassHandle(),
host_klass,
cp_patches,
parsed_name,
@@ -1036,8 +1039,15 @@
Handle protection_domain,
ClassFileStream* st,
bool verify,
+ KlassHandle old_class,
TRAPS) {
+ bool redefine_classes_locked = false;
+ if (!Thread::current()->redefine_classes_mutex()->owned_by_self()) {
+ Thread::current()->redefine_classes_mutex()->lock();
+ redefine_classes_locked = true;
+ }
+
// Classloaders that support parallelism, e.g. bootstrap classloader,
// or all classloaders with UnsyncloadClass do not acquire lock here
bool DoObjectLock = true;
@@ -1065,9 +1075,14 @@
instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name,
class_loader,
protection_domain,
+ old_class,
parsed_name,
verify,
THREAD);
+ if (!old_class.is_null() && !k.is_null()) {
+ k->set_redefining(true);
+ k->set_old_version(old_class());
+ }
const char* pkg = "java/";
if (!HAS_PENDING_EXCEPTION &&
@@ -1103,13 +1118,18 @@
// Add class just loaded
// If a class loader supports parallel classloading handle parallel define requests
// find_or_define_instance_class may return a different instanceKlass
- if (is_parallelCapable(class_loader)) {
+ // (tw) TODO: for class redefinition the parallel version does not work, check if this is a problem?
+ if (is_parallelCapable(class_loader) && old_class.is_null()) {
k = find_or_define_instance_class(class_name, class_loader, k, THREAD);
} else {
- define_instance_class(k, THREAD);
+ define_instance_class(k, old_class, THREAD);
}
}
+ if (redefine_classes_locked) {
+ Thread::current()->redefine_classes_mutex()->unlock();
+ }
+
// If parsing the class file or define_instance_class failed, we
// need to remove the placeholder added on our behalf. But we
// must make sure parsed_name is valid first (it won't be if we had
@@ -1138,7 +1158,7 @@
MutexLocker mu(SystemDictionary_lock, THREAD);
oop check = find_class_or_placeholder(parsed_name, class_loader);
- assert(check == k(), "should be present in the dictionary");
+ assert((check == k() && !k->is_redefining()) || (k->is_redefining() && check == k->old_version()), "should be present in the dictionary");
oop check2 = find_class_or_placeholder(h_name, h_loader);
assert(check == check2, "name inconsistancy in SystemDictionary");
@@ -1424,7 +1444,11 @@
}
}
-void SystemDictionary::define_instance_class(instanceKlassHandle k, TRAPS) {
+void SystemDictionary::rollback_redefinition() {
+ dictionary()->rollback_redefinition();
+}
+
+void SystemDictionary::define_instance_class(instanceKlassHandle k, KlassHandle old_class, TRAPS) {
Handle class_loader_h(THREAD, k->class_loader());
@@ -1451,13 +1475,23 @@
symbolHandle name_h(THREAD, k->name());
unsigned int d_hash = dictionary()->compute_hash(name_h, class_loader_h);
int d_index = dictionary()->hash_to_index(d_hash);
- check_constraints(d_index, d_hash, k, class_loader_h, true, CHECK);
+
+ // (tw) Update version of the klassOop in the system dictionary
+ // TODO: Check for thread safety!
+ if (!old_class.is_null()) {
+ bool ok = dictionary()->update_klass(d_index, d_hash, name_h, class_loader_h, k, old_class);
+ assert (ok, "must have found old class and updated!");
+ }
+ check_constraints(d_index, d_hash, k, class_loader_h, old_class.is_null(), CHECK);
+
+ if(!old_class.is_null() && TraceRedefineClasses >= 3){ tty->print_cr("Class has been updated!"); }
// Register class just loaded with class loader (placed in Vector)
// Note we do this before updating the dictionary, as this can
// fail with an OutOfMemoryError (if it does, we will *not* put this
// class in the dictionary and will not update the class hierarchy).
- if (k->class_loader() != NULL) {
+ // (tw) Only register if not redefining a class.
+ if (k->class_loader() != NULL && old_class.is_null()) {
methodHandle m(THREAD, Universe::loader_addClass_method());
JavaValue result(T_VOID);
JavaCallArguments args(class_loader_h);
@@ -1483,8 +1517,9 @@
}
k->eager_initialize(THREAD);
+ // (tw) Only notify jvmti if not redefining a class.
// notify jvmti
- if (JvmtiExport::should_post_class_load()) {
+ if (JvmtiExport::should_post_class_load() && old_class.is_null()) {
assert(THREAD->is_Java_thread(), "thread->is_Java_thread()");
JvmtiExport::post_class_load((JavaThread *) THREAD, k());
@@ -1558,7 +1593,7 @@
}
}
- define_instance_class(k, THREAD);
+ define_instance_class(k, KlassHandle(), THREAD);
Handle linkage_exception = Handle(); // null handle
@@ -1698,6 +1733,14 @@
Universe::flush_dependents_on(k);
}
+// (tw) Remove from hierarchy - Undo add_to_hierarchy.
+void SystemDictionary::remove_from_hierarchy(instanceKlassHandle k) {
+ assert(k.not_null(), "just checking");
+
+ k->remove_from_sibling_list();
+
+ // TODO: Remove from interfaces.
+}
// ----------------------------------------------------------------------------
// GC support
@@ -1778,10 +1821,8 @@
}
-void SystemDictionary::preloaded_oops_do(OopClosure* f) {
- f->do_oop((oop*) &wk_klass_name_limits[0]);
- f->do_oop((oop*) &wk_klass_name_limits[1]);
-
+// (tw) Iterate over all pre-loaded classes in the dictionary.
+void SystemDictionary::preloaded_classes_do(OopClosure *f) {
for (int k = (int)FIRST_WKID; k < (int)WKID_LIMIT; k++) {
f->do_oop((oop*) &_well_known_klasses[k]);
}
@@ -1795,6 +1836,26 @@
}
}
+ // TODO: Check if we need to call FilterFieldsMap
+}
+
+void SystemDictionary::preloaded_oops_do(OopClosure* f) {
+ f->do_oop((oop*) &wk_klass_name_limits[0]);
+ f->do_oop((oop*) &wk_klass_name_limits[1]);
+
+ for (int k = (int)FIRST_WKID; k < (int)WKID_LIMIT; k++) {
+ f->do_oop((oop*) &_well_known_klasses[k]);
+ }
+
+ {
+ for (int i = 0; i < T_VOID+1; i++) {
+ if (_box_klasses[i] != NULL) {
+ assert(i >= T_BOOLEAN, "checking");
+ f->do_oop((oop*) &_box_klasses[i]);
+ }
+ }
+ }
+
// The basic type mirrors would have already been processed in
// Universe::oops_do(), via a call to shared_oops_do(), so should
// not be processed again.
@@ -1813,6 +1874,11 @@
dictionary()->classes_do(f);
}
+// (tw) Iterate over all classes in the dictionary.
+void SystemDictionary::classes_do(ObjectClosure *closure) {
+ dictionary()->classes_do(closure);
+}
+
// Added for initialize_itable_for_klass
// Just the classes from defining class loaders
// Don't iterate over placeholders
@@ -1962,7 +2028,9 @@
// Preload ref klasses and set reference types
instanceKlass::cast(WK_KLASS(Reference_klass))->set_reference_type(REF_OTHER);
- instanceRefKlass::update_nonstatic_oop_maps(WK_KLASS(Reference_klass));
+
+ // (tw) This is now done in parseClassFile in order to support class redefinition
+ // instanceRefKlass::update_nonstatic_oop_maps(WK_KLASS(Reference_klass));
initialize_wk_klasses_through(WK_KLASS_ENUM_NAME(PhantomReference_klass), scan, CHECK);
instanceKlass::cast(WK_KLASS(SoftReference_klass))->set_reference_type(REF_SOFT);
@@ -2064,7 +2132,11 @@
// also holds array classes
assert(check->klass_part()->oop_is_instance(), "noninstance in systemdictionary");
- if ((defining == true) || (k() != check)) {
+ if ((defining == true) && ((k() != check) && k->old_version() != check)) {
+ ResourceMark rm(Thread::current());
+ tty->print_cr("(%d / %d) (%s/%s)", k->revision_number(), check->klass_part()->revision_number(), k->name()->as_C_string(), check->klass_part()->name()->as_C_string());
+ k()->print();
+ check->print();
linkage_error = "loader (instance of %s): attempted duplicate class "
"definition for name: \"%s\"";
} else {
@@ -2718,8 +2790,9 @@
if (probe == NULL) {
probe = SystemDictionary::find_shared_class(class_name);
}
+ // (tw) Relaxed assertion to allow different class versions. Also allow redefining classes lie around (because of rollback).
guarantee(probe != NULL &&
- (!probe->is_klass() || probe == obj()),
+ (!probe->is_klass() || (!((klassOop)(obj()))->klass_part()->is_redefining()) || ((klassOop)probe)->klass_part()->is_same_or_older_version((klassOop)(obj()))) || ((klassOop)(obj()))->klass_part()->is_redefining(),
"Loaded klasses should be in SystemDictionary");
}
diff -r f5603a6e5042 src/share/vm/classfile/systemDictionary.hpp
--- a/src/share/vm/classfile/systemDictionary.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/classfile/systemDictionary.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -260,7 +260,7 @@
// Resolve from stream (called by jni_DefineClass and JVM_DefineClass)
static klassOop resolve_from_stream(symbolHandle class_name, Handle class_loader,
Handle protection_domain,
- ClassFileStream* st, bool verify, TRAPS);
+ ClassFileStream* st, bool verify, KlassHandle old_class, TRAPS);
// Lookup an already loaded class. If not found NULL is returned.
static klassOop find(symbolHandle class_name, Handle class_loader, Handle protection_domain, TRAPS);
@@ -304,6 +304,8 @@
// Iterate over all klasses in dictionary
// Just the classes from defining class loaders
static void classes_do(void f(klassOop));
+ static void classes_do(ObjectClosure *closure);
+ static void preloaded_classes_do(OopClosure *closure);
// Added for initialize_itable_for_klass to handle exceptions
static void classes_do(void f(klassOop, TRAPS), TRAPS);
// All classes, and their class loaders
@@ -408,6 +410,8 @@
initialize_wk_klasses_until((WKID) limit, start_id, THREAD);
}
+ static void rollback_redefinition();
+
public:
#define WK_KLASS_DECLARE(name, ignore_symbol, option) \
static klassOop name() { return check_klass_##option(_well_known_klasses[WK_KLASS_ENUM_NAME(name)]); }
@@ -576,7 +580,7 @@
// after waiting, but before reentering SystemDictionary_lock
// to preserve lock order semantics.
static void double_lock_wait(Handle lockObject, TRAPS);
- static void define_instance_class(instanceKlassHandle k, TRAPS);
+ static void define_instance_class(instanceKlassHandle k, KlassHandle old_class, TRAPS);
static instanceKlassHandle find_or_define_instance_class(symbolHandle class_name,
Handle class_loader,
instanceKlassHandle k, TRAPS);
@@ -595,6 +599,11 @@
// Setup link to hierarchy
static void add_to_hierarchy(instanceKlassHandle k, TRAPS);
+public:
+
+ // Remove link to hierarchy
+ static void remove_from_hierarchy(instanceKlassHandle k);
+
private:
// We pass in the hashtable index so we can calculate it outside of
// the SystemDictionary_lock.
diff -r f5603a6e5042 src/share/vm/classfile/verifier.cpp
--- a/src/share/vm/classfile/verifier.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/classfile/verifier.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -70,7 +70,7 @@
return !need_verify;
}
-bool Verifier::verify(instanceKlassHandle klass, Verifier::Mode mode, bool should_verify_class, TRAPS) {
+bool Verifier::verify(instanceKlassHandle klass, Verifier::Mode mode, bool should_verify_class, bool may_use_old_verifier, TRAPS) {
ResourceMark rm(THREAD);
HandleMark hm;
@@ -94,17 +94,19 @@
split_verifier.verify_class(THREAD);
exception_name = split_verifier.result();
if (klass->major_version() < NOFAILOVER_MAJOR_VERSION &&
- FailOverToOldVerifier && !HAS_PENDING_EXCEPTION &&
+ FailOverToOldVerifier && may_use_old_verifier && !HAS_PENDING_EXCEPTION &&
(exception_name == vmSymbols::java_lang_VerifyError() ||
exception_name == vmSymbols::java_lang_ClassFormatError())) {
if (TraceClassInitialization) {
tty->print_cr(
"Fail over class verification to old verifier for: %s", klassName);
}
+ assert(may_use_old_verifier, "");
exception_name = inference_verify(
klass, message_buffer, message_buffer_len, THREAD);
}
} else {
+ assert(may_use_old_verifier, "");
exception_name = inference_verify(
klass, message_buffer, message_buffer_len, THREAD);
}
@@ -119,6 +121,9 @@
}
tty->print_cr("End class verification for: %s", klassName);
}
+ } else if (TraceClassInitialization) {
+ // (tw) Output not verified classes
+ tty->print_cr("Class %s was not verified", klassName);
}
if (HAS_PENDING_EXCEPTION) {
@@ -170,7 +175,7 @@
// NOTE: this is called too early in the bootstrapping process to be
// guarded by Universe::is_gte_jdk14x_version()/UseNewReflection.
(refl_magic_klass == NULL ||
- !klass->is_subtype_of(refl_magic_klass) ||
+ !(klass->is_subtype_of(refl_magic_klass) || klass->is_subtype_of(refl_magic_klass->klass_part()->newest_version())) ||
VerifyReflectionBytecodes)
);
}
@@ -240,7 +245,7 @@
ClassVerifier::ClassVerifier(
instanceKlassHandle klass, char* msg, size_t msg_len, TRAPS)
: _thread(THREAD), _exception_type(symbolHandle()), _message(msg),
- _message_buffer_len(msg_len), _klass(klass) {
+ _message_buffer_len(msg_len), _klass(klass->newest_version()), _klass_to_verify(klass) {
_this_type = VerificationType::reference_type(klass->name());
}
@@ -253,7 +258,7 @@
_klass->external_name());
}
- objArrayHandle methods(THREAD, _klass->methods());
+ objArrayHandle methods(THREAD, _klass_to_verify->methods());
int num_methods = methods->length();
for (int index = 0; index < num_methods; index++) {
@@ -2043,7 +2048,10 @@
VerificationType stack_object_type =
current_frame->pop_stack(ref_class_type, CHECK_VERIFY(this));
if (current_type() != stack_object_type) {
- assert(cp->cache() == NULL, "not rewritten yet");
+
+ // (tw) TODO: Check if relaxing the following assertion is correct. For class redefinition we might call the verifier twice.
+ //assert(cp->cache() == NULL, "not rewritten yet");
+
symbolHandle ref_class_name = symbolHandle(THREAD,
cp->klass_name_at(cp->klass_ref_index_at(index)));
// See the comments in verify_field_instructions() for
diff -r f5603a6e5042 src/share/vm/classfile/verifier.hpp
--- a/src/share/vm/classfile/verifier.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/classfile/verifier.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -37,7 +37,7 @@
* Otherwise, no exception is thrown and the return indicates the
* error.
*/
- static bool verify(instanceKlassHandle klass, Mode mode, bool should_verify_class, TRAPS);
+ static bool verify(instanceKlassHandle klass, Mode mode, bool should_verify_class, bool may_use_old_verifier, TRAPS);
// Return false if the class is loaded by the bootstrap loader,
// or if defineClass was called requesting skipping verification
@@ -88,7 +88,10 @@
char* _message;
size_t _message_buffer_len;
+public:
void verify_method(methodHandle method, TRAPS);
+
+private:
char* generate_code_data(methodHandle m, u4 code_length, TRAPS);
void verify_exception_handler_table(u4 code_length, char* code_data, int& min, int& max, TRAPS);
void verify_local_variable_table(u4 code_length, char* code_data, TRAPS);
@@ -157,6 +160,7 @@
bool name_in_supers(symbolOop ref_name, instanceKlassHandle current);
+ instanceKlassHandle _klass_to_verify;
instanceKlassHandle _klass; // the class being verified
methodHandle _method; // current method being verified
VerificationType _this_type; // the verification type of the current class
diff -r f5603a6e5042 src/share/vm/classfile/vmSymbols.hpp
--- a/src/share/vm/classfile/vmSymbols.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/classfile/vmSymbols.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -127,6 +127,10 @@
template(tag_runtime_invisible_parameter_annotations,"RuntimeInvisibleParameterAnnotations") \
template(tag_annotation_default, "AnnotationDefault") \
template(tag_enclosing_method, "EnclosingMethod") \
+ template(tag_static_field_redefinition_policy, "StaticFieldRedefinitionPolicy") \
+ template(tag_field_redefinition_policy, "FieldRedefinitionPolicy") \
+ template(tag_method_redefinition_policy, "MethodRedefinitionPolicy") \
+ template(tag_code_sections, "CodeSections") \
\
/* exception klasses: at least all exceptions thrown by the VM have entries here */ \
template(java_lang_ArithmeticException, "java/lang/ArithmeticException") \
@@ -349,6 +353,9 @@
template(erasedType_name, "erasedType") \
template(genericInvoker_name, "genericInvoker") \
template(append_name, "append") \
+ /* mutator in case of class redefinition */ \
+ template(static_transformer_name, "$staticTransformer") \
+ template(transformer_name, "$transformer") \
\
/* non-intrinsic name/signature pairs: */ \
template(register_method_name, "register") \
diff -r f5603a6e5042 src/share/vm/code/nmethod.cpp
--- a/src/share/vm/code/nmethod.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/code/nmethod.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -2055,15 +2055,14 @@
methodOop method = deps.method_argument(0);
for (int j = 0; j < dependee_methods->length(); j++) {
if ((methodOop) dependee_methods->obj_at(j) == method) {
- // RC_TRACE macro has an embedded ResourceMark
- RC_TRACE(0x01000000,
- ("Found evol dependency of nmethod %s.%s(%s) compile_id=%d on method %s.%s(%s)",
+ ResourceMark rm(Thread::current());
+ TRACE_RC3("Found evol dependency of nmethod %s.%s(%s) compile_id=%d on method %s.%s(%s)",
_method->method_holder()->klass_part()->external_name(),
_method->name()->as_C_string(),
_method->signature()->as_C_string(), compile_id(),
method->method_holder()->klass_part()->external_name(),
method->name()->as_C_string(),
- method->signature()->as_C_string()));
+ method->signature()->as_C_string());
if (TraceDependencies || LogCompilation)
deps.log_dependency(dependee);
return true;
diff -r f5603a6e5042 src/share/vm/compiler/compileBroker.cpp
--- a/src/share/vm/compiler/compileBroker.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/compiler/compileBroker.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -1027,6 +1027,7 @@
}
// RedefineClasses() has replaced this method; just return
+ // (tw) This is important for the new version of hotswapping: Old code will only execute properly in the interpreter!
if (method->is_old()) {
return NULL;
}
@@ -1352,6 +1353,8 @@
// Never compile a method if breakpoints are present in it
if (method()->number_of_breakpoints() == 0) {
+ thread->compilation_mutex()->lock();
+ thread->set_should_bailout(false);
// Compile the method.
if ((UseCompiler || AlwaysCompileLoopMethods) && CompileBroker::should_compile_new_jobs()) {
#ifdef COMPILER1
@@ -1375,6 +1378,7 @@
// After compilation is disabled, remove remaining methods from queue
method->clear_queued_for_compilation();
}
+ thread->compilation_mutex()->unlock();
}
}
}
@@ -1535,7 +1539,11 @@
//assert(false, "compiler should always document failure");
// The compiler elected, without comment, not to register a result.
// Do not attempt further compilations of this method.
- ci_env.record_method_not_compilable("compile failed");
+ if (((CompilerThread *)Thread::current())->should_bailout()) {
+ ci_env.record_failure("compile externally aborted");
+ } else {
+ ci_env.record_method_not_compilable("compile failed");
+ }
}
if (ci_env.failing()) {
@@ -1882,3 +1890,15 @@
st->cr();
#endif
}
+
+// (tw) Clean up compiler interface after a class redefinition step
+void CompileBroker::cleanup_after_redefinition() {
+ int num_threads = _method_threads->length();
+
+ ciObjectFactory::sort_ci_objects(ciObjectFactory::_shared_ci_objects);
+ for (int i=0; iat(i)->env() != NULL && _method_threads->at(i)->env() != (ciEnv *)badAddress) {
+ _method_threads->at(i)->env()->cleanup_after_redefinition();
+ }
+ }
+}
\ No newline at end of file
diff -r f5603a6e5042 src/share/vm/compiler/compileBroker.hpp
--- a/src/share/vm/compiler/compileBroker.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/compiler/compileBroker.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -379,4 +379,6 @@
static void print_last_compile();
static void print_compiler_threads_on(outputStream* st);
+
+ static void cleanup_after_redefinition();
};
diff -r f5603a6e5042 src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp
--- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -140,6 +140,13 @@
}
}
+
+HeapWord* CompactibleFreeListSpace::forward_compact_top(size_t size,
+ CompactPoint* cp, HeapWord* compact_top) {
+ ShouldNotReachHere();
+ return NULL;
+}
+
// Like CompactibleSpace forward() but always calls cross_threshold() to
// update the block offset table. Removed initialize_threshold call because
// CFLS does not use a block offset array for contiguous spaces.
diff -r f5603a6e5042 src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp
--- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -140,6 +140,7 @@
// Support for compacting cms
HeapWord* cross_threshold(HeapWord* start, HeapWord* end);
+ HeapWord* forward_compact_top(size_t size, CompactPoint* cp, HeapWord* compact_top);
HeapWord* forward(oop q, size_t size, CompactPoint* cp, HeapWord* compact_top);
// Initialization helpers.
diff -r f5603a6e5042 src/share/vm/gc_implementation/includeDB_gc_g1
--- a/src/share/vm/gc_implementation/includeDB_gc_g1 Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/gc_implementation/includeDB_gc_g1 Fri Dec 17 13:23:04 2010 +0100
@@ -330,8 +330,6 @@
ptrQueue.hpp allocation.hpp
ptrQueue.hpp sizes.hpp
-ptrQueue.inline.hpp ptrQueue.hpp
-
satbQueue.cpp allocation.inline.hpp
satbQueue.cpp mutexLocker.hpp
satbQueue.cpp satbQueue.hpp
diff -r f5603a6e5042 src/share/vm/gc_implementation/shared/markSweep.cpp
--- a/src/share/vm/gc_implementation/shared/markSweep.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/gc_implementation/shared/markSweep.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -25,6 +25,8 @@
#include "incls/_precompiled.incl"
#include "incls/_markSweep.cpp.incl"
+GrowableArray* MarkSweep::_rescued_oops = NULL;
+
Stack MarkSweep::_marking_stack;
Stack MarkSweep::_revisit_mdo_stack;
Stack MarkSweep::_revisit_klass_stack;
@@ -345,3 +347,86 @@
}
#endif
+
+// (tw) Copy the rescued objects to their destination address after compaction.
+void MarkSweep::copy_rescued_objects_back() {
+
+ if (_rescued_oops != NULL) {
+
+ for (int i=0; i<_rescued_oops->length(); i++) {
+ oop rescued_obj = _rescued_oops->at(i);
+
+ int size = rescued_obj->size();
+ oop new_obj = rescued_obj->forwardee();
+
+ if (rescued_obj->blueprint()->new_version() != NULL) {
+ MarkSweep::update_fields(rescued_obj, new_obj);
+ } else {
+ Copy::aligned_disjoint_words((HeapWord*)rescued_obj, (HeapWord*)new_obj, size);
+ }
+
+ FREE_RESOURCE_ARRAY(HeapWord, rescued_obj, size);
+
+ new_obj->init_mark();
+ assert(new_obj->is_oop(), "must be a valid oop");
+ }
+ _rescued_oops->clear();
+ _rescued_oops = NULL;
+ }
+}
+
+// (tw) Update instances of a class whose fields changed.
+void MarkSweep::update_fields(oop q, oop new_location) {
+
+ assert(q->blueprint()->new_version() != NULL, "class of old object must have new version");
+
+ klassOop old_klass_oop = q->klass();
+ klassOop new_klass_oop = q->blueprint()->new_version();
+
+ instanceKlass *old_klass = instanceKlass::cast(old_klass_oop);
+ instanceKlass *new_klass = instanceKlass::cast(new_klass_oop);
+
+ int size = q->size_given_klass(old_klass);
+ int new_size = q->size_given_klass(new_klass);
+
+ oop tmp_obj = q;
+
+ if (new_klass_oop->klass_part()->is_copying_backwards()) {
+ if (((HeapWord *)q >= (HeapWord *)new_location && (HeapWord *)q < (HeapWord *)new_location + new_size) ||
+ ((HeapWord *)new_location >= (HeapWord *)q && (HeapWord *)new_location < (HeapWord *)q + size)) {
+ tmp_obj = (oop)resource_allocate_bytes(size * HeapWordSize);
+ Copy::aligned_disjoint_words((HeapWord*)q, (HeapWord*)tmp_obj, size);
+ }
+ }
+
+ int *cur = new_klass_oop->klass_part()->update_information();
+
+ tmp_obj->set_klass_no_check(new_klass_oop);
+
+ if (cur == NULL) {
+ assert(size == new_size, "just checking");
+ Copy::conjoint_words(((HeapWord *)tmp_obj), ((HeapWord *)new_location), size);
+ } else {
+ int destOffset = 0;
+ while (*cur != 0) {
+ if (*cur > 0) {
+ int size = *cur;
+ cur++;
+ int offset = *cur;
+ Copy::conjoint_jbytes(((char *)tmp_obj) + offset, ((char *)new_location) + destOffset, size);
+ destOffset += size;
+ cur++;
+ } else {
+ assert(*cur < 0, "");
+ int skip = -*cur;
+ Copy::fill_to_bytes(((char*)new_location) + destOffset, skip, 0);
+ destOffset += skip;
+ cur++;
+ }
+ }
+ }
+
+ if (tmp_obj != q) {
+ FREE_RESOURCE_ARRAY(HeapWord, tmp_obj, size);
+ }
+}
diff -r f5603a6e5042 src/share/vm/gc_implementation/shared/markSweep.hpp
--- a/src/share/vm/gc_implementation/shared/markSweep.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/gc_implementation/shared/markSweep.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -103,8 +103,12 @@
friend class AdjustPointerClosure;
friend class KeepAliveClosure;
friend class VM_MarkSweep;
+ friend class GenMarkSweep;
friend void marksweep_init();
+public:
+ static GrowableArray* _rescued_oops;
+
//
// Vars
//
@@ -190,6 +194,8 @@
template static inline void mark_and_push(T* p);
static inline void push_objarray(oop obj, size_t index);
+ static void copy_rescued_objects_back();
+ static void update_fields(oop q, oop new_location);
static void follow_stack(); // Empty marking stack.
static void preserve_mark(oop p, markOop mark);
diff -r f5603a6e5042 src/share/vm/includeDB_core
--- a/src/share/vm/includeDB_core Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/includeDB_core Fri Dec 17 13:23:04 2010 +0100
@@ -1782,6 +1782,7 @@
genMarkSweep.cpp thread_.inline.hpp
genMarkSweep.cpp vmSymbols.hpp
genMarkSweep.cpp vmThread.hpp
+genMarkSweep.cpp jvmtiRedefineClassesTrace.hpp
genMarkSweep.hpp markSweep.hpp
@@ -2625,6 +2626,7 @@
klassVtable.hpp handles.hpp
klassVtable.hpp oopsHierarchy.hpp
+linkResolver.cpp jvmtiRedefineClassesTrace.hpp
linkResolver.cpp bytecode.hpp
linkResolver.cpp collectedHeap.inline.hpp
linkResolver.cpp compilationPolicy.hpp
@@ -3860,6 +3862,7 @@
space.cpp systemDictionary.hpp
space.cpp universe.inline.hpp
space.cpp vmSymbols.hpp
+space.cpp jvmtiRedefineClassesTrace.hpp
space.hpp allocation.hpp
space.hpp blockOffsetTable.hpp
diff -r f5603a6e5042 src/share/vm/includeDB_jvmti
--- a/src/share/vm/includeDB_jvmti Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/includeDB_jvmti Fri Dec 17 13:23:04 2010 +0100
@@ -231,7 +231,10 @@
jvmtiRedefineClasses.cpp systemDictionary.hpp
jvmtiRedefineClasses.cpp universe.inline.hpp
jvmtiRedefineClasses.cpp verifier.hpp
+jvmtiRedefineClasses.cpp jvmtiClassFileReconstituter.hpp
+jvmtiRedefineClasses.cpp compileBroker.hpp
+jvmtiRedefineClasses.hpp vmGCOperations.hpp
jvmtiRedefineClasses.hpp jvmtiEnv.hpp
jvmtiRedefineClasses.hpp jvmtiRedefineClassesTrace.hpp
jvmtiRedefineClasses.hpp objArrayKlass.hpp
diff -r f5603a6e5042 src/share/vm/interpreter/interpreterRuntime.cpp
--- a/src/share/vm/interpreter/interpreterRuntime.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/interpreter/interpreterRuntime.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -366,7 +366,7 @@
assert(h_exception.not_null(), "NULL exceptions should be handled by athrow");
assert(h_exception->is_oop(), "just checking");
// Check that exception is a subclass of Throwable, otherwise we have a VerifyError
- if (!(h_exception->is_a(SystemDictionary::Throwable_klass()))) {
+ if (!(h_exception->is_a(SystemDictionary::Throwable_klass()->klass_part()->newest_version())) && !(h_exception->is_a(SystemDictionary::Throwable_klass()))) {
if (ExitVMOnVerifyError) vm_exit(-1);
ShouldNotReachHere();
}
@@ -622,6 +622,81 @@
JvmtiExport::post_raw_breakpoint(thread, method, bcp);
IRT_END
+// (tw) Correctly resolve method when running old code.
+IRT_ENTRY(void, InterpreterRuntime::forward_method(JavaThread *thread))
+ {
+ MonitorLockerEx ml(RedefinitionSync_lock);
+ while (Threads::wait_at_instrumentation_entry()) {
+ ml.wait();
+ }
+ }
+ frame f = last_frame(thread);
+ methodOop m = f.interpreter_frame_method();
+ methodOop forward_method = m->forward_method();
+ if (forward_method != NULL) {
+ int bci = f.interpreter_frame_bci();
+
+ if (TraceRedefineClasses >= 3) {
+ tty->print_cr("Executing NOP in method %s at bci %d %d", m->name()->as_C_string(), bci, m->is_in_code_section(bci + 1));
+ }
+
+ int next_bci = bci - 1;
+ // First try bci before NOP.
+ if (!m->is_in_code_section(next_bci)) {
+ // Try bci after NOP.
+ next_bci = bci + 1;
+ if (!m->is_in_code_section(next_bci)) return;
+ }
+
+ int new_bci = m->calculate_forward_bci(next_bci, forward_method);
+ if (TraceRedefineClasses >= 2) {
+ tty->print_cr("Transfering execution of %s to new method old_bci=%d new_bci=%d", forward_method->name()->as_C_string(), bci, new_bci);
+ }
+ RegisterMap reg_map(thread);
+ vframe* vf = vframe::new_vframe(&f, ®_map, thread);
+ interpretedVFrame *iframe = (interpretedVFrame *)vf;
+ iframe->set_method(forward_method, new_bci - 1);
+ }
+IRT_END
+
+// (tw) Correctly resolve method when running old code.
+IRT_ENTRY(methodOop, InterpreterRuntime::find_correct_method(JavaThread *thread, oopDesc* receiverOop, int vTableIndex))
+ // extract receiver from the outgoing argument list if necessary
+ Handle receiver(thread, receiverOop);
+
+ // TODO: Check for invokeinterface!
+ Bytecodes::Code bytecode = Bytecodes::_invokevirtual;
+
+ int method_holder_revision_number = method(thread)->method_holder()->klass_part()->revision_number();
+ klassOop klass = receiverOop->klass();
+ while (klass->klass_part()->revision_number() > method_holder_revision_number) {
+ klass = klass->klass_part()->old_version();
+ }
+
+ // TODO: Check for correctness if different vtable indices in different versions?
+
+ return ((instanceKlass *)klass->klass_part())->method_at_vtable(vTableIndex);
+IRT_END
+
+// Correctly resolve interface method when running old code.
+IRT_ENTRY(methodOop, InterpreterRuntime::find_correct_interface_method(JavaThread *thread, oopDesc* receiverOop, oopDesc* interface_klass, int vTableIndex))
+
+ // extract receiver from the outgoing argument list if necessary
+ Handle receiver(thread, receiverOop);
+
+ // TODO: Check for invokeinterface!
+ Bytecodes::Code bytecode = Bytecodes::_invokevirtual;
+
+ int method_holder_revision_number = method(thread)->method_holder()->klass_part()->revision_number();
+ klassOop klass = receiverOop->klass();
+ while (klass->klass_part()->revision_number() > method_holder_revision_number) {
+ klass = klass->klass_part()->old_version();
+ }
+
+ methodOop result = ((instanceKlass *)klass->klass_part())->method_at_itable((klassOop)interface_klass, vTableIndex, THREAD);
+ return result;
+IRT_END
+
IRT_ENTRY(void, InterpreterRuntime::resolve_invoke(JavaThread* thread, Bytecodes::Code bytecode))
// extract receiver from the outgoing argument list if necessary
Handle receiver(thread, NULL);
@@ -650,6 +725,10 @@
if (JvmtiExport::can_hotswap_or_post_breakpoint()) {
int retry_count = 0;
while (info.resolved_method()->is_old()) {
+ // (tw) If we are executing an old method, this is OK!
+ if (method(thread)->is_old()) {
+ break;
+ }
// It is very unlikely that method is redefined more than 100 times
// in the middle of resolve. If it is looping here more than 100 times
// means then there could be a bug here.
diff -r f5603a6e5042 src/share/vm/interpreter/interpreterRuntime.hpp
--- a/src/share/vm/interpreter/interpreterRuntime.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/interpreter/interpreterRuntime.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -115,6 +115,9 @@
static void post_method_entry(JavaThread *thread);
static void post_method_exit (JavaThread *thread);
static int interpreter_contains(address pc);
+ static void forward_method(JavaThread *thread);
+ static methodOop find_correct_method(JavaThread *thread, oopDesc* receiver, int vTableIndex);
+ static methodOop find_correct_interface_method(JavaThread *thread, oopDesc* receiver, oopDesc* interface_klass, int vTableIndex);
// Native signature handlers
static void prepare_native_call(JavaThread* thread, methodOopDesc* method);
diff -r f5603a6e5042 src/share/vm/interpreter/linkResolver.cpp
--- a/src/share/vm/interpreter/linkResolver.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/interpreter/linkResolver.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -114,8 +114,8 @@
// Klass resolution
void LinkResolver::check_klass_accessability(KlassHandle ref_klass, KlassHandle sel_klass, TRAPS) {
- if (!Reflection::verify_class_access(ref_klass->as_klassOop(),
- sel_klass->as_klassOop(),
+ if (!Reflection::verify_class_access(ref_klass->as_klassOop()->klass_part()->newest_version(),
+ sel_klass->as_klassOop()->klass_part()->newest_version(),
true)) {
ResourceMark rm(THREAD);
Exceptions::fthrow(
@@ -227,7 +227,7 @@
// We'll check for the method name first, as that's most likely
// to be false (so we'll short-circuit out of these tests).
if (sel_method->name() == vmSymbols::clone_name() &&
- sel_klass() == SystemDictionary::Object_klass() &&
+ sel_klass()->klass_part()->newest_version() == SystemDictionary::Object_klass()->klass_part()->newest_version() &&
resolved_klass->oop_is_array()) {
// We need to change "protected" to "public".
assert(flags.is_protected(), "clone not protected?");
@@ -293,6 +293,146 @@
}
+void LinkResolver::lookup_method(methodHandle& resolved_method, KlassHandle resolved_klass,
+ symbolHandle method_name, symbolHandle method_signature, bool is_interface, KlassHandle current_klass, TRAPS) {
+
+ // Interface method lookup?
+ if (is_interface) {
+
+ // lookup method in this interface or its super, java.lang.Object
+ lookup_instance_method_in_klasses(resolved_method, resolved_klass, method_name, method_signature, CHECK);
+
+ if (resolved_method.is_null()) {
+ // lookup method in all the super-interfaces
+ lookup_method_in_interfaces(resolved_method, resolved_klass, method_name, method_signature, CHECK);
+ }
+
+ // Other methods
+ } else {
+
+ // 2. lookup method in resolved klass and its super klasses
+ lookup_method_in_klasses(resolved_method, resolved_klass, method_name, method_signature, CHECK);
+
+ if (resolved_method.is_null()) { // not found in the class hierarchy
+ // 3. lookup method in all the interfaces implemented by the resolved klass
+ lookup_method_in_interfaces(resolved_method, resolved_klass, method_name, method_signature, CHECK);
+
+ if (resolved_method.is_null()) {
+ // JSR 292: see if this is an implicitly generated method MethodHandle.invoke(*...)
+ lookup_implicit_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, CHECK);
+ }
+ }
+ }
+}
+
+void LinkResolver::lookup_correct_field(fieldDescriptor &fd, KlassHandle &sel_klass, KlassHandle resolved_klass, KlassHandle current_klass, symbolOop field_name, symbolOop field_sig, bool is_static) {
+
+ // First attempt unversioned
+ sel_klass = KlassHandle(Thread::current(), instanceKlass::cast(resolved_klass())->find_field(field_name, field_sig, &fd));
+
+
+ if (!current_klass.is_null() && !current_klass->is_newest_version()) {
+
+ // Look for the policy defined in the new version of the class (_not_ in the newest, but only in the newer relative to current klass).
+ int redefinition_policy = current_klass->new_version()->klass_part()->field_redefinition_policy();
+ if (is_static) {
+ redefinition_policy = current_klass->new_version()->klass_part()->static_field_redefinition_policy();
+ }
+
+ assert(redefinition_policy != Klass::StaticCheck, "if the policy is static check, then we can never reach here");
+
+ if (redefinition_policy != Klass::DynamicCheck) {
+
+ if (redefinition_policy == Klass::AccessOldMembers) {
+ // Forget looked up fields
+ sel_klass = KlassHandle(Thread::current(), (oop)NULL);
+ }
+
+ assert(redefinition_policy == Klass::AccessOldMembers || redefinition_policy == Klass::AccessDeletedMembers, "");
+
+ if (sel_klass.is_null() || fd.is_static() != is_static /* access old static field field is changed from static to non-static */) {
+
+ // Select correct version for resolved klass.
+ find_correct_resolved_klass(resolved_klass, current_klass);
+
+ sel_klass = KlassHandle(Thread::current(), instanceKlass::cast(resolved_klass())->find_field(field_name, field_sig, &fd));
+
+ if (sel_klass.is_null()) {
+ TRACE_RC2("Trying to resolve field (%s) in old universe failed => exception is the correct behaviour", field_name->as_C_string());
+ } else {
+ assert(sel_klass->new_version() != NULL, "must be old class!");
+ TRACE_RC2("Resolved a field in the old universe (%s)!", field_name->as_C_string());
+ }
+ }
+ }
+ }
+}
+
+void LinkResolver::lookup_correct_method(methodHandle& resolved_method, KlassHandle resolved_klass, KlassHandle current_klass,
+ symbolHandle method_name, symbolHandle method_signature, bool is_interface, TRAPS) {
+
+ // First attempt unversioned
+ lookup_method(resolved_method, resolved_klass, method_name, method_signature, is_interface, current_klass, CHECK);
+
+ // (tw) Are we in an old method that wants to see a different view on the world?
+ if (!current_klass.is_null() && !current_klass->is_newest_version()) {
+
+ // Look for the policy defined in the new version of the class (_not_ in the newest, but only in the newer relative to current klass).
+ int method_redefinition_policy = current_klass->new_version()->klass_part()->method_redefinition_policy();
+ assert(method_redefinition_policy != Klass::StaticCheck, "if the policy is static check, then we can never reach here");
+
+ if (method_redefinition_policy != Klass::DynamicCheck) {
+
+ // We do not throw the exception
+ if (method_redefinition_policy == Klass::AccessOldMembers) {
+ // Forget any new member lookup
+ resolved_method = methodHandle(THREAD, NULL);
+ }
+
+ assert(method_redefinition_policy == Klass::AccessOldMembers || method_redefinition_policy == Klass::AccessDeletedMembers, "");
+
+ if (resolved_method.is_null()) {
+
+ // Select correct version for resolved klass.
+ find_correct_resolved_klass(resolved_klass, current_klass);
+
+ // Now do the lookup in a second attempt with a different resolved klass.
+ lookup_method(resolved_method, resolved_klass, method_name, method_signature, is_interface, current_klass, CHECK);
+
+ IF_TRACE_RC2 {
+ ResourceMark rm(THREAD);
+ if (resolved_method.is_null()) {
+ TRACE_RC2("Trying to resolve method (%s) in old universe failed => exception is the correct behaviour", method_name->as_C_string());
+ } else {
+ assert(resolved_method->is_old(), "must be old method!");
+ TRACE_RC2("Resolved a method in the old universe (%s)!", resolved_method->name()->as_C_string());
+ }
+ }
+ }
+ }
+ }
+
+ if (resolved_method.is_null()) {
+ // no method found
+ ResourceMark rm(THREAD);
+ THROW_MSG(vmSymbols::java_lang_NoSuchMethodError(),
+ methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()),
+ method_name(),
+ method_signature()));
+ }
+}
+
+void LinkResolver::find_correct_resolved_klass(KlassHandle &resolved_klass, KlassHandle ¤t_klass) {
+ int current_klass_revision = current_klass->revision_number();
+ int resolved_klass_revision = resolved_klass->revision_number();
+ TRACE_RC2("The two different revision numbers for interfaces: current=%d / resolved_callee=%d", current_klass_revision, resolved_klass_revision);
+
+ while (resolved_klass->revision_number() > current_klass_revision) {
+ assert(resolved_klass->old_version(), "must have old version");
+ resolved_klass = KlassHandle(Thread::current(), resolved_klass->old_version());
+ }
+}
+
void LinkResolver::resolve_method(methodHandle& resolved_method, KlassHandle resolved_klass,
symbolHandle method_name, symbolHandle method_signature,
KlassHandle current_klass, bool check_access, TRAPS) {
@@ -304,27 +444,8 @@
THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf);
}
- // 2. lookup method in resolved klass and its super klasses
- lookup_method_in_klasses(resolved_method, resolved_klass, method_name, method_signature, CHECK);
-
- if (resolved_method.is_null()) { // not found in the class hierarchy
- // 3. lookup method in all the interfaces implemented by the resolved klass
- lookup_method_in_interfaces(resolved_method, resolved_klass, method_name, method_signature, CHECK);
-
- if (resolved_method.is_null()) {
- // JSR 292: see if this is an implicitly generated method MethodHandle.invoke(*...)
- lookup_implicit_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, CHECK);
- }
-
- if (resolved_method.is_null()) {
- // 4. method lookup failed
- ResourceMark rm(THREAD);
- THROW_MSG(vmSymbols::java_lang_NoSuchMethodError(),
- methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()),
- method_name(),
- method_signature()));
- }
- }
+ // 2. and 3. and 4. lookup method in resolved klass and its super klasses
+ lookup_correct_method(resolved_method, resolved_klass, current_klass, method_name, method_signature, false, CHECK);
// 5. check if method is concrete
if (resolved_method->is_abstract() && !resolved_klass->is_abstract()) {
@@ -391,20 +512,7 @@
}
// lookup method in this interface or its super, java.lang.Object
- lookup_instance_method_in_klasses(resolved_method, resolved_klass, method_name, method_signature, CHECK);
-
- if (resolved_method.is_null()) {
- // lookup method in all the super-interfaces
- lookup_method_in_interfaces(resolved_method, resolved_klass, method_name, method_signature, CHECK);
- if (resolved_method.is_null()) {
- // no method found
- ResourceMark rm(THREAD);
- THROW_MSG(vmSymbols::java_lang_NoSuchMethodError(),
- methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()),
- method_name(),
- method_signature()));
- }
- }
+ lookup_correct_method(resolved_method, resolved_klass, current_klass, method_name, method_signature, true, CHECK);
if (check_access) {
HandleMark hm(THREAD);
@@ -492,9 +600,14 @@
THROW_MSG(vmSymbols::java_lang_NoSuchFieldError(), field->as_C_string());
}
+ KlassHandle ref_klass(THREAD, pool->pool_holder()->klass_part());
+
// Resolve instance field
fieldDescriptor fd; // find_field initializes fd if found
- KlassHandle sel_klass(THREAD, instanceKlass::cast(resolved_klass())->find_field(field, sig, &fd));
+
+ KlassHandle sel_klass;
+ lookup_correct_field(fd, sel_klass, resolved_klass, ref_klass, field, sig, is_static);
+
// check if field exists; i.e., if a klass containing the field def has been selected
if (sel_klass.is_null()){
ResourceMark rm(THREAD);
@@ -502,7 +615,6 @@
}
// check access
- KlassHandle ref_klass(THREAD, pool->pool_holder());
check_field_accessability(ref_klass, resolved_klass, sel_klass, fd, CHECK);
// check for errors
@@ -513,7 +625,7 @@
}
// Final fields can only be accessed from its own class.
- if (is_put && fd.access_flags().is_final() && sel_klass() != pool->pool_holder()) {
+ if (is_put && fd.access_flags().is_final() && sel_klass() != pool->pool_holder()->klass_part()->active_version() && sel_klass() != pool->pool_holder()) {
THROW(vmSymbols::java_lang_IllegalAccessError());
}
@@ -715,7 +827,7 @@
bool check_access, bool check_null_and_abstract, TRAPS) {
methodHandle resolved_method;
linktime_resolve_virtual_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK);
- runtime_resolve_virtual_method(result, resolved_method, resolved_klass, recv, receiver_klass, check_null_and_abstract, CHECK);
+ runtime_resolve_virtual_method(result, resolved_method, resolved_klass, recv, receiver_klass, current_klass, check_null_and_abstract, CHECK);
}
// throws linktime exceptions
@@ -744,6 +856,7 @@
KlassHandle resolved_klass,
Handle recv,
KlassHandle recv_klass,
+ KlassHandle current_klass,
bool check_null_and_abstract,
TRAPS) {
@@ -792,7 +905,39 @@
// recv_klass might be an arrayKlassOop but all vtables start at
// the same place. The cast is to avoid virtual call and assertion.
instanceKlass* inst = (instanceKlass*)recv_klass()->klass_part();
+
+ // (tw) The type of the virtual method call and the type of the receiver do not need to
+ // have anything in common, as the receiver type could've been hotswapped.
+ // Does not always work (method could be resolved with correct dynamic type and later
+ // be called at the same place with a wrong dynamic type).
+ // (tw) TODO: Need to handle the static type vs dynamic type issue more generally.
+
+ // The vTable must be based on the view of the world of the resolved method
+ klassOop method_holder = resolved_method->method_holder();
+
+ if (method_holder->klass_part()->new_version() != NULL) {
+ // We are executing in old code
+ TRACE_RC2("Calling a method in old code");
+ while (method_holder->klass_part()->revision_number() < inst->revision_number()) {
+ inst = (instanceKlass *)(inst->old_version()->klass_part());
+ }
+ }
+
+ if (inst->is_subtype_of(method_holder)) {
selected_method = methodHandle(THREAD, inst->method_at_vtable(vtable_index));
+ } else {
+
+ tty->print_cr("Failure:");
+ inst->as_klassOop()->print();
+ inst->super()->print();
+ juint off = inst->super_check_offset();
+ klassOop sup = *(klassOop*)( (address)inst->as_klassOop() + off );
+ sup->print();
+ method_holder->print();
+
+ bool b = inst->is_subtype_of(method_holder);
+ THROW_MSG(vmSymbols::java_lang_NoSuchMethodError(), "(tw) A virtual method was called, but the type of the receiver is not related with the type of the class of the called method!");
+ }
}
}
diff -r f5603a6e5042 src/share/vm/interpreter/linkResolver.hpp
--- a/src/share/vm/interpreter/linkResolver.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/interpreter/linkResolver.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -100,7 +100,11 @@
// It does all necessary link-time checks & throws exceptions if necessary.
class LinkResolver: AllStatic {
- private:
+private:
+ static void lookup_method (methodHandle& result, KlassHandle resolved_klass, symbolHandle name, symbolHandle signature, bool is_interface, KlassHandle current_klass, TRAPS);
+ static void lookup_correct_field (fieldDescriptor &fd, KlassHandle &sel_klass, KlassHandle resolved_klass, KlassHandle current_klass, symbolOop field_name, symbolOop field_sig, bool is_static);
+ static void lookup_correct_method (methodHandle& result, KlassHandle resolved_klass, KlassHandle current_klass, symbolHandle name, symbolHandle signature, bool is_interface, TRAPS);
+ static void find_correct_resolved_klass (KlassHandle &resolved_klass, KlassHandle ¤t_klass);
static void lookup_method_in_klasses (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS);
static void lookup_instance_method_in_klasses (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS);
static void lookup_method_in_interfaces (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS);
@@ -123,7 +127,7 @@
static void linktime_resolve_interface_method (methodHandle& resolved_method, KlassHandle resolved_klass, symbolHandle method_name, symbolHandle method_signature, KlassHandle current_klass, bool check_access, TRAPS);
static void runtime_resolve_special_method (CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, KlassHandle current_klass, bool check_access, TRAPS);
- static void runtime_resolve_virtual_method (CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, Handle recv, KlassHandle recv_klass, bool check_null_and_abstract, TRAPS);
+ static void runtime_resolve_virtual_method (CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, Handle recv, KlassHandle recv_klass, KlassHandle current_klass, bool check_null_and_abstract, TRAPS);
static void runtime_resolve_interface_method (CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, Handle recv, KlassHandle recv_klass, bool check_null_and_abstract, TRAPS);
static void check_field_accessability (KlassHandle ref_klass, KlassHandle resolved_klass, KlassHandle sel_klass, fieldDescriptor& fd, TRAPS);
diff -r f5603a6e5042 src/share/vm/interpreter/oopMapCache.cpp
--- a/src/share/vm/interpreter/oopMapCache.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/interpreter/oopMapCache.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -530,9 +530,9 @@
if (!_array[i].is_empty() && _array[i].method()->is_old()) {
// Cache entry is occupied by an old redefined method and we don't want
// to pin it down so flush the entry.
- RC_TRACE(0x08000000, ("flush: %s(%s): cached entry @%d",
+ TRACE_RC3("flush: %s(%s): cached entry @%d",
_array[i].method()->name()->as_C_string(),
- _array[i].method()->signature()->as_C_string(), i));
+ _array[i].method()->signature()->as_C_string(), i);
_array[i].flush();
}
diff -r f5603a6e5042 src/share/vm/interpreter/templateTable.hpp
--- a/src/share/vm/interpreter/templateTable.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/interpreter/templateTable.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -303,8 +303,8 @@
static void shouldnotreachhere();
// jvmti support
- static void jvmti_post_field_access(Register cache, Register index, bool is_static, bool has_tos);
- static void jvmti_post_field_mod(Register cache, Register index, bool is_static);
+ static void jvmti_post_field_access(Register cache, Register index, int byte_no, bool is_static, bool has_tos);
+ static void jvmti_post_field_mod(Register cache, Register index, int byte_no, bool is_static);
static void jvmti_post_fast_field_mod();
// debugging of TemplateGenerator
diff -r f5603a6e5042 src/share/vm/memory/genMarkSweep.cpp
--- a/src/share/vm/memory/genMarkSweep.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/memory/genMarkSweep.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -372,6 +372,7 @@
// in the same order in phase2, phase3 and phase4. We don't quite do that
// here (perm_gen first rather than last), so we tell the validate code
// to use a higher index (saved from phase2) when verifying perm_gen.
+ assert(_rescued_oops == NULL, "must be empty before processing");
GenCollectedHeap* gch = GenCollectedHeap::heap();
Generation* pg = gch->perm_gen();
@@ -385,10 +386,14 @@
VALIDATE_MARK_SWEEP_ONLY(reset_live_oop_tracking(false));
+ MarkSweep::copy_rescued_objects_back();
+
GenCompactClosure blk;
gch->generation_iterate(&blk, true);
VALIDATE_MARK_SWEEP_ONLY(compaction_complete());
+ MarkSweep::copy_rescued_objects_back();
+
pg->post_compact(); // Shared spaces verification.
}
diff -r f5603a6e5042 src/share/vm/memory/permGen.cpp
--- a/src/share/vm/memory/permGen.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/memory/permGen.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -44,7 +44,12 @@
for (;;) {
{
- MutexLocker ml(Heap_lock);
+ // (tw) Only lock when not at a safepoint (necessary to use the split verifier from the VmThread)
+ Monitor *lock = Heap_lock;
+ if (SafepointSynchronize::is_at_safepoint()) {
+ lock = NULL;
+ }
+ MutexLockerEx ml(lock);
if ((obj = gen->allocate(size, false)) != NULL) {
return obj;
}
diff -r f5603a6e5042 src/share/vm/memory/space.cpp
--- a/src/share/vm/memory/space.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/memory/space.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -359,6 +359,31 @@
_compaction_top = bottom();
}
+// (tw) Calculates the compact_top that will be used for placing the next object with the giving size on the heap.
+HeapWord* CompactibleSpace::forward_compact_top(size_t size,
+CompactPoint* cp, HeapWord* compact_top) {
+ // First check if we should switch compaction space
+ assert(this == cp->space, "'this' should be current compaction space.");
+ size_t compaction_max_size = pointer_delta(end(), compact_top);
+ while (size > compaction_max_size) {
+ // switch to next compaction space
+ cp->space->set_compaction_top(compact_top);
+ cp->space = cp->space->next_compaction_space();
+ if (cp->space == NULL) {
+ cp->gen = GenCollectedHeap::heap()->prev_gen(cp->gen);
+ assert(cp->gen != NULL, "compaction must succeed");
+ cp->space = cp->gen->first_compaction_space();
+ assert(cp->space != NULL, "generation must have a first compaction space");
+ }
+ compact_top = cp->space->bottom();
+ cp->space->set_compaction_top(compact_top);
+ cp->threshold = cp->space->initialize_threshold();
+ compaction_max_size = pointer_delta(cp->space->end(), compact_top);
+ }
+
+ return compact_top;
+}
+
HeapWord* CompactibleSpace::forward(oop q, size_t size,
CompactPoint* cp, HeapWord* compact_top) {
// q is alive
@@ -382,7 +407,7 @@
}
// store the forwarding pointer into the mark word
- if ((HeapWord*)q != compact_top) {
+ if ((HeapWord*)q != compact_top || (size_t)q->size() != size) {
q->forward_to(oop(compact_top));
assert(q->is_gc_marked(), "encoding the pointer should preserve the mark");
} else {
@@ -430,7 +455,204 @@
// Faster object search.
void ContiguousSpace::prepare_for_compaction(CompactPoint* cp) {
- SCAN_AND_FORWARD(cp, top, block_is_always_obj, obj_size);
+ if (!Universe::is_redefining_gc_run()) {
+ SCAN_AND_FORWARD(cp, top, block_is_always_obj, obj_size);
+ return;
+ }
+
+ /* Compute the new addresses for the live objects and store it in the mark
+ * Used by universe::mark_sweep_phase2()
+ */
+ HeapWord* compact_top; /* This is where we are currently compacting to. */
+
+ /* We're sure to be here before any objects are compacted into this
+ * space, so this is a good time to initialize this:
+ */
+ set_compaction_top(bottom());
+
+ if (cp->space == NULL) {
+ assert(cp->gen != NULL, "need a generation");
+ assert(cp->threshold == NULL, "just checking");
+ assert(cp->gen->first_compaction_space() == this, "just checking");
+ cp->space = cp->gen->first_compaction_space();
+ compact_top = cp->space->bottom();
+ cp->space->set_compaction_top(compact_top);
+ cp->threshold = cp->space->initialize_threshold();
+ } else {
+ compact_top = cp->space->compaction_top();
+ }
+
+ /* We allow some amount of garbage towards the bottom of the space, so
+ * we don't start compacting before there is a significant gain to be made.
+ * Occasionally, we want to ensure a full compaction, which is determined
+ * by the MarkSweepAlwaysCompactCount parameter.
+ */
+ int invocations = SharedHeap::heap()->perm_gen()->stat_record()->invocations;
+ bool skip_dead = ((invocations % MarkSweepAlwaysCompactCount) != 0);
+
+ size_t allowed_deadspace = 0;
+ if (skip_dead) {
+ int ratio = (int)allowed_dead_ratio();
+ allowed_deadspace = (capacity() * ratio / 100) / HeapWordSize;
+ }
+
+ HeapWord* q = bottom();
+ HeapWord* t = end();
+
+ HeapWord* end_of_live= q; /* One byte beyond the last byte of the last
+ live object. */
+ HeapWord* first_dead = end();/* The first dead object. */
+ LiveRange* liveRange = NULL; /* The current live range, recorded in the
+ first header of preceding free area. */
+ _first_dead = first_dead;
+
+ const intx interval = PrefetchScanIntervalInBytes;
+
+ while (q < t) {
+ assert(!block_is_obj(q) ||
+ oop(q)->mark()->is_marked() || oop(q)->mark()->is_unlocked() ||
+ oop(q)->mark()->has_bias_pattern(),
+ "these are the only valid states during a mark sweep");
+ if (block_is_obj(q) && oop(q)->is_gc_marked()) {
+ /* prefetch beyond q */
+ Prefetch::write(q, interval);
+ /* size_t size = oop(q)->size(); changing this for cms for perm gen */
+ size_t size = block_size(q);
+
+ size_t forward_size = size;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Compute the forward sizes and leave out objects whose position could
+ // possibly overlap other objects.
+
+ // (tw) There is a new version of the class of q => different size
+ if (oop(q)->blueprint()->new_version() != NULL && oop(q)->blueprint()->new_version()->klass_part()->update_information() != NULL) {
+
+ size_t new_size = oop(q)->size_given_klass(oop(q)->blueprint()->new_version()->klass_part());
+ assert(size != new_size || oop(q)->is_perm(), "instances without changed size have to be updated prior to GC run");
+ forward_size = new_size;
+ }
+
+ compact_top = cp->space->forward_compact_top(forward_size, cp, compact_top);
+
+ bool rescueing = false;
+ if (rescueing = must_rescue(oop(q), oop(compact_top))) {
+ if (MarkSweep::_rescued_oops == NULL) {
+ MarkSweep::_rescued_oops = new GrowableArray(128);
+ }
+ TRACE_RC5("rescue obj %d klass=%s", MarkSweep::_rescued_oops->length(), oop(q)->klass()->klass_part()->name()->as_C_string());
+ MarkSweep::_rescued_oops->append(oop(q));
+ } else {
+ compact_top = cp->space->forward(oop(q), forward_size, cp, compact_top);
+ }
+
+ if ((size != forward_size || rescueing) && q < first_dead) {
+ // (tw) This object moves => first_dead must be set to here!
+ first_dead = q;
+ }
+ //////////////////////////////////////////////////////////////////////////
+
+ end_of_live = q + size; //forward_size;
+ q += size;
+ } else {
+ /* run over all the contiguous dead objects */
+ HeapWord* end = q;
+ do {
+ /* prefetch beyond end */
+ Prefetch::write(end, interval);
+ end += block_size(end);
+ } while (end < t && (!block_is_obj(end) || !oop(end)->is_gc_marked()));
+
+ /* see if we might want to pretend this object is alive so that
+ * we don't have to compact quite as often.
+ */
+ if (allowed_deadspace > 0 && q == compact_top) {
+ size_t sz = pointer_delta(end, q);
+ if (insert_deadspace(allowed_deadspace, q, sz)) {
+ compact_top = cp->space->forward(oop(q), sz, cp, compact_top);
+ q = end;
+ end_of_live = end;
+ continue;
+ }
+ }
+
+ /* otherwise, it really is a free region. */
+
+ /* for the previous LiveRange, record the end of the live objects. */
+ if (liveRange) {
+ liveRange->set_end(q);
+ }
+
+ /* record the current LiveRange object.
+ * liveRange->start() is overlaid on the mark word.
+ */
+ liveRange = (LiveRange*)q;
+ liveRange->set_start(end);
+ liveRange->set_end(end);
+
+ /* see if this is the first dead region. */
+ if (q < first_dead) {
+ first_dead = q;
+ }
+
+ /* move on to the next object */
+ q = end;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Compute the forwarding addresses for the objects that need to be
+ // rescued.
+ // TODO: empty the _rescued_oops after ALL spaces are compacted!
+ if (MarkSweep::_rescued_oops != NULL) {
+ TRACE_RC2("Calculating new forward sizes for %d objects!", MarkSweep::_rescued_oops->length());
+
+ for (int i=0; ilength(); i++) {
+ oop q = MarkSweep::_rescued_oops->at(i);
+
+ /* size_t size = oop(q)->size(); changing this for cms for perm gen */
+ size_t size = block_size((HeapWord*)q);
+
+ size_t forward_size = size;
+
+ // (tw) There is a new version of the class of q => different size
+ if (oop(q)->blueprint()->new_version() != NULL) {
+
+ size_t new_size = oop(q)->size_given_klass(oop(q)->blueprint()->new_version()->klass_part());
+ assert(size != new_size || oop(q)->is_perm(), "instances without changed size have to be updated prior to GC run");
+ forward_size = new_size;
+ }
+
+ compact_top = cp->space->forward(oop(q), forward_size, cp, compact_top);
+ assert(compact_top <= t, "must not write over end of space!");
+ }
+ MarkSweep::_rescued_oops->clear();
+ MarkSweep::_rescued_oops = NULL;
+ }
+ //////////////////////////////////////////////////////////////////////////
+
+ assert(q == t, "just checking");
+ if (liveRange != NULL) {
+ liveRange->set_end(q);
+ }
+ _end_of_live = end_of_live;
+ if (end_of_live < first_dead) {
+ first_dead = end_of_live;
+ }
+ _first_dead = first_dead;
+
+ if (_first_dead > top()) {
+ _first_dead = top();
+ }
+
+ if (_end_of_live > top()) {
+ _end_of_live = top();
+ }
+ assert(_first_dead <= top(), "Must be smaller equal");
+ assert(_end_of_live <= top(), "Must be smaller equal");
+
+ /* save the compaction_top of the compaction space. */
+ cp->space->set_compaction_top(compact_top);
}
void Space::adjust_pointers() {
@@ -471,17 +693,322 @@
assert(q == t, "just checking");
}
+
+#ifdef ASSERT
+
+int CompactibleSpace::space_index(oop obj) {
+ GenCollectedHeap* heap = GenCollectedHeap::heap();
+
+ if (heap->is_in_permanent(obj)) {
+ return -1;
+ }
+
+ int index = 0;
+ for (int i = heap->n_gens() - 1; i >= 0; i--) {
+ Generation* gen = heap->get_gen(i);
+ CompactibleSpace* space = gen->first_compaction_space();
+ while (space != NULL) {
+ if (space->is_in_reserved(obj)) {
+ return index;
+ }
+ space = space->next_compaction_space();
+ index++;
+ }
+ }
+
+ tty->print_cr("could not compute space_index for %08xh", obj);
+ index = 0;
+ for (int i = heap->n_gens() - 1; i >= 0; i--) {
+ Generation* gen = heap->get_gen(i);
+ tty->print_cr(" generation %s: %08xh - %08xh", gen->name(), gen->reserved().start(), gen->reserved().end());
+
+ CompactibleSpace* space = gen->first_compaction_space();
+ while (space != NULL) {
+ tty->print_cr(" %2d space %08xh - %08xh", index, space->bottom(), space->end());
+ space = space->next_compaction_space();
+ index++;
+ }
+ }
+
+ ShouldNotReachHere();
+ return 0;
+}
+#endif
+
+bool CompactibleSpace::must_rescue(oop old_obj, oop new_obj) {
+
+ assert(is_in_reserved(old_obj), "old_obj must be in this space");
+
+ if (old_obj->is_perm()) {
+ // This object is in perm gen; check for invariant obj->klass() <= obj
+ if (oop(old_obj)->blueprint()->new_version() != NULL) {
+ return true;
+ }
+ }
+
+ int size = old_obj->size();
+ int original_size = size;
+ if (oop(old_obj)->blueprint()->is_redefining()) {
+ assert(oop(old_obj)->blueprint()->old_version() != NULL, "must not be null");
+ original_size = oop(old_obj)->size_given_klass(oop(old_obj)->blueprint()->old_version()->klass_part());
+ } else if (oop(old_obj)->blueprint()->new_version() != NULL) {
+ size = oop(old_obj)->size_given_klass(oop(old_obj)->blueprint()->new_version()->klass_part());
+ }
+
+ bool normalComparison = (old_obj + original_size < new_obj + size);
+
+ if (is_in_reserved(new_obj)) {
+ // Old and new address are in same space, so just compare the address.
+ // Must rescue if object moves towards the top of the space.
+ assert(space_index(old_obj) == space_index(new_obj), "old_obj and new_obj must be in same space");
+ return normalComparison;
+
+ } else {
+
+ assert(space_index(old_obj) != space_index(new_obj), "old_obj and new_obj must be in different spaces");
+
+ Generation* tenured_gen = GenCollectedHeap::heap()->get_gen(1);
+ if (tenured_gen->is_in_reserved(new_obj)) {
+ // Must never rescue when moving from the new into the old generation.
+ assert(GenCollectedHeap::heap()->get_gen(0)->is_in_reserved(old_obj), "old_obj must be in DefNewGeneration");
+ assert(space_index(old_obj) > space_index(new_obj), "must be");
+ return false;
+
+ } else if (tenured_gen->is_in_reserved(old_obj)) {
+ // Must always rescue when moving from the old into the new generation.
+ assert(GenCollectedHeap::heap()->get_gen(0)->is_in_reserved(new_obj), "new_obj must be in DefNewGeneration");
+ assert(space_index(old_obj) < space_index(new_obj), "must be");
+ return true;
+
+ } else {
+ // In the new generation, eden is located before the from space, so a
+ // simple pointer comparison is sufficient.
+ assert(GenCollectedHeap::heap()->get_gen(0)->is_in_reserved(old_obj), "old_obj must be in DefNewGeneration");
+ assert(GenCollectedHeap::heap()->get_gen(0)->is_in_reserved(new_obj), "new_obj must be in DefNewGeneration");
+ assert((normalComparison) == (space_index(old_obj) < space_index(new_obj)), "slow and fast computation must yield same result");
+ return normalComparison;
+ }
+ }
+}
+
+oop CompactibleSpace::rescue(oop old_obj) {
+ assert(must_rescue(old_obj, old_obj->forwardee()), "do not call otherwise");
+
+ int size = old_obj->size();
+ oop rescued_obj = (oop)resource_allocate_bytes(size * HeapWordSize);
+ Copy::aligned_disjoint_words((HeapWord*)old_obj, (HeapWord*)rescued_obj, size);
+
+ if (MarkSweep::_rescued_oops == NULL) {
+ MarkSweep::_rescued_oops = new GrowableArray(128);
+ }
+
+ MarkSweep::_rescued_oops->append(rescued_obj);
+ return rescued_obj;
+}
+
void CompactibleSpace::adjust_pointers() {
// Check first is there is any work to do.
if (used() == 0) {
return; // Nothing to do.
}
+ /* adjust all the interior pointers to point at the new locations of objects
+ * Used by MarkSweep::mark_sweep_phase3() */
- SCAN_AND_ADJUST_POINTERS(adjust_obj_size);
+ HeapWord* q = bottom();
+ HeapWord* t = _end_of_live; /* Established by "prepare_for_compaction". */
+
+ assert(_first_dead <= _end_of_live, "Stands to reason, no?");
+
+ debug_only(HeapWord* prev_q = NULL);
+ debug_only(HeapWord* prev_prev_q = NULL);
+ debug_only(HeapWord* prev_prev_prev_q = NULL);
+ if (q < t && _first_dead > q &&
+ !oop(q)->is_gc_marked()) {
+ /* we have a chunk of the space which hasn't moved and we've
+ * reinitialized the mark word during the previous pass, so we can't
+ * use is_gc_marked for the traversal. */
+ HeapWord* end = _first_dead;
+
+ while (q < end) {
+ /* I originally tried to conjoin "block_start(q) == q" to the
+ * assertion below, but that doesn't work, because you can't
+ * accurately traverse previous objects to get to the current one
+ * after their pointers (including pointers into permGen) have been
+ * updated, until the actual compaction is done. dld, 4/00 */
+ assert(block_is_obj(q),
+ "should be at block boundaries, and should be looking at objs");
+
+ VALIDATE_MARK_SWEEP_ONLY(MarkSweep::track_interior_pointers(oop(q)));
+
+ /* point all the oops to the new location */
+ size_t size = oop(q)->adjust_pointers();
+ size = adjust_obj_size(size);
+
+ VALIDATE_MARK_SWEEP_ONLY(MarkSweep::check_interior_pointers());
+ VALIDATE_MARK_SWEEP_ONLY(MarkSweep::validate_live_oop(oop(q), size));
+
+ debug_only(prev_prev_prev_q = prev_prev_q);
+ debug_only(prev_prev_q = prev_q);
+ debug_only(prev_q = q);
+ q += size;
+ }
+
+ // (tw) first_dead can be live object!
+ q = _first_dead;
+
+// if (_first_dead == t) {
+// q = t;
+// } else {
+// /* $$$ This is funky. Using this to read the previously written
+// * LiveRange. See also use below. */
+// q = (HeapWord*)oop(_first_dead)->mark()->decode_pointer();
+// }
+ }
+
+ const intx interval = PrefetchScanIntervalInBytes;
+
+ debug_only(prev_q = NULL);
+ debug_only(prev_prev_q = NULL);
+ debug_only(prev_prev_prev_q = NULL);
+ while (q < t) {
+ /* prefetch beyond q */
+ Prefetch::write(q, interval);
+ if (oop(q)->is_gc_marked()) {
+ /* q is alive */
+ VALIDATE_MARK_SWEEP_ONLY(MarkSweep::track_interior_pointers(oop(q)));
+ /* point all the oops to the new location */
+ size_t size = oop(q)->adjust_pointers();
+ size = adjust_obj_size(size);
+ VALIDATE_MARK_SWEEP_ONLY(MarkSweep::check_interior_pointers());
+ VALIDATE_MARK_SWEEP_ONLY(MarkSweep::validate_live_oop(oop(q), size));
+ debug_only(prev_prev_prev_q = prev_prev_q);
+ debug_only(prev_prev_q = prev_q);
+ debug_only(prev_q = q);
+ q += size;
+ } else {
+ /* q is not a live object, so its mark should point at the next
+ * live object */
+ debug_only(prev_prev_prev_q = prev_prev_q);
+ debug_only(prev_prev_q = prev_q);
+ debug_only(prev_q = q);
+ q = (HeapWord*) oop(q)->mark()->decode_pointer();
+ assert(q > prev_q, "we should be moving forward through memory");
+ }
+ }
+
+ assert(q == t, "just checking");
}
void CompactibleSpace::compact() {
- SCAN_AND_COMPACT(obj_size);
+
+ if(!Universe::is_redefining_gc_run()) {
+ SCAN_AND_COMPACT(obj_size);
+ return;
+ }
+
+ /* Copy all live objects to their new location
+ * Used by MarkSweep::mark_sweep_phase4() */
+
+ HeapWord* q = bottom();
+ HeapWord* const t = _end_of_live;
+ debug_only(HeapWord* prev_q = NULL);
+ debug_only(HeapWord* prev_prev_q = NULL);
+ debug_only(HeapWord* prev_compaction_top = NULL);
+ debug_only(int old_size = 0);
+
+ if (q < t && _first_dead > q &&
+ !oop(q)->is_gc_marked()) {
+ /* we have a chunk of the space which hasn't moved and we've reinitialized
+ * the mark word during the previous pass, so we can't use is_gc_marked for
+ * the traversal. */
+ HeapWord* const end = _first_dead;
+
+#ifdef ASSERT
+ while (q < end) {
+ size_t size = oop(q)->size();
+ assert(!oop(q)->is_gc_marked(),
+ "should be unmarked (special dense prefix handling)");
+ VALIDATE_MARK_SWEEP_ONLY(MarkSweep::live_oop_moved_to(q, size, q));
+ debug_only(prev_prev_q = prev_q);
+ debug_only(prev_q = q);
+ q += size;
+ }
+#endif
+ // (tw) first_dead can be live object!
+ q = _first_dead;
+
+ //if (_first_dead == t) {
+ // q = t;
+ //} else {
+ ///* $$$ Funky */
+ //q = (HeapWord*) oop(_first_dead)->mark()->decode_pointer();
+ //}
+ }
+
+ const intx scan_interval = PrefetchScanIntervalInBytes;
+ const intx copy_interval = PrefetchCopyIntervalInBytes;
+ while (q < t) {
+ if (!oop(q)->is_gc_marked()) {
+ /* mark is pointer to next marked oop */
+ debug_only(prev_prev_q = prev_q);
+ debug_only(prev_q = q);
+ q = (HeapWord*) oop(q)->mark()->decode_pointer();
+ assert(q > prev_q, "we should be moving forward through memory");
+ } else {
+ /* prefetch beyond q */
+ Prefetch::read(q, scan_interval);
+
+ /* size and destination */
+ size_t size = obj_size(q);
+ HeapWord* compaction_top = (HeapWord*)oop(q)->forwardee();
+
+ size_t original_size = size;
+
+ if (must_rescue(oop(q), oop(q)->forwardee())) {
+ oop dest_obj = rescue(oop(q));
+ debug_only(Copy::fill_to_words(q, original_size, 0));
+ } else {
+
+ /* prefetch beyond compaction_top */
+ Prefetch::write(compaction_top, copy_interval);
+
+ /* copy object and reinit its mark */
+ VALIDATE_MARK_SWEEP_ONLY(MarkSweep::live_oop_moved_to(q, size,
+ compaction_top));
+ assert(q != compaction_top || oop(q)->blueprint()->new_version() != NULL, "everything in this pass should be moving");
+
+ if (oop(q)->blueprint()->new_version() != NULL) {
+ MarkSweep::update_fields(oop(q), oop(compaction_top));
+ } else {
+ Copy::aligned_conjoint_words(q, compaction_top, size);
+ }
+ oop(compaction_top)->init_mark();
+ assert(oop(compaction_top)->klass() != NULL, "should have a class");
+ }
+
+ debug_only(prev_compaction_top = compaction_top);
+ debug_only(prev_prev_q = prev_q);
+ debug_only(prev_q = q);
+ debug_only(old_size = (int)size);
+ q += original_size;
+ }
+ }
+
+ /* Reset space after compaction is complete */
+ reset_after_compaction();
+ /* We do this clear, below, since it has overloaded meanings for some */
+ /* space subtypes. For example, OffsetTableContigSpace's that were */
+ /* compacted into will have had their offset table thresholds updated */
+ /* continuously, but those that weren't need to have their thresholds */
+ /* re-initialized. Also mangles unused area for debugging. */
+ if (is_empty()) {
+ clear(SpaceDecorator::Mangle);
+ } else {
+ if (ZapUnusedHeapArea) mangle_unused_area();
+ }
+
+
+ //SCAN_AND_COMPACT(obj_size);
}
void Space::print_short() const { print_short_on(tty); }
diff -r f5603a6e5042 src/share/vm/memory/space.hpp
--- a/src/share/vm/memory/space.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/memory/space.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -419,6 +419,9 @@
// indicates when the next such action should be taken.
virtual void prepare_for_compaction(CompactPoint* cp);
// MarkSweep support phase3
+ DEBUG_ONLY(int space_index(oop obj));
+ bool must_rescue(oop old_obj, oop new_obj);
+ oop rescue(oop old_obj);
virtual void adjust_pointers();
// MarkSweep support phase4
virtual void compact();
@@ -449,6 +452,10 @@
virtual HeapWord* forward(oop q, size_t size, CompactPoint* cp,
HeapWord* compact_top);
+ // (tw)
+ virtual HeapWord* forward_compact_top(size_t size, CompactPoint* cp,
+ HeapWord* compact_top);
+
// Return a size with adjusments as required of the space.
virtual size_t adjust_object_size_v(size_t size) const { return size; }
diff -r f5603a6e5042 src/share/vm/memory/universe.cpp
--- a/src/share/vm/memory/universe.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/memory/universe.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -25,6 +25,8 @@
# include "incls/_precompiled.incl"
# include "incls/_universe.cpp.incl"
+bool Universe::_is_redefining_gc_run = false;
+
// Known objects
klassOop Universe::_boolArrayKlassObj = NULL;
klassOop Universe::_byteArrayKlassObj = NULL;
@@ -131,6 +133,43 @@
f(systemObjArrayKlassObj());
}
+// (tw) This method should iterate all pointers that are not within heap objects.
+void Universe::root_oops_do(OopClosure *oopClosure) {
+
+ class AlwaysTrueClosure: public BoolObjectClosure {
+ public:
+ void do_object(oop p) { ShouldNotReachHere(); }
+ bool do_object_b(oop p) { return true; }
+ };
+ AlwaysTrueClosure always_true;
+
+ Universe::oops_do(oopClosure);
+ ReferenceProcessor::oops_do(oopClosure);
+ JNIHandles::oops_do(oopClosure); // Global (strong) JNI handles
+ Threads::oops_do(oopClosure, NULL);
+ ObjectSynchronizer::oops_do(oopClosure);
+ FlatProfiler::oops_do(oopClosure);
+ JvmtiExport::oops_do(oopClosure);
+ // SO_AllClasses
+ SystemDictionary::oops_do(oopClosure);
+ vmSymbols::oops_do(oopClosure);
+
+ // 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, oopClosure);
+
+ CodeCache::oops_do(oopClosure);
+ SymbolTable::oops_do(oopClosure);
+ StringTable::oops_do(oopClosure);
+
+ // (tw) TODO: Check if this is correct?
+ //CodeCache::scavenge_root_nmethods_oops_do(oopClosure);
+ //Management::oops_do(oopClosure);
+ //ref_processor()->weak_oops_do(&oopClosure);
+ //PSScavenge::reference_processor()->weak_oops_do(&oopClosure);
+}
+
void Universe::oops_do(OopClosure* f, bool do_all) {
f->do_oop((oop*) &_int_mirror);
@@ -1424,10 +1463,9 @@
}
// RC_TRACE macro has an embedded ResourceMark
- RC_TRACE(0x00000100,
- ("add: %s(%s): adding prev version ref for cached method @%d",
+ TRACE_RC2("add: %s(%s): adding prev version ref for cached method @%d",
method->name()->as_C_string(), method->signature()->as_C_string(),
- _prev_methods->length()));
+ _prev_methods->length());
methodHandle method_h(method);
jweak method_ref = JNIHandles::make_weak_global(method_h);
@@ -1454,9 +1492,8 @@
JNIHandles::destroy_weak_global(method_ref);
_prev_methods->remove_at(i);
} else {
- // RC_TRACE macro has an embedded ResourceMark
- RC_TRACE(0x00000400, ("add: %s(%s): previous cached method @%d is alive",
- m->name()->as_C_string(), m->signature()->as_C_string(), i));
+ TRACE_RC2("add: %s(%s): previous cached method @%d is alive",
+ m->name()->as_C_string(), m->signature()->as_C_string(), i);
}
}
} // end add_previous_version()
diff -r f5603a6e5042 src/share/vm/memory/universe.hpp
--- a/src/share/vm/memory/universe.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/memory/universe.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -113,6 +113,8 @@
friend class SystemDictionary;
friend class VMStructs;
friend class CompactingPermGenGen;
+ friend class Space;
+ friend class ContiguousSpace;
friend class VM_PopulateDumpSharedSpace;
friend jint universe_init();
@@ -246,7 +248,18 @@
static void compute_verify_oop_data();
+ static bool _is_redefining_gc_run;
+
public:
+
+ static bool is_redefining_gc_run() {
+ return _is_redefining_gc_run;
+ }
+
+ static void set_redefining_gc_run(bool b) {
+ _is_redefining_gc_run = b;
+ }
+
// Known classes in the VM
static klassOop boolArrayKlassObj() { return _boolArrayKlassObj; }
static klassOop byteArrayKlassObj() { return _byteArrayKlassObj; }
@@ -388,6 +401,8 @@
// Iteration
+ static void root_oops_do(OopClosure *f);
+
// Apply "f" to the addresses of all the direct heap pointers maintained
// as static fields of "Universe".
static void oops_do(OopClosure* f, bool do_all = false);
@@ -404,6 +419,7 @@
// Debugging
static bool verify_in_progress() { return _verify_in_progress; }
+ static void set_verify_in_progress(bool b) { _verify_in_progress = b; }
static void verify(bool allow_dirty = true, bool silent = false, bool option = true);
static int verify_count() { return _verify_count; }
static void print();
diff -r f5603a6e5042 src/share/vm/oops/arrayKlass.cpp
--- a/src/share/vm/oops/arrayKlass.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/arrayKlass.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -117,9 +117,9 @@
bool arrayKlass::compute_is_subtype_of(klassOop k) {
// An array is a subtype of Serializable, Clonable, and Object
- return k == SystemDictionary::Object_klass()
- || k == SystemDictionary::Cloneable_klass()
- || k == SystemDictionary::Serializable_klass();
+ return k->klass_part()->newest_version() == SystemDictionary::Object_klass()->klass_part()->newest_version()
+ || k->klass_part()->newest_version() == SystemDictionary::Cloneable_klass()->klass_part()->newest_version()
+ || k->klass_part()->newest_version() == SystemDictionary::Serializable_klass()->klass_part()->newest_version();
}
diff -r f5603a6e5042 src/share/vm/oops/constMethodKlass.cpp
--- a/src/share/vm/oops/constMethodKlass.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/constMethodKlass.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -93,6 +93,7 @@
MarkSweep::mark_and_push(cm->adr_method());
MarkSweep::mark_and_push(cm->adr_stackmap_data());
MarkSweep::mark_and_push(cm->adr_exception_table());
+ MarkSweep::mark_and_push(cm->adr_code_section_table());
// Performance tweak: We skip iterating over the klass pointer since we
// know that Universe::constMethodKlassObj never moves.
}
@@ -105,6 +106,7 @@
PSParallelCompact::mark_and_push(cm, cm_oop->adr_method());
PSParallelCompact::mark_and_push(cm, cm_oop->adr_stackmap_data());
PSParallelCompact::mark_and_push(cm, cm_oop->adr_exception_table());
+ PSParallelCompact::mark_and_push(cm, cm_oop->adr_code_section_table());
// Performance tweak: We skip iterating over the klass pointer since we
// know that Universe::constMethodKlassObj never moves.
}
@@ -116,6 +118,7 @@
blk->do_oop(cm->adr_method());
blk->do_oop(cm->adr_stackmap_data());
blk->do_oop(cm->adr_exception_table());
+ blk->do_oop(cm->adr_code_section_table());
// Get size before changing pointers.
// Don't call size() or oop_size() since that is a virtual call.
int size = cm->object_size();
@@ -133,6 +136,8 @@
if (mr.contains(adr)) blk->do_oop(adr);
adr = cm->adr_exception_table();
if (mr.contains(adr)) blk->do_oop(adr);
+ adr = cm->adr_code_section_table();
+ if (mr.contains(adr)) blk->do_oop(adr);
// Get size before changing pointers.
// Don't call size() or oop_size() since that is a virtual call.
int size = cm->object_size();
@@ -148,6 +153,7 @@
MarkSweep::adjust_pointer(cm->adr_method());
MarkSweep::adjust_pointer(cm->adr_stackmap_data());
MarkSweep::adjust_pointer(cm->adr_exception_table());
+ MarkSweep::adjust_pointer(cm->adr_code_section_table());
// Get size before changing pointers.
// Don't call size() or oop_size() since that is a virtual call.
int size = cm->object_size();
@@ -167,6 +173,7 @@
#if 0
PSParallelCompact::adjust_pointer(cm_oop->adr_method());
PSParallelCompact::adjust_pointer(cm_oop->adr_exception_table());
+ PSParallelCompact::adjust_pointer(cm_oop->adr_code_section_table());
PSParallelCompact::adjust_pointer(cm_oop->adr_stackmap_data());
#endif
oop* const beg_oop = cm_oop->oop_block_beg();
diff -r f5603a6e5042 src/share/vm/oops/constMethodOop.hpp
--- a/src/share/vm/oops/constMethodOop.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/constMethodOop.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -108,7 +108,7 @@
public:
oop* oop_block_beg() const { return adr_method(); }
- oop* oop_block_end() const { return adr_exception_table() + 1; }
+ oop* oop_block_end() const { return adr_code_section_table() + 1; }
private:
//
@@ -126,6 +126,10 @@
// table is pointing to Universe::the_empty_int_array
typeArrayOop _exception_table;
+
+ // (tw) Table mapping code sections for method forward points.
+ typeArrayOop _code_section_table;
+
//
// End of the oop block.
//
@@ -178,6 +182,28 @@
void set_exception_table(typeArrayOop e) { oop_store_without_check((oop*) &_exception_table, (oop) e); }
bool has_exception_handler() const { return exception_table() != NULL && exception_table()->length() > 0; }
+ // code section table
+ typeArrayOop code_section_table() const { return _code_section_table; }
+ void set_code_section_table(typeArrayOop e) { oop_store_without_check((oop*) &_code_section_table, (oop) e); }
+ bool has_code_section_table() const { return code_section_table() != NULL && code_section_table()->length() > 0; }
+ static const int ValuesPerCodeSectionEntry = 3;
+ int code_section_entries() const {
+ if (!has_code_section_table()) return 0;
+ return _code_section_table->length() / ValuesPerCodeSectionEntry;
+ }
+
+ int code_section_new_index_at(int index) const {
+ return _code_section_table->short_at(index * ValuesPerCodeSectionEntry);
+ }
+
+ int code_section_original_index_at(int index) const {
+ return _code_section_table->short_at(index * ValuesPerCodeSectionEntry + 1);
+ }
+
+ int code_section_length_at(int index) const {
+ return _code_section_table->short_at(index * ValuesPerCodeSectionEntry + 2);
+ }
+
void init_fingerprint() {
const uint64_t initval = CONST64(0x8000000000000000);
_fingerprint = initval;
@@ -279,6 +305,7 @@
oop* adr_method() const { return (oop*)&_method; }
oop* adr_stackmap_data() const { return (oop*)&_stackmap_data; }
oop* adr_exception_table() const { return (oop*)&_exception_table; }
+ oop* adr_code_section_table() const { return (oop*)&_code_section_table; }
bool is_conc_safe() { return _is_conc_safe; }
void set_is_conc_safe(bool v) { _is_conc_safe = v; }
diff -r f5603a6e5042 src/share/vm/oops/cpCacheOop.cpp
--- a/src/share/vm/oops/cpCacheOop.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/cpCacheOop.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -28,9 +28,15 @@
// Implememtation of ConstantPoolCacheEntry
+void ConstantPoolCacheEntry::copy_from(ConstantPoolCacheEntry *other) {
+ _flags = other->_flags; // flags
+}
+
void ConstantPoolCacheEntry::initialize_entry(int index) {
assert(0 < index && index < 0x10000, "sanity check");
_indices = index;
+ _f1 = NULL;
+ _f2 = 0;
assert(constant_pool_index() == index, "");
}
@@ -42,7 +48,7 @@
int ConstantPoolCacheEntry::as_flags(TosState state, bool is_final,
bool is_vfinal, bool is_volatile,
- bool is_method_interface, bool is_method) {
+ bool is_method_interface, bool is_method, bool is_old_method) {
int f = state;
assert( state < number_of_states, "Invalid state in as_flags");
@@ -57,7 +63,9 @@
if (is_method_interface) f |= 1;
f <<= 1;
if (is_method) f |= 1;
- f <<= ConstantPoolCacheEntry::hotSwapBit;
+ f <<= 1;
+ if (is_old_method) f |= 1;
+ f <<= ConstantPoolCacheEntry::oldMethodBit;
// Preserve existing flag bit values
#ifdef ASSERT
int old_state = ((_flags >> tosBits) & 0x0F);
@@ -133,7 +141,7 @@
const int field_index = orig_field_index / instanceKlass::next_offset;
assert(field_index <= field_index_mask,
"field index does not fit in low flag bits");
- set_flags(as_flags(field_type, is_final, false, is_volatile, false, false) |
+ set_flags(as_flags(field_type, is_final, false, is_volatile, false, false, false) |
(field_index & field_index_mask));
set_bytecode_1(get_code);
set_bytecode_2(put_code);
@@ -149,7 +157,8 @@
int vtable_index) {
assert(!is_secondary_entry(), "");
assert(method->interpreter_entry() != NULL, "should have been set at this point");
- assert(!method->is_obsolete(), "attempt to write obsolete method to cpCache");
+ // (tw) No longer valid assert
+ //assert(!method->is_obsolete(), "attempt to write obsolete method to cpCache");
bool change_to_virtual = (invoke_code == Bytecodes::_invokeinterface);
int byte_no = -1;
@@ -163,6 +172,9 @@
} else {
assert(vtable_index >= 0, "valid index");
set_f2(vtable_index);
+
+ // (tw) save method holder in f1 for virtual calls
+ set_f1(method());
}
byte_no = 2;
break;
@@ -208,7 +220,7 @@
needs_vfinal_flag,
false,
change_to_virtual,
- true)|
+ true, method->is_old())|
method()->size_of_parameters());
// Note: byte_no also appears in TemplateTable::resolve.
@@ -248,7 +260,7 @@
assert(instanceKlass::cast(interf)->is_interface(), "must be an interface");
set_f1(interf);
set_f2(index);
- set_flags(as_flags(as_TosState(method->result_type()), method->is_final_method(), false, false, false, true) | method()->size_of_parameters());
+ set_flags(as_flags(as_TosState(method->result_type()), method->is_final_method(), false, false, false, true, method->is_old()) | method()->size_of_parameters());
set_bytecode_1(Bytecodes::_invokeinterface);
}
@@ -279,7 +291,7 @@
}
bool is_final = true;
assert(signature_invoker->is_final_method(), "is_final");
- set_flags(as_flags(as_TosState(signature_invoker->result_type()), is_final, false, false, false, true) | param_size);
+ set_flags(as_flags(as_TosState(signature_invoker->result_type()), is_final, false, false, false, true, false) | param_size);
// do not do set_bytecode on a secondary CP cache entry
//set_bytecode_1(Bytecodes::_invokedynamic);
}
@@ -376,26 +388,13 @@
// If this constantPoolCacheEntry refers to old_method then update it
// to refer to new_method.
bool ConstantPoolCacheEntry::adjust_method_entry(methodOop old_method,
- methodOop new_method, bool * trace_name_printed) {
+ methodOop new_method) {
if (is_vfinal()) {
+
// virtual and final so f2() contains method ptr instead of vtable index
- if (f2() == (intptr_t)old_method) {
- // match old_method so need an update
- _f2 = (intptr_t)new_method;
- if (RC_TRACE_IN_RANGE(0x00100000, 0x00400000)) {
- if (!(*trace_name_printed)) {
- // RC_TRACE_MESG macro has an embedded ResourceMark
- RC_TRACE_MESG(("adjust: name=%s",
- Klass::cast(old_method->method_holder())->external_name()));
- *trace_name_printed = true;
- }
- // RC_TRACE macro has an embedded ResourceMark
- RC_TRACE(0x00400000, ("cpc vf-entry update: %s(%s)",
- new_method->name()->as_C_string(),
- new_method->signature()->as_C_string()));
- }
-
+ if((methodOop)f2() != NULL && ((methodOop)f2())->method_holder()->klass_part()->new_version()) {
+ initialize_entry(constant_pool_index());
return true;
}
@@ -403,65 +402,28 @@
return false;
}
- if ((oop)_f1 == NULL) {
- // NULL f1() means this is a virtual entry so bail out
- // We are assuming that the vtable index does not need change.
+ // (tw) check how to update interface methods!
+ if (bytecode_1() == Bytecodes::_invokevirtual || bytecode_2() == Bytecodes::_invokevirtual) {
+
+ if(((methodOop)f1())->method_holder()->klass_part()->new_version()) {
+ initialize_entry(constant_pool_index());
+ return true;
+ }
+
return false;
}
if ((oop)_f1 == old_method) {
_f1 = new_method;
- if (RC_TRACE_IN_RANGE(0x00100000, 0x00400000)) {
- if (!(*trace_name_printed)) {
- // RC_TRACE_MESG macro has an embedded ResourceMark
- RC_TRACE_MESG(("adjust: name=%s",
- Klass::cast(old_method->method_holder())->external_name()));
- *trace_name_printed = true;
- }
- // RC_TRACE macro has an embedded ResourceMark
- RC_TRACE(0x00400000, ("cpc entry update: %s(%s)",
- new_method->name()->as_C_string(),
- new_method->signature()->as_C_string()));
- }
-
+ return true;
+ } else if(_f1 != NULL && (bytecode_1() != Bytecodes::_invokeinterface && ((methodOop)f1())->method_holder()->klass_part()->new_version())) {
+ initialize_entry(constant_pool_index());
return true;
}
return false;
}
-bool ConstantPoolCacheEntry::is_interesting_method_entry(klassOop k) {
- if (!is_method_entry()) {
- // not a method entry so not interesting by default
- return false;
- }
-
- methodOop m = NULL;
- if (is_vfinal()) {
- // virtual and final so _f2 contains method ptr instead of vtable index
- m = (methodOop)_f2;
- } else if ((oop)_f1 == NULL) {
- // NULL _f1 means this is a virtual entry so also not interesting
- return false;
- } else {
- if (!((oop)_f1)->is_method()) {
- // _f1 can also contain a klassOop for an interface
- return false;
- }
- m = (methodOop)_f1;
- }
-
- assert(m != NULL && m->is_method(), "sanity check");
- if (m == NULL || !m->is_method() || m->method_holder() != k) {
- // robustness for above sanity checks or method is not in
- // the interesting class
- return false;
- }
-
- // the method is in the interesting class so the entry is interesting
- return true;
-}
-
void ConstantPoolCacheEntry::print(outputStream* st, int index) const {
// print separator
if (index == 0) tty->print_cr(" -------------");
@@ -502,38 +464,18 @@
// RedefineClasses() API support:
// If any entry of this constantPoolCache points to any of
// old_methods, replace it with the corresponding new_method.
-void constantPoolCacheOopDesc::adjust_method_entries(methodOop* old_methods, methodOop* new_methods,
- int methods_length, bool * trace_name_printed) {
-
- if (methods_length == 0) {
- // nothing to do if there are no methods
- return;
- }
-
- // get shorthand for the interesting class
- klassOop old_holder = old_methods[0]->method_holder();
+void constantPoolCacheOopDesc::adjust_entries(methodOop* old_methods, methodOop* new_methods,
+ int methods_length) {
for (int i = 0; i < length(); i++) {
- if (!entry_at(i)->is_interesting_method_entry(old_holder)) {
- // skip uninteresting methods
- continue;
- }
+ if (entry_at(i)->is_field_entry()) {
- // The constantPoolCache contains entries for several different
- // things, but we only care about methods. In fact, we only care
- // about methods in the same class as the one that contains the
- // old_methods. At this point, we have an interesting entry.
-
- for (int j = 0; j < methods_length; j++) {
- methodOop old_method = old_methods[j];
- methodOop new_method = new_methods[j];
-
- if (entry_at(i)->adjust_method_entry(old_method, new_method,
- trace_name_printed)) {
- // current old_method matched this entry and we updated it so
- // break out and get to the next interesting entry if there one
- break;
- }
+ // (tw) TODO: Update only field offsets and modify only constant pool entries that
+ // point to changed fields
+ entry_at(i)->initialize_entry(entry_at(i)->constant_pool_index());
+
+ } else if(entry_at(i)->is_method_entry()) {
+ entry_at(i)->adjust_method_entry(NULL, NULL);
}
}
}
diff -r f5603a6e5042 src/share/vm/oops/cpCacheOop.hpp
--- a/src/share/vm/oops/cpCacheOop.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/cpCacheOop.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -127,19 +127,24 @@
void set_bytecode_2(Bytecodes::Code code);
void set_f1(oop f1) {
oop existing_f1 = _f1; // read once
- assert(existing_f1 == NULL || existing_f1 == f1, "illegal field change");
+ // (tw) need to relax assertion for redefinition
+ // assert(existing_f1 == NULL || existing_f1 == f1, "illegal field change");
oop_store(&_f1, f1);
}
void set_f1_if_null_atomic(oop f1);
- void set_f2(intx f2) { assert(_f2 == 0 || _f2 == f2, "illegal field change"); _f2 = f2; }
+ void set_f2(intx f2) {
+ // (tw) need to relax assertion for redefinition
+ // assert(_f2 == 0 || _f2 == f2, "illegal field change");
+ _f2 = f2; }
int as_flags(TosState state, bool is_final, bool is_vfinal, bool is_volatile,
- bool is_method_interface, bool is_method);
+ bool is_method_interface, bool is_method, bool is_old_method);
void set_flags(intx flags) { _flags = flags; }
public:
// specific bit values in flag field
// Note: the interpreter knows this layout!
enum FlagBitValues {
+ oldMethodBit = 22,
hotSwapBit = 23,
methodInterface = 24,
volatileField = 25,
@@ -159,6 +164,8 @@
void initialize_entry(int original_index); // initialize primary entry
void initialize_secondary_entry(int main_index); // initialize secondary entry
+ void copy_from(ConstantPoolCacheEntry *other);
+
void set_field( // sets entry to resolved field state
Bytecodes::Code get_code, // the bytecode used for reading the field
Bytecodes::Code put_code, // the bytecode used for writing the field
@@ -287,9 +294,7 @@
// trace_name_printed is set to true if the current call has
// printed the klass name so that other routines in the adjust_*
// group don't print the klass name.
- bool adjust_method_entry(methodOop old_method, methodOop new_method,
- bool * trace_name_printed);
- bool is_interesting_method_entry(klassOop k);
+ bool adjust_method_entry(methodOop old_method, methodOop new_method);
bool is_field_entry() const { return (_flags & (1 << hotSwapBit)) == 0; }
bool is_method_entry() const { return (_flags & (1 << hotSwapBit)) != 0; }
@@ -397,12 +402,7 @@
return (base_offset() + ConstantPoolCacheEntry::size_in_bytes() * index);
}
- // RedefineClasses() API support:
- // If any entry of this constantPoolCache points to any of
- // old_methods, replace it with the corresponding new_method.
- // trace_name_printed is set to true if the current call has
- // printed the klass name so that other routines in the adjust_*
- // group don't print the klass name.
- void adjust_method_entries(methodOop* old_methods, methodOop* new_methods,
- int methods_length, bool * trace_name_printed);
+ // (tw) Update method and field references
+ void adjust_entries(methodOop* old_methods, methodOop* new_methods,
+ int methods_length);
};
diff -r f5603a6e5042 src/share/vm/oops/instanceKlass.cpp
--- a/src/share/vm/oops/instanceKlass.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/instanceKlass.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -157,12 +157,116 @@
}
+void instanceKlass::initialize_redefined_class() {
+
+ TRACE_RC3("initializing redefined class %s", name()->as_C_string());
+
+ assert(!is_initialized(), "");
+ assert(this->old_version() != NULL, "");
+ assert(is_linked(), "must be linked before");
+
+
+ instanceKlassHandle this_oop(Thread::current(), this->as_klassOop());
+ class UpdateStaticFieldClosure : public FieldClosure {
+
+ private:
+ instanceKlassHandle this_oop;
+
+ public:
+ UpdateStaticFieldClosure(instanceKlassHandle this_oop) {
+ this->this_oop = this_oop;
+ }
+
+ virtual void do_field(fieldDescriptor* fd) {
+ fieldDescriptor result;
+ bool found = ((instanceKlass *)(this_oop->old_version()->klass_part()))->find_local_field(fd->name(), fd->signature(), &result);
+
+ if (found && result.is_static()) {
+ int old_offset = result.offset();
+ assert(result.field_type() == fd->field_type(), "");
+
+ oop new_location = this_oop();
+ oop old_location = this_oop->old_version();
+ int offset = fd->offset();
+ TRACE_RC3("Copying static field value for field %s old_offset=%d new_offset=%d", fd->name()->as_C_string(), old_offset, offset);
+
+ oop cur_oop;
+
+ switch(result.field_type()) {
+
+ // Found static field with same name and type in the old klass => copy value from old to new klass
+
+ case T_BOOLEAN:
+ new_location->bool_field_put(offset, old_location->bool_field(old_offset));
+ DEBUG_ONLY(old_location->byte_field_put(old_offset, 0));
+ break;
+
+ case T_CHAR:
+ new_location->char_field_put(offset, old_location->char_field(old_offset));
+ DEBUG_ONLY(old_location->char_field_put(old_offset, 0));
+ break;
+
+ case T_FLOAT:
+ new_location->float_field_put(offset, old_location->float_field(old_offset));
+ DEBUG_ONLY(old_location->float_field_put(old_offset, 0));
+ break;
+
+ case T_DOUBLE:
+ new_location->double_field_put(offset, old_location->double_field(old_offset));
+ DEBUG_ONLY(old_location->double_field_put(old_offset, 0));
+ break;
+
+ case T_BYTE:
+ new_location->byte_field_put(offset, old_location->byte_field(old_offset));
+ DEBUG_ONLY(old_location->byte_field_put(old_offset, 0));
+ break;
+
+ case T_SHORT:
+ new_location->short_field_put(offset, old_location->short_field(old_offset));
+ DEBUG_ONLY(old_location->short_field_put(old_offset, 0));
+ break;
+
+ case T_INT:
+ new_location->int_field_put(offset, old_location->int_field(old_offset));
+ DEBUG_ONLY(old_location->int_field_put(old_offset, 0));
+ break;
+
+ case T_LONG:
+ new_location->long_field_put(offset, old_location->long_field(old_offset));
+ DEBUG_ONLY(old_location->long_field_put(old_offset, 0));
+ break;
+
+ case T_OBJECT:
+ case T_ARRAY:
+ cur_oop = old_location->obj_field(old_offset);
+ new_location->obj_field_raw_put(offset, cur_oop);
+ old_location->obj_field_raw_put(old_offset, NULL);
+ break;
+
+ default:
+ ShouldNotReachHere();
+ }
+ } else {
+
+ TRACE_RC2("New static field %s has_initial_value=%d", fd->name()->as_C_string(), (int)(fd->has_initial_value()));
+ // field not found
+ // (tw) TODO: Probably this call is not necessary here!
+ ClassFileParser::initialize_static_field(fd, Thread::current());
+ }
+ }
+ };
+
+ UpdateStaticFieldClosure cl(this_oop);
+ this->do_local_static_fields(&cl);
+}
+
+
bool instanceKlass::verify_code(
instanceKlassHandle this_oop, bool throw_verifyerror, TRAPS) {
// 1) Verify the bytecodes
Verifier::Mode mode =
throw_verifyerror ? Verifier::ThrowException : Verifier::NoException;
- return Verifier::verify(this_oop, mode, this_oop->should_verify_class(), CHECK_false);
+ return Verifier::verify(this_oop, mode, this_oop->should_verify_class(), true, CHECK_false);
}
@@ -269,7 +373,13 @@
jt->get_thread_stat()->perf_recursion_counts_addr(),
jt->get_thread_stat()->perf_timers_addr(),
PerfClassTraceTime::CLASS_VERIFY);
- bool verify_ok = verify_code(this_oop, throw_verifyerror, THREAD);
+ if (this_oop->is_redefining()) {
+ Thread::current()->set_pretend_new_universe(true);
+ }
+ bool verify_ok = verify_code(this_oop, throw_verifyerror, THREAD);
+ if (this_oop->is_redefining()) {
+ Thread::current()->set_pretend_new_universe(false);
+ }
if (!verify_ok) {
return false;
}
@@ -304,7 +414,8 @@
}
#endif
this_oop->set_init_state(linked);
- if (JvmtiExport::should_post_class_prepare()) {
+ // (tw) Must check for old version in order to prevent infinite loops.
+ if (JvmtiExport::should_post_class_prepare() && this_oop->old_version() == NULL /* JVMTI deadlock otherwise */) {
Thread *thread = THREAD;
assert(thread->is_Java_thread(), "thread->is_Java_thread()");
JvmtiExport::post_class_prepare((JavaThread *) thread, this_oop());
@@ -571,6 +682,18 @@
return false;
}
+bool instanceKlass::implements_interface_any_version(klassOop k) const {
+ k = k->klass_part()->newest_version();
+ if (this->newest_version() == k) return true;
+ assert(Klass::cast(k)->is_interface(), "should be an interface class");
+ for (int i = 0; i < transitive_interfaces()->length(); i++) {
+ if (((klassOop)transitive_interfaces()->obj_at(i))->klass_part()->newest_version() == k) {
+ return true;
+ }
+ }
+ return false;
+}
+
objArrayOop instanceKlass::allocate_objArray(int n, int length, TRAPS) {
if (length < 0) THROW_0(vmSymbols::java_lang_NegativeArraySizeException());
if (length > arrayOopDesc::max_array_length(T_OBJECT)) {
@@ -691,7 +814,25 @@
}
void instanceKlass::call_class_initializer_impl(instanceKlassHandle this_oop, TRAPS) {
+
+ ResourceMark rm(THREAD);
methodHandle h_method(THREAD, this_oop->class_initializer());
+
+ if (this_oop->revision_number() != -1){
+ methodOop m = NULL;
+ if (AllowAdvancedClassRedefinition) {
+ m = this_oop->find_method(vmSymbols::static_transformer_name(), vmSymbols::void_method_signature());
+ }
+ methodHandle method(m);
+ if (method() != NULL && method()->is_static()) {
+ TRACE_RC2("Calling static transformer instead of static initializer");
+ h_method = method;
+ } else if (!((instanceKlass*)this_oop->old_version()->klass_part())->is_not_initialized()) {
+ // Only execute the static initializer, if it was not yet executed for the old version of the class.
+ return;
+ }
+ }
+
assert(!this_oop->is_initialized(), "we cannot initialize twice");
if (TraceClassInitialization) {
tty->print("%d Initializing ", call_class_initializer_impl_counter++);
@@ -843,6 +984,153 @@
}
}
+void instanceKlass::store_update_information(GrowableArray &values) {
+ int *arr = NEW_C_HEAP_ARRAY(int, values.length());
+ for (int i=0; i typeInfoPair;
+
+void instanceKlass::store_type_check_information(GrowableArray< Pair > &values) {
+ Pair *arr = NEW_C_HEAP_ARRAY(typeInfoPair, values.length());
+ for (int i=0; ias_klassOop();
+
+ if (_fields_not_changed) {
+
+ class MyFieldClosure : public FieldClosure {
+
+ FieldEvolutionClosure *_cl;
+ public:
+ MyFieldClosure(FieldEvolutionClosure *cl) {_cl = cl; }
+ virtual void do_field(fieldDescriptor* fd) {
+ _cl->do_changed_field(fd, fd);
+ }
+ };
+
+ MyFieldClosure mfc(cl);
+ do_nonstatic_fields(&mfc);
+ } else {
+
+ _fields_not_changed = true;
+
+
+ GrowableArray fds;
+
+
+ while (true) {
+
+ for (int i=0; ifields()->length(); i+=instanceKlass::next_offset) {
+
+ fd.initialize(cur_new_klass_oop, i);
+
+ if (fd.is_static()) {
+ continue;
+ }
+
+ fds.append(fd);
+ }
+
+ if (cur_new_klass->super() != NULL) {
+ cur_new_klass_oop = cur_new_klass->super();
+ cur_new_klass = instanceKlass::cast(cur_new_klass_oop);
+ } else {
+ break;
+ }
+ }
+
+ GrowableArray sortedFds;
+
+ while (fds.length() > 0) {
+
+ int minOffset = 0x7fffffff;
+ int minIndex = -1;
+ for (int i=0; ioffset();
+ if (curOffset < minOffset) {
+ minOffset = curOffset;
+ minIndex = i;
+ }
+ }
+
+ sortedFds.append(fds.at(minIndex));
+ fds.remove_at(minIndex);
+ }
+
+
+ for (int i=0; ifields()->length(); j+=instanceKlass::next_offset) {
+
+ old_fd.initialize(cur_old_klass_oop, j);
+
+ if (old_fd.is_static()) {
+ continue;
+ }
+
+ if (old_fd.name() == fd.name() && old_fd.signature() == fd.signature()) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found && cur_old_klass->super()) {
+ cur_old_klass_oop = cur_old_klass->super();
+ cur_old_klass = instanceKlass::cast(cur_old_klass_oop);
+ } else {
+ break;
+ }
+ }
+
+ if (found) {
+ if (old_fd.offset() != fd.offset()) {
+ _fields_not_changed = false;
+ }
+ cl->do_changed_field(&old_fd, &fd);
+ } else {
+ _fields_not_changed = false;
+ cl->do_new_field(&fd);
+ }
+ }
+ }
+}
+
void instanceKlass::do_local_static_fields(FieldClosure* cl) {
fieldDescriptor fd;
int length = fields()->length();
@@ -1229,6 +1517,20 @@
return id;
}
+bool instanceKlass::update_jmethod_id(methodOop method, jmethodID newMethodID) {
+ size_t idnum = (size_t)method->method_idnum();
+ jmethodID* jmeths = methods_jmethod_ids_acquire();
+ size_t length; // length assigned as debugging crumb
+ jmethodID id = NULL;
+ if (jmeths != NULL && // If there is a cache
+ (length = (size_t)jmeths[0]) > idnum) { // and if it is long enough,
+ jmeths[idnum+1] = newMethodID; // Set the id (may be NULL)
+ return true;
+ }
+
+ return false;
+}
+
// Cache an itable index
void instanceKlass::set_cached_itable_index(size_t idnum, int index) {
@@ -1419,6 +1721,13 @@
last = b;
b = b->next();
}
+
+ // (tw) Hack as dependencies get wrong version of klassOop
+ if(this->old_version() != NULL) {
+ ((instanceKlass *)this->old_version()->klass_part())->remove_dependent_nmethod(nm);
+ return;
+ }
+
#ifdef ASSERT
tty->print_cr("### %s can't find dependent nmethod:", this->external_name());
nm->print();
@@ -2351,6 +2660,9 @@
klassOop mirrored_klass = java_lang_Class::as_klassOop(obj);
st->print(BULLET"fake entry for mirror: ");
mirrored_klass->print_value_on(st);
+ if (mirrored_klass != NULL) {
+ st->print_cr("revision: %d (oldest=%d, newest=%d)", mirrored_klass->klass_part()->revision_number(), mirrored_klass->klass_part()->oldest_version()->klass_part()->revision_number(), mirrored_klass->klass_part()->newest_version()->klass_part()->revision_number());
+ }
st->cr();
st->print(BULLET"fake entry resolved_constructor: ");
methodOop ctor = java_lang_Class::resolved_constructor(obj);
@@ -2552,9 +2864,8 @@
GrowableArray(2, true);
}
- // RC_TRACE macro has an embedded ResourceMark
- RC_TRACE(0x00000100, ("adding previous version ref for %s @%d, EMCP_cnt=%d",
- ikh->external_name(), _previous_versions->length(), emcp_method_count));
+ TRACE_RC3("adding previous version ref for %s @%d, EMCP_cnt=%d",
+ ikh->external_name(), _previous_versions->length(), emcp_method_count);
constantPoolHandle cp_h(ikh->constants());
jobject cp_ref;
if (cp_h->is_shared()) {
@@ -2570,8 +2881,7 @@
if (emcp_method_count == 0) {
// non-shared ConstantPool gets a weak reference
pv_node = new PreviousVersionNode(cp_ref, !cp_h->is_shared(), NULL);
- RC_TRACE(0x00000400,
- ("add: all methods are obsolete; flushing any EMCP weak refs"));
+ TRACE_RC3("add: all methods are obsolete; flushing any EMCP weak refs");
} else {
int local_count = 0;
GrowableArray* method_refs = new (ResourceObj::C_HEAP)
@@ -2600,8 +2910,7 @@
// caller is the VMThread and we are at a safepoint, this is a good
// time to clear out unused weak references.
- RC_TRACE(0x00000400, ("add: previous version length=%d",
- _previous_versions->length()));
+ TRACE_RC3("add: previous version length=%d", _previous_versions->length());
// skip the last entry since we just added it
for (int i = _previous_versions->length() - 2; i >= 0; i--) {
@@ -2626,13 +2935,12 @@
// do anything special with the index.
continue;
} else {
- RC_TRACE(0x00000400, ("add: previous version @%d is alive", i));
+ TRACE_RC3("add: previous version @%d is alive", i);
}
GrowableArray* method_refs = pv_node->prev_EMCP_methods();
if (method_refs != NULL) {
- RC_TRACE(0x00000400, ("add: previous methods length=%d",
- method_refs->length()));
+ TRACE_RC3("add: previous methods length=%d", method_refs->length());
for (int j = method_refs->length() - 1; j >= 0; j--) {
jweak method_ref = method_refs->at(j);
assert(method_ref != NULL, "weak method ref was unexpectedly cleared");
@@ -2651,11 +2959,8 @@
JNIHandles::destroy_weak_global(method_ref);
method_refs->remove_at(j);
} else {
- // RC_TRACE macro has an embedded ResourceMark
- RC_TRACE(0x00000400,
- ("add: %s(%s): previous method @%d in version @%d is alive",
- method->name()->as_C_string(), method->signature()->as_C_string(),
- j, i));
+ TRACE_RC3("add: %s(%s): previous method @%d in version @%d is alive",
+ method->name()->as_C_string(), method->signature()->as_C_string(), j, i);
}
}
}
@@ -2738,9 +3043,8 @@
// The current RedefineClasses() call has made all EMCP
// versions of this method obsolete so mark it as obsolete
// and remove the weak ref.
- RC_TRACE(0x00000400,
- ("add: %s(%s): flush obsolete method @%d in version @%d",
- m_name->as_C_string(), m_signature->as_C_string(), k, j));
+ TRACE_RC3("add: %s(%s): flush obsolete method @%d in version @%d",
+ m_name->as_C_string(), m_signature->as_C_string(), k, j);
method->set_is_obsolete();
JNIHandles::destroy_weak_global(method_ref);
diff -r f5603a6e5042 src/share/vm/oops/instanceKlass.hpp
--- a/src/share/vm/oops/instanceKlass.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/instanceKlass.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -86,6 +86,22 @@
virtual void do_field(fieldDescriptor* fd) = 0;
};
+// (tw) Iterates over the fields of the old and new class
+class FieldEvolutionClosure : public StackObj {
+public:
+ virtual void do_new_field(fieldDescriptor* fd) = 0;
+ virtual void do_old_field(fieldDescriptor* fd) = 0;
+ virtual void do_changed_field(fieldDescriptor* old_fd, fieldDescriptor *new_fd) = 0;
+};
+
+// (tw) Iterates over the methods of the old and new class
+class MethodEvolutionClosure : public StackObj {
+public:
+ virtual void do_new_method(methodOop oop) = 0;
+ virtual void do_old_method(methodOop oop) = 0;
+ virtual void do_changed_method(methodOop oldOop, methodOop newOop) = 0;
+};
+
#ifndef PRODUCT
// Print fields.
// If "obj" argument to constructor is NULL, prints static fields, otherwise prints non-static fields.
@@ -244,6 +260,11 @@
JvmtiCachedClassFieldMap* _jvmti_cached_class_field_map; // JVMTI: used during heap iteration
volatile u2 _idnum_allocated_count; // JNI/JVMTI: increments with the addition of methods, old ids don't change
+ // (tw) Field that allows for a short-path when calculating updated fields for the second time and
+ // no fields changed. Testing performance impact with this, can be removed later when the update
+ // information is cached.
+ bool _fields_not_changed;
+
// embedded Java vtable follows here
// embedded Java itables follows here
// embedded static fields follows here
@@ -374,6 +395,7 @@
// initialization (virtuals from Klass)
bool should_be_initialized() const; // means that initialize should be called
void initialize(TRAPS);
+ void initialize_redefined_class();
void link_class(TRAPS);
bool link_class_or_fail(TRAPS); // returns false on failure
void unlink_class();
@@ -519,6 +541,7 @@
static void get_jmethod_id_length_value(jmethodID* cache, size_t idnum,
size_t *length_p, jmethodID* id_p);
jmethodID jmethod_id_or_null(methodOop method);
+ bool update_jmethod_id(methodOop method, jmethodID newMethodID);
// cached itable index support
void set_cached_itable_index(size_t idnum, int index);
@@ -600,6 +623,7 @@
// subclass/subinterface checks
bool implements_interface(klassOop k) const;
+ bool implements_interface_any_version(klassOop k) const;
// Access to implementors of an interface. We only store the count
// of implementors, and in case, there are only a few
@@ -629,6 +653,12 @@
void do_local_static_fields(FieldClosure* cl);
void do_nonstatic_fields(FieldClosure* cl); // including inherited fields
void do_local_static_fields(void f(fieldDescriptor*, TRAPS), TRAPS);
+ void do_fields_evolution(FieldEvolutionClosure *cl);
+ void store_update_information(GrowableArray &values);
+ void clear_update_information();
+ void store_type_check_information(GrowableArray< Pair > &values);
+ void clear_type_check_information();
+
void methods_do(void f(methodOop method));
void array_klasses_do(void f(klassOop k));
diff -r f5603a6e5042 src/share/vm/oops/instanceKlassKlass.cpp
--- a/src/share/vm/oops/instanceKlassKlass.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/instanceKlassKlass.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -487,6 +487,28 @@
instanceKlass* ik = instanceKlass::cast(klassOop(obj));
klassKlass::oop_print_on(obj, st);
+ // (tw) Output revision number and revision numbers of older / newer and oldest / newest version of this class.
+
+ st->print(BULLET"revision: %d", ik->revision_number());
+
+ if (ik->new_version() != NULL) {
+ st->print(" (newer=%d)", ik->new_version()->klass_part()->revision_number());
+ }
+
+ if (ik->newest_version() != ik->new_version() && ik->newest_version() != obj) {
+ st->print(" (newest=%d)", ik->newest_version()->klass_part()->revision_number());
+ }
+
+ if (ik->old_version() != NULL) {
+ st->print(" (old=%d)", ik->old_version()->klass_part()->revision_number());
+ }
+
+ if (ik->oldest_version() != ik->old_version() && ik->oldest_version() != obj) {
+ st->print(" (oldest=%d)", ik->oldest_version()->klass_part()->revision_number());
+ }
+
+ st->cr();
+
st->print(BULLET"instance size: %d", ik->size_helper()); st->cr();
st->print(BULLET"klass size: %d", ik->object_size()); st->cr();
st->print(BULLET"access: "); ik->access_flags().print_on(st); st->cr();
@@ -685,7 +707,7 @@
}
guarantee(sib->as_klassOop()->is_klass(), "should be klass");
guarantee(sib->as_klassOop()->is_perm(), "should be in permspace");
- guarantee(sib->super() == super, "siblings should have same superklass");
+ guarantee(sib->super() == super || super->klass_part()->newest_version() == SystemDictionary::Object_klass(), "siblings should have same superklass");
sib = sib->next_sibling();
}
diff -r f5603a6e5042 src/share/vm/oops/instanceRefKlass.cpp
--- a/src/share/vm/oops/instanceRefKlass.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/instanceRefKlass.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -363,10 +363,13 @@
instanceKlass* ik = instanceKlass::cast(k);
// Check that we have the right class
- debug_only(static bool first_time = true);
- assert(k == SystemDictionary::Reference_klass() && first_time,
- "Invalid update of maps");
- debug_only(first_time = false);
+
+ // (tw) Asserts no longer valid for class redefinition
+ // debug_only(static bool first_time = true);
+
+ //assert(k == SystemDictionary::Reference_klass() && first_time,
+ // "Invalid update of maps");
+ //debug_only(first_time = false);
assert(ik->nonstatic_oop_map_count() == 1, "just checking");
OopMapBlock* map = ik->start_of_nonstatic_oop_maps();
diff -r f5603a6e5042 src/share/vm/oops/klass.cpp
--- a/src/share/vm/oops/klass.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/klass.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -40,6 +40,26 @@
return false;
}
+void Klass::update_supers_to_newest_version() {
+
+ if (super() != NULL) set_super(super()->klass_part()->newest_version());
+
+ for (uint i=0; iklass_part()->newest_version();
+ }
+ }
+
+ // Scan the array-of-objects
+ int cnt = secondary_supers()->length();
+ for (int i = 0; i < cnt; i++) {
+ klassOop cur = (klassOop)secondary_supers()->obj_at(i);
+ if (cur != NULL) {
+ secondary_supers()->obj_at_put(i, cur->klass_part()->newest_version());
+ }
+ }
+}
bool Klass::search_secondary_supers(klassOop k) const {
// Put some extra logic here out-of-line, before the search proper.
// This cuts down the size of the inline method.
@@ -145,6 +165,16 @@
kl->set_alloc_count(0);
kl->set_alloc_size(0);
+ kl->set_redefinition_flags(Klass::NoRedefinition);
+ kl->set_redefining(false);
+ kl->set_new_version(NULL);
+ kl->set_old_version(NULL);
+ kl->set_redefinition_index(-1);
+ kl->set_revision_number(-1);
+ kl->set_field_redefinition_policy(DynamicCheck);
+ kl->set_static_field_redefinition_policy(AccessDeletedMembers);
+ kl->set_method_redefinition_policy(AccessDeletedMembers);
+
kl->set_prototype_header(markOopDesc::prototype());
kl->set_biased_lock_revocation_count(0);
kl->set_last_biased_lock_bulk_revocation_time(0);
@@ -217,7 +247,7 @@
set_super(NULL);
oop_store_without_check((oop*) &_primary_supers[0], (oop) this->as_klassOop());
assert(super_depth() == 0, "Object must already be initialized properly");
- } else if (k != super() || k == SystemDictionary::Object_klass()) {
+ } else if (k != super() || k->klass_part()->super() == NULL) {
assert(super() == NULL || super() == SystemDictionary::Object_klass(),
"initialize this only once to a non-trivial value");
set_super(k);
diff -r f5603a6e5042 src/share/vm/oops/klass.hpp
--- a/src/share/vm/oops/klass.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/klass.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -150,6 +150,7 @@
void* operator new(size_t ignored, KlassHandle& klass, int size, TRAPS);
};
+template class Pair;
class Klass : public Klass_vtbl {
friend class VMStructs;
@@ -198,6 +199,39 @@
oop* oop_block_beg() const { return adr_secondary_super_cache(); }
oop* oop_block_end() const { return adr_next_sibling() + 1; }
+ // (tw) Different class redefinition flags of code evolution.
+ enum RedefinitionFlags {
+
+ // This class is not redefined at all!
+ NoRedefinition,
+
+ // There are changes to the class meta data.
+ ModifyClass = 1,
+
+ // The size of the class meta data changes.
+ ModifyClassSize = ModifyClass << 1,
+
+ // There are change to the instance format.
+ ModifyInstances = ModifyClassSize << 1,
+
+ // The size of instances changes.
+ ModifyInstanceSize = ModifyInstances << 1,
+
+ // A super type of this class is removed.
+ RemoveSuperType = ModifyInstanceSize << 1,
+
+ // This class (or one of its super classes) has an instance transformer method.
+ HasInstanceTransformer = RemoveSuperType << 1,
+ };
+
+ // (tw) Different policies dealing with deleted fields / methods in old code.
+ enum RedefinitionPolicy {
+ StaticCheck,
+ DynamicCheck,
+ AccessDeletedMembers,
+ AccessOldMembers
+ };
+
protected:
//
// The oop block. All oop fields must be declared here and only oop fields
@@ -217,6 +251,10 @@
oop _java_mirror;
// Superclass
klassOop _super;
+ // Old class
+ klassOop _old_version;
+ // New class
+ klassOop _new_version;
// Class name. Instance classes: java/lang/String, etc. Array classes: [I,
// [Ljava/lang/String;, etc. Set to zero for all other kinds of classes.
symbolOop _name;
@@ -232,6 +270,19 @@
jint _modifier_flags; // Processed access flags, for use by Class.getModifiers.
AccessFlags _access_flags; // Access flags. The class/interface distinction is stored here.
+ // (tw) Non-oop fields for enhanced class redefinition
+ jint _revision_number; // The revision number for redefined classes
+ jint _redefinition_index; // Index of this class when performing the redefinition
+ bool _subtype_changed;
+ int _redefinition_flags; // Level of class redefinition
+ bool _is_copying_backwards; // Does the class need to copy fields backwards? => possibly overwrite itself?
+ int * _update_information; // Update information
+ Pair * _type_check_information; // Offsets of object fields that need a type check
+ char _method_redefinition_policy;
+ char _field_redefinition_policy;
+ char _static_field_redefinition_policy;
+ bool _is_redefining;
+
#ifndef PRODUCT
int _verify_count; // to avoid redundant verifies
#endif
@@ -279,6 +330,99 @@
klassOop secondary_super_cache() const { return _secondary_super_cache; }
void set_secondary_super_cache(klassOop k) { oop_store_without_check((oop*) &_secondary_super_cache, (oop) k); }
+ // BEGIN class redefinition utilities
+
+ // double links between new and old version of a class
+ klassOop old_version() const { return _old_version; }
+ void set_old_version(klassOop klass) { assert(_old_version == NULL || klass == NULL, "Can only be set once!"); _old_version = klass; }
+ klassOop new_version() const { return _new_version; }
+ void set_new_version(klassOop klass) { assert(_new_version == NULL || klass == NULL, "Can only be set once!"); _new_version = klass; }
+
+ // A subtype of this class is no longer a subtype
+ bool has_subtype_changed() const { return _subtype_changed; }
+ void set_subtype_changed(bool b) { assert(is_newest_version() || new_version()->klass_part()->is_newest_version(), "must be newest or second newest version");
+ _subtype_changed = b; }
+ // state of being redefined
+ int redefinition_index() const { return _redefinition_index; }
+ void set_redefinition_index(int index) { _redefinition_index = index; }
+ void set_redefining(bool b) { _is_redefining = b; }
+ bool is_redefining() const { return _is_redefining; }
+ int redefinition_flags() const { return _redefinition_flags; }
+ bool check_redefinition_flag(int flags) const { return (_redefinition_flags & flags) != 0; }
+ void set_redefinition_flags(int flags) { _redefinition_flags = flags; }
+ bool is_copying_backwards() const { return _is_copying_backwards; }
+ void set_copying_backwards(bool b) { _is_copying_backwards = b; }
+
+ // update information
+ int *update_information() const { return _update_information; }
+ void set_update_information(int *info) { _update_information = info; }
+ Pair *type_check_information() const { return _type_check_information; }
+ void set_type_check_information(Pair *info) { _type_check_information = info; }
+
+ bool is_same_or_older_version(klassOop klass) const {
+ if (Klass::cast(klass) == this) { return true; }
+ else if (_old_version == NULL) { return false; }
+ else { return _old_version->klass_part()->is_same_or_older_version(klass); }
+ }
+
+ // Revision number for redefined classes, -1 for originally loaded classes
+ jint revision_number() const {
+ return _revision_number;
+ }
+
+ bool was_redefined() const {
+ return _revision_number != -1;
+ }
+
+ void set_revision_number(jint number) {
+ _revision_number = number;
+ }
+
+ char method_redefinition_policy() {
+ return _method_redefinition_policy;
+ }
+
+ void set_method_redefinition_policy(char v) {
+ _method_redefinition_policy = v;
+ }
+
+ char field_redefinition_policy() {
+ return _field_redefinition_policy;
+ }
+
+ void set_field_redefinition_policy(char v) {
+ _field_redefinition_policy = v;
+ }
+
+ char static_field_redefinition_policy() {
+ return _static_field_redefinition_policy;
+ }
+
+ void set_static_field_redefinition_policy(char v) {
+ _static_field_redefinition_policy = v;
+ }
+
+ klassOop oldest_version() const {
+ if (_old_version == NULL) { return this->as_klassOop(); }
+ else { return _old_version->klass_part()->oldest_version(); };
+ }
+
+ klassOop newest_version() const {
+ if (_new_version == NULL) { return this->as_klassOop(); }
+ else { return _new_version->klass_part()->newest_version(); };
+ }
+
+ klassOop active_version() const {
+ if (_new_version == NULL || _new_version->klass_part()->is_redefining()) { return this->as_klassOop(); assert(!this->is_redefining(), "just checking"); }
+ else { return _new_version->klass_part()->active_version(); };
+ }
+
+ bool is_newest_version() const {
+ return _new_version == NULL;
+ }
+
+ // END class redefinition utilities
+
objArrayOop secondary_supers() const { return _secondary_supers; }
void set_secondary_supers(objArrayOop k) { oop_store_without_check((oop*) &_secondary_supers, (oop) k); }
@@ -339,6 +483,8 @@
void set_next_sibling(klassOop s);
oop* adr_super() const { return (oop*)&_super; }
+ oop* adr_old_version() const { return (oop*)&_old_version; }
+ oop* adr_new_version() const { return (oop*)&_new_version; }
oop* adr_primary_supers() const { return (oop*)&_primary_supers[0]; }
oop* adr_secondary_super_cache() const { return (oop*)&_secondary_super_cache; }
oop* adr_secondary_supers()const { return (oop*)&_secondary_supers; }
@@ -469,6 +615,7 @@
return search_secondary_supers(k);
}
}
+ void update_supers_to_newest_version();
bool search_secondary_supers(klassOop k) const;
// Find LCA in class hierarchy
diff -r f5603a6e5042 src/share/vm/oops/klassKlass.cpp
--- a/src/share/vm/oops/klassKlass.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/klassKlass.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -45,6 +45,8 @@
Klass* k = Klass::cast(klassOop(obj));
// If we are alive it is valid to keep our superclass and subtype caches alive
MarkSweep::mark_and_push(k->adr_super());
+ MarkSweep::mark_and_push(k->adr_old_version());
+ MarkSweep::mark_and_push(k->adr_new_version());
for (juint i = 0; i < Klass::primary_super_limit(); i++)
MarkSweep::mark_and_push(k->adr_primary_supers()+i);
MarkSweep::mark_and_push(k->adr_secondary_super_cache());
@@ -65,6 +67,8 @@
Klass* k = Klass::cast(klassOop(obj));
// If we are alive it is valid to keep our superclass and subtype caches alive
PSParallelCompact::mark_and_push(cm, k->adr_super());
+ PSParallelCompact::mark_and_push(cm, k->adr_old_version());
+ PSParallelCompact::mark_and_push(cm, k->adr_new_version());
for (juint i = 0; i < Klass::primary_super_limit(); i++)
PSParallelCompact::mark_and_push(cm, k->adr_primary_supers()+i);
PSParallelCompact::mark_and_push(cm, k->adr_secondary_super_cache());
@@ -85,6 +89,8 @@
int size = oop_size(obj);
Klass* k = Klass::cast(klassOop(obj));
blk->do_oop(k->adr_super());
+ blk->do_oop(k->adr_old_version());
+ blk->do_oop(k->adr_new_version());
for (juint i = 0; i < Klass::primary_super_limit(); i++)
blk->do_oop(k->adr_primary_supers()+i);
blk->do_oop(k->adr_secondary_super_cache());
@@ -114,6 +120,10 @@
oop* adr;
adr = k->adr_super();
if (mr.contains(adr)) blk->do_oop(adr);
+ adr = k->adr_old_version();
+ if (mr.contains(adr)) blk->do_oop(adr);
+ adr = k->adr_new_version();
+ if (mr.contains(adr)) blk->do_oop(adr);
for (juint i = 0; i < Klass::primary_super_limit(); i++) {
adr = k->adr_primary_supers()+i;
if (mr.contains(adr)) blk->do_oop(adr);
@@ -149,6 +159,8 @@
Klass* k = Klass::cast(klassOop(obj));
MarkSweep::adjust_pointer(k->adr_super());
+ MarkSweep::adjust_pointer(k->adr_new_version());
+ MarkSweep::adjust_pointer(k->adr_old_version());
for (juint i = 0; i < Klass::primary_super_limit(); i++)
MarkSweep::adjust_pointer(k->adr_primary_supers()+i);
MarkSweep::adjust_pointer(k->adr_secondary_super_cache());
diff -r f5603a6e5042 src/share/vm/oops/klassVtable.cpp
--- a/src/share/vm/oops/klassVtable.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/klassVtable.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -82,7 +82,8 @@
vtable_length = Universe::base_vtable_size();
}
- if (super == NULL && !Universe::is_bootstrapping() &&
+ // (tw) TODO: Check if we can relax the condition on a fixed base vtable size
+ /*if (super == NULL && !Universe::is_bootstrapping() &&
vtable_length != Universe::base_vtable_size()) {
// Someone is attempting to redefine java.lang.Object incorrectly. The
// only way this should happen is from
@@ -92,9 +93,9 @@
vtable_length = Universe::base_vtable_size();
}
assert(super != NULL || vtable_length == Universe::base_vtable_size(),
- "bad vtable size for class Object");
+ "bad vtable size for class Object");*/
assert(vtable_length % vtableEntry::size() == 0, "bad vtable length");
- assert(vtable_length >= Universe::base_vtable_size(), "vtable too small");
+ //assert(vtable_length >= Universe::base_vtable_size(), "vtable too small");
}
int klassVtable::index_of(methodOop m, int len) const {
@@ -611,17 +612,13 @@
if (unchecked_method_at(index) == old_method) {
put_method_at(new_method, index);
- if (RC_TRACE_IN_RANGE(0x00100000, 0x00400000)) {
+ IF_TRACE_RC4 {
if (!(*trace_name_printed)) {
- // RC_TRACE_MESG macro has an embedded ResourceMark
- RC_TRACE_MESG(("adjust: name=%s",
- Klass::cast(old_method->method_holder())->external_name()));
+ TRACE_RC4("adjust: name=%s", Klass::cast(old_method->method_holder())->external_name());
*trace_name_printed = true;
}
- // RC_TRACE macro has an embedded ResourceMark
- RC_TRACE(0x00100000, ("vtable method update: %s(%s)",
- new_method->name()->as_C_string(),
- new_method->signature()->as_C_string()));
+ TRACE_RC4("vtable method update: %s(%s)", new_method->name()->as_C_string(),
+ new_method->signature()->as_C_string());
}
}
}
@@ -994,17 +991,13 @@
if (ime->method() == old_method) {
ime->initialize(new_method);
- if (RC_TRACE_IN_RANGE(0x00100000, 0x00400000)) {
+ IF_TRACE_RC4 {
if (!(*trace_name_printed)) {
- // RC_TRACE_MESG macro has an embedded ResourceMark
- RC_TRACE_MESG(("adjust: name=%s",
- Klass::cast(old_method->method_holder())->external_name()));
+ TRACE_RC4("adjust: name=%s", Klass::cast(old_method->method_holder())->external_name());
*trace_name_printed = true;
}
- // RC_TRACE macro has an embedded ResourceMark
- RC_TRACE(0x00200000, ("itable method update: %s(%s)",
- new_method->name()->as_C_string(),
- new_method->signature()->as_C_string()));
+ TRACE_RC4("itable method update: %s(%s)", new_method->name()->as_C_string(),
+ new_method->signature()->as_C_string());
}
break;
}
@@ -1198,6 +1191,7 @@
void klassVtable::verify_against(outputStream* st, klassVtable* vt, int index) {
vtableEntry* vte = &vt->table()[index];
+ if (vte->method() == NULL || table()[index].method() == NULL) return;
if (vte->method()->name() != table()[index].method()->name() ||
vte->method()->signature() != table()[index].method()->signature()) {
fatal("mismatched name/signature of vtable entries");
@@ -1217,6 +1211,8 @@
void vtableEntry::verify(klassVtable* vt, outputStream* st) {
NOT_PRODUCT(FlagSetting fs(IgnoreLockingAssertions, true));
+ // (tw) TODO: Check: Does not hold?
+ if (method() != NULL) {
assert(method() != NULL, "must have set method");
method()->verify();
// we sub_type, because it could be a miranda method
@@ -1224,7 +1220,13 @@
#ifndef PRODUCT
print();
#endif
- fatal(err_msg("vtableEntry " PTR_FORMAT ": method is from subclass", this));
+ klassOop first_klass = vt->klass()();
+ klassOop second_klass = method()->method_holder();
+ // (tw) the following fatal does not work for old versions of classes
+ if (first_klass->klass_part()->is_newest_version()) {
+ //fatal1("vtableEntry %#lx: method is from subclass", this);
+ }
+ }
}
}
@@ -1232,7 +1234,7 @@
void vtableEntry::print() {
ResourceMark rm;
- tty->print("vtableEntry %s: ", method()->name()->as_C_string());
+ tty->print("vtableEntry %s: ", (method() == NULL) ? "null" : method()->name()->as_C_string());
if (Verbose) {
tty->print("m %#lx ", (address)method());
}
@@ -1304,7 +1306,7 @@
for (int i = 0; i < length(); i++) {
methodOop m = unchecked_method_at(i);
if (m != NULL) {
- if (m->is_old()) {
+ if (m->is_old() || !m->method_holder()->klass_part()->is_newest_version()) {
return false;
}
}
diff -r f5603a6e5042 src/share/vm/oops/methodKlass.cpp
--- a/src/share/vm/oops/methodKlass.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/methodKlass.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -78,6 +78,10 @@
m->set_adapter_entry(NULL);
m->clear_code(); // from_c/from_i get set to c2i/i2i
+ m->set_forward_method(NULL);
+ m->set_new_version(NULL);
+ m->set_old_version(NULL);
+
if (access_flags.is_native()) {
m->clear_native_function();
m->set_signature_handler(NULL);
@@ -106,6 +110,9 @@
// Performance tweak: We skip iterating over the klass pointer since we
// know that Universe::methodKlassObj never moves.
MarkSweep::mark_and_push(m->adr_constMethod());
+ MarkSweep::mark_and_push(m->adr_forward_method());
+ MarkSweep::mark_and_push(m->adr_new_version());
+ MarkSweep::mark_and_push(m->adr_old_version());
MarkSweep::mark_and_push(m->adr_constants());
if (m->method_data() != NULL) {
MarkSweep::mark_and_push(m->adr_method_data());
@@ -120,6 +127,9 @@
// Performance tweak: We skip iterating over the klass pointer since we
// know that Universe::methodKlassObj never moves.
PSParallelCompact::mark_and_push(cm, m->adr_constMethod());
+ PSParallelCompact::mark_and_push(cm, m->adr_forward_method());
+ PSParallelCompact::mark_and_push(cm, m->adr_new_version());
+ PSParallelCompact::mark_and_push(cm, m->adr_old_version());
PSParallelCompact::mark_and_push(cm, m->adr_constants());
#ifdef COMPILER2
if (m->method_data() != NULL) {
@@ -138,6 +148,9 @@
// Performance tweak: We skip iterating over the klass pointer since we
// know that Universe::methodKlassObj never moves
blk->do_oop(m->adr_constMethod());
+ blk->do_oop(m->adr_forward_method());
+ blk->do_oop(m->adr_new_version());
+ blk->do_oop(m->adr_old_version());
blk->do_oop(m->adr_constants());
if (m->method_data() != NULL) {
blk->do_oop(m->adr_method_data());
@@ -157,6 +170,12 @@
oop* adr;
adr = m->adr_constMethod();
if (mr.contains(adr)) blk->do_oop(adr);
+ adr = m->adr_new_version();
+ if (mr.contains(adr)) blk->do_oop(adr);
+ adr = m->adr_forward_method();
+ if (mr.contains(adr)) blk->do_oop(adr);
+ adr = m->adr_old_version();
+ if (mr.contains(adr)) blk->do_oop(adr);
adr = m->adr_constants();
if (mr.contains(adr)) blk->do_oop(adr);
if (m->method_data() != NULL) {
@@ -176,6 +195,9 @@
// Performance tweak: We skip iterating over the klass pointer since we
// know that Universe::methodKlassObj never moves.
MarkSweep::adjust_pointer(m->adr_constMethod());
+ MarkSweep::adjust_pointer(m->adr_forward_method());
+ MarkSweep::adjust_pointer(m->adr_new_version());
+ MarkSweep::adjust_pointer(m->adr_old_version());
MarkSweep::adjust_pointer(m->adr_constants());
if (m->method_data() != NULL) {
MarkSweep::adjust_pointer(m->adr_method_data());
@@ -192,6 +214,8 @@
assert(obj->is_method(), "should be method");
methodOop m = methodOop(obj);
PSParallelCompact::adjust_pointer(m->adr_constMethod());
+ PSParallelCompact::adjust_pointer(m->adr_new_version());
+ PSParallelCompact::adjust_pointer(m->adr_old_version());
PSParallelCompact::adjust_pointer(m->adr_constants());
#ifdef COMPILER2
if (m->method_data() != NULL) {
@@ -210,6 +234,12 @@
p = m->adr_constMethod();
PSParallelCompact::adjust_pointer(p, beg_addr, end_addr);
+ p = m->adr_forward_method();
+ PSParallelCompact::adjust_pointer(p, beg_addr, end_addr);
+ p = m->adr_new_version();
+ PSParallelCompact::adjust_pointer(p, beg_addr, end_addr);
+ p = m->adr_old_version();
+ PSParallelCompact::adjust_pointer(p, beg_addr, end_addr);
p = m->adr_constants();
PSParallelCompact::adjust_pointer(p, beg_addr, end_addr);
@@ -234,7 +264,18 @@
methodOop m = methodOop(obj);
// get the effect of PrintOopAddress, always, for methods:
st->print_cr(" - this oop: "INTPTR_FORMAT, (intptr_t)m);
- st->print (" - method holder: "); m->method_holder()->print_value_on(st); st->cr();
+ st->print (" - method holder: "); m->method_holder()->print_value_on(st);
+
+ if (m->method_holder()->klass_part()->new_version() != NULL) {
+ st->print(" (old)");
+ }
+ st->cr();
+
+ st->print_cr(" - is obsolete: %d", (int)(m->is_obsolete()));
+ st->print_cr(" - is old: %d", (int)(m->is_old()));
+ st->print_cr(" - new version: %d", (int)(m->new_version()));
+ st->print_cr(" - old version: %d", (int)(m->old_version()));
+ st->print_cr(" - holder revision: %d", m->method_holder()->klass_part()->revision_number());
st->print (" - constants: "INTPTR_FORMAT" ", (address)m->constants());
m->constants()->print_value_on(st); st->cr();
st->print (" - access: 0x%x ", m->access_flags().as_int()); m->access_flags().print_on(st); st->cr();
diff -r f5603a6e5042 src/share/vm/oops/methodOop.cpp
--- a/src/share/vm/oops/methodOop.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/methodOop.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -305,6 +305,70 @@
}
+bool methodOopDesc::is_in_code_section(int bci) {
+ // There is no table => every bci is in the code section table.
+ if (!constMethod()->has_code_section_table()) return true;
+
+ constMethodOop m = constMethod();
+ for (int i = 0; i < m->code_section_entries(); ++i) {
+ u2 new_index = m->code_section_new_index_at(i);
+ u2 length = m->code_section_length_at(i);
+ if (bci >= new_index && bci < new_index + length) {
+ // We are in a specified code section.
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int methodOopDesc::calculate_forward_bci(int bci, methodOop new_method) {
+ int original_bci = -1;
+ if (constMethod()->has_code_section_table()) {
+ assert(is_in_code_section(bci), "can only forward in section");
+ // First calculate back to original bci.
+ constMethodOop m = constMethod();
+ for (int i = 0; i < m->code_section_entries(); ++i) {
+ u2 new_index = m->code_section_new_index_at(i);
+ u2 original_index = m->code_section_original_index_at(i);
+ u2 length = m->code_section_length_at(i);
+ if (bci >= new_index && bci < new_index + length) {
+ // We are in a specified code section.
+ original_bci = bci - new_index + original_index;
+ break;
+ }
+ }
+ assert (original_bci != -1, "must have been in code section");
+ } else {
+ // No code sections specified => we are in an original method.
+ original_bci = bci;
+ }
+
+ // We know the original bci => match to new method.
+ int new_bci = -1;
+ if (new_method->constMethod()->has_code_section_table()) {
+ // Map to new bci.
+ constMethodOop m = new_method->constMethod();
+ for (int i = 0; i < m->code_section_entries(); ++i) {
+ u2 new_index = m->code_section_new_index_at(i);
+ u2 original_index = m->code_section_original_index_at(i);
+ u2 length = m->code_section_length_at(i);
+ if (original_bci >= original_index && original_bci < original_index + length) {
+ new_bci = original_bci - original_index + new_index;
+ break;
+ }
+ }
+ assert (new_bci != -1, "must have found new code section");
+
+ } else {
+ // We are in an original method.
+ new_bci = original_bci;
+ }
+
+ return new_bci;
+}
+
+
int methodOopDesc::extra_stack_words() {
// not an inline function, to avoid a header dependency on Interpreter
return extra_stack_entries() * Interpreter::stackElementSize;
@@ -986,6 +1050,9 @@
// Reset correct method/const method, method size, and parameter info
newcm->set_method(newm());
newm->set_constMethod(newcm);
+ newm->set_forward_method(newm->forward_method());
+ newm->set_new_version(newm->new_version());
+ newm->set_old_version(newm->old_version());
assert(newcm->method() == newm(), "check");
newm->constMethod()->set_code_size(new_code_length);
newm->constMethod()->set_constMethod_size(new_const_method_size);
diff -r f5603a6e5042 src/share/vm/oops/methodOop.hpp
--- a/src/share/vm/oops/methodOop.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/methodOop.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -96,6 +96,11 @@
AccessFlags _access_flags; // Access flags
int _vtable_index; // vtable index of this method (see VtableIndexFlag)
// note: can have vtables with >2**16 elements (because of inheritance)
+ // (tw) Newer version of method available?
+ methodOop _forward_method;
+ methodOop _new_version;
+ methodOop _old_version;
+
#ifdef CC_INTERP
int _result_index; // C++ interpreter needs for converting results to/from stack
#endif
@@ -150,6 +155,32 @@
int name_index() const { return constMethod()->name_index(); }
void set_name_index(int index) { constMethod()->set_name_index(index); }
+ methodOop forward_method() const {return _forward_method; }
+ void set_forward_method(methodOop m) { _forward_method = m; }
+ bool has_forward_method() const { return forward_method() != NULL; }
+ methodOop new_version() const {return _new_version; }
+ void set_new_version(methodOop m) { _new_version = m; }
+ methodOop newest_version() { if(_new_version == NULL) return this; else return new_version()->newest_version(); }
+
+ methodOop old_version() const {return _old_version; };
+ void set_old_version(methodOop m) {
+ if (m == NULL) {
+ _old_version = NULL;
+ return;
+ }
+
+ assert(_old_version == NULL, "may only be set once");
+ assert(this->code_size() == m->code_size(), "must have same code length");
+ _old_version = m;
+ }
+
+ methodOop oldest_version() const {
+ if(_old_version == NULL) return (methodOop)this;
+ else {
+ return old_version()->oldest_version();
+ }
+ }
+
// signature
symbolOop signature() const { return _constants->symbol_at(signature_index()); }
int signature_index() const { return constMethod()->signature_index(); }
@@ -614,6 +645,10 @@
// Inline cache support
void cleanup_inline_caches();
+ // (tw) Method forwarding support.
+ bool is_in_code_section(int bci);
+ int calculate_forward_bci(int bci, methodOop new_method);
+
// Find if klass for method is loaded
bool is_klass_loaded_by_klass_index(int klass_index) const;
bool is_klass_loaded(int refinfo_index, bool must_be_resolved = false) const;
@@ -669,6 +704,9 @@
// Garbage collection support
oop* adr_constMethod() const { return (oop*)&_constMethod; }
+ oop* adr_forward_method() const { return (oop*)&_forward_method; }
+ oop* adr_new_version() const { return (oop*)&_new_version; }
+ oop* adr_old_version() const { return (oop*)&_old_version; }
oop* adr_constants() const { return (oop*)&_constants; }
oop* adr_method_data() const { return (oop*)&_method_data; }
};
diff -r f5603a6e5042 src/share/vm/oops/oop.hpp
--- a/src/share/vm/oops/oop.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/oop.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -81,6 +81,7 @@
narrowOop* compressed_klass_addr();
void set_klass(klassOop k);
+ void set_klass_no_check(klassOop k);
// For klass field compression
int klass_gap() const;
@@ -121,6 +122,7 @@
bool is_objArray() const;
bool is_symbol() const;
bool is_klass() const;
+ bool is_instanceKlass() const;
bool is_thread() const;
bool is_method() const;
bool is_constMethod() const;
diff -r f5603a6e5042 src/share/vm/oops/oop.inline.hpp
--- a/src/share/vm/oops/oop.inline.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/oops/oop.inline.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -78,6 +78,14 @@
}
}
+inline void oopDesc::set_klass_no_check(klassOop k) {
+ if (UseCompressedOops) {
+ oop_store_without_check(compressed_klass_addr(), (oop)k);
+ } else {
+ oop_store_without_check(klass_addr(), (oop) k);
+ }
+}
+
inline int oopDesc::klass_gap() const {
return *(int*)(((intptr_t)this) + klass_gap_offset_in_bytes());
}
@@ -111,6 +119,7 @@
inline bool oopDesc::is_javaArray() const { return blueprint()->oop_is_javaArray(); }
inline bool oopDesc::is_symbol() const { return blueprint()->oop_is_symbol(); }
inline bool oopDesc::is_klass() const { return blueprint()->oop_is_klass(); }
+inline bool oopDesc::is_instanceKlass() const { return blueprint()->oop_is_instanceKlass(); }
inline bool oopDesc::is_thread() const { return blueprint()->oop_is_thread(); }
inline bool oopDesc::is_method() const { return blueprint()->oop_is_method(); }
inline bool oopDesc::is_constMethod() const { return blueprint()->oop_is_constMethod(); }
diff -r f5603a6e5042 src/share/vm/prims/jni.cpp
--- a/src/share/vm/prims/jni.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/prims/jni.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -299,7 +299,7 @@
}
}
klassOop k = SystemDictionary::resolve_from_stream(class_name, class_loader,
- Handle(), &st, true,
+ Handle(), &st, true, KlassHandle(),
CHECK_NULL);
if (TraceClassResolution && k != NULL) {
diff -r f5603a6e5042 src/share/vm/prims/jvm.cpp
--- a/src/share/vm/prims/jvm.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/prims/jvm.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -811,7 +811,7 @@
Handle protection_domain (THREAD, JNIHandles::resolve(pd));
klassOop k = SystemDictionary::resolve_from_stream(class_name, class_loader,
protection_domain, &st,
- verify != 0,
+ verify != 0, KlassHandle(),
CHECK_NULL);
if (TraceClassResolution && k != NULL) {
diff -r f5603a6e5042 src/share/vm/prims/jvmtiClassFileReconstituter.cpp
--- a/src/share/vm/prims/jvmtiClassFileReconstituter.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/prims/jvmtiClassFileReconstituter.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -120,6 +120,7 @@
constMethodHandle const_method(thread(), method->constMethod());
u2 line_num_cnt = 0;
int stackmap_len = 0;
+ int local_variable_table_length = 0;
// compute number and length of attributes -- FIXME: for now no LVT
int attr_count = 0;
@@ -154,6 +155,25 @@
attr_size += 2 + 4 + stackmap_len;
}
}
+ if (method->has_localvariable_table()) {
+ local_variable_table_length = method->localvariable_table_length();
+ ++attr_count;
+ if (local_variable_table_length != 0) {
+ // Compute the size of the local variable table attribute (VM stores raw):
+ // LocalVariableTable_attribute {
+ // u2 attribute_name_index;
+ // u4 attribute_length;
+ // u2 local_variable_table_length;
+ // {
+ // u2 start_pc;
+ // u2 length;
+ // u2 name_index;
+ // u2 descriptor_index;
+ // u2 index;
+ // }
+ attr_size += 2 + 4 + 2 + local_variable_table_length * (2 + 2 + 2 + 2 + 2);
+ }
+ }
typeArrayHandle exception_table(thread(), const_method->exception_table());
int exception_table_length = exception_table->length();
@@ -188,6 +208,10 @@
write_stackmap_table_attribute(method, stackmap_len);
}
+ if (local_variable_table_length != 0) {
+ write_local_variable_table_attribute(method, local_variable_table_length);
+ }
+
// FIXME: write LVT attribute
}
@@ -355,6 +379,36 @@
}
}
+// Write LineNumberTable attribute
+// JVMSpec| LocalVariableTable_attribute {
+// JVMSpec| u2 attribute_name_index;
+// JVMSpec| u4 attribute_length;
+// JVMSpec| u2 local_variable_table_length;
+// JVMSpec| { u2 start_pc;
+// JVMSpec| u2 length;
+// JVMSpec| u2 name_index;
+// JVMSpec| u2 descriptor_index;
+// JVMSpec| u2 index;
+// JVMSpec| } local_variable_table[local_variable_table_length];
+// JVMSpec| }
+void JvmtiClassFileReconstituter::write_local_variable_table_attribute(methodHandle method, u2 num_entries) {
+ write_attribute_name_index("LocalVariableTable");
+ write_u4(2 + num_entries * (2 + 2 + 2 + 2 + 2));
+ write_u2(num_entries);
+
+ assert(method->localvariable_table_length() == num_entries, "just checking");
+
+ LocalVariableTableElement *elem = method->localvariable_table_start();
+ for (int j=0; jlocalvariable_table_length(); j++) {
+ write_u2(elem->start_bci);
+ write_u2(elem->length);
+ write_u2(elem->name_cp_index);
+ write_u2(elem->descriptor_cp_index);
+ write_u2(elem->slot);
+ elem++;
+ }
+}
+
// Write stack map table attribute
// JSR-202| StackMapTable_attribute {
// JSR-202| u2 attribute_name_index;
diff -r f5603a6e5042 src/share/vm/prims/jvmtiClassFileReconstituter.hpp
--- a/src/share/vm/prims/jvmtiClassFileReconstituter.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/prims/jvmtiClassFileReconstituter.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -114,6 +114,7 @@
void write_source_debug_extension_attribute();
u2 line_number_table_entries(methodHandle method);
void write_line_number_table_attribute(methodHandle method, u2 num_entries);
+ void write_local_variable_table_attribute(methodHandle method, u2 num_entries);
void write_stackmap_table_attribute(methodHandle method, int stackmap_table_len);
u2 inner_classes_attribute_length();
void write_inner_classes_attribute(int length);
diff -r f5603a6e5042 src/share/vm/prims/jvmtiEnv.cpp
--- a/src/share/vm/prims/jvmtiEnv.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/prims/jvmtiEnv.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -240,7 +240,10 @@
class_definitions[index].klass = jcls;
}
VM_RedefineClasses op(class_count, class_definitions, jvmti_class_load_kind_retransform);
- VMThread::execute(&op);
+ {
+ MutexLocker sd_mutex(RedefineClasses_lock);
+ VMThread::execute(&op);
+ }
return (op.check_error());
} /* end RetransformClasses */
@@ -249,9 +252,12 @@
// class_definitions - pre-checked for NULL
jvmtiError
JvmtiEnv::RedefineClasses(jint class_count, const jvmtiClassDefinition* class_definitions) {
-//TODO: add locking
+
VM_RedefineClasses op(class_count, class_definitions, jvmti_class_load_kind_redefine);
- VMThread::execute(&op);
+ {
+ MutexLocker sd_mutex(RedefineClasses_lock);
+ VMThread::execute(&op);
+ }
return (op.check_error());
} /* end RedefineClasses */
diff -r f5603a6e5042 src/share/vm/prims/jvmtiExport.cpp
--- a/src/share/vm/prims/jvmtiExport.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/prims/jvmtiExport.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -756,7 +756,7 @@
void JvmtiExport::post_pending_compiled_method_unload_events() {
JavaThread* self = JavaThread::current();
- assert(!self->owns_locks(), "can't hold locks");
+ assert(!self->owns_locks_but_redefine_classes_lock(), "can't hold locks");
// Indicates if this is the first activiation of this function.
// In theory the profiler's callback could call back into VM and provoke
@@ -2392,7 +2392,7 @@
// iterate over any code blob descriptors collected and post a
// DYNAMIC_CODE_GENERATED event to the profiler.
JvmtiDynamicCodeEventCollector::~JvmtiDynamicCodeEventCollector() {
- assert(!JavaThread::current()->owns_locks(), "all locks must be released to post deferred events");
+ assert(!JavaThread::current()->owns_locks_but_redefine_classes_lock(), "all locks must be released to post deferred events");
// iterate over any code blob descriptors that we collected
if (_code_blobs != NULL) {
for (int i=0; i<_code_blobs->length(); i++) {
diff -r f5603a6e5042 src/share/vm/prims/jvmtiImpl.cpp
--- a/src/share/vm/prims/jvmtiImpl.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/prims/jvmtiImpl.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -254,6 +254,8 @@
void JvmtiBreakpoint::each_method_version_do(method_action meth_act) {
((methodOopDesc*)_method->*meth_act)(_bci);
+ // (tw) TODO: Check how we can implement this differently here!
+
// add/remove breakpoint to/from versions of the method that
// are EMCP. Directly or transitively obsolete methods are
// not saved in the PreviousVersionInfo.
@@ -293,10 +295,10 @@
for (int i = methods->length() - 1; i >= 0; i--) {
methodHandle method = methods->at(i);
if (method->name() == m_name && method->signature() == m_signature) {
- RC_TRACE(0x00000800, ("%sing breakpoint in %s(%s)",
+ TRACE_RC3("%sing breakpoint in %s(%s)",
meth_act == &methodOopDesc::set_breakpoint ? "sett" : "clear",
method->name()->as_C_string(),
- method->signature()->as_C_string()));
+ method->signature()->as_C_string());
assert(!method->is_obsolete(), "only EMCP methods here");
((methodOopDesc*)method()->*meth_act)(_bci);
diff -r f5603a6e5042 src/share/vm/prims/jvmtiRedefineClasses.cpp
--- a/src/share/vm/prims/jvmtiRedefineClasses.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/prims/jvmtiRedefineClasses.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -1,26 +1,26 @@
/*
- * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
+* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* This code is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License version 2 only, as
+* published by the Free Software Foundation.
+*
+* This code is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+* version 2 for more details (a copy is included in the LICENSE file that
+* accompanied this code).
+*
+* You should have received a copy of the GNU General Public License version
+* 2 along with this work; if not, write to the Free Software Foundation,
+* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+*
+* Please 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 "incls/_precompiled.incl"
# include "incls/_jvmtiRedefineClasses.cpp.incl"
@@ -28,479 +28,601 @@
objArrayOop VM_RedefineClasses::_old_methods = NULL;
objArrayOop VM_RedefineClasses::_new_methods = NULL;
-methodOop* VM_RedefineClasses::_matching_old_methods = NULL;
-methodOop* VM_RedefineClasses::_matching_new_methods = NULL;
-methodOop* VM_RedefineClasses::_deleted_methods = NULL;
-methodOop* VM_RedefineClasses::_added_methods = NULL;
+int* VM_RedefineClasses::_matching_old_methods = NULL;
+int* VM_RedefineClasses::_matching_new_methods = NULL;
+int* VM_RedefineClasses::_deleted_methods = NULL;
+int* VM_RedefineClasses::_added_methods = NULL;
int VM_RedefineClasses::_matching_methods_length = 0;
int VM_RedefineClasses::_deleted_methods_length = 0;
int VM_RedefineClasses::_added_methods_length = 0;
klassOop VM_RedefineClasses::_the_class_oop = NULL;
-
-VM_RedefineClasses::VM_RedefineClasses(jint class_count,
- const jvmtiClassDefinition *class_defs,
- JvmtiClassLoadKind class_load_kind) {
+// Holds the revision number of the current class redefinition
+int VM_RedefineClasses::_revision_number = -1;
+
+VM_RedefineClasses::VM_RedefineClasses(jint class_count, const jvmtiClassDefinition *class_defs, JvmtiClassLoadKind class_load_kind)
+ : VM_GC_Operation(Universe::heap()->total_full_collections()) {
+ RC_TIMER_START(_timer_total);
_class_count = class_count;
_class_defs = class_defs;
_class_load_kind = class_load_kind;
- _res = JVMTI_ERROR_NONE;
+ _updated_oops = NULL;
+ _result = JVMTI_ERROR_NONE;
}
+VM_RedefineClasses::~VM_RedefineClasses() {
+ {
+ MonitorLockerEx ml(RedefinitionSync_lock);
+ Threads::set_wait_at_instrumentation_entry(false);
+ ml.notify_all();
+ }
+
+ unlock_threads();
+ RC_TIMER_STOP(_timer_total);
+
+ if (TimeRedefineClasses) {
+ tty->print_cr("Timing Prologue: %d", _timer_prologue.milliseconds());
+ tty->print_cr("Timing Class Loading: %d", _timer_class_loading.milliseconds());
+ tty->print_cr("Timing Waiting for Lock: %d", _timer_wait_for_locks.milliseconds());
+ tty->print_cr("Timing Class Linking: %d", _timer_class_linking.milliseconds());
+ tty->print_cr("Timing Check Type: %d", _timer_check_type.milliseconds());
+ tty->print_cr("Timing Prepare Redefinition: %d", _timer_prepare_redefinition.milliseconds());
+ tty->print_cr("Timing Redefinition GC: %d", _timer_redefinition.milliseconds());
+ tty->print_cr("Timing Epilogue: %d", _timer_vm_op_epilogue.milliseconds());
+ tty->print_cr("------------------------------------------------------------------");
+ tty->print_cr("Total Time: %d", _timer_total.milliseconds());
+ }
+}
+
+// Searches for all affected classes and performs a sorting such that a supertype is always before a subtype.
+jvmtiError VM_RedefineClasses::find_sorted_affected_classes(GrowableArray *all_affected_klasses) {
+
+ // Create array with all classes for which the redefine command was given
+ GrowableArray klasses_to_redefine;
+ for (int i=0; i<_class_count; i++) {
+ oop mirror = JNIHandles::resolve_non_null(_class_defs[i].klass);
+ instanceKlassHandle klass_handle(Thread::current(), java_lang_Class::as_klassOop(mirror));
+ klasses_to_redefine.append(klass_handle);
+ assert(klass_handle->new_version() == NULL, "Must be new class");
+ }
+
+ // Find classes not directly redefined, but affected by a redefinition (because one of its supertypes is redefined)
+ GrowableArray affected_classes;
+ FindAffectedKlassesClosure closure(&klasses_to_redefine, &affected_classes);
+
+ // Trace affected classes
+ TRACE_RC1("Klasses affected: %d", affected_classes.length());
+ IF_TRACE_RC2 {
+ for (int i=0; iname()->as_C_string());
+ }
+ }
+
+ // Add the array of affected classes and the array of redefined classes to get a list of all classes that need a redefinition
+ all_affected_klasses->appendAll(&klasses_to_redefine);
+ all_affected_klasses->appendAll(&affected_classes);
+
+ // Sort the affected klasses such that a supertype is always on a smaller array index than its subtype.
+ jvmtiError result = do_topological_class_sorting(_class_defs, _class_count, &affected_classes, all_affected_klasses, Thread::current());
+ IF_TRACE_RC2 {
+ TRACE_RC2("Redefine order: ");
+ for (int i=0; ilength(); i++) {
+ TRACE_RC2("%s", all_affected_klasses->at(i)->name()->as_C_string());
+ }
+ }
+
+ return result;
+}
+
+// Searches for the class bytes of the given class and returns them as a byte array.
+jvmtiError VM_RedefineClasses::find_class_bytes(instanceKlassHandle the_class, const unsigned char **class_bytes, jint *class_byte_count, jboolean *not_changed) {
+
+ *not_changed = false;
+
+ // Search for the index in the redefinition array that corresponds to the current class
+ int j;
+ for (j=0; j<_class_count; j++) {
+ oop mirror = JNIHandles::resolve_non_null(_class_defs[j].klass);
+ klassOop the_class_oop = java_lang_Class::as_klassOop(mirror);
+ if (the_class_oop == the_class()) {
+ break;
+ }
+ }
+
+ if (j == _class_count) {
+
+ *not_changed = true;
+
+ // Redefine with same bytecodes. This is a class that is only indirectly affected by redefinition,
+ // so the user did not specify a different bytecode for that class.
+
+ if (the_class->get_cached_class_file_bytes() == NULL) {
+ // not cached, we need to reconstitute the class file from VM representation
+ constantPoolHandle constants(Thread::current(), the_class->constants());
+ ObjectLocker ol(constants, Thread::current()); // lock constant pool while we query it
+
+ JvmtiClassFileReconstituter reconstituter(the_class);
+ if (reconstituter.get_error() != JVMTI_ERROR_NONE) {
+ return reconstituter.get_error();
+ }
+
+ *class_byte_count = (jint)reconstituter.class_file_size();
+ *class_bytes = (unsigned char*)reconstituter.class_file_bytes();
+
+ } else {
+
+ // it is cached, get it from the cache
+ *class_byte_count = the_class->get_cached_class_file_len();
+ *class_bytes = the_class->get_cached_class_file_bytes();
+ }
+
+ } else {
+
+ // Redefine with bytecodes at index j
+ *class_bytes = _class_defs[j].class_bytes;
+ *class_byte_count = _class_defs[j].class_byte_count;
+ }
+
+ return JVMTI_ERROR_NONE;
+}
+
+// Prologue of the VM operation, called on the Java thread in parallel to normal program execution
bool VM_RedefineClasses::doit_prologue() {
- if (_class_count == 0) {
- _res = JVMTI_ERROR_NONE;
+
+ _revision_number++;
+ TRACE_RC1("Redefinition with revision number %d started!", _revision_number);
+
+ assert(Thread::current()->is_Java_thread(), "must be Java thread");
+ RC_TIMER_START(_timer_prologue);
+
+ if (!check_arguments()) {
+ RC_TIMER_STOP(_timer_prologue);
return false;
}
- if (_class_defs == NULL) {
- _res = JVMTI_ERROR_NULL_POINTER;
+
+ // We first load new class versions in the prologue, because somewhere down the
+ // call chain it is required that the current thread is a Java thread.
+ _new_classes = new (ResourceObj::C_HEAP) GrowableArray(5, true);
+ _result = load_new_class_versions(Thread::current());
+
+ TRACE_RC1("Loaded new class versions!");
+ if (_result != JVMTI_ERROR_NONE) {
+ TRACE_RC1("error occured: %d!", _result);
+ delete _new_classes;
+ _new_classes = NULL;
+ RC_TIMER_STOP(_timer_prologue);
return false;
}
+
+ TRACE_RC2("nearly finished");
+ VM_GC_Operation::doit_prologue();
+ RC_TIMER_STOP(_timer_prologue);
+ TRACE_RC2("doit_prologue finished!");
+ return true;
+}
+
+// Checks basic properties of the arguments of the redefinition command.
+bool VM_RedefineClasses::check_arguments() {
+
+ if (_class_count == 0) RC_ABORT(JVMTI_ERROR_NONE);
+ if (_class_defs == NULL) RC_ABORT(JVMTI_ERROR_NULL_POINTER);
for (int i = 0; i < _class_count; i++) {
- if (_class_defs[i].klass == NULL) {
- _res = JVMTI_ERROR_INVALID_CLASS;
- return false;
+ if (_class_defs[i].klass == NULL) RC_ABORT(JVMTI_ERROR_INVALID_CLASS);
+ if (_class_defs[i].class_byte_count == 0) RC_ABORT(JVMTI_ERROR_INVALID_CLASS_FORMAT);
+ if (_class_defs[i].class_bytes == NULL) RC_ABORT(JVMTI_ERROR_NULL_POINTER);
+ }
+
+ return true;
+}
+
+jvmtiError VM_RedefineClasses::check_exception() const {
+ Thread* THREAD = Thread::current();
+ if (HAS_PENDING_EXCEPTION) {
+
+ symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name();
+ TRACE_RC1("parse_stream exception: '%s'", ex_name->as_C_string());
+ if (TraceRedefineClasses >= 1) {
+ java_lang_Throwable::print(PENDING_EXCEPTION, tty);
+ tty->print_cr("");
}
- if (_class_defs[i].class_byte_count == 0) {
- _res = JVMTI_ERROR_INVALID_CLASS_FORMAT;
- return false;
- }
- if (_class_defs[i].class_bytes == NULL) {
- _res = JVMTI_ERROR_NULL_POINTER;
- return false;
+ CLEAR_PENDING_EXCEPTION;
+
+ if (ex_name == vmSymbols::java_lang_UnsupportedClassVersionError()) {
+ return JVMTI_ERROR_UNSUPPORTED_VERSION;
+ } else if (ex_name == vmSymbols::java_lang_ClassFormatError()) {
+ return JVMTI_ERROR_INVALID_CLASS_FORMAT;
+ } else if (ex_name == vmSymbols::java_lang_ClassCircularityError()) {
+ return JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION;
+ } else if (ex_name == vmSymbols::java_lang_NoClassDefFoundError()) {
+ // The message will be "XXX (wrong name: YYY)"
+ return JVMTI_ERROR_NAMES_DONT_MATCH;
+ } else if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) {
+ return JVMTI_ERROR_OUT_OF_MEMORY;
+ } else {
+ // Just in case more exceptions can be thrown..
+ return JVMTI_ERROR_FAILS_VERIFICATION;
}
}
- // Start timer after all the sanity checks; not quite accurate, but
- // better than adding a bunch of stop() calls.
- RC_TIMER_START(_timer_vm_op_prologue);
-
- // We first load new class versions in the prologue, because somewhere down the
- // call chain it is required that the current thread is a Java thread.
- _res = load_new_class_versions(Thread::current());
- if (_res != JVMTI_ERROR_NONE) {
- // Free os::malloc allocated memory in load_new_class_version.
- os::free(_scratch_classes);
- RC_TIMER_STOP(_timer_vm_op_prologue);
- return false;
+ return JVMTI_ERROR_NONE;
+}
+
+// Loads all new class versions and stores the instanceKlass handles in an array.
+jvmtiError VM_RedefineClasses::load_new_class_versions(TRAPS) {
+
+ ResourceMark rm(THREAD);
+
+ TRACE_RC1("===================================================================");
+ TRACE_RC1("load new class versions (%d)", _class_count);
+
+ // Retrieve an array of all classes that need to be redefined
+ GrowableArray all_affected_klasses;
+ jvmtiError err = find_sorted_affected_classes(&all_affected_klasses);
+ if (err != JVMTI_ERROR_NONE) {
+ TRACE_RC1("Error finding sorted affected classes: %d", (int)err);
+ return err;
}
- RC_TIMER_STOP(_timer_vm_op_prologue);
- return true;
-}
-
-void VM_RedefineClasses::doit() {
- Thread *thread = Thread::current();
-
- if (UseSharedSpaces) {
- // Sharing is enabled so we remap the shared readonly space to
- // shared readwrite, private just in case we need to redefine
- // a shared class. We do the remap during the doit() phase of
- // the safepoint to be safer.
- if (!CompactingPermGenGen::remap_shared_readonly_as_readwrite()) {
- RC_TRACE_WITH_THREAD(0x00000001, thread,
- ("failed to remap shared readonly space to readwrite, private"));
- _res = JVMTI_ERROR_INTERNAL;
- return;
+
+ JvmtiThreadState *state = JvmtiThreadState::state_for(JavaThread::current());
+
+ _max_redefinition_flags = Klass::NoRedefinition;
+ jvmtiError result = JVMTI_ERROR_NONE;
+
+ for (int i=0; iname()->as_C_string());
+
+ the_class->link_class(THREAD);
+ result = check_exception();
+ if (result != JVMTI_ERROR_NONE) break;
+
+ // Find new class bytes
+ const unsigned char* class_bytes;
+ jint class_byte_count;
+ jvmtiError error;
+ jboolean not_changed;
+ if ((error = find_class_bytes(the_class, &class_bytes, &class_byte_count, ¬_changed)) != JVMTI_ERROR_NONE) {
+ TRACE_RC1("Error finding class bytes: %d", (int)error);
+ result = error;
+ break;
+ }
+ assert(class_bytes != NULL && class_byte_count != 0, "Class bytes defined at this point!");
+
+
+ // Set redefined class handle in JvmtiThreadState class.
+ // This redefined class is sent to agent event handler for class file
+ // load hook event.
+ state->set_class_being_redefined(&the_class, _class_load_kind);
+
+ TRACE_RC2("Before resolving from stream");
+
+ RC_TIMER_STOP(_timer_prologue);
+ RC_TIMER_START(_timer_class_loading);
+
+
+ // Parse the stream.
+ Handle the_class_loader(THREAD, the_class->class_loader());
+ Handle protection_domain(THREAD, the_class->protection_domain());
+ symbolHandle the_class_sym = symbolHandle(THREAD, the_class->name());
+ ClassFileStream st((u1*) class_bytes, class_byte_count, (char *)"__VM_RedefineClasses__");
+ instanceKlassHandle new_class(THREAD, SystemDictionary::resolve_from_stream(the_class_sym,
+ the_class_loader,
+ protection_domain,
+ &st,
+ true,
+ the_class,
+ THREAD));
+
+ not_changed = false;
+
+ RC_TIMER_STOP(_timer_class_loading);
+ RC_TIMER_START(_timer_prologue);
+
+ TRACE_RC2("After resolving class from stream!");
+ // Clear class_being_redefined just to be sure.
+ state->clear_class_being_redefined();
+
+ result = check_exception();
+ if (result != JVMTI_ERROR_NONE) break;
+
+#ifdef ASSERT
+
+ assert(new_class() != NULL, "Class could not be loaded!");
+ assert(new_class() != the_class(), "must be different");
+ assert(new_class->new_version() == NULL && new_class->old_version() != NULL, "");
+
+
+ objArrayOop k_interfaces = new_class->local_interfaces();
+ for (int j=0; jlength(); j++) {
+ assert(((klassOop)k_interfaces->obj_at(j))->klass_part()->is_newest_version(), "just checking");
+ }
+
+ if (!THREAD->is_Compiler_thread()) {
+
+ TRACE_RC2("name=%s loader=%d protection_domain=%d", the_class->name()->as_C_string(), (int)(the_class->class_loader()), (int)(the_class->protection_domain()));
+ // If we are on the compiler thread, we must not try to resolve a class.
+ klassOop systemLookup = SystemDictionary::resolve_or_null(the_class->name(), the_class->class_loader(), the_class->protection_domain(), THREAD);
+
+ if (systemLookup != NULL) {
+ assert(systemLookup == new_class->old_version(), "Old class must be in system dictionary!");
+
+
+ Klass *subklass = new_class()->klass_part()->subklass();
+ while (subklass != NULL) {
+ assert(subklass->new_version() == NULL, "Most recent version of class!");
+ subklass = subklass->next_sibling();
+ }
+ } else {
+ // This can happen for reflection generated classes.. ?
+ CLEAR_PENDING_EXCEPTION;
+ }
+ }
+
+#endif
+
+ IF_TRACE_RC1 {
+ if (new_class->layout_helper() != the_class->layout_helper()) {
+ TRACE_RC1("Instance size change for class %s: new=%d old=%d", new_class->name()->as_C_string(), new_class->layout_helper(), the_class->layout_helper());
+ }
+ }
+
+ // Set the new version of the class
+ new_class->set_revision_number(_revision_number);
+ new_class->set_redefinition_index(i);
+ the_class->set_new_version(new_class());
+ _new_classes->append(new_class);
+
+ assert(new_class->new_version() == NULL, "");
+
+ int redefinition_flags = Klass::NoRedefinition;
+
+ if (not_changed) {
+ redefinition_flags = Klass::NoRedefinition;
+ } else if (AllowAdvancedClassRedefinition) {
+ redefinition_flags = calculate_redefinition_flags(new_class);
+ } else {
+ jvmtiError allowed = check_redefinition_allowed(new_class);
+ if (allowed != JVMTI_ERROR_NONE) {
+ TRACE_RC1("Error redefinition not allowed!");
+ result = allowed;
+ break;
+ }
+ redefinition_flags = Klass::ModifyClass;
+ }
+
+ if (new_class->super() != NULL) {
+ redefinition_flags = redefinition_flags | new_class->super()->klass_part()->redefinition_flags();
+ }
+
+ for (int j=0; jlocal_interfaces()->length(); j++) {
+ redefinition_flags = redefinition_flags | ((klassOop)new_class->local_interfaces()->obj_at(j))->klass_part()->redefinition_flags();
+ }
+
+ new_class->set_redefinition_flags(redefinition_flags);
+
+ _max_redefinition_flags = _max_redefinition_flags | redefinition_flags;
+
+ if ((redefinition_flags & Klass::ModifyInstances) != 0) {
+ // TODO: Check if watch access flags of static fields are updated correctly.
+ calculate_instance_update_information(_new_classes->at(i)());
+ } else {
+ assert(new_class->layout_helper() >> 1 == new_class->old_version()->klass_part()->layout_helper() >> 1, "must be equal");
+ assert(new_class->fields()->length() == ((instanceKlass*)new_class->old_version()->klass_part())->fields()->length(), "must be equal");
+
+ fieldDescriptor fd_new;
+ fieldDescriptor fd_old;
+ for (int i=0; ifields()->length(); i+=instanceKlass::next_offset) {
+ fd_new.initialize(new_class(), i);
+ fd_old.initialize(new_class->old_version(), i);
+ transfer_special_access_flags(&fd_old, &fd_new);
+ }
+ }
+
+ IF_TRACE_RC3 {
+ if (new_class->super() != NULL) {
+ TRACE_RC3("Super class is %s", new_class->super()->klass_part()->name()->as_C_string());
+ }
+ }
+
+#ifdef ASSERT
+ assert(new_class->super() == NULL || new_class->super()->klass_part()->new_version() == NULL, "Super klass must be newest version!");
+
+ the_class->vtable()->verify(tty);
+ new_class->vtable()->verify(tty);
+#endif
+
+ TRACE_RC2("Verification done!");
+
+ if (i == all_affected_klasses.length() - 1) {
+
+ // This was the last class processed => check if additional classes have been loaded in the meantime
+
+ RC_TIMER_STOP(_timer_prologue);
+ lock_threads();
+ RC_TIMER_START(_timer_prologue);
+
+ for (int j=0; jklass_part()->subklass();
+ Klass *cur_klass = initial_subklass;
+ while(cur_klass != NULL) {
+
+ if(cur_klass->oop_is_instance() && cur_klass->is_newest_version()) {
+ instanceKlassHandle handle(THREAD, cur_klass->as_klassOop());
+ if (!all_affected_klasses.contains(handle)) {
+
+ int k = i + 1;
+ for (; kis_subtype_of(cur_klass->as_klassOop())) {
+ break;
+ }
+ }
+ all_affected_klasses.insert_before(k, handle);
+ TRACE_RC2("Adding newly loaded class to affected classes: %s", cur_klass->name()->as_C_string());
+ }
+ }
+
+ cur_klass = cur_klass->next_sibling();
+ }
+ }
+
+ int new_count = all_affected_klasses.length() - 1 - i;
+ if (new_count != 0) {
+
+ unlock_threads();
+ TRACE_RC1("Found new number of affected classes: %d", new_count);
+ }
}
}
- for (int i = 0; i < _class_count; i++) {
- redefine_single_class(_class_defs[i].klass, _scratch_classes[i], thread);
+ if (result != JVMTI_ERROR_NONE) {
+ rollback();
+ return result;
}
- // Disable any dependent concurrent compilations
- SystemDictionary::notice_modification();
-
- // Set flag indicating that some invariants are no longer true.
- // See jvmtiExport.hpp for detailed explanation.
- JvmtiExport::set_has_redefined_a_class();
+
+ RC_TIMER_STOP(_timer_prologue);
+ RC_TIMER_START(_timer_class_linking);
+ // Link and verify new classes _after_ all classes have been updated in the system dictionary!
+ for (int i=0; inew_version());
+
+ TRACE_RC2("Linking class %d/%d %s", i, all_affected_klasses.length(), the_class->name()->as_C_string());
+ new_class->link_class(THREAD);
+
+ result = check_exception();
+ if (result != JVMTI_ERROR_NONE) break;
+ }
+ RC_TIMER_STOP(_timer_class_linking);
+ RC_TIMER_START(_timer_prologue);
+
+ if (result != JVMTI_ERROR_NONE) {
+ rollback();
+ return result;
+ }
+
+ TRACE_RC2("All classes loaded!");
#ifdef ASSERT
- SystemDictionary::classes_do(check_class, thread);
+ for (int i=0; inew_version() != NULL, "Must have been redefined");
+ instanceKlassHandle new_version = instanceKlassHandle(THREAD, the_class->new_version());
+ assert(new_version->new_version() == NULL, "Must be newest version");
+
+ if (!(new_version->super() == NULL || new_version->super()->klass_part()->new_version() == NULL)) {
+ new_version()->print();
+ new_version->super()->print();
+ }
+ assert(new_version->super() == NULL || new_version->super()->klass_part()->new_version() == NULL, "Super class must be newest version");
+ }
+
+ SystemDictionary::classes_do(check_class, THREAD);
+
#endif
+
+ TRACE_RC1("Finished verification!");
+ return JVMTI_ERROR_NONE;
}
-void VM_RedefineClasses::doit_epilogue() {
- // Free os::malloc allocated memory.
- // The memory allocated in redefine will be free'ed in next VM operation.
- os::free(_scratch_classes);
-
- if (RC_TRACE_ENABLED(0x00000004)) {
- // Used to have separate timers for "doit" and "all", but the timer
- // overhead skewed the measurements.
- jlong doit_time = _timer_rsc_phase1.milliseconds() +
- _timer_rsc_phase2.milliseconds();
- jlong all_time = _timer_vm_op_prologue.milliseconds() + doit_time;
-
- RC_TRACE(0x00000004, ("vm_op: all=" UINT64_FORMAT
- " prologue=" UINT64_FORMAT " doit=" UINT64_FORMAT, all_time,
- _timer_vm_op_prologue.milliseconds(), doit_time));
- RC_TRACE(0x00000004,
- ("redefine_single_class: phase1=" UINT64_FORMAT " phase2=" UINT64_FORMAT,
- _timer_rsc_phase1.milliseconds(), _timer_rsc_phase2.milliseconds()));
+void VM_RedefineClasses::lock_threads() {
+
+ RC_TIMER_START(_timer_wait_for_locks);
+
+
+ JavaThread *javaThread = Threads::first();
+ while (javaThread != NULL) {
+ if (javaThread->is_Compiler_thread() && javaThread != Thread::current()) {
+ CompilerThread *compilerThread = (CompilerThread *)javaThread;
+ compilerThread->set_should_bailout(true);
+ }
+ javaThread = javaThread->next();
}
+
+ int cnt = 0;
+ javaThread = Threads::first();
+ while (javaThread != NULL) {
+ if (javaThread->is_Compiler_thread() && javaThread != Thread::current()) {
+ CompilerThread *compilerThread = (CompilerThread *)javaThread;
+ compilerThread->compilation_mutex()->lock();
+ cnt++;
+ }
+ javaThread = javaThread->next();
+ }
+
+ TRACE_RC2("Locked %d compiler threads", cnt);
+
+ cnt = 0;
+ javaThread = Threads::first();
+ while (javaThread != NULL) {
+ if (javaThread != Thread::current()) {
+ javaThread->redefine_classes_mutex()->lock();
+ }
+ javaThread = javaThread->next();
+ }
+
+
+ TRACE_RC2("Locked %d threads", cnt);
+
+ RC_TIMER_STOP(_timer_wait_for_locks);
}
-bool VM_RedefineClasses::is_modifiable_class(oop klass_mirror) {
- // classes for primitives cannot be redefined
- if (java_lang_Class::is_primitive(klass_mirror)) {
- return false;
+void VM_RedefineClasses::unlock_threads() {
+
+ int cnt = 0;
+ JavaThread *javaThread = Threads::first();
+ Thread *thread = Thread::current();
+ while (javaThread != NULL) {
+ if (javaThread->is_Compiler_thread() && javaThread != Thread::current()) {
+ CompilerThread *compilerThread = (CompilerThread *)javaThread;
+ if (compilerThread->compilation_mutex()->owned_by_self()) {
+ compilerThread->compilation_mutex()->unlock();
+ cnt++;
+ }
+ }
+ javaThread = javaThread->next();
}
- klassOop the_class_oop = java_lang_Class::as_klassOop(klass_mirror);
- // classes for arrays cannot be redefined
- if (the_class_oop == NULL || !Klass::cast(the_class_oop)->oop_is_instance()) {
- return false;
+
+ TRACE_RC2("Unlocked %d compiler threads", cnt);
+
+ cnt = 0;
+ javaThread = Threads::first();
+ while (javaThread != NULL) {
+ if (javaThread != Thread::current()) {
+ if (javaThread->redefine_classes_mutex()->owned_by_self()) {
+ javaThread->redefine_classes_mutex()->unlock();
+ }
+ }
+ javaThread = javaThread->next();
}
- return true;
+
+ TRACE_RC2("Unlocked %d threads", cnt);
}
-// Append the current entry at scratch_i in scratch_cp to *merge_cp_p
-// where the end of *merge_cp_p is specified by *merge_cp_length_p. For
-// direct CP entries, there is just the current entry to append. For
-// indirect and double-indirect CP entries, there are zero or more
-// referenced CP entries along with the current entry to append.
-// Indirect and double-indirect CP entries are handled by recursive
-// calls to append_entry() as needed. The referenced CP entries are
-// always appended to *merge_cp_p before the referee CP entry. These
-// referenced CP entries may already exist in *merge_cp_p in which case
-// there is nothing extra to append and only the current entry is
-// appended.
-void VM_RedefineClasses::append_entry(constantPoolHandle scratch_cp,
- int scratch_i, constantPoolHandle *merge_cp_p, int *merge_cp_length_p,
- TRAPS) {
-
- // append is different depending on entry tag type
- switch (scratch_cp->tag_at(scratch_i).value()) {
-
- // The old verifier is implemented outside the VM. It loads classes,
- // but does not resolve constant pool entries directly so we never
- // see Class entries here with the old verifier. Similarly the old
- // verifier does not like Class entries in the input constant pool.
- // The split-verifier is implemented in the VM so it can optionally
- // and directly resolve constant pool entries to load classes. The
- // split-verifier can accept either Class entries or UnresolvedClass
- // entries in the input constant pool. We revert the appended copy
- // back to UnresolvedClass so that either verifier will be happy
- // with the constant pool entry.
- case JVM_CONSTANT_Class:
- {
- // revert the copy to JVM_CONSTANT_UnresolvedClass
- (*merge_cp_p)->unresolved_klass_at_put(*merge_cp_length_p,
- scratch_cp->klass_name_at(scratch_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.
- map_index(scratch_cp, scratch_i, *merge_cp_length_p);
- }
- (*merge_cp_length_p)++;
- } break;
-
- // these are direct CP entries so they can be directly appended,
- // but double and long take two constant pool entries
- case JVM_CONSTANT_Double: // fall through
- case JVM_CONSTANT_Long:
- {
- scratch_cp->copy_entry_to(scratch_i, *merge_cp_p, *merge_cp_length_p,
- THREAD);
-
- 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.
- map_index(scratch_cp, scratch_i, *merge_cp_length_p);
- }
- (*merge_cp_length_p) += 2;
- } break;
-
- // these are direct CP entries so they can be directly appended
- case JVM_CONSTANT_Float: // fall through
- case JVM_CONSTANT_Integer: // fall through
- case JVM_CONSTANT_Utf8: // fall through
-
- // This was an indirect CP entry, but it has been changed into
- // an interned string so this entry can be directly appended.
- case JVM_CONSTANT_String: // fall through
-
- // These were indirect CP entries, but they have been changed into
- // symbolOops so these entries can be directly appended.
- case JVM_CONSTANT_UnresolvedClass: // fall through
- case JVM_CONSTANT_UnresolvedString:
- {
- scratch_cp->copy_entry_to(scratch_i, *merge_cp_p, *merge_cp_length_p,
- THREAD);
-
- 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.
- map_index(scratch_cp, scratch_i, *merge_cp_length_p);
- }
- (*merge_cp_length_p)++;
- } break;
-
- // this is an indirect CP entry so it needs special handling
- case JVM_CONSTANT_NameAndType:
- {
- int name_ref_i = scratch_cp->name_ref_index_at(scratch_i);
- int new_name_ref_i = 0;
- bool match = (name_ref_i < *merge_cp_length_p) &&
- scratch_cp->compare_entry_to(name_ref_i, *merge_cp_p, name_ref_i,
- THREAD);
- if (!match) {
- // forward reference in *merge_cp_p or not a direct match
-
- int found_i = scratch_cp->find_matching_entry(name_ref_i, *merge_cp_p,
- THREAD);
- if (found_i != 0) {
- guarantee(found_i != name_ref_i,
- "compare_entry_to() and find_matching_entry() do not agree");
-
- // Found a matching entry somewhere else in *merge_cp_p so
- // just need a mapping entry.
- new_name_ref_i = found_i;
- map_index(scratch_cp, name_ref_i, found_i);
- } else {
- // no match found so we have to append this entry to *merge_cp_p
- append_entry(scratch_cp, name_ref_i, merge_cp_p, merge_cp_length_p,
- THREAD);
- // The above call to append_entry() can only append one entry
- // so the post call query of *merge_cp_length_p is only for
- // the sake of consistency.
- new_name_ref_i = *merge_cp_length_p - 1;
- }
- }
-
- int signature_ref_i = scratch_cp->signature_ref_index_at(scratch_i);
- int new_signature_ref_i = 0;
- match = (signature_ref_i < *merge_cp_length_p) &&
- scratch_cp->compare_entry_to(signature_ref_i, *merge_cp_p,
- signature_ref_i, THREAD);
- if (!match) {
- // forward reference in *merge_cp_p or not a direct match
-
- int found_i = scratch_cp->find_matching_entry(signature_ref_i,
- *merge_cp_p, THREAD);
- if (found_i != 0) {
- guarantee(found_i != signature_ref_i,
- "compare_entry_to() and find_matching_entry() do not agree");
-
- // Found a matching entry somewhere else in *merge_cp_p so
- // just need a mapping entry.
- new_signature_ref_i = found_i;
- map_index(scratch_cp, signature_ref_i, found_i);
- } else {
- // no match found so we have to append this entry to *merge_cp_p
- append_entry(scratch_cp, signature_ref_i, merge_cp_p,
- merge_cp_length_p, THREAD);
- // The above call to append_entry() can only append one entry
- // so the post call query of *merge_cp_length_p is only for
- // the sake of consistency.
- new_signature_ref_i = *merge_cp_length_p - 1;
- }
- }
-
- // If the referenced entries already exist in *merge_cp_p, then
- // both new_name_ref_i and new_signature_ref_i will both be 0.
- // In that case, all we are appending is the current entry.
- if (new_name_ref_i == 0) {
- new_name_ref_i = name_ref_i;
- } else {
- RC_TRACE(0x00080000,
- ("NameAndType entry@%d name_ref_index change: %d to %d",
- *merge_cp_length_p, name_ref_i, new_name_ref_i));
- }
- if (new_signature_ref_i == 0) {
- new_signature_ref_i = signature_ref_i;
- } else {
- RC_TRACE(0x00080000,
- ("NameAndType entry@%d signature_ref_index change: %d to %d",
- *merge_cp_length_p, signature_ref_i, new_signature_ref_i));
- }
-
- (*merge_cp_p)->name_and_type_at_put(*merge_cp_length_p,
- new_name_ref_i, new_signature_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.
- map_index(scratch_cp, scratch_i, *merge_cp_length_p);
- }
- (*merge_cp_length_p)++;
- } break;
-
- // this is a double-indirect CP entry so it needs special handling
- case JVM_CONSTANT_Fieldref: // fall through
- case JVM_CONSTANT_InterfaceMethodref: // fall through
- case JVM_CONSTANT_Methodref:
- {
- int klass_ref_i = scratch_cp->uncached_klass_ref_index_at(scratch_i);
- int new_klass_ref_i = 0;
- bool match = (klass_ref_i < *merge_cp_length_p) &&
- scratch_cp->compare_entry_to(klass_ref_i, *merge_cp_p, klass_ref_i,
- THREAD);
- if (!match) {
- // forward reference in *merge_cp_p or not a direct match
-
- int found_i = scratch_cp->find_matching_entry(klass_ref_i, *merge_cp_p,
- THREAD);
- if (found_i != 0) {
- guarantee(found_i != klass_ref_i,
- "compare_entry_to() and find_matching_entry() do not agree");
-
- // Found a matching entry somewhere else in *merge_cp_p so
- // just need a mapping entry.
- new_klass_ref_i = found_i;
- map_index(scratch_cp, klass_ref_i, found_i);
- } else {
- // no match found so we have to append this entry to *merge_cp_p
- append_entry(scratch_cp, klass_ref_i, merge_cp_p, merge_cp_length_p,
- THREAD);
- // The above call to append_entry() can only append one entry
- // so the post call query of *merge_cp_length_p is only for
- // the sake of consistency. Without the optimization where we
- // use JVM_CONSTANT_UnresolvedClass, then up to two entries
- // could be appended.
- new_klass_ref_i = *merge_cp_length_p - 1;
- }
- }
-
- int name_and_type_ref_i =
- scratch_cp->uncached_name_and_type_ref_index_at(scratch_i);
- int new_name_and_type_ref_i = 0;
- match = (name_and_type_ref_i < *merge_cp_length_p) &&
- scratch_cp->compare_entry_to(name_and_type_ref_i, *merge_cp_p,
- name_and_type_ref_i, THREAD);
- if (!match) {
- // forward reference in *merge_cp_p or not a direct match
-
- int found_i = scratch_cp->find_matching_entry(name_and_type_ref_i,
- *merge_cp_p, THREAD);
- if (found_i != 0) {
- guarantee(found_i != name_and_type_ref_i,
- "compare_entry_to() and find_matching_entry() do not agree");
-
- // Found a matching entry somewhere else in *merge_cp_p so
- // just need a mapping entry.
- new_name_and_type_ref_i = found_i;
- map_index(scratch_cp, name_and_type_ref_i, found_i);
- } else {
- // no match found so we have to append this entry to *merge_cp_p
- append_entry(scratch_cp, name_and_type_ref_i, merge_cp_p,
- merge_cp_length_p, THREAD);
- // The above call to append_entry() can append more than
- // one entry so the post call query of *merge_cp_length_p
- // is required in order to get the right index for the
- // JVM_CONSTANT_NameAndType entry.
- new_name_and_type_ref_i = *merge_cp_length_p - 1;
- }
- }
-
- // If the referenced entries already exist in *merge_cp_p, then
- // both new_klass_ref_i and new_name_and_type_ref_i will both be
- // 0. In that case, all we are appending is the current entry.
- if (new_klass_ref_i == 0) {
- new_klass_ref_i = klass_ref_i;
- }
- if (new_name_and_type_ref_i == 0) {
- new_name_and_type_ref_i = name_and_type_ref_i;
- }
-
- const char *entry_name;
- switch (scratch_cp->tag_at(scratch_i).value()) {
- case JVM_CONSTANT_Fieldref:
- entry_name = "Fieldref";
- (*merge_cp_p)->field_at_put(*merge_cp_length_p, new_klass_ref_i,
- new_name_and_type_ref_i);
- break;
- case JVM_CONSTANT_InterfaceMethodref:
- entry_name = "IFMethodref";
- (*merge_cp_p)->interface_method_at_put(*merge_cp_length_p,
- new_klass_ref_i, new_name_and_type_ref_i);
- break;
- case JVM_CONSTANT_Methodref:
- entry_name = "Methodref";
- (*merge_cp_p)->method_at_put(*merge_cp_length_p, new_klass_ref_i,
- new_name_and_type_ref_i);
- break;
- default:
- guarantee(false, "bad switch");
- break;
- }
-
- if (klass_ref_i != new_klass_ref_i) {
- RC_TRACE(0x00080000, ("%s entry@%d class_index changed: %d to %d",
- entry_name, *merge_cp_length_p, klass_ref_i, new_klass_ref_i));
- }
- if (name_and_type_ref_i != new_name_and_type_ref_i) {
- RC_TRACE(0x00080000,
- ("%s entry@%d name_and_type_index changed: %d to %d",
- entry_name, *merge_cp_length_p, name_and_type_ref_i,
- new_name_and_type_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.
- map_index(scratch_cp, scratch_i, *merge_cp_length_p);
- }
- (*merge_cp_length_p)++;
- } break;
-
- // At this stage, Class or UnresolvedClass could be here, but not
- // ClassIndex
- case JVM_CONSTANT_ClassIndex: // fall through
-
- // Invalid is used as the tag for the second constant pool entry
- // occupied by JVM_CONSTANT_Double or JVM_CONSTANT_Long. It should
- // not be seen by itself.
- case JVM_CONSTANT_Invalid: // fall through
-
- // At this stage, String or UnresolvedString could be here, but not
- // StringIndex
- case JVM_CONSTANT_StringIndex: // fall through
-
- // At this stage JVM_CONSTANT_UnresolvedClassInError should not be
- // here
- case JVM_CONSTANT_UnresolvedClassInError: // fall through
-
- default:
- {
- // leave a breadcrumb
- jbyte bad_value = scratch_cp->tag_at(scratch_i).value();
- ShouldNotReachHere();
- } break;
- } // end switch tag value
-} // end append_entry()
-
-
-void VM_RedefineClasses::swap_all_method_annotations(int i, int j, instanceKlassHandle scratch_class) {
- typeArrayOop save;
-
- save = scratch_class->get_method_annotations_of(i);
- scratch_class->set_method_annotations_of(i, scratch_class->get_method_annotations_of(j));
- scratch_class->set_method_annotations_of(j, save);
-
- save = scratch_class->get_method_parameter_annotations_of(i);
- scratch_class->set_method_parameter_annotations_of(i, scratch_class->get_method_parameter_annotations_of(j));
- scratch_class->set_method_parameter_annotations_of(j, save);
-
- save = scratch_class->get_method_default_annotations_of(i);
- scratch_class->set_method_default_annotations_of(i, scratch_class->get_method_default_annotations_of(j));
- scratch_class->set_method_default_annotations_of(j, save);
-}
-
-
-jvmtiError VM_RedefineClasses::compare_and_normalize_class_versions(
- instanceKlassHandle the_class,
- instanceKlassHandle scratch_class) {
+jvmtiError VM_RedefineClasses::check_redefinition_allowed(instanceKlassHandle scratch_class) {
+
+
+
+ // Compatibility mode => check for unsupported modification
+
+
+ assert(scratch_class->old_version() != NULL, "must have old version");
+ instanceKlassHandle the_class(scratch_class->old_version());
+
int i;
// Check superclasses, or rather their names, since superclasses themselves can be
// requested to replace.
// Check for NULL superclass first since this might be java.lang.Object
if (the_class->super() != scratch_class->super() &&
- (the_class->super() == NULL || scratch_class->super() == NULL ||
- Klass::cast(the_class->super())->name() !=
- Klass::cast(scratch_class->super())->name())) {
- return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED;
+ (the_class->super() == NULL || scratch_class->super() == NULL ||
+ Klass::cast(the_class->super())->name() !=
+ Klass::cast(scratch_class->super())->name())) {
+ return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED;
}
// Check if the number, names and order of directly implemented interfaces are the same.
@@ -518,8 +640,8 @@
}
for (i = 0; i < n_intfs; i++) {
if (Klass::cast((klassOop) k_interfaces->obj_at(i))->name() !=
- Klass::cast((klassOop) k_new_interfaces->obj_at(i))->name()) {
- return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED;
+ Klass::cast((klassOop) k_new_interfaces->obj_at(i))->name()) {
+ return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED;
}
}
@@ -554,10 +676,10 @@
}
// offset
if (k_old_fields->short_at(i + instanceKlass::low_offset) !=
- k_new_fields->short_at(i + instanceKlass::low_offset) ||
- k_old_fields->short_at(i + instanceKlass::high_offset) !=
- k_new_fields->short_at(i + instanceKlass::high_offset)) {
- return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED;
+ k_new_fields->short_at(i + instanceKlass::low_offset) ||
+ k_old_fields->short_at(i + instanceKlass::high_offset) !=
+ k_new_fields->short_at(i + instanceKlass::high_offset)) {
+ return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED;
}
// name and signature
jshort name_index = k_old_fields->short_at(i + instanceKlass::name_index_offset);
@@ -573,6 +695,7 @@
}
}
+
// Do a parallel walk through the old and new methods. Detect
// cases where they match (exist in both), have been added in
// the new methods, or have been deleted (exist only in the
@@ -674,12 +797,8 @@
idnum_owner->set_method_idnum(new_num);
}
k_new_method->set_method_idnum(old_num);
- swap_all_method_annotations(old_num, new_num, scratch_class);
}
}
- RC_TRACE(0x00008000, ("Method matched: new: %s [%d] == old: %s [%d]",
- k_new_method->name_and_sig_as_C_string(), ni,
- k_old_method->name_and_sig_as_C_string(), oi));
// advance to next pair of methods
++oi;
++ni;
@@ -688,11 +807,11 @@
// method added, see if it is OK
new_flags = (jushort) k_new_method->access_flags().get_flags();
if ((new_flags & JVM_ACC_PRIVATE) == 0
- // hack: private should be treated as final, but alas
- || (new_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0
- ) {
- // new methods must be private
- return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED;
+ // hack: private should be treated as final, but alas
+ || (new_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0
+ ) {
+ // new methods must be private
+ return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED;
}
{
u2 num = the_class->next_method_idnum();
@@ -707,24 +826,19 @@
idnum_owner->set_method_idnum(new_num);
}
k_new_method->set_method_idnum(num);
- swap_all_method_annotations(new_num, num, scratch_class);
}
- RC_TRACE(0x00008000, ("Method added: new: %s [%d]",
- k_new_method->name_and_sig_as_C_string(), ni));
++ni; // advance to next new method
break;
case deleted:
// method deleted, see if it is OK
old_flags = (jushort) k_old_method->access_flags().get_flags();
if ((old_flags & JVM_ACC_PRIVATE) == 0
- // hack: private should be treated as final, but alas
- || (old_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0
- ) {
- // deleted methods must be private
- return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED;
+ // hack: private should be treated as final, but alas
+ || (old_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0
+ ) {
+ // deleted methods must be private
+ return JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED;
}
- RC_TRACE(0x00008000, ("Method deleted: old: %s [%d]",
- k_old_method->name_and_sig_as_C_string(), oi));
++oi; // advance to next old method
break;
default:
@@ -735,2081 +849,2158 @@
return JVMTI_ERROR_NONE;
}
-
-// Find new constant pool index value for old constant pool index value
-// by seaching the index map. Returns zero (0) if there is no mapped
-// value for the old constant pool index.
-int VM_RedefineClasses::find_new_index(int old_index) {
- if (_index_map_count == 0) {
- // map is empty so nothing can be found
- return 0;
+int VM_RedefineClasses::calculate_redefinition_flags(instanceKlassHandle new_class) {
+
+ int result = Klass::NoRedefinition;
+
+
+
+ TRACE_RC2("Comparing different class versions of class %s", new_class->name()->as_C_string());
+
+ assert(new_class->old_version() != NULL, "must have old version");
+ instanceKlassHandle the_class(new_class->old_version());
+
+ // Check whether class is in the error init state.
+ if (the_class->is_in_error_state()) {
+ // TBD #5057930: special error code is needed in 1.6
+ //result = Klass::union_redefinition_level(result, Klass::Invalid);
}
- if (old_index < 1 || old_index >= _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, but it can
- // happen if a corrupt annotation is processed.
- return 0;
+ int i;
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Check superclasses
+ assert(new_class->super() == NULL || new_class->super()->klass_part()->is_newest_version(), "");
+ if (the_class->super() != new_class->super()) {
+ // Super class changed
+
+ klassOop cur_klass = the_class->super();
+ while (cur_klass != NULL) {
+ if (!new_class->is_subclass_of(cur_klass->klass_part()->newest_version())) {
+ TRACE_RC2("Removed super class %s", cur_klass->klass_part()->name()->as_C_string());
+ result = result | Klass::RemoveSuperType | Klass::ModifyInstances | Klass::ModifyClass;
+
+ if (!cur_klass->klass_part()->has_subtype_changed()) {
+ TRACE_RC2("Subtype changed of class %s", cur_klass->klass_part()->name()->as_C_string());
+ cur_klass->klass_part()->set_subtype_changed(true);
+ }
+ }
+
+ cur_klass = cur_klass->klass_part()->super();
+ }
+
+ cur_klass = new_class->super();
+ while (cur_klass != NULL) {
+ if (!the_class->is_subclass_of(cur_klass->klass_part()->old_version())) {
+ TRACE_RC2("Added super class %s", cur_klass->klass_part()->name()->as_C_string());
+ result = result | Klass::ModifyClass | Klass::ModifyInstances;
+ }
+ cur_klass = cur_klass->klass_part()->super();
+ }
}
- int value = _index_map_p->at(old_index);
- if (value == -1) {
- // the old_index is not mapped
- return 0;
- }
-
- return value;
-} // end find_new_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,
- int index1, constantPoolHandle cp2, int index2) {
-
- jbyte t1 = cp1->tag_at(index1).value();
- if (t1 != JVM_CONSTANT_Class && t1 != JVM_CONSTANT_UnresolvedClass) {
- return false; // wrong entry type; not our special case
- }
-
- jbyte t2 = cp2->tag_at(index2).value();
- if (t2 != JVM_CONSTANT_Class && t2 != JVM_CONSTANT_UnresolvedClass) {
- return false; // wrong entry type; not our special case
- }
-
- if (t1 == t2) {
- return false; // not a mismatch; not our special case
- }
-
- char *s1 = cp1->klass_name_at(index1)->as_C_string();
- char *s2 = cp2->klass_name_at(index2)->as_C_string();
- if (strcmp(s1, s2) != 0) {
- return false; // strings don't match; not our special case
- }
-
- return true; // made it through the gauntlet; this is our special case
-} // end is_unresolved_class_mismatch()
-
-
-// Returns true if the current mismatch is due to a resolved/unresolved
-// string pair. Otherwise, returns false.
-bool VM_RedefineClasses::is_unresolved_string_mismatch(constantPoolHandle cp1,
- int index1, constantPoolHandle cp2, int index2) {
-
- jbyte t1 = cp1->tag_at(index1).value();
- if (t1 != JVM_CONSTANT_String && t1 != JVM_CONSTANT_UnresolvedString) {
- return false; // wrong entry type; not our special case
- }
-
- jbyte t2 = cp2->tag_at(index2).value();
- if (t2 != JVM_CONSTANT_String && t2 != JVM_CONSTANT_UnresolvedString) {
- return false; // wrong entry type; not our special case
- }
-
- if (t1 == t2) {
- return false; // not a mismatch; not our special case
- }
-
- char *s1 = cp1->string_at_noresolve(index1);
- char *s2 = cp2->string_at_noresolve(index2);
- if (strcmp(s1, s2) != 0) {
- return false; // strings don't match; not our special case
- }
-
- return true; // made it through the gauntlet; this is our special case
-} // end is_unresolved_string_mismatch()
-
-
-jvmtiError VM_RedefineClasses::load_new_class_versions(TRAPS) {
- // For consistency allocate memory using os::malloc wrapper.
- _scratch_classes = (instanceKlassHandle *)
- os::malloc(sizeof(instanceKlassHandle) * _class_count);
- if (_scratch_classes == NULL) {
- return JVMTI_ERROR_OUT_OF_MEMORY;
- }
-
- ResourceMark rm(THREAD);
-
- JvmtiThreadState *state = JvmtiThreadState::state_for(JavaThread::current());
- // state can only be NULL if the current thread is exiting which
- // should not happen since we're trying to do a RedefineClasses
- guarantee(state != NULL, "exiting thread calling load_new_class_versions");
- for (int i = 0; i < _class_count; i++) {
- oop mirror = JNIHandles::resolve_non_null(_class_defs[i].klass);
- // classes for primitives cannot be redefined
- if (!is_modifiable_class(mirror)) {
- return JVMTI_ERROR_UNMODIFIABLE_CLASS;
- }
- klassOop the_class_oop = java_lang_Class::as_klassOop(mirror);
- instanceKlassHandle the_class = instanceKlassHandle(THREAD, the_class_oop);
- symbolHandle the_class_sym = symbolHandle(THREAD, the_class->name());
-
- // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark
- RC_TRACE_WITH_THREAD(0x00000001, THREAD,
- ("loading name=%s (avail_mem=" UINT64_FORMAT "K)",
- the_class->external_name(), os::available_memory() >> 10));
-
- ClassFileStream st((u1*) _class_defs[i].class_bytes,
- _class_defs[i].class_byte_count, (char *)"__VM_RedefineClasses__");
-
- // Parse the stream.
- Handle the_class_loader(THREAD, the_class->class_loader());
- Handle protection_domain(THREAD, the_class->protection_domain());
- // Set redefined class handle in JvmtiThreadState class.
- // This redefined class is sent to agent event handler for class file
- // load hook event.
- state->set_class_being_redefined(&the_class, _class_load_kind);
-
- klassOop k = SystemDictionary::parse_stream(the_class_sym,
- the_class_loader,
- protection_domain,
- &st,
- THREAD);
- // Clear class_being_redefined just to be sure.
- state->clear_class_being_redefined();
-
- // TODO: if this is retransform, and nothing changed we can skip it
-
- instanceKlassHandle scratch_class (THREAD, k);
-
- if (HAS_PENDING_EXCEPTION) {
- symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name();
- // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark
- RC_TRACE_WITH_THREAD(0x00000002, THREAD, ("parse_stream exception: '%s'",
- ex_name->as_C_string()));
- CLEAR_PENDING_EXCEPTION;
-
- if (ex_name == vmSymbols::java_lang_UnsupportedClassVersionError()) {
- return JVMTI_ERROR_UNSUPPORTED_VERSION;
- } else if (ex_name == vmSymbols::java_lang_ClassFormatError()) {
- return JVMTI_ERROR_INVALID_CLASS_FORMAT;
- } else if (ex_name == vmSymbols::java_lang_ClassCircularityError()) {
- return JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION;
- } else if (ex_name == vmSymbols::java_lang_NoClassDefFoundError()) {
- // The message will be "XXX (wrong name: YYY)"
- return JVMTI_ERROR_NAMES_DONT_MATCH;
- } else if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) {
- return JVMTI_ERROR_OUT_OF_MEMORY;
- } else { // Just in case more exceptions can be thrown..
- return JVMTI_ERROR_FAILS_VERIFICATION;
+ //////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Check interfaces
+
+ // Interfaces removed?
+ objArrayOop old_interfaces = the_class->transitive_interfaces();
+ for (i = 0; ilength(); i++) {
+ instanceKlassHandle old_interface((klassOop)old_interfaces->obj_at(i));
+ if (!new_class->implements_interface_any_version(old_interface())) {
+ result = result | Klass::RemoveSuperType | Klass::ModifyClass;
+ TRACE_RC2("Removed interface %s", old_interface->name()->as_C_string());
+
+ if (!old_interface->has_subtype_changed()) {
+ TRACE_RC2("Subtype changed of interface %s", old_interface->name()->as_C_string());
+ old_interface->set_subtype_changed(true);
}
}
-
- // Ensure class is linked before redefine
- if (!the_class->is_linked()) {
- the_class->link_class(THREAD);
- if (HAS_PENDING_EXCEPTION) {
- symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name();
- // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark
- RC_TRACE_WITH_THREAD(0x00000002, THREAD, ("link_class exception: '%s'",
- ex_name->as_C_string()));
- CLEAR_PENDING_EXCEPTION;
- if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) {
- return JVMTI_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Interfaces added?
+ objArrayOop new_interfaces = new_class->transitive_interfaces();
+ for (i = 0; ilength(); i++) {
+ if (!the_class->implements_interface_any_version((klassOop)new_interfaces->obj_at(i))) {
+ result = result | Klass::ModifyClass;
+ TRACE_RC2("Added interface %s", ((klassOop)new_interfaces->obj_at(i))->klass_part()->name()->as_C_string());
+ }
+ }
+
+
+ // Check whether class modifiers are the same.
+ jushort old_flags = (jushort) the_class->access_flags().get_flags();
+ jushort new_flags = (jushort) new_class->access_flags().get_flags();
+ if (old_flags != new_flags) {
+ // TODO (tw): Can this have any effects?
+ }
+
+ // Check if the number, names, types and order of fields declared in these classes
+ // are the same.
+ typeArrayOop k_old_fields = the_class->fields();
+ typeArrayOop k_new_fields = new_class->fields();
+ int n_fields = k_old_fields->length();
+ if (n_fields != k_new_fields->length()) {
+ result = result | Klass::ModifyInstances;
+ } else {
+ for (i = 0; i < n_fields; i += instanceKlass::next_offset) {
+ // access
+ old_flags = k_old_fields->ushort_at(i + instanceKlass::access_flags_offset);
+ new_flags = k_new_fields->ushort_at(i + instanceKlass::access_flags_offset);
+ if ((old_flags ^ new_flags) & JVM_RECOGNIZED_FIELD_MODIFIERS) {
+ // (tw) Can this have any effects?
+ }
+ // offset
+ if (k_old_fields->short_at(i + instanceKlass::low_offset) !=
+ k_new_fields->short_at(i + instanceKlass::low_offset) ||
+ k_old_fields->short_at(i + instanceKlass::high_offset) !=
+ k_new_fields->short_at(i + instanceKlass::high_offset)) {
+ result = result | Klass::ModifyInstances;
+ }
+ // name and signature
+ jshort name_index = k_old_fields->short_at(i + instanceKlass::name_index_offset);
+ jshort sig_index = k_old_fields->short_at(i +instanceKlass::signature_index_offset);
+ symbolOop name_sym1 = the_class->constants()->symbol_at(name_index);
+ symbolOop sig_sym1 = the_class->constants()->symbol_at(sig_index);
+ name_index = k_new_fields->short_at(i + instanceKlass::name_index_offset);
+ sig_index = k_new_fields->short_at(i + instanceKlass::signature_index_offset);
+ symbolOop name_sym2 = new_class->constants()->symbol_at(name_index);
+ symbolOop sig_sym2 = new_class->constants()->symbol_at(sig_index);
+ if (name_sym1 != name_sym2 || sig_sym1 != sig_sym2) {
+ result = result | Klass::ModifyInstances;
+ }
+ }
+ }
+
+ // Do a parallel walk through the old and new methods. Detect
+ // cases where they match (exist in both), have been added in
+ // the new methods, or have been deleted (exist only in the
+ // old methods). The class file parser places methods in order
+ // by method name, but does not order overloaded methods by
+ // signature. In order to determine what fate befell the methods,
+ // this code places the overloaded new methods that have matching
+ // old methods in the same order as the old methods and places
+ // new overloaded methods at the end of overloaded methods of
+ // that name. The code for this order normalization is adapted
+ // from the algorithm used in instanceKlass::find_method().
+ // Since we are swapping out of order entries as we find them,
+ // we only have to search forward through the overloaded methods.
+ // Methods which are added and have the same name as an existing
+ // method (but different signature) will be put at the end of
+ // the methods with that name, and the name mismatch code will
+ // handle them.
+ objArrayHandle k_old_methods(the_class->methods());
+ objArrayHandle k_new_methods(new_class->methods());
+ int n_old_methods = k_old_methods->length();
+ int n_new_methods = k_new_methods->length();
+
+ int ni = 0;
+ int oi = 0;
+ while (true) {
+ methodOop k_old_method;
+ methodOop k_new_method;
+ enum { matched, added, deleted, undetermined } method_was = undetermined;
+
+ if (oi >= n_old_methods) {
+ if (ni >= n_new_methods) {
+ break; // we've looked at everything, done
+ }
+ // New method at the end
+ k_new_method = (methodOop) k_new_methods->obj_at(ni);
+ method_was = added;
+ } else if (ni >= n_new_methods) {
+ // Old method, at the end, is deleted
+ k_old_method = (methodOop) k_old_methods->obj_at(oi);
+ method_was = deleted;
+ } else {
+ // There are more methods in both the old and new lists
+ k_old_method = (methodOop) k_old_methods->obj_at(oi);
+ k_new_method = (methodOop) k_new_methods->obj_at(ni);
+ if (k_old_method->name() != k_new_method->name()) {
+ // Methods are sorted by method name, so a mismatch means added
+ // or deleted
+ if (k_old_method->name()->fast_compare(k_new_method->name()) > 0) {
+ method_was = added;
} else {
- return JVMTI_ERROR_INTERNAL;
+ method_was = deleted;
+ }
+ } else if (k_old_method->signature() == k_new_method->signature()) {
+ // Both the name and signature match
+ method_was = matched;
+ } else {
+ // The name matches, but the signature doesn't, which means we have to
+ // search forward through the new overloaded methods.
+ int nj; // outside the loop for post-loop check
+ for (nj = ni + 1; nj < n_new_methods; nj++) {
+ methodOop m = (methodOop)k_new_methods->obj_at(nj);
+ if (k_old_method->name() != m->name()) {
+ // reached another method name so no more overloaded methods
+ method_was = deleted;
+ break;
+ }
+ if (k_old_method->signature() == m->signature()) {
+ // found a match so swap the methods
+ k_new_methods->obj_at_put(ni, m);
+ k_new_methods->obj_at_put(nj, k_new_method);
+ k_new_method = m;
+ method_was = matched;
+ break;
+ }
+ }
+
+ if (nj >= n_new_methods) {
+ // reached the end without a match; so method was deleted
+ method_was = deleted;
}
}
}
- // Do the validity checks in compare_and_normalize_class_versions()
- // before verifying the byte codes. By doing these checks first, we
- // limit the number of functions that require redirection from
- // the_class to scratch_class. In particular, we don't have to
- // modify JNI GetSuperclass() and thus won't change its performance.
- jvmtiError res = compare_and_normalize_class_versions(the_class,
- scratch_class);
- if (res != JVMTI_ERROR_NONE) {
- return res;
+ switch (method_was) {
+ case matched:
+ // methods match, be sure modifiers do too
+ old_flags = (jushort) k_old_method->access_flags().get_flags();
+ new_flags = (jushort) k_new_method->access_flags().get_flags();
+ if ((old_flags ^ new_flags) & ~(JVM_ACC_NATIVE)) {
+ // (tw) Can this have any effects? Probably yes on vtables?
+ result = result | Klass::ModifyClass;
}
-
- // verify what the caller passed us
{
- // The bug 6214132 caused the verification to fail.
- // Information about the_class and scratch_class is temporarily
- // recorded into jvmtiThreadState. This data is used to redirect
- // the_class to scratch_class in the JVM_* functions called by the
- // verifier. Please, refer to jvmtiThreadState.hpp for the detailed
- // description.
- RedefineVerifyMark rvm(&the_class, &scratch_class, state);
- Verifier::verify(
- scratch_class, Verifier::ThrowException, true, THREAD);
- }
-
- if (HAS_PENDING_EXCEPTION) {
- symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name();
- // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark
- RC_TRACE_WITH_THREAD(0x00000002, THREAD,
- ("verify_byte_codes exception: '%s'", ex_name->as_C_string()));
- CLEAR_PENDING_EXCEPTION;
- if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) {
- return JVMTI_ERROR_OUT_OF_MEMORY;
- } else {
- // tell the caller the bytecodes are bad
- return JVMTI_ERROR_FAILS_VERIFICATION;
+ u2 new_num = k_new_method->method_idnum();
+ u2 old_num = k_old_method->method_idnum();
+ if (new_num != old_num) {
+ methodOop idnum_owner = new_class->method_with_idnum(old_num);
+ if (idnum_owner != NULL) {
+ // There is already a method assigned this idnum -- switch them
+ idnum_owner->set_method_idnum(new_num);
+ }
+ k_new_method->set_method_idnum(old_num);
+ TRACE_RC2("swapping idnum of new and old method %d / %d!", new_num, old_num);
+ // swap_all_method_annotations(old_num, new_num, new_class);
}
}
-
- res = merge_cp_and_rewrite(the_class, scratch_class, THREAD);
- if (res != JVMTI_ERROR_NONE) {
- return res;
+ TRACE_RC3("Method matched: new: %s [%d] == old: %s [%d]",
+ k_new_method->name_and_sig_as_C_string(), ni,
+ k_old_method->name_and_sig_as_C_string(), oi);
+ // advance to next pair of methods
+ ++oi;
+ ++ni;
+ break;
+ case added:
+ // method added, see if it is OK
+ new_flags = (jushort) k_new_method->access_flags().get_flags();
+ if ((new_flags & JVM_ACC_PRIVATE) == 0
+ // hack: private should be treated as final, but alas
+ || (new_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0
+ ) {
+ // new methods must be private
+ result = result | Klass::ModifyClass;
}
-
- if (VerifyMergedCPBytecodes) {
- // verify what we have done during constant pool merging
- {
- RedefineVerifyMark rvm(&the_class, &scratch_class, state);
- Verifier::verify(scratch_class, Verifier::ThrowException, true, THREAD);
+ {
+ u2 num = the_class->next_method_idnum();
+ if (num == constMethodOopDesc::UNSET_IDNUM) {
+ // cannot add any more methods
+ result = result | Klass::ModifyClass;
}
-
- if (HAS_PENDING_EXCEPTION) {
- symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name();
- // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark
- RC_TRACE_WITH_THREAD(0x00000002, THREAD,
- ("verify_byte_codes post merge-CP exception: '%s'",
- ex_name->as_C_string()));
- CLEAR_PENDING_EXCEPTION;
- if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) {
- return JVMTI_ERROR_OUT_OF_MEMORY;
- } else {
- // tell the caller that constant pool merging screwed up
- return JVMTI_ERROR_INTERNAL;
+ u2 new_num = k_new_method->method_idnum();
+ methodOop idnum_owner = new_class->method_with_idnum(num);
+ if (idnum_owner != NULL) {
+ // There is already a method assigned this idnum -- switch them
+ idnum_owner->set_method_idnum(new_num);
+ }
+ k_new_method->set_method_idnum(num);
+ //swap_all_method_annotations(new_num, num, new_class);
+ }
+ TRACE_RC1("Method added: new: %s [%d]",
+ k_new_method->name_and_sig_as_C_string(), ni);
+ ++ni; // advance to next new method
+ break;
+ case deleted:
+ // method deleted, see if it is OK
+ old_flags = (jushort) k_old_method->access_flags().get_flags();
+ if ((old_flags & JVM_ACC_PRIVATE) == 0
+ // hack: private should be treated as final, but alas
+ || (old_flags & (JVM_ACC_FINAL|JVM_ACC_STATIC)) == 0
+ ) {
+ // deleted methods must be private
+ result = result | Klass::ModifyClass;
+ }
+ TRACE_RC1("Method deleted: old: %s [%d]",
+ k_old_method->name_and_sig_as_C_string(), oi);
+ ++oi; // advance to next old method
+ break;
+ default:
+ ShouldNotReachHere();
+ }
+ }
+
+ if (new_class()->size() != new_class->old_version()->size()) {
+ result |= Klass::ModifyClassSize;
+ }
+
+ if (new_class->size_helper() != ((instanceKlass*)(new_class->old_version()->klass_part()))->size_helper()) {
+ result |= Klass::ModifyInstanceSize;
+ }
+
+ methodHandle instanceTransformerMethod(new_class->find_method(vmSymbols::transformer_name(), vmSymbols::void_method_signature()));
+ if (!instanceTransformerMethod.is_null() && !instanceTransformerMethod->is_static()) {
+ result |= Klass::HasInstanceTransformer;
+ }
+
+ // (tw) Check method bodies to be able to return NoChange?
+ return result;
+}
+
+void VM_RedefineClasses::calculate_instance_update_information(klassOop new_version) {
+
+ class UpdateFieldsEvolutionClosure : public FieldEvolutionClosure {
+
+ private:
+
+ GrowableArray info;
+ int curPosition;
+ bool copy_backwards;
+
+ public:
+
+ bool does_copy_backwards() {
+ return copy_backwards;
+ }
+
+ UpdateFieldsEvolutionClosure(klassOop klass) {
+
+ int base_offset = instanceOopDesc::base_offset_in_bytes();
+
+ if (klass->klass_part()->newest_version() == SystemDictionary::Class_klass()->klass_part()->newest_version()) {
+ base_offset += java_lang_Class::number_of_fake_oop_fields*size_of_type(T_OBJECT);
+ }
+
+ info.append(base_offset);
+ info.append(0);
+ curPosition = base_offset;
+ copy_backwards = false;
+ }
+
+ GrowableArray &finish() {
+ info.append(0);
+ return info;
+ }
+
+ virtual void do_new_field(fieldDescriptor* fd){
+ int size = size_of_type(fd->field_type());
+ fill(size);
+ }
+
+ private:
+
+ void fill(int size) {
+ if (info.length() > 0 && info.at(info.length() - 1) < 0) {
+ (*info.adr_at(info.length() - 1)) -= size;
+ } else {
+ info.append(-size);
+ }
+
+ curPosition += size;
+ }
+
+ int size_of_type(BasicType type) {
+ int size = 0;
+ switch(type) {
+ case T_BOOLEAN:
+ size = sizeof(jboolean);
+ break;
+
+ case T_CHAR:
+ size = (sizeof(jchar));
+ break;
+
+ case T_FLOAT:
+ size = (sizeof(jfloat));
+ break;
+
+ case T_DOUBLE:
+ size = (sizeof(jdouble));
+ break;
+
+ case T_BYTE:
+ size = (sizeof(jbyte));
+ break;
+
+ case T_SHORT:
+ size = (sizeof(jshort));
+ break;
+
+ case T_INT:
+ size = (sizeof(jint));
+ break;
+
+ case T_LONG:
+ size = (sizeof(jlong));
+ break;
+
+ case T_OBJECT:
+ case T_ARRAY:
+ if (UseCompressedOops) {
+ size = sizeof(narrowOop);
+ } else {
+ size = (sizeof(oop));
+ }
+ break;
+
+ default:
+ ShouldNotReachHere();
+ }
+
+ assert(size > 0, "");
+ return size;
+
+ }
+
+ public:
+
+ virtual void do_old_field(fieldDescriptor* fd){}
+
+ virtual void do_changed_field(fieldDescriptor* old_fd, fieldDescriptor *new_fd){
+
+ int alignment = new_fd->offset() - curPosition;
+ if (alignment > 0) {
+ // This field was aligned, so we need to make sure that we fill the gap
+ fill(alignment);
+ }
+
+ assert(old_fd->field_type() == new_fd->field_type(), "");
+ assert(curPosition == new_fd->offset(), "must be correct offset!");
+
+ int offset = old_fd->offset();
+ int size = size_of_type(old_fd->field_type());
+
+ int prevEnd = -1;
+ if (info.length() > 0 && info.at(info.length() - 1) > 0) {
+ prevEnd = info.at(info.length() - 2) + info.at(info.length() - 1);
+ }
+
+ if (prevEnd == offset) {
+ info.at_put(info.length() - 2, info.at(info.length() - 2) + size);
+ } else {
+ info.append(size);
+ info.append(offset);
+ }
+
+ if (old_fd->offset() < new_fd->offset()) {
+ copy_backwards = true;
+ }
+
+ transfer_special_access_flags(old_fd, new_fd);
+
+ curPosition += size;
+ }
+ };
+
+ UpdateFieldsEvolutionClosure cl(new_version);
+ ((instanceKlass*)new_version->klass_part())->do_fields_evolution(&cl);
+
+ GrowableArray result = cl.finish();
+ ((instanceKlass*)new_version->klass_part())->store_update_information(result);
+ ((instanceKlass*)new_version->klass_part())->set_copying_backwards(cl.does_copy_backwards());
+
+ IF_TRACE_RC2 {
+ TRACE_RC2("Instance update information for %s:", new_version->klass_part()->name()->as_C_string());
+ if (cl.does_copy_backwards()) {
+ TRACE_RC2("\tDoes copy backwards!");
+ }
+ for (int i=0; i 0) {
+ TRACE_RC2("\t%d COPY from %d", curNum, result.at(i + 1));
+ i++;
+ } else {
+ TRACE_RC2("\tEND");
+ }
+ }
+ }
+}
+
+symbolOop VM_RedefineClasses::signature_to_class_name(symbolOop signature) {
+ assert(FieldType::is_obj(signature), "");
+ return oopFactory::new_symbol_handle(signature->as_C_string() + 1, signature->utf8_length() - 2, Thread::current())();
+}
+
+void VM_RedefineClasses::calculate_type_check_information() {
+ class MarkAffectedClassesClosure : public ObjectClosure {
+
+ virtual void do_object(oop obj) {
+ klassOop klass = (klassOop)obj;
+
+ if (klass->klass_part()->is_redefining()) {
+ klass = klass->klass_part()->old_version();
+ }
+
+ // We found an instance klass!
+ instanceKlass *cur_instance_klass = instanceKlass::cast(klass);
+ GrowableArray< Pair > type_check_information;
+
+ class MyFieldClosure : public FieldClosure {
+
+ public:
+
+ GrowableArray< Pair > *_arr;
+
+ MyFieldClosure(GrowableArray< Pair > *arr) {
+ _arr = arr;
+ }
+
+ virtual void do_field(fieldDescriptor* fd) {
+ if (fd->field_type() == T_OBJECT) {
+ symbolOop signature = fd->signature();
+ if (FieldType::is_obj(signature)) {
+ symbolHandle name = signature_to_class_name(signature);
+ klassOop field_klass;
+ if (is_field_dangerous(name, fd, field_klass)) {
+ TRACE_RC2("Found dangerous field %s in klass %s of type %s", fd->name()->as_C_string(), fd->field_holder()->klass_part()->name()->as_C_string(), name->as_C_string());
+ _arr->append(Pair(fd->offset(), field_klass->klass_part()->newest_version()));
+ }
+ }
+
+ // Array fields can never be a problem!
+ }
+ }
+
+ bool is_field_dangerous(symbolHandle klass_name, fieldDescriptor *fd, klassOop &field_klass) {
+ field_klass = SystemDictionary::find(klass_name, fd->field_holder()->klass_part()->class_loader(), fd->field_holder()->klass_part()->protection_domain(), Thread::current());
+ if(field_klass != NULL) {
+ if (field_klass->klass_part()->is_redefining()) {
+ field_klass = field_klass->klass_part()->old_version();
+ }
+ if (field_klass->klass_part()->has_subtype_changed()) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ MyFieldClosure fieldClosure(&type_check_information);
+ cur_instance_klass->do_nonstatic_fields(&fieldClosure);
+
+ if (type_check_information.length() > 0) {
+ type_check_information.append(Pair(-1, NULL));
+ cur_instance_klass->store_type_check_information(type_check_information);
+ }
+ }
+ };
+
+ MarkAffectedClassesClosure markAffectedClassesClosure;
+ SystemDictionary::classes_do(&markAffectedClassesClosure);
+}
+
+bool VM_RedefineClasses::check_field_value_types() {
+
+ Thread *THREAD = Thread::current();
+ class CheckFieldTypesClosure : public ObjectClosure {
+
+ private:
+
+ bool _result;
+
+ public:
+
+ CheckFieldTypesClosure() {
+ _result = true;
+ }
+
+ bool result() { return _result; }
+
+ virtual void do_object(oop obj) {
+
+ if (!_result) {
+ return;
+ }
+
+ if (obj->is_objArray()) {
+
+ objArrayOop array = objArrayOop(obj);
+
+ klassOop element_klass = objArrayKlass::cast(array->klass())->element_klass();
+
+ if (element_klass->klass_part()->has_subtype_changed()) {
+ int length = array->length();
+ for (int i=0; iobj_at(i);
+ if (element != NULL && element->blueprint()->newest_version()->klass_part()->is_redefining()) {
+ // Check subtype relationship to static type of array
+ if (!element->blueprint()->newest_version()->klass_part()->is_subtype_of(element_klass->klass_part()->newest_version())) {
+ TRACE_RC1("Array value is INVALID - abort redefinition (static_type=%s, index=%d, dynamic_type=%s)", element_klass->klass_part()->name()->as_C_string(), i, element->blueprint()->name()->as_C_string());
+ _result = false;
+ break;
+ }
+ }
+ }
+ }
+
+ } else {
+ Pair *cur = obj->klass()->klass_part()->type_check_information();
+ if (cur != NULL) {
+ // Type check information exists for this oop
+ while ((*cur).left() != -1) {
+ check_field(obj, (*cur).left(), (*cur).right());
+ cur++;
+ }
}
}
}
- Rewriter::rewrite(scratch_class, THREAD);
- if (HAS_PENDING_EXCEPTION) {
- symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name();
- CLEAR_PENDING_EXCEPTION;
- if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) {
- return JVMTI_ERROR_OUT_OF_MEMORY;
- } else {
- return JVMTI_ERROR_INTERNAL;
- }
- }
-
- _scratch_classes[i] = scratch_class;
-
- // RC_TRACE_WITH_THREAD macro has an embedded ResourceMark
- RC_TRACE_WITH_THREAD(0x00000001, THREAD,
- ("loaded name=%s (avail_mem=" UINT64_FORMAT "K)",
- the_class->external_name(), os::available_memory() >> 10));
- }
-
- return JVMTI_ERROR_NONE;
-}
-
-
-// Map old_index to new_index as needed. scratch_cp is only needed
-// for RC_TRACE() calls.
-void VM_RedefineClasses::map_index(constantPoolHandle scratch_cp,
- int old_index, int new_index) {
- if (find_new_index(old_index) != 0) {
- // old_index is already mapped
- return;
- }
-
- if (old_index == new_index) {
- // no mapping is needed
- return;
- }
-
- _index_map_p->at_put(old_index, new_index);
- _index_map_count++;
-
- RC_TRACE(0x00040000, ("mapped tag %d at index %d to %d",
- scratch_cp->tag_at(old_index).value(), 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
-// in *merge_cp_p. Also creates a map of indices from entries in
-// scratch_cp to the corresponding entry in *merge_cp_p. Index map
-// entries are only created for entries in scratch_cp that occupy a
-// different location in *merged_cp_p.
-bool VM_RedefineClasses::merge_constant_pools(constantPoolHandle old_cp,
- constantPoolHandle scratch_cp, constantPoolHandle *merge_cp_p,
- int *merge_cp_length_p, TRAPS) {
-
- if (merge_cp_p == NULL) {
- assert(false, "caller must provide scatch constantPool");
- return false; // robustness
- }
- if (merge_cp_length_p == NULL) {
- assert(false, "caller must provide scatch CP length");
- return false; // robustness
- }
- // Worst case we need old_cp->length() + scratch_cp()->length(),
- // but the caller might be smart so make sure we have at least
- // the minimum.
- if ((*merge_cp_p)->length() < old_cp->length()) {
- assert(false, "merge area too small");
- return false; // robustness
- }
-
- RC_TRACE_WITH_THREAD(0x00010000, THREAD,
- ("old_cp_len=%d, scratch_cp_len=%d", old_cp->length(),
- scratch_cp->length()));
-
- {
- // Pass 0:
- // The old_cp is copied to *merge_cp_p; this means that any code
- // using old_cp does not have to change. This work looks like a
- // perfect fit for constantPoolOop::copy_cp_to(), but we need to
- // handle one special case:
- // - revert JVM_CONSTANT_Class to JVM_CONSTANT_UnresolvedClass
- // This will make verification happy.
-
- int old_i; // index into old_cp
-
- // index zero (0) is not used in constantPools
- for (old_i = 1; old_i < old_cp->length(); old_i++) {
- // leave debugging crumb
- jbyte old_tag = old_cp->tag_at(old_i).value();
- switch (old_tag) {
- case JVM_CONSTANT_Class:
- // revert the copy to JVM_CONSTANT_UnresolvedClass
- (*merge_cp_p)->unresolved_klass_at_put(old_i,
- old_cp->klass_name_at(old_i));
- break;
-
- case JVM_CONSTANT_Double:
- case JVM_CONSTANT_Long:
- // just copy the entry to *merge_cp_p, but double and long take
- // two constant pool entries
- old_cp->copy_entry_to(old_i, *merge_cp_p, old_i, CHECK_0);
- old_i++;
- break;
-
- default:
- // just copy the entry to *merge_cp_p
- old_cp->copy_entry_to(old_i, *merge_cp_p, old_i, CHECK_0);
- break;
- }
- } // end for each old_cp entry
-
- // 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.
- (*merge_cp_length_p) = old_i;
- }
-
- // merge_cp_len should be the same as old_cp->length() at this point
- // so this trace message is really a "warm-and-breathing" message.
- RC_TRACE_WITH_THREAD(0x00020000, THREAD,
- ("after pass 0: merge_cp_len=%d", *merge_cp_length_p));
-
- int scratch_i; // index into scratch_cp
- {
- // Pass 1a:
- // Compare scratch_cp entries to the old_cp entries that we have
- // already copied to *merge_cp_p. In this pass, we are eliminating
- // exact duplicates (matching entry at same index) so we only
- // compare entries in the common indice range.
- int increment = 1;
- int pass1a_length = MIN2(old_cp->length(), scratch_cp->length());
- for (scratch_i = 1; scratch_i < pass1a_length; scratch_i += increment) {
- switch (scratch_cp->tag_at(scratch_i).value()) {
- case JVM_CONSTANT_Double:
- case JVM_CONSTANT_Long:
- // double and long take two constant pool entries
- increment = 2;
- break;
-
- default:
- increment = 1;
- break;
- }
-
- bool match = scratch_cp->compare_entry_to(scratch_i, *merge_cp_p,
- scratch_i, CHECK_0);
- if (match) {
- // found a match at the same index so nothing more to do
- continue;
- } else if (is_unresolved_class_mismatch(scratch_cp, scratch_i,
- *merge_cp_p, scratch_i)) {
- // The mismatch in compare_entry_to() above is because of a
- // resolved versus unresolved class entry at the same index
- // with the same string value. Since Pass 0 reverted any
- // class entries to unresolved class entries in *merge_cp_p,
- // we go with the unresolved class entry.
- continue;
- } else if (is_unresolved_string_mismatch(scratch_cp, scratch_i,
- *merge_cp_p, scratch_i)) {
- // The mismatch in compare_entry_to() above is because of a
- // resolved versus unresolved string entry at the same index
- // with the same string value. We can live with whichever
- // happens to be at scratch_i in *merge_cp_p.
- continue;
- }
-
- int found_i = scratch_cp->find_matching_entry(scratch_i, *merge_cp_p,
- CHECK_0);
- if (found_i != 0) {
- guarantee(found_i != scratch_i,
- "compare_entry_to() and find_matching_entry() do not agree");
-
- // Found a matching entry somewhere else in *merge_cp_p so
- // just need a mapping entry.
- map_index(scratch_cp, scratch_i, found_i);
- continue;
- }
-
- // The find_matching_entry() call above could fail to find a match
- // due to a resolved versus unresolved class or string entry situation
- // like we solved above with the is_unresolved_*_mismatch() calls.
- // However, we would have to call is_unresolved_*_mismatch() over
- // all of *merge_cp_p (potentially) and that doesn't seem to be
- // worth the time.
-
- // No match found so we have to append this entry and any unique
- // referenced entries to *merge_cp_p.
- append_entry(scratch_cp, scratch_i, merge_cp_p, merge_cp_length_p,
- CHECK_0);
- }
- }
-
- RC_TRACE_WITH_THREAD(0x00020000, THREAD,
- ("after pass 1a: merge_cp_len=%d, scratch_i=%d, index_map_len=%d",
- *merge_cp_length_p, scratch_i, _index_map_count));
-
- if (scratch_i < scratch_cp->length()) {
- // Pass 1b:
- // old_cp is smaller than scratch_cp so there are entries in
- // scratch_cp that we have not yet processed. We take care of
- // those now.
- int increment = 1;
- for (; scratch_i < scratch_cp->length(); scratch_i += increment) {
- switch (scratch_cp->tag_at(scratch_i).value()) {
- case JVM_CONSTANT_Double:
- case JVM_CONSTANT_Long:
- // double and long take two constant pool entries
- increment = 2;
- break;
-
- default:
- increment = 1;
- break;
- }
-
- int found_i =
- scratch_cp->find_matching_entry(scratch_i, *merge_cp_p, CHECK_0);
- if (found_i != 0) {
- // Found a matching entry somewhere else in *merge_cp_p so
- // just need a mapping entry.
- map_index(scratch_cp, scratch_i, found_i);
- continue;
- }
-
- // No match found so we have to append this entry and any unique
- // referenced entries to *merge_cp_p.
- append_entry(scratch_cp, scratch_i, merge_cp_p, merge_cp_length_p,
- CHECK_0);
- }
-
- 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));
- }
-
- return true;
-} // end merge_constant_pools()
-
-
-// Merge constant pools between the_class and scratch_class and
-// potentially rewrite bytecodes in scratch_class to use the merged
-// constant pool.
-jvmtiError VM_RedefineClasses::merge_cp_and_rewrite(
- instanceKlassHandle the_class, instanceKlassHandle scratch_class,
- TRAPS) {
- // worst case merged constant pool length is old and new combined
- int merge_cp_length = the_class->constants()->length()
- + scratch_class->constants()->length();
-
- constantPoolHandle old_cp(THREAD, the_class->constants());
- constantPoolHandle scratch_cp(THREAD, scratch_class->constants());
-
- // Constant pools are not easily reused so we allocate a new one
- // each time.
- // merge_cp is created unsafe for concurrent GC processing. It
- // should be marked safe before discarding it because, even if
- // garbage. If it crosses a card boundary, it may be scanned
- // in order to find the start of the first complete object on the card.
- constantPoolHandle merge_cp(THREAD,
- oopFactory::new_constantPool(merge_cp_length,
- methodOopDesc::IsUnsafeConc,
- THREAD));
- int orig_length = old_cp->orig_length();
- if (orig_length == 0) {
- // This old_cp is an actual original constant pool. We save
- // the original length in the merged constant pool so that
- // merge_constant_pools() can be more efficient. If a constant
- // pool has a non-zero orig_length() value, then that constant
- // pool was created by a merge operation in RedefineClasses.
- merge_cp->set_orig_length(old_cp->length());
- } else {
- // This old_cp is a merged constant pool from a previous
- // RedefineClasses() calls so just copy the orig_length()
- // value.
- merge_cp->set_orig_length(old_cp->orig_length());
- }
-
- ResourceMark rm(THREAD);
- _index_map_count = 0;
- _index_map_p = new intArray(scratch_cp->length(), -1);
-
- bool result = merge_constant_pools(old_cp, scratch_cp, &merge_cp,
- &merge_cp_length, THREAD);
- if (!result) {
- // The merge can fail due to memory allocation failure or due
- // to robustness checks.
- return JVMTI_ERROR_INTERNAL;
- }
-
- RC_TRACE_WITH_THREAD(0x00010000, THREAD,
- ("merge_cp_len=%d, index_map_len=%d", merge_cp_length, _index_map_count));
-
- if (_index_map_count == 0) {
- // there is nothing to map between the new and merged constant pools
-
- if (old_cp->length() == scratch_cp->length()) {
- // The old and new constant pools are the same length and the
- // index map is empty. This means that the three constant pools
- // are equivalent (but not the same). Unfortunately, the new
- // constant pool has not gone through link resolution nor have
- // the new class bytecodes gone through constant pool cache
- // rewriting so we can't use the old constant pool with the new
- // class.
-
- merge_cp()->set_is_conc_safe(true);
- merge_cp = constantPoolHandle(); // toss the merged constant pool
- } else if (old_cp->length() < scratch_cp->length()) {
- // The old constant pool has fewer entries than the new constant
- // pool and the index map is empty. This means the new constant
- // pool is a superset of the old constant pool. However, the old
- // class bytecodes have already gone through constant pool cache
- // rewriting so we can't use the new constant pool with the old
- // class.
-
- merge_cp()->set_is_conc_safe(true);
- merge_cp = constantPoolHandle(); // toss the merged constant pool
- } else {
- // The old constant pool has more entries than the new constant
- // pool and the index map is empty. This means that both the old
- // and merged constant pools are supersets of the new constant
- // pool.
-
- // Replace the new constant pool with a shrunken copy of the
- // merged constant pool; the previous new constant pool will
- // get GCed.
- set_new_constant_pool(scratch_class, merge_cp, merge_cp_length, true,
- THREAD);
- // drop local ref to the merged constant pool
- merge_cp()->set_is_conc_safe(true);
- merge_cp = constantPoolHandle();
- }
- } else {
- if (RC_TRACE_ENABLED(0x00040000)) {
- // don't want to loop unless we are tracing
- int count = 0;
- for (int i = 1; i < _index_map_p->length(); i++) {
- int value = _index_map_p->at(i);
-
- if (value != -1) {
- RC_TRACE_WITH_THREAD(0x00040000, THREAD,
- ("index_map[%d]: old=%d new=%d", count, i, value));
- count++;
+ void check_field(oop obj, int offset, klassOop static_type) {
+ oop field_value = obj->obj_field(offset);
+ if (field_value != NULL) {
+ // Field is not null
+ if (field_value->klass()->klass_part()->newest_version()->klass_part()->is_subtype_of(static_type)) {
+ // We are OK
+ TRACE_RC3("Field value is OK (klass=%s, static_type=%s, offset=%d, dynamic_type=%s)", obj->klass()->klass_part()->name()->as_C_string(), static_type->klass_part()->name()->as_C_string(), offset, field_value->klass()->klass_part()->name()->as_C_string());
+ } else {
+ // Failure!
+ TRACE_RC1("Field value is INVALID - abort redefinition (klass=%s, static_type=%s, offset=%d, dynamic_type=%s)", obj->klass()->klass_part()->name()->as_C_string(), static_type->klass_part()->name()->as_C_string(), offset, field_value->klass()->klass_part()->name()->as_C_string());
+ _result = false;
}
}
}
-
- // We have entries mapped between the new and merged constant pools
- // so we have to rewrite some constant pool references.
- if (!rewrite_cp_refs(scratch_class, THREAD)) {
- return JVMTI_ERROR_INTERNAL;
+ };
+
+ CheckFieldTypesClosure myObjectClosure;
+
+ // make sure that heap is parsable (fills TLABs with filler objects)
+ Universe::heap()->ensure_parsability(false); // no need to retire TLABs
+
+ // do the iteration
+ // If this operation encounters a bad object when using CMS,
+ // consider using safe_object_iterate() which avoids perm gen
+ // objects that may contain bad references.
+ Universe::heap()->object_iterate(&myObjectClosure);
+
+ // when sharing is enabled we must iterate over the shared spaces
+ if (UseSharedSpaces) {
+ GenCollectedHeap* gch = GenCollectedHeap::heap();
+ CompactingPermGenGen* gen = (CompactingPermGenGen*)gch->perm_gen();
+ gen->ro_space()->object_iterate(&myObjectClosure);
+ gen->rw_space()->object_iterate(&myObjectClosure);
+ }
+
+ return myObjectClosure.result();
+}
+
+void VM_RedefineClasses::clear_type_check_information() {
+ class ClearTypeCheckInformationClosure : public ObjectClosure {
+ virtual void do_object(oop obj) {
+ klassOop klass = (klassOop)obj;
+
+ if (klass->klass_part()->is_redefining()) {
+ klass = klass->klass_part()->old_version();
+ }
+
+ // We found an instance klass!
+ instanceKlass *cur_instance_klass = instanceKlass::cast(klass);
+ cur_instance_klass->clear_type_check_information();
}
-
- // Replace the new constant pool with a shrunken copy of the
- // merged constant pool so now the rewritten bytecodes have
- // valid references; the previous new constant pool will get
- // GCed.
- set_new_constant_pool(scratch_class, merge_cp, merge_cp_length, true,
- THREAD);
- merge_cp()->set_is_conc_safe(true);
+ };
+
+ ClearTypeCheckInformationClosure clearTypeCheckInformationClosure;
+ SystemDictionary::classes_do(&clearTypeCheckInformationClosure);
+}
+
+void VM_RedefineClasses::update_active_methods() {
+
+ TRACE_RC2("Updating active methods");
+ JavaThread *java_thread = Threads::first();
+ while (java_thread != NULL) {
+
+ int stack_depth = 0;
+ if (java_thread->has_last_Java_frame()) {
+
+ TRACE_RC4("checking stack of Java thread %s", java_thread->name());
+
+ // vframes are resource allocated
+ Thread* current_thread = Thread::current();
+ ResourceMark rm(current_thread);
+ HandleMark hm(current_thread);
+
+ RegisterMap reg_map(java_thread);
+ frame f = java_thread->last_frame();
+ vframe* vf = vframe::new_vframe(&f, ®_map, java_thread);
+ frame* last_entry_frame = NULL;
+
+ while (vf != NULL) {
+ if (vf->is_java_frame()) {
+ // java frame (interpreted, compiled, ...)
+ javaVFrame *jvf = javaVFrame::cast(vf);
+
+ if (!(jvf->method()->is_native())) {
+ int bci = jvf->bci();
+ TRACE_RC4("found method: %s / bci=%d", jvf->method()->name()->as_C_string(), bci);
+ ResourceMark rm(Thread::current());
+ HandleMark hm;
+ instanceKlassHandle klass(jvf->method()->method_holder());
+
+ if (jvf->method()->new_version() != NULL && jvf->is_interpreted_frame()) {
+
+
+ TRACE_RC2("Found method that should just be updated to the newest version %s", jvf->method()->name_and_sig_as_C_string());
+
+ IF_TRACE_RC5 {
+ int code_size = jvf->method()->code_size();
+ char *code_base_old = (char*)jvf->method()->code_base();
+ char *code_base_new = (char*)jvf->method()->new_version()->code_base();
+ for (int i=0; iprint_cr("old=%d new=%d", *code_base_old++, *code_base_new++);
+ }
+ jvf->method()->print_codes_on(tty);
+ jvf->method()->new_version()->print_codes_on(tty);
+ }
+
+ assert(jvf->is_interpreted_frame(), "Every frame must be interpreted!");
+ interpretedVFrame *iframe = (interpretedVFrame *)jvf;
+
+
+ IF_TRACE_RC5 {
+ constantPoolCacheOop cp_old = jvf->method()->constants()->cache();
+ tty->print_cr("old cp");
+ for (int i=0; ilength(); i++) {
+ cp_old->entry_at(i)->print(tty, i);
+ }
+ constantPoolCacheOop cp_new = jvf->method()->new_version()->constants()->cache();
+ tty->print_cr("new cp");
+ for (int i=0; ilength(); i++) {
+ cp_new->entry_at(i)->print(tty, i);
+ }
+ }
+
+ iframe->set_method(jvf->method()->new_version(), bci);
+ TRACE_RC2("Updated method to newer version");
+ assert(jvf->method()->new_version() == NULL, "must be latest version");
+
+ }
+ }
+ }
+ vf = vf->sender();
+ }
+ }
+
+ // Advance to next thread
+ java_thread = java_thread->next();
}
- assert(old_cp()->is_conc_safe(), "Just checking");
- assert(scratch_cp()->is_conc_safe(), "Just checking");
-
- return JVMTI_ERROR_NONE;
-} // end merge_cp_and_rewrite()
-
-
-// Rewrite constant pool references in klass scratch_class.
-bool VM_RedefineClasses::rewrite_cp_refs(instanceKlassHandle scratch_class,
- TRAPS) {
-
- // rewrite constant pool references in the methods:
- if (!rewrite_cp_refs_in_methods(scratch_class, THREAD)) {
- // propagate failure back to caller
+}
+
+void VM_RedefineClasses::method_forwarding() {
+
+ int forwarding_count = 0;
+ JavaThread *java_thread = Threads::first();
+ while (java_thread != NULL) {
+
+ int stack_depth = 0;
+ if (java_thread->has_last_Java_frame()) {
+
+ TRACE_RC4("checking stack of Java thread %s", java_thread->name());
+
+ // vframes are resource allocated
+ Thread* current_thread = Thread::current();
+ ResourceMark rm(current_thread);
+ HandleMark hm(current_thread);
+
+ RegisterMap reg_map(java_thread);
+ frame f = java_thread->last_frame();
+ vframe* vf = vframe::new_vframe(&f, ®_map, java_thread);
+ frame* last_entry_frame = NULL;
+
+ while (vf != NULL) {
+ if (vf->is_java_frame()) {
+ // java frame (interpreted, compiled, ...)
+ javaVFrame *jvf = javaVFrame::cast(vf);
+
+ if (!(jvf->method()->is_native())) {
+ TRACE_RC3("found method: %s", jvf->method()->name()->as_C_string());
+ ResourceMark rm(Thread::current());
+ HandleMark hm;
+ instanceKlassHandle klass(jvf->method()->method_holder());
+ methodOop m = jvf->method();
+ int bci = jvf->bci();
+ TRACE_RC3("klass redef %d", klass->is_redefining());
+
+ if (klass->new_version() != NULL && m->new_version() == NULL) {
+ TRACE_RC3("found potential forwarding method: %s", m->name()->as_C_string());
+
+ klassOop new_klass = klass->newest_version();
+ methodOop new_method = new_klass->klass_part()->lookup_method(m->name(), m->signature());
+ TRACE_RC2("%d %d", new_method, new_method->constMethod()->has_code_section_table());
+
+ if (new_method != NULL && new_method->constMethod()->has_code_section_table()) {
+ TRACE_RC3("found code section table for method: %s", new_method->name()->as_C_string());
+ m->set_forward_method(new_method);
+ if (new_method->max_locals() != m->max_locals()) {
+ tty->print_cr("new_m max locals: %d old_m max locals: %d", new_method->max_locals(), m->max_locals());
+ }
+ assert(new_method->max_locals() == m->max_locals(), "number of locals must match");
+ assert(new_method->max_stack() == m->max_stack(), "number of stack values must match");
+ if (jvf->is_interpreted_frame()) {
+ if (m->is_in_code_section(bci)) {
+ // We must transfer now and cannot delay until next NOP.
+ int new_bci = m->calculate_forward_bci(bci, new_method);
+ interpretedVFrame* iframe = interpretedVFrame::cast(jvf);
+ TRACE_RC2("Transfering execution of %s to new method old_bci=%d new_bci=%d", new_method->name()->as_C_string(), bci, new_bci);
+ iframe->set_method(new_method, new_bci);
+ } else {
+ TRACE_RC2("Delaying method forwarding of %s because %d is not in a code section", new_method->name()->as_C_string(), bci);
+ }
+ } else {
+ TRACE_RC2("Delaying method forwarding of %s because method is compiled", new_method->name()->as_C_string());
+ }
+ }
+ }
+ }
+ }
+ vf = vf->sender();
+ }
+ }
+
+ // Advance to next thread
+ java_thread = java_thread->next();
+ }
+
+ TRACE_RC1("Method forwarding applied to %d methods", forwarding_count);
+}
+
+bool VM_RedefineClasses::check_method_stacks() {
+
+ JavaThread *java_thread = Threads::first();
+ while (java_thread != NULL) {
+
+ int stack_depth = 0;
+ if (java_thread->has_last_Java_frame()) {
+
+ TRACE_RC4("checking stack of Java thread %s", java_thread->name());
+
+ // vframes are resource allocated
+ Thread* current_thread = Thread::current();
+ ResourceMark rm(current_thread);
+ HandleMark hm(current_thread);
+
+ RegisterMap reg_map(java_thread);
+ frame f = java_thread->last_frame();
+ vframe* vf = vframe::new_vframe(&f, ®_map, java_thread);
+ frame* last_entry_frame = NULL;
+
+ while (vf != NULL) {
+ if (vf->is_java_frame()) {
+ // java frame (interpreted, compiled, ...)
+ javaVFrame *jvf = javaVFrame::cast(vf);
+
+ if (!(jvf->method()->is_native())) {
+ TRACE_RC4("found method: %s", jvf->method()->name()->as_C_string());
+ ResourceMark rm(Thread::current());
+ HandleMark hm;
+ instanceKlassHandle klass(jvf->method()->method_holder());
+
+ StackValueCollection *locals = jvf->locals();
+ const size_t message_buffer_len = klass->name()->utf8_length() + 1024;
+ char* message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len);
+
+ for (int i=0; isize(); i++) {
+ StackValue *stack_value = locals->at(i);
+ if (stack_value->type() == T_OBJECT) {
+ Handle obj = stack_value->get_obj();
+ if (!obj.is_null() && obj->klass()->klass_part()->newest_version()->klass_part()->check_redefinition_flag(Klass::RemoveSuperType)) {
+
+ // OK, so this is a possible failure => check local variable table, if it could be OK.
+ bool result = false;
+ methodOop method = jvf->method();
+ if (method->has_localvariable_table()) {
+ LocalVariableTableElement *elem = jvf->method()->localvariable_table_start();
+ for (int j=0; jlocalvariable_table_length(); j++) {
+
+ if (elem->slot == i) {
+
+ // Matching index found
+
+ if (elem->start_bci <= jvf->bci() && elem->start_bci + elem->length > jvf->bci()) {
+
+ // Also in range!!
+ symbolOop signature = jvf->method()->constants()->symbol_at(elem->descriptor_cp_index);
+ symbolOop klass_name = signature_to_class_name(signature);
+
+ klassOop local_klass = SystemDictionary::find(klass_name, jvf->method()->method_holder()->klass_part()->class_loader(), jvf->method()->method_holder()->klass_part()->protection_domain(), Thread::current())->klass_part()->newest_version();
+ klassOop cur = obj->klass()->klass_part()->newest_version();
+
+ // Field is not null
+ if (cur->klass_part()->newest_version()->klass_part()->is_subtype_of(local_klass)) {
+ // We are OK
+ TRACE_RC3("Local variable value is OK (local_klass=%s, cur_klass=%s)", local_klass->klass_part()->name()->as_C_string(), cur->klass_part()->name()->as_C_string());
+ result = true;
+ } else {
+ // Failure!
+ TRACE_RC1("Local variable value is INVALID - abort redefinition (local_klass=%s, cur_klass=%s)", local_klass->klass_part()->name()->as_C_string(), cur->klass_part()->name()->as_C_string());
+ return false;
+ }
+ }
+ }
+
+ elem++;
+ }
+ } else {
+ TRACE_RC2("Method %s does not have a local variable table => abort", method->name_and_sig_as_C_string());
+ }
+
+ if (!result) {
+ return false;
+ }
+
+ TRACE_RC3("Verifying class %s", jvf->method()->method_holder()->klass_part()->name()->as_C_string());
+
+ symbolHandle exception_name;
+ const size_t message_buffer_len = klass->name()->utf8_length() + 1024;
+ char* message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len);
+
+ if (TraceRedefineClasses >= 4) {
+ ClassVerifier::_verify_verbose = true;
+ }
+
+ Thread::current()->set_pretend_new_universe(true);
+ ClassVerifier split_verifier(klass, message_buffer, message_buffer_len, Thread::current());
+ split_verifier.verify_method(jvf->method(), Thread::current());
+ exception_name = split_verifier.result();
+ Thread::current()->set_pretend_new_universe(false);
+
+ if (TraceRedefineClasses >= 4) {
+ ClassVerifier::_verify_verbose = false;
+ }
+
+ if (!exception_name.is_null()) {
+
+ TRACE_RC1("Verification of class %s failed", jvf->method()->method_holder()->klass_part()->name()->as_C_string());
+ TRACE_RC1("Exception: %s", exception_name->as_C_string());
+ TRACE_RC1("Message: %s", message_buffer);
+ Thread::current()->clear_pending_exception();
+ return false;
+ }
+
+ }
+ }
+ }
+ }
+ }
+ vf = vf->sender();
+ }
+ }
+
+ // Advance to next thread
+ java_thread = java_thread->next();
+ }
+
+ return true;
+}
+
+bool VM_RedefineClasses::check_method(methodOop method) {
+
+
+ return true;
+}
+
+// Warning: destroys redefinition level values of klasses.
+bool VM_RedefineClasses::check_loaded_methods() {
+
+ class CheckLoadedMethodsClosure : public ObjectClosure {
+
+ private:
+
+ bool _result;
+ GrowableArray *_dangerous_klasses;
+
+ public:
+ CheckLoadedMethodsClosure(GrowableArray *dangerous_klasses) {
+ _result = true;
+ _dangerous_klasses = dangerous_klasses;
+ }
+
+ bool result() {
+ return _result;
+ }
+
+ bool is_class_dangerous(klassOop k) {
+ return k->klass_part()->newest_version()->klass_part()->check_redefinition_flag(Klass::RemoveSuperType);
+ }
+
+ bool can_be_affected(instanceKlass *klass) {
+
+ constantPoolOop cp = klass->constants();
+
+ Thread *THREAD = Thread::current();
+ klassOop k;
+ symbolOop symbol;
+
+ for (int i=1; ilength(); i++) {
+ jbyte tag = cp->tag_at(i).value();
+ switch(tag) {
+ case JVM_CONSTANT_Long:
+ case JVM_CONSTANT_Double:
+ i++;
+ break;
+
+ case JVM_CONSTANT_Utf8:
+ case JVM_CONSTANT_Unicode:
+ case JVM_CONSTANT_Integer:
+ case JVM_CONSTANT_Float:
+ case JVM_CONSTANT_String:
+ case JVM_CONSTANT_Fieldref:
+ case JVM_CONSTANT_Methodref:
+ case JVM_CONSTANT_InterfaceMethodref:
+ case JVM_CONSTANT_ClassIndex:
+ case JVM_CONSTANT_UnresolvedString:
+ case JVM_CONSTANT_StringIndex:
+ case JVM_CONSTANT_UnresolvedClassInError:
+ case JVM_CONSTANT_Object:
+ // do nothing
+ break;
+
+ case JVM_CONSTANT_Class:
+ k = cp->klass_at(i, CHECK_(true));
+ if (is_class_dangerous(k)) {
+ TRACE_RC2("Class %s is potentially affected, because at cp[%d] references class %s", klass->name()->as_C_string(), i, k->klass_part()->name()->as_C_string());
+ return true;
+ }
+ break;
+
+ case JVM_CONSTANT_NameAndType:
+ symbol = cp->symbol_at(cp->signature_ref_index_at(i));
+ if (symbol->byte_at(0) == '(') {
+ // This must be a method
+ SignatureStream signatureStream(symbol);
+ while (true) {
+
+ if (signatureStream.is_array()) {
+ symbolOop cur_signature = signatureStream.as_symbol(Thread::current());
+ if (is_type_signature_dangerous(cur_signature)) {
+ return true;
+ }
+ } else if (signatureStream.is_object()) {
+ if (is_symbol_dangerous(signatureStream.as_symbol(Thread::current()))) {
+ return true;
+ }
+ }
+
+ if (signatureStream.at_return_type()) {
+ break;
+ }
+
+ signatureStream.next();
+ }
+
+ } else if (is_type_signature_dangerous(symbol)) {
+ return true;
+ }
+ break;
+
+ case JVM_CONSTANT_UnresolvedClass:
+ symbol = cp->unresolved_klass_at(i);
+ if (is_symbol_dangerous(symbol)) {
+ return true;
+ }
+ break;
+
+ default:
+ ShouldNotReachHere();
+ }
+ }
+
+ return false;
+ }
+
+ bool is_type_signature_dangerous(symbolOop signature) {
+ // This must be a field type
+ if (FieldType::is_obj(signature)) {
+ symbolOop name = signature_to_class_name(signature);
+ if (is_symbol_dangerous(name)) {
+ return true;
+ }
+ } else if (FieldType::is_array(signature)) {
+ jint dimension;
+ symbolOop object_key;
+ FieldType::get_array_info(signature, &dimension, &object_key, Thread::current());
+ if (is_symbol_dangerous(object_key)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool is_symbol_dangerous(symbolOop symbol) {
+ for (int i=0; i<_dangerous_klasses->length(); i++) {
+ if(_dangerous_klasses->at(i)->klass_part()->name() == symbol) {
+ TRACE_RC2("Found constant pool index %d references class %s", i, symbol->as_C_string());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ virtual void do_object(oop obj) {
+
+ if (!_result) return;
+
+ klassOop klassObj = (klassOop)obj;
+ Thread *THREAD = Thread::current();
+
+ // We found an instance klass!
+ instanceKlass *klass = instanceKlass::cast(klassObj);
+ instanceKlassHandle handle(klassObj);
+
+ TRACE_RC4("Check if verification is necessary for class %s major_version=%d", handle->name()->as_C_string(), handle->major_version());
+
+ if (!can_be_affected(klass)) {
+ TRACE_RC4("Skipping verification of class %s major_version=%d", handle->name()->as_C_string(), handle->major_version());
+ return;
+ }
+
+ if (handle->major_version() < Verifier::STACKMAP_ATTRIBUTE_MAJOR_VERSION) {
+ TRACE_RC1("Failing because cannot verify class %s major_version=%d", handle->name()->as_C_string(), handle->major_version());
+ _result = false;
+ return;
+ }
+
+ TRACE_RC3("Verifying class %s", handle->name()->as_C_string());
+
+ if (!Verifier::verify(handle, Verifier::NoException, true, false, Thread::current())) {
+
+ TRACE_RC1("Verification of class %s failed", handle->name()->as_C_string());
+ //symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name();
+ //TRACE_RC2("exception when verifying class: '%s'", ex_name->as_C_string());
+ //PENDING_EXCEPTION->print();
+ CLEAR_PENDING_EXCEPTION;
+ _result = false;
+ }
+
+ /*int method_count = klass->methods()->length();
+ for (int i=0; imethods()->obj_at(i);
+ if (!check_method(cur_method)) {
+ TRACE_RC1("Failed to verify consistency of method %s of klass %s", cur_method->name()->as_C_string(), klass->name()->as_C_string());
+ }
+ }*/
+ }
+ };
+
+ // TODO: Check bytecodes in case of interface => class or class => interface etc..
+
+ GrowableArray dangerous_klasses;
+ for (int i=0; i<_new_classes->length(); i++) {
+ instanceKlassHandle handle = _new_classes->at(i);
+ if (handle->check_redefinition_flag(Klass::RemoveSuperType)) {
+ dangerous_klasses.append(handle());
+ }
+ }
+
+ CheckLoadedMethodsClosure checkLoadedMethodsClosure(&dangerous_klasses);
+ Thread::current()->set_pretend_new_universe(true);
+ SystemDictionary::classes_do(&checkLoadedMethodsClosure);
+ Thread::current()->set_pretend_new_universe(false);
+
+
+ return checkLoadedMethodsClosure.result();
+}
+
+bool VM_RedefineClasses::check_type_consistency() {
+
+ Universe::set_verify_in_progress(true);
+
+ calculate_type_check_information();
+ bool result = check_field_value_types();
+ clear_type_check_information();
+ if (!result) {
+ TRACE_RC1("Aborting redefinition because of wrong field or array element value!");
+ Universe::set_verify_in_progress(false);
return false;
}
- // rewrite constant pool references in the class_annotations:
- if (!rewrite_cp_refs_in_class_annotations(scratch_class, THREAD)) {
- // propagate failure back to caller
+ result = check_method_stacks();
+ if (!result) {
+ TRACE_RC1("Aborting redefinition because of wrong value on the stack");
+ Universe::set_verify_in_progress(false);
return false;
}
- // rewrite constant pool references in the fields_annotations:
- if (!rewrite_cp_refs_in_fields_annotations(scratch_class, THREAD)) {
- // propagate failure back to caller
+ result = check_loaded_methods();
+ if (!result) {
+ TRACE_RC1("Aborting redefinition because of wrong loaded method");
+ Universe::set_verify_in_progress(false);
return false;
}
- // rewrite constant pool references in the methods_annotations:
- if (!rewrite_cp_refs_in_methods_annotations(scratch_class, THREAD)) {
- // propagate failure back to caller
+ TRACE_RC1("Verification passed => hierarchy change is valid!");
+ Universe::set_verify_in_progress(false);
+ return true;
+}
+
+void VM_RedefineClasses::rollback() {
+ TRACE_RC1("Rolling back redefinition!");
+ SystemDictionary::rollback_redefinition();
+
+ TRACE_RC1("After rolling back system dictionary!");
+ for (int i=0; i<_new_classes->length(); i++) {
+ SystemDictionary::remove_from_hierarchy(_new_classes->at(i));
+ }
+
+ for (int i=0; i<_new_classes->length(); i++) {
+ instanceKlassHandle new_class = _new_classes->at(i);
+ new_class->set_redefining(false);
+ new_class->old_version()->klass_part()->set_new_version(NULL);
+ new_class->set_old_version(NULL);
+ }
+
+}
+
+template void VM_RedefineClasses::do_oop_work(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);
+ if (obj->is_instanceKlass()) {
+ klassOop klass = (klassOop)obj;
+ if (klass->klass_part()->new_version() != NULL && klass->klass_part()->new_version()->klass_part()->is_redefining()) {
+ obj = klass->klass_part()->new_version();
+ oopDesc::encode_store_heap_oop_not_null(p, obj);
+ }
+ } else if (obj->blueprint()->newest_version() == SystemDictionary::Class_klass()->klass_part()->newest_version()) {
+
+ klassOop klass_oop = java_lang_Class::as_klassOop(obj);
+ if (klass_oop != NULL) {
+ if (klass_oop->klass_part()->new_version() != NULL && klass_oop->klass_part()->new_version()->klass_part()->is_redefining()) {
+ obj = klass_oop->klass_part()->new_version()->klass_part()->java_mirror();
+ oopDesc::encode_store_heap_oop_not_null(p, obj);
+ } else if (klass_oop->klass_part()->is_redefining()) {
+ obj = klass_oop->klass_part()->java_mirror();
+ oopDesc::encode_store_heap_oop_not_null(p, obj);
+ }
+ }
+ }
+ }
+}
+
+void VM_RedefineClasses::swap_marks(oop first, oop second) {
+ markOop first_mark = first->mark();
+ markOop second_mark = second->mark();
+ first->set_mark(second_mark);
+ second->set_mark(first_mark);
+}
+
+void VM_RedefineClasses::doit() {
+ Thread *thread = Thread::current();
+
+ TRACE_RC1("Entering doit!");
+
+
+ if ((_max_redefinition_flags & Klass::RemoveSuperType) != 0) {
+
+ RC_TIMER_START(_timer_check_type);
+
+ if (!check_type_consistency()) {
+ // (tw) TODO: Rollback the class redefinition
+ rollback();
+ TRACE_RC1("Detected type inconsistency!");
+ _result = JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED;
+ RC_TIMER_STOP(_timer_check_type);
+ return;
+ }
+
+ RC_TIMER_STOP(_timer_check_type);
+
+ } else {
+ TRACE_RC1("No type narrowing => skipping check for type inconsistency");
+ }
+
+ if (UseMethodForwardPoints) {
+ TRACE_RC1("Check stack for forwarding methods to new version");
+ method_forwarding();
+ }
+
+ if (UseSharedSpaces) {
+ // Sharing is enabled so we remap the shared readonly space to
+ // shared readwrite, private just in case we need to redefine
+ // a shared class. We do the remap during the doit() phase of
+ // the safepoint to be safer.
+ if (!CompactingPermGenGen::remap_shared_readonly_as_readwrite()) {
+ TRACE_RC1("failed to remap shared readonly space to readwrite, private");
+ _result = JVMTI_ERROR_INTERNAL;
+ return;
+ }
+ }
+
+ RC_TIMER_START(_timer_prepare_redefinition);
+ for (int i = 0; i < _new_classes->length(); i++) {
+ redefine_single_class(_new_classes->at(i), thread);
+ }
+
+ // Deoptimize all compiled code that depends on this class
+ flush_dependent_code(instanceKlassHandle(Thread::current(), (klassOop)NULL), Thread::current());
+
+ // Adjust constantpool caches and vtables for all classes
+ // that reference methods of the evolved class.
+ SystemDictionary::classes_do(adjust_cpool_cache, Thread::current());
+
+ RC_TIMER_STOP(_timer_prepare_redefinition);
+ RC_TIMER_START(_timer_redefinition);
+
+ class ChangePointersOopClosure : public OopClosure {
+ virtual void do_oop(oop* o) {
+ do_oop_work(o);
+ }
+
+ virtual void do_oop(narrowOop* o) {
+ do_oop_work(o);
+ }
+ };
+
+ class ChangePointersObjectClosure : public ObjectClosure {
+
+ private:
+
+ OopClosure *_closure;
+ bool _needs_instance_update;
+ GrowableArray *_updated_oops;
+
+ public:
+ ChangePointersObjectClosure(OopClosure *closure) : _closure(closure), _needs_instance_update(false), _updated_oops(NULL) {}
+
+ bool needs_instance_update() {
+ return _needs_instance_update;
+ }
+
+ GrowableArray *updated_oops() { return _updated_oops; }
+
+ virtual void do_object(oop obj) {
+ if (!obj->is_instanceKlass()) {
+ obj->oop_iterate(_closure);
+
+ if (obj->blueprint()->is_redefining()) {
+
+ if (obj->blueprint()->check_redefinition_flag(Klass::HasInstanceTransformer)) {
+ if (_updated_oops == NULL) {
+ _updated_oops = new (ResourceObj::C_HEAP) GrowableArray(100, true);
+ }
+ _updated_oops->append(obj);
+ }
+
+ if(obj->blueprint()->update_information() != NULL || obj->is_perm()) {
+
+ assert(obj->blueprint()->old_version() != NULL, "must have old version");
+ obj->set_klass_no_check(obj->blueprint()->old_version());
+
+ if (obj->size() != obj->size_given_klass(obj->blueprint()->new_version()->klass_part()) || obj->is_perm()) {
+ // We need an instance update => set back to old klass
+ _needs_instance_update = true;
+
+ } else {
+ MarkSweep::update_fields(obj, obj);
+ assert(obj->blueprint()->is_redefining(), "update fields resets the klass");
+ }
+ }
+ }
+
+ } else {
+ instanceKlass *klass = instanceKlass::cast((klassOop)obj);
+ if (klass->is_redefining()) {
+ // Initialize the new class! Special static initialization that does not execute the
+ // static constructor but copies static field values from the old class if name
+ // and signature of a static field match.
+ klass->initialize_redefined_class();
+ }
+ klass->iterate_static_fields(_closure);
+ }
+ }
+ };
+
+ ChangePointersOopClosure oopClosure;
+ ChangePointersObjectClosure objectClosure(&oopClosure);
+
+ {
+ SharedHeap::heap()->gc_prologue(true);
+ Universe::root_oops_do(&oopClosure);
+ Universe::heap()->object_iterate(&objectClosure);
+ SharedHeap::heap()->gc_epilogue(false);
+ }
+
+ // Swap marks to have same hashcodes
+ for (int i=0; i<_new_classes->length(); i++) {
+ swap_marks(_new_classes->at(i)(), _new_classes->at(i)->old_version());
+ swap_marks(_new_classes->at(i)->java_mirror(), _new_classes->at(i)->old_version()->klass_part()->java_mirror());
+ ((instanceKlass*)_new_classes->at(i)->old_version()->klass_part())->constants()->set_pool_holder(_new_classes->at(i)->old_version());
+ }
+
+ _updated_oops = objectClosure.updated_oops();
+
+ if (objectClosure.needs_instance_update()){
+
+ // Do a full garbage collection to update the instance sizes accordingly
+ TRACE_RC1("Before performing full GC!");
+ Universe::set_redefining_gc_run(true);
+ JvmtiGCFullMarker jgcm;
+ notify_gc_begin(true);
+ Universe::heap()->collect_as_vm_thread(GCCause::_heap_inspection);
+ notify_gc_end();
+ Universe::set_redefining_gc_run(false);
+ TRACE_RC1("GC done!");
+ }
+
+
+ IF_TRACE_RC1 {
+ if (_updated_oops != NULL) {
+ TRACE_RC1("%d object(s) updated!", _updated_oops->length());
+ } else {
+ TRACE_RC1("No objects updated!");
+ }
+ }
+
+ // Unmark klassOops as "redefining"
+ for (int i=0; i<_new_classes->length(); i++) {
+ klassOop cur = _new_classes->at(i)();
+ _new_classes->at(i)->set_redefining(false);
+ _new_classes->at(i)->clear_update_information();
+ _new_classes->at(i)->update_supers_to_newest_version();
+
+ if (((instanceKlass *)cur->klass_part()->old_version()->klass_part())->array_klasses() != NULL) {
+ update_array_classes_to_newest_version(((instanceKlass *)cur->klass_part()->old_version()->klass_part())->array_klasses());
+
+ // Transfer the array classes, otherwise we might get cast exceptions when casting array types.
+ ((instanceKlass*)cur->klass_part())->set_array_klasses(((instanceKlass*)cur->klass_part()->old_version()->klass_part())->array_klasses());
+
+ oop new_mirror = _new_classes->at(i)->java_mirror();
+ oop old_mirror = _new_classes->at(i)->old_version()->klass_part()->java_mirror();
+ java_lang_Class::set_array_klass(new_mirror, java_lang_Class::array_klass(old_mirror));
+ }
+ }
+
+ for (int i=T_BOOLEAN; i<=T_LONG; i++) {
+ update_array_classes_to_newest_version(Universe::typeArrayKlassObj((BasicType)i));
+ }
+
+ // Disable any dependent concurrent compilations
+ SystemDictionary::notice_modification();
+
+ // Set flag indicating that some invariants are no longer true.
+ // See jvmtiExport.hpp for detailed explanation.
+ JvmtiExport::set_has_redefined_a_class();
+
+ // Clean up caches in the compiler interface and compiler threads
+ CompileBroker::cleanup_after_redefinition();
+
+#ifdef ASSERT
+
+ // Universe::verify();
+ // JNIHandles::verify();
+
+ SystemDictionary::classes_do(check_class, thread);
+#endif
+
+ update_active_methods();
+ RC_TIMER_STOP(_timer_redefinition);
+
+}
+
+void VM_RedefineClasses::update_array_classes_to_newest_version(klassOop smallest_dimension) {
+
+ arrayKlass *curArrayKlass = arrayKlass::cast(smallest_dimension);
+ assert(curArrayKlass->lower_dimension() == NULL, "argument must be smallest dimension");
+
+
+ while (curArrayKlass != NULL) {
+ klassOop higher_dimension = curArrayKlass->higher_dimension();
+ klassOop lower_dimension = curArrayKlass->lower_dimension();
+ curArrayKlass->update_supers_to_newest_version();
+
+ curArrayKlass = NULL;
+ if (higher_dimension != NULL) {
+ curArrayKlass = arrayKlass::cast(higher_dimension);
+ }
+ }
+
+}
+
+void VM_RedefineClasses::doit_epilogue() {
+
+ RC_TIMER_START(_timer_vm_op_epilogue);
+
+ unlock_threads();
+
+ ResourceMark mark;
+
+ VM_GC_Operation::doit_epilogue();
+ TRACE_RC1("GC Operation epilogue finished! ");
+
+ GrowableArray instanceTransformerMethods;
+
+ // Call static transformers
+ for (int i=0; i<_new_classes->length(); i++) {
+
+ instanceKlassHandle klass = _new_classes->at(i);
+
+ // Find instance transformer method
+
+ if (klass->check_redefinition_flag(Klass::HasInstanceTransformer)) {
+
+ TRACE_RC5("Call instance transformer of %s instance", klass->name()->as_C_string());
+ klassOop cur_klass = klass();
+ while (cur_klass != NULL) {
+ methodOop method = ((instanceKlass*)cur_klass->klass_part())->find_method(vmSymbols::transformer_name(), vmSymbols::void_method_signature());
+ if (method != NULL) {
+ methodHandle instanceTransformerMethod(method);
+ instanceTransformerMethods.append(instanceTransformerMethod);
+ break;
+ } else {
+ cur_klass = cur_klass->klass_part()->super();
+ }
+ }
+ assert(cur_klass != NULL, "must have instance transformer method");
+ } else {
+ instanceTransformerMethods.append(methodHandle(Thread::current(), NULL));
+ }
+ }
+
+
+ // Call instance transformers
+ if (_updated_oops != NULL) {
+
+ for (int i=0; i<_updated_oops->length(); i++) {
+ assert(_updated_oops->at(i) != NULL, "must not be null!");
+ Handle cur(_updated_oops->at(i));
+ instanceKlassHandle klass(cur->klass());
+
+ if (klass->check_redefinition_flag(Klass::HasInstanceTransformer)) {
+
+ methodHandle method = instanceTransformerMethods.at(klass->redefinition_index());
+
+ TRACE_RC5("executing transformer method");
+
+ Thread *__the_thread__ = Thread::current();
+ JavaValue result(T_VOID);
+ JavaCallArguments args(cur);
+ JavaCalls::call(&result,
+ method,
+ &args,
+ THREAD);
+
+ // TODO: What to do with an exception here?
+ if (HAS_PENDING_EXCEPTION) {
+ symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name();
+ TRACE_RC2("exception when executing transformer: '%s'", ex_name->as_C_string());
+ CLEAR_PENDING_EXCEPTION;
+ }
+ }
+ }
+
+ delete _updated_oops;
+ _updated_oops = NULL;
+ }
+
+ // Free the array of scratch classes
+ delete _new_classes;
+ _new_classes = NULL;
+ TRACE_RC1("Redefinition finished!");
+
+ RC_TIMER_STOP(_timer_vm_op_epilogue);
+}
+
+bool VM_RedefineClasses::is_modifiable_class(oop klass_mirror) {
+ // classes for primitives cannot be redefined
+ if (java_lang_Class::is_primitive(klass_mirror)) {
return false;
}
-
- // rewrite constant pool references in the methods_parameter_annotations:
- if (!rewrite_cp_refs_in_methods_parameter_annotations(scratch_class,
- THREAD)) {
- // propagate failure back to caller
+ klassOop the_class_oop = java_lang_Class::as_klassOop(klass_mirror);
+ // classes for arrays cannot be redefined
+ if (the_class_oop == NULL || !Klass::cast(the_class_oop)->oop_is_instance()) {
return false;
}
-
- // rewrite constant pool references in the methods_default_annotations:
- if (!rewrite_cp_refs_in_methods_default_annotations(scratch_class,
- THREAD)) {
- // propagate failure back to caller
- return false;
- }
-
- 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) {
-
- objArrayHandle methods(THREAD, scratch_class->methods());
-
- if (methods.is_null() || methods->length() == 0) {
- // no methods so nothing to do
- return true;
- }
-
- // rewrite constant pool references in the methods:
- for (int i = methods->length() - 1; i >= 0; i--) {
- methodHandle method(THREAD, (methodOop)methods->obj_at(i));
- methodHandle new_method;
- rewrite_cp_refs_in_method(method, &new_method, CHECK_false);
- if (!new_method.is_null()) {
- // the method has been replaced so save the new method version
- methods->obj_at_put(i, new_method());
- }
- }
-
return true;
}
-
-// Rewrite constant pool references in the specific method. This code
-// was adapted from Rewriter::rewrite_method().
-void VM_RedefineClasses::rewrite_cp_refs_in_method(methodHandle method,
- methodHandle *new_method_p, TRAPS) {
-
- *new_method_p = methodHandle(); // default is no new method
-
- // We cache a pointer to the bytecodes here in code_base. If GC
- // moves the methodOop, then the bytecodes will also move which
- // will likely cause a crash. We create a No_Safepoint_Verifier
- // object to detect whether we pass a possible safepoint in this
- // code block.
- No_Safepoint_Verifier nsv;
-
- // Bytecodes and their length
- address code_base = method->code_base();
- int code_length = method->code_size();
-
- int bc_length;
- for (int bci = 0; bci < code_length; bci += bc_length) {
- address bcp = code_base + bci;
- Bytecodes::Code c = (Bytecodes::Code)(*bcp);
-
- bc_length = Bytecodes::length_for(c);
- if (bc_length == 0) {
- // More complicated bytecodes report a length of zero so
- // we have to try again a slightly different way.
- bc_length = Bytecodes::length_at(bcp);
- }
-
- assert(bc_length != 0, "impossible bytecode length");
-
- switch (c) {
- case Bytecodes::_ldc:
- {
- int cp_index = *(bcp + 1);
- int new_index = find_new_index(cp_index);
-
- if (StressLdcRewrite && new_index == 0) {
- // If we are stressing ldc -> ldc_w rewriting, then we
- // always need a new_index value.
- new_index = cp_index;
- }
- if (new_index != 0) {
- // the original index is mapped so we have more work to do
- if (!StressLdcRewrite && new_index <= max_jubyte) {
- // The new value can still use ldc instead of ldc_w
- // unless we are trying to stress ldc -> ldc_w rewriting
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("%s@" INTPTR_FORMAT " old=%d, new=%d", Bytecodes::name(c),
- bcp, cp_index, new_index));
- *(bcp + 1) = new_index;
- } else {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("%s->ldc_w@" INTPTR_FORMAT " old=%d, new=%d",
- Bytecodes::name(c), bcp, cp_index, new_index));
- // the new value needs ldc_w instead of ldc
- u_char inst_buffer[4]; // max instruction size is 4 bytes
- bcp = (address)inst_buffer;
- // construct new instruction sequence
- *bcp = Bytecodes::_ldc_w;
- bcp++;
- // Rewriter::rewrite_method() does not rewrite ldc -> ldc_w.
- // See comment below for difference between put_Java_u2()
- // and put_native_u2().
- Bytes::put_Java_u2(bcp, new_index);
-
- Relocator rc(method, NULL /* no RelocatorListener needed */);
- methodHandle m;
- {
- Pause_No_Safepoint_Verifier pnsv(&nsv);
-
- // ldc is 2 bytes and ldc_w is 3 bytes
- m = rc.insert_space_at(bci, 3, inst_buffer, THREAD);
- if (m.is_null() || HAS_PENDING_EXCEPTION) {
- guarantee(false, "insert_space_at() failed");
- }
- }
-
- // return the new method so that the caller can update
- // the containing class
- *new_method_p = method = m;
- // switch our bytecode processing loop from the old method
- // to the new method
- code_base = method->code_base();
- code_length = method->code_size();
- bcp = code_base + bci;
- c = (Bytecodes::Code)(*bcp);
- bc_length = Bytecodes::length_for(c);
- assert(bc_length != 0, "sanity check");
- } // end we need ldc_w instead of ldc
- } // end if there is a mapped index
- } break;
-
- // these bytecodes have a two-byte constant pool index
- case Bytecodes::_anewarray : // fall through
- case Bytecodes::_checkcast : // fall through
- case Bytecodes::_getfield : // fall through
- case Bytecodes::_getstatic : // fall through
- case Bytecodes::_instanceof : // fall through
- case Bytecodes::_invokeinterface: // fall through
- case Bytecodes::_invokespecial : // fall through
- case Bytecodes::_invokestatic : // fall through
- case Bytecodes::_invokevirtual : // fall through
- case Bytecodes::_ldc_w : // fall through
- case Bytecodes::_ldc2_w : // fall through
- case Bytecodes::_multianewarray : // fall through
- case Bytecodes::_new : // fall through
- case Bytecodes::_putfield : // fall through
- case Bytecodes::_putstatic :
- {
- address p = bcp + 1;
- int cp_index = Bytes::get_Java_u2(p);
- int new_index = find_new_index(cp_index);
- if (new_index != 0) {
- // the original index is mapped so update w/ new value
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("%s@" INTPTR_FORMAT " old=%d, new=%d", Bytecodes::name(c),
- bcp, cp_index, new_index));
- // Rewriter::rewrite_method() uses put_native_u2() in this
- // situation because it is reusing the constant pool index
- // location for a native index into the constantPoolCache.
- // Since we are updating the constant pool index prior to
- // verification and constantPoolCache initialization, we
- // need to keep the new index in Java byte order.
- Bytes::put_Java_u2(p, new_index);
- }
- } break;
- }
- } // end for each bytecode
-} // end rewrite_cp_refs_in_method()
-
-
-// Rewrite constant pool references in the class_annotations field.
-bool VM_RedefineClasses::rewrite_cp_refs_in_class_annotations(
- instanceKlassHandle scratch_class, TRAPS) {
-
- typeArrayHandle class_annotations(THREAD,
- scratch_class->class_annotations());
- if (class_annotations.is_null() || class_annotations->length() == 0) {
- // no class_annotations so nothing to do
- return true;
+#ifdef ASSERT
+
+void VM_RedefineClasses::verify_classes(klassOop k_oop_latest, oop initiating_loader, TRAPS) {
+ klassOop k_oop = k_oop_latest;
+ while (k_oop != NULL) {
+
+ instanceKlassHandle k_handle(THREAD, k_oop);
+ Verifier::verify(k_handle, Verifier::ThrowException, true, true, THREAD);
+ k_oop = k_oop->klass_part()->old_version();
}
-
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("class_annotations length=%d", class_annotations->length()));
-
- int byte_i = 0; // byte index into class_annotations
- return rewrite_cp_refs_in_annotations_typeArray(class_annotations, byte_i,
- THREAD);
}
-
-// Rewrite constant pool references in an annotations typeArray. This
-// "structure" is adapted from the RuntimeVisibleAnnotations_attribute
-// that is described in section 4.8.15 of the 2nd-edition of the VM spec:
-//
-// annotations_typeArray {
-// u2 num_annotations;
-// annotation annotations[num_annotations];
-// }
-//
-bool VM_RedefineClasses::rewrite_cp_refs_in_annotations_typeArray(
- typeArrayHandle annotations_typeArray, int &byte_i_ref, TRAPS) {
-
- if ((byte_i_ref + 2) > annotations_typeArray->length()) {
- // not enough room for num_annotations field
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("length() is too small for num_annotations field"));
- return false;
- }
-
- u2 num_annotations = Bytes::get_Java_u2((address)
- annotations_typeArray->byte_at_addr(byte_i_ref));
- byte_i_ref += 2;
-
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("num_annotations=%d", num_annotations));
-
- int calc_num_annotations = 0;
- for (; calc_num_annotations < num_annotations; calc_num_annotations++) {
- if (!rewrite_cp_refs_in_annotation_struct(annotations_typeArray,
- byte_i_ref, THREAD)) {
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("bad annotation_struct at %d", calc_num_annotations));
- // propagate failure back to caller
- return false;
- }
- }
- assert(num_annotations == calc_num_annotations, "sanity check");
-
- return true;
-} // end rewrite_cp_refs_in_annotations_typeArray()
-
-
-// Rewrite constant pool references in the annotation struct portion of
-// an annotations_typeArray. This "structure" is from section 4.8.15 of
-// the 2nd-edition of the VM spec:
-//
-// struct annotation {
-// u2 type_index;
-// u2 num_element_value_pairs;
-// {
-// u2 element_name_index;
-// element_value value;
-// } element_value_pairs[num_element_value_pairs];
-// }
-//
-bool VM_RedefineClasses::rewrite_cp_refs_in_annotation_struct(
- typeArrayHandle annotations_typeArray, int &byte_i_ref, TRAPS) {
- if ((byte_i_ref + 2 + 2) > annotations_typeArray->length()) {
- // not enough room for smallest annotation_struct
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("length() is too small for annotation_struct"));
- return false;
- }
-
- u2 type_index = rewrite_cp_ref_in_annotation_data(annotations_typeArray,
- byte_i_ref, "mapped old type_index=%d", THREAD);
-
- u2 num_element_value_pairs = Bytes::get_Java_u2((address)
- annotations_typeArray->byte_at_addr(
- byte_i_ref));
- byte_i_ref += 2;
-
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("type_index=%d num_element_value_pairs=%d", type_index,
- num_element_value_pairs));
-
- int calc_num_element_value_pairs = 0;
- for (; calc_num_element_value_pairs < num_element_value_pairs;
- calc_num_element_value_pairs++) {
- if ((byte_i_ref + 2) > annotations_typeArray->length()) {
- // not enough room for another element_name_index, let alone
- // the rest of another component
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("length() is too small for element_name_index"));
- return false;
- }
-
- u2 element_name_index = rewrite_cp_ref_in_annotation_data(
- annotations_typeArray, byte_i_ref,
- "mapped old element_name_index=%d", THREAD);
-
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("element_name_index=%d", element_name_index));
-
- if (!rewrite_cp_refs_in_element_value(annotations_typeArray,
- byte_i_ref, THREAD)) {
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("bad element_value at %d", calc_num_element_value_pairs));
- // propagate failure back to caller
- return false;
- }
- } // end for each component
- assert(num_element_value_pairs == calc_num_element_value_pairs,
- "sanity check");
-
- return true;
-} // end rewrite_cp_refs_in_annotation_struct()
-
-
-// Rewrite a constant pool reference at the current position in
-// annotations_typeArray if needed. Returns the original constant
-// pool reference if a rewrite was not needed or the new constant
-// pool reference if a rewrite was needed.
-u2 VM_RedefineClasses::rewrite_cp_ref_in_annotation_data(
- typeArrayHandle annotations_typeArray, int &byte_i_ref,
- const char * trace_mesg, TRAPS) {
-
- address cp_index_addr = (address)
- annotations_typeArray->byte_at_addr(byte_i_ref);
- u2 old_cp_index = Bytes::get_Java_u2(cp_index_addr);
- u2 new_cp_index = find_new_index(old_cp_index);
- if (new_cp_index != 0) {
- RC_TRACE_WITH_THREAD(0x02000000, THREAD, (trace_mesg, old_cp_index));
- Bytes::put_Java_u2(cp_index_addr, new_cp_index);
- old_cp_index = new_cp_index;
- }
- byte_i_ref += 2;
- return old_cp_index;
-}
-
-
-// Rewrite constant pool references in the element_value portion of an
-// annotations_typeArray. This "structure" is from section 4.8.15.1 of
-// the 2nd-edition of the VM spec:
-//
-// struct element_value {
-// u1 tag;
-// union {
-// u2 const_value_index;
-// {
-// u2 type_name_index;
-// u2 const_name_index;
-// } enum_const_value;
-// u2 class_info_index;
-// annotation annotation_value;
-// struct {
-// u2 num_values;
-// element_value values[num_values];
-// } array_value;
-// } value;
-// }
-//
-bool VM_RedefineClasses::rewrite_cp_refs_in_element_value(
- typeArrayHandle annotations_typeArray, int &byte_i_ref, TRAPS) {
-
- if ((byte_i_ref + 1) > annotations_typeArray->length()) {
- // not enough room for a tag let alone the rest of an element_value
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("length() is too small for a tag"));
- return false;
- }
-
- u1 tag = annotations_typeArray->byte_at(byte_i_ref);
- byte_i_ref++;
- RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("tag='%c'", tag));
-
- switch (tag) {
- // These BaseType tag values are from Table 4.2 in VM spec:
- case 'B': // byte
- case 'C': // char
- case 'D': // double
- case 'F': // float
- case 'I': // int
- case 'J': // long
- case 'S': // short
- case 'Z': // boolean
-
- // The remaining tag values are from Table 4.8 in the 2nd-edition of
- // the VM spec:
- case 's':
- {
- // For the above tag values (including the BaseType values),
- // value.const_value_index is right union field.
-
- if ((byte_i_ref + 2) > annotations_typeArray->length()) {
- // not enough room for a const_value_index
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("length() is too small for a const_value_index"));
- return false;
- }
-
- u2 const_value_index = rewrite_cp_ref_in_annotation_data(
- annotations_typeArray, byte_i_ref,
- "mapped old const_value_index=%d", THREAD);
-
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("const_value_index=%d", const_value_index));
- } break;
-
- case 'e':
- {
- // for the above tag value, value.enum_const_value is right union field
-
- if ((byte_i_ref + 4) > annotations_typeArray->length()) {
- // not enough room for a enum_const_value
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("length() is too small for a enum_const_value"));
- return false;
- }
-
- u2 type_name_index = rewrite_cp_ref_in_annotation_data(
- annotations_typeArray, byte_i_ref,
- "mapped old type_name_index=%d", THREAD);
-
- u2 const_name_index = rewrite_cp_ref_in_annotation_data(
- annotations_typeArray, byte_i_ref,
- "mapped old const_name_index=%d", THREAD);
-
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("type_name_index=%d const_name_index=%d", type_name_index,
- const_name_index));
- } break;
-
- case 'c':
- {
- // for the above tag value, value.class_info_index is right union field
-
- if ((byte_i_ref + 2) > annotations_typeArray->length()) {
- // not enough room for a class_info_index
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("length() is too small for a class_info_index"));
- return false;
- }
-
- u2 class_info_index = rewrite_cp_ref_in_annotation_data(
- annotations_typeArray, byte_i_ref,
- "mapped old class_info_index=%d", THREAD);
-
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("class_info_index=%d", class_info_index));
- } break;
-
- case '@':
- // For the above tag value, value.attr_value is the right union
- // field. This is a nested annotation.
- if (!rewrite_cp_refs_in_annotation_struct(annotations_typeArray,
- byte_i_ref, THREAD)) {
- // propagate failure back to caller
- return false;
- }
- break;
-
- case '[':
- {
- if ((byte_i_ref + 2) > annotations_typeArray->length()) {
- // not enough room for a num_values field
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("length() is too small for a num_values field"));
- return false;
- }
-
- // For the above tag value, value.array_value is the right union
- // field. This is an array of nested element_value.
- u2 num_values = Bytes::get_Java_u2((address)
- annotations_typeArray->byte_at_addr(byte_i_ref));
- byte_i_ref += 2;
- RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("num_values=%d", num_values));
-
- int calc_num_values = 0;
- for (; calc_num_values < num_values; calc_num_values++) {
- if (!rewrite_cp_refs_in_element_value(
- annotations_typeArray, byte_i_ref, THREAD)) {
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("bad nested element_value at %d", calc_num_values));
- // propagate failure back to caller
- return false;
- }
- }
- assert(num_values == calc_num_values, "sanity check");
- } break;
-
- default:
- RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("bad tag=0x%x", tag));
- return false;
- } // end decode tag field
-
- return true;
-} // end rewrite_cp_refs_in_element_value()
-
-
-// Rewrite constant pool references in a fields_annotations field.
-bool VM_RedefineClasses::rewrite_cp_refs_in_fields_annotations(
- instanceKlassHandle scratch_class, TRAPS) {
-
- objArrayHandle fields_annotations(THREAD,
- scratch_class->fields_annotations());
-
- if (fields_annotations.is_null() || fields_annotations->length() == 0) {
- // no fields_annotations so nothing to do
- return true;
- }
-
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("fields_annotations length=%d", fields_annotations->length()));
-
- for (int i = 0; i < fields_annotations->length(); i++) {
- typeArrayHandle field_annotations(THREAD,
- (typeArrayOop)fields_annotations->obj_at(i));
- if (field_annotations.is_null() || field_annotations->length() == 0) {
- // this field does not have any annotations so skip it
- continue;
- }
-
- int byte_i = 0; // byte index into field_annotations
- if (!rewrite_cp_refs_in_annotations_typeArray(field_annotations, byte_i,
- THREAD)) {
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("bad field_annotations at %d", i));
- // propagate failure back to caller
- return false;
- }
- }
-
- return true;
-} // end rewrite_cp_refs_in_fields_annotations()
-
-
-// Rewrite constant pool references in a methods_annotations field.
-bool VM_RedefineClasses::rewrite_cp_refs_in_methods_annotations(
- instanceKlassHandle scratch_class, TRAPS) {
-
- objArrayHandle methods_annotations(THREAD,
- scratch_class->methods_annotations());
-
- if (methods_annotations.is_null() || methods_annotations->length() == 0) {
- // no methods_annotations so nothing to do
- return true;
- }
-
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("methods_annotations length=%d", methods_annotations->length()));
-
- for (int i = 0; i < methods_annotations->length(); i++) {
- typeArrayHandle method_annotations(THREAD,
- (typeArrayOop)methods_annotations->obj_at(i));
- if (method_annotations.is_null() || method_annotations->length() == 0) {
- // this method does not have any annotations so skip it
- continue;
- }
-
- int byte_i = 0; // byte index into method_annotations
- if (!rewrite_cp_refs_in_annotations_typeArray(method_annotations, byte_i,
- THREAD)) {
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("bad method_annotations at %d", i));
- // propagate failure back to caller
- return false;
- }
- }
-
- return true;
-} // end rewrite_cp_refs_in_methods_annotations()
-
-
-// Rewrite constant pool references in a methods_parameter_annotations
-// field. This "structure" is adapted from the
-// RuntimeVisibleParameterAnnotations_attribute described in section
-// 4.8.17 of the 2nd-edition of the VM spec:
-//
-// methods_parameter_annotations_typeArray {
-// u1 num_parameters;
-// {
-// u2 num_annotations;
-// annotation annotations[num_annotations];
-// } parameter_annotations[num_parameters];
-// }
-//
-bool VM_RedefineClasses::rewrite_cp_refs_in_methods_parameter_annotations(
- instanceKlassHandle scratch_class, TRAPS) {
-
- objArrayHandle methods_parameter_annotations(THREAD,
- scratch_class->methods_parameter_annotations());
-
- if (methods_parameter_annotations.is_null()
- || methods_parameter_annotations->length() == 0) {
- // no methods_parameter_annotations so nothing to do
- return true;
- }
-
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("methods_parameter_annotations length=%d",
- methods_parameter_annotations->length()));
-
- for (int i = 0; i < methods_parameter_annotations->length(); i++) {
- typeArrayHandle method_parameter_annotations(THREAD,
- (typeArrayOop)methods_parameter_annotations->obj_at(i));
- if (method_parameter_annotations.is_null()
- || method_parameter_annotations->length() == 0) {
- // this method does not have any parameter annotations so skip it
- continue;
- }
-
- if (method_parameter_annotations->length() < 1) {
- // not enough room for a num_parameters field
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("length() is too small for a num_parameters field at %d", i));
- return false;
- }
-
- int byte_i = 0; // byte index into method_parameter_annotations
-
- u1 num_parameters = method_parameter_annotations->byte_at(byte_i);
- byte_i++;
-
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("num_parameters=%d", num_parameters));
-
- int calc_num_parameters = 0;
- for (; calc_num_parameters < num_parameters; calc_num_parameters++) {
- if (!rewrite_cp_refs_in_annotations_typeArray(
- method_parameter_annotations, byte_i, THREAD)) {
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("bad method_parameter_annotations at %d", calc_num_parameters));
- // propagate failure back to caller
- return false;
- }
- }
- assert(num_parameters == calc_num_parameters, "sanity check");
- }
-
- return true;
-} // end rewrite_cp_refs_in_methods_parameter_annotations()
-
-
-// Rewrite constant pool references in a methods_default_annotations
-// field. This "structure" is adapted from the AnnotationDefault_attribute
-// that is described in section 4.8.19 of the 2nd-edition of the VM spec:
-//
-// methods_default_annotations_typeArray {
-// element_value default_value;
-// }
-//
-bool VM_RedefineClasses::rewrite_cp_refs_in_methods_default_annotations(
- instanceKlassHandle scratch_class, TRAPS) {
-
- objArrayHandle methods_default_annotations(THREAD,
- scratch_class->methods_default_annotations());
-
- if (methods_default_annotations.is_null()
- || methods_default_annotations->length() == 0) {
- // no methods_default_annotations so nothing to do
- return true;
- }
-
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("methods_default_annotations length=%d",
- methods_default_annotations->length()));
-
- for (int i = 0; i < methods_default_annotations->length(); i++) {
- typeArrayHandle method_default_annotations(THREAD,
- (typeArrayOop)methods_default_annotations->obj_at(i));
- if (method_default_annotations.is_null()
- || method_default_annotations->length() == 0) {
- // this method does not have any default annotations so skip it
- continue;
- }
-
- int byte_i = 0; // byte index into method_default_annotations
-
- if (!rewrite_cp_refs_in_element_value(
- method_default_annotations, byte_i, THREAD)) {
- RC_TRACE_WITH_THREAD(0x02000000, THREAD,
- ("bad default element_value at %d", i));
- // propagate failure back to caller
- return false;
- }
- }
-
- return true;
-} // end rewrite_cp_refs_in_methods_default_annotations()
-
-
-// Rewrite constant pool references in the method's stackmap table.
-// These "structures" are adapted from the StackMapTable_attribute that
-// is described in section 4.8.4 of the 6.0 version of the VM spec
-// (dated 2005.10.26):
-// file:///net/quincunx.sfbay/export/gbracha/ClassFile-Java6.pdf
-//
-// stack_map {
-// u2 number_of_entries;
-// stack_map_frame entries[number_of_entries];
-// }
-//
-void VM_RedefineClasses::rewrite_cp_refs_in_stack_map_table(
- methodHandle method, TRAPS) {
-
- if (!method->has_stackmap_table()) {
- return;
- }
-
- typeArrayOop stackmap_data = method->stackmap_data();
- address stackmap_p = (address)stackmap_data->byte_at_addr(0);
- address stackmap_end = stackmap_p + stackmap_data->length();
-
- assert(stackmap_p + 2 <= stackmap_end, "no room for number_of_entries");
- u2 number_of_entries = Bytes::get_Java_u2(stackmap_p);
- stackmap_p += 2;
-
- RC_TRACE_WITH_THREAD(0x04000000, THREAD,
- ("number_of_entries=%u", number_of_entries));
-
- // walk through each stack_map_frame
- u2 calc_number_of_entries = 0;
- for (; calc_number_of_entries < number_of_entries; calc_number_of_entries++) {
- // The stack_map_frame structure is a u1 frame_type followed by
- // 0 or more bytes of data:
- //
- // union stack_map_frame {
- // same_frame;
- // same_locals_1_stack_item_frame;
- // same_locals_1_stack_item_frame_extended;
- // chop_frame;
- // same_frame_extended;
- // append_frame;
- // full_frame;
- // }
-
- assert(stackmap_p + 1 <= stackmap_end, "no room for frame_type");
- // The Linux compiler does not like frame_type to be u1 or u2. It
- // issues the following warning for the first if-statement below:
- //
- // "warning: comparison is always true due to limited range of data type"
- //
- u4 frame_type = *stackmap_p;
- stackmap_p++;
-
- // same_frame {
- // u1 frame_type = SAME; /* 0-63 */
- // }
- if (frame_type >= 0 && frame_type <= 63) {
- // nothing more to do for same_frame
- }
-
- // same_locals_1_stack_item_frame {
- // u1 frame_type = SAME_LOCALS_1_STACK_ITEM; /* 64-127 */
- // verification_type_info stack[1];
- // }
- else if (frame_type >= 64 && frame_type <= 127) {
- rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end,
- calc_number_of_entries, frame_type, THREAD);
- }
-
- // reserved for future use
- else if (frame_type >= 128 && frame_type <= 246) {
- // nothing more to do for reserved frame_types
- }
-
- // same_locals_1_stack_item_frame_extended {
- // u1 frame_type = SAME_LOCALS_1_STACK_ITEM_EXTENDED; /* 247 */
- // u2 offset_delta;
- // verification_type_info stack[1];
- // }
- else if (frame_type == 247) {
- stackmap_p += 2;
- rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end,
- calc_number_of_entries, frame_type, THREAD);
- }
-
- // chop_frame {
- // u1 frame_type = CHOP; /* 248-250 */
- // u2 offset_delta;
- // }
- else if (frame_type >= 248 && frame_type <= 250) {
- stackmap_p += 2;
- }
-
- // same_frame_extended {
- // u1 frame_type = SAME_FRAME_EXTENDED; /* 251*/
- // u2 offset_delta;
- // }
- else if (frame_type == 251) {
- stackmap_p += 2;
- }
-
- // append_frame {
- // u1 frame_type = APPEND; /* 252-254 */
- // u2 offset_delta;
- // verification_type_info locals[frame_type - 251];
- // }
- else if (frame_type >= 252 && frame_type <= 254) {
- assert(stackmap_p + 2 <= stackmap_end,
- "no room for offset_delta");
- stackmap_p += 2;
- u1 len = frame_type - 251;
- for (u1 i = 0; i < len; i++) {
- rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end,
- calc_number_of_entries, frame_type, THREAD);
- }
- }
-
- // full_frame {
- // u1 frame_type = FULL_FRAME; /* 255 */
- // u2 offset_delta;
- // u2 number_of_locals;
- // verification_type_info locals[number_of_locals];
- // u2 number_of_stack_items;
- // verification_type_info stack[number_of_stack_items];
- // }
- else if (frame_type == 255) {
- assert(stackmap_p + 2 + 2 <= stackmap_end,
- "no room for smallest full_frame");
- stackmap_p += 2;
-
- u2 number_of_locals = Bytes::get_Java_u2(stackmap_p);
- stackmap_p += 2;
-
- for (u2 locals_i = 0; locals_i < number_of_locals; locals_i++) {
- rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end,
- calc_number_of_entries, frame_type, THREAD);
- }
-
- // Use the largest size for the number_of_stack_items, but only get
- // the right number of bytes.
- u2 number_of_stack_items = Bytes::get_Java_u2(stackmap_p);
- stackmap_p += 2;
-
- for (u2 stack_i = 0; stack_i < number_of_stack_items; stack_i++) {
- rewrite_cp_refs_in_verification_type_info(stackmap_p, stackmap_end,
- calc_number_of_entries, frame_type, THREAD);
- }
- }
- } // end while there is a stack_map_frame
- assert(number_of_entries == calc_number_of_entries, "sanity check");
-} // end rewrite_cp_refs_in_stack_map_table()
-
-
-// Rewrite constant pool references in the verification type info
-// portion of the method's stackmap table. These "structures" are
-// adapted from the StackMapTable_attribute that is described in
-// section 4.8.4 of the 6.0 version of the VM spec (dated 2005.10.26):
-// file:///net/quincunx.sfbay/export/gbracha/ClassFile-Java6.pdf
-//
-// The verification_type_info structure is a u1 tag followed by 0 or
-// more bytes of data:
-//
-// union verification_type_info {
-// Top_variable_info;
-// Integer_variable_info;
-// Float_variable_info;
-// Long_variable_info;
-// Double_variable_info;
-// Null_variable_info;
-// UninitializedThis_variable_info;
-// Object_variable_info;
-// Uninitialized_variable_info;
-// }
-//
-void VM_RedefineClasses::rewrite_cp_refs_in_verification_type_info(
- address& stackmap_p_ref, address stackmap_end, u2 frame_i,
- u1 frame_type, TRAPS) {
-
- assert(stackmap_p_ref + 1 <= stackmap_end, "no room for tag");
- u1 tag = *stackmap_p_ref;
- stackmap_p_ref++;
-
- switch (tag) {
- // Top_variable_info {
- // u1 tag = ITEM_Top; /* 0 */
- // }
- // verificationType.hpp has zero as ITEM_Bogus instead of ITEM_Top
- case 0: // fall through
-
- // Integer_variable_info {
- // u1 tag = ITEM_Integer; /* 1 */
- // }
- case ITEM_Integer: // fall through
-
- // Float_variable_info {
- // u1 tag = ITEM_Float; /* 2 */
- // }
- case ITEM_Float: // fall through
-
- // Double_variable_info {
- // u1 tag = ITEM_Double; /* 3 */
- // }
- case ITEM_Double: // fall through
-
- // Long_variable_info {
- // u1 tag = ITEM_Long; /* 4 */
- // }
- case ITEM_Long: // fall through
-
- // Null_variable_info {
- // u1 tag = ITEM_Null; /* 5 */
- // }
- case ITEM_Null: // fall through
-
- // UninitializedThis_variable_info {
- // u1 tag = ITEM_UninitializedThis; /* 6 */
- // }
- case ITEM_UninitializedThis:
- // nothing more to do for the above tag types
- break;
-
- // Object_variable_info {
- // u1 tag = ITEM_Object; /* 7 */
- // u2 cpool_index;
- // }
- case ITEM_Object:
- {
- assert(stackmap_p_ref + 2 <= stackmap_end, "no room for cpool_index");
- u2 cpool_index = Bytes::get_Java_u2(stackmap_p_ref);
- u2 new_cp_index = find_new_index(cpool_index);
- if (new_cp_index != 0) {
- RC_TRACE_WITH_THREAD(0x04000000, THREAD,
- ("mapped old cpool_index=%d", cpool_index));
- Bytes::put_Java_u2(stackmap_p_ref, new_cp_index);
- cpool_index = new_cp_index;
- }
- stackmap_p_ref += 2;
-
- RC_TRACE_WITH_THREAD(0x04000000, THREAD,
- ("frame_i=%u, frame_type=%u, cpool_index=%d", frame_i,
- frame_type, cpool_index));
- } break;
-
- // Uninitialized_variable_info {
- // u1 tag = ITEM_Uninitialized; /* 8 */
- // u2 offset;
- // }
- case ITEM_Uninitialized:
- assert(stackmap_p_ref + 2 <= stackmap_end, "no room for offset");
- stackmap_p_ref += 2;
- break;
-
- default:
- RC_TRACE_WITH_THREAD(0x04000000, THREAD,
- ("frame_i=%u, frame_type=%u, bad tag=0x%x", frame_i, frame_type, tag));
- ShouldNotReachHere();
- break;
- } // end switch (tag)
-} // end rewrite_cp_refs_in_verification_type_info()
-
-
-// Change the constant pool associated with klass scratch_class to
-// scratch_cp. If shrink is true, then scratch_cp_length elements
-// are copied from scratch_cp to a smaller constant pool and the
-// smaller constant pool is associated with scratch_class.
-void VM_RedefineClasses::set_new_constant_pool(
- instanceKlassHandle scratch_class, constantPoolHandle scratch_cp,
- int scratch_cp_length, bool shrink, TRAPS) {
- assert(!shrink || scratch_cp->length() >= scratch_cp_length, "sanity check");
-
- if (shrink) {
- // scratch_cp is a merged constant pool and has enough space for a
- // worst case merge situation. We want to associate the minimum
- // sized constant pool with the klass to save space.
- constantPoolHandle smaller_cp(THREAD,
- oopFactory::new_constantPool(scratch_cp_length,
- methodOopDesc::IsUnsafeConc,
- THREAD));
- // preserve orig_length() value in the smaller copy
- int orig_length = scratch_cp->orig_length();
- assert(orig_length != 0, "sanity check");
- smaller_cp->set_orig_length(orig_length);
- scratch_cp->copy_cp_to(1, scratch_cp_length - 1, smaller_cp, 1, THREAD);
- scratch_cp = smaller_cp;
- smaller_cp()->set_is_conc_safe(true);
- }
-
- // attach new constant pool to klass
- scratch_cp->set_pool_holder(scratch_class());
-
- // attach klass to new constant pool
- scratch_class->set_constants(scratch_cp());
-
- int i; // for portability
-
- // update each field in klass to use new constant pool indices as needed
- typeArrayHandle fields(THREAD, scratch_class->fields());
- int n_fields = fields->length();
- for (i = 0; i < n_fields; i += instanceKlass::next_offset) {
- jshort cur_index = fields->short_at(i + instanceKlass::name_index_offset);
- jshort new_index = find_new_index(cur_index);
- if (new_index != 0) {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("field-name_index change: %d to %d", cur_index, new_index));
- fields->short_at_put(i + instanceKlass::name_index_offset, new_index);
- }
- cur_index = fields->short_at(i + instanceKlass::signature_index_offset);
- new_index = find_new_index(cur_index);
- if (new_index != 0) {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("field-signature_index change: %d to %d", cur_index, new_index));
- fields->short_at_put(i + instanceKlass::signature_index_offset,
- new_index);
- }
- cur_index = fields->short_at(i + instanceKlass::initval_index_offset);
- new_index = find_new_index(cur_index);
- if (new_index != 0) {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("field-initval_index change: %d to %d", cur_index, new_index));
- fields->short_at_put(i + instanceKlass::initval_index_offset, new_index);
- }
- cur_index = fields->short_at(i + instanceKlass::generic_signature_offset);
- new_index = find_new_index(cur_index);
- if (new_index != 0) {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("field-generic_signature change: %d to %d", cur_index, new_index));
- fields->short_at_put(i + instanceKlass::generic_signature_offset,
- new_index);
- }
- } // end for each field
-
- // Update constant pool indices in the inner classes info to use
- // new constant indices as needed. The inner classes info is a
- // quadruple:
- // (inner_class_info, outer_class_info, inner_name, inner_access_flags)
- typeArrayOop inner_class_list = scratch_class->inner_classes();
- int icl_length = (inner_class_list == NULL) ? 0 : inner_class_list->length();
- if (icl_length > 0) {
- typeArrayHandle inner_class_list_h(THREAD, inner_class_list);
- for (int i = 0; i < icl_length;
- i += instanceKlass::inner_class_next_offset) {
- int cur_index = inner_class_list_h->ushort_at(i
- + instanceKlass::inner_class_inner_class_info_offset);
- if (cur_index == 0) {
- continue; // JVM spec. allows null inner class refs so skip it
- }
- int new_index = find_new_index(cur_index);
- if (new_index != 0) {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("inner_class_info change: %d to %d", cur_index, new_index));
- inner_class_list_h->ushort_at_put(i
- + instanceKlass::inner_class_inner_class_info_offset, new_index);
- }
- cur_index = inner_class_list_h->ushort_at(i
- + instanceKlass::inner_class_outer_class_info_offset);
- new_index = find_new_index(cur_index);
- if (new_index != 0) {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("outer_class_info change: %d to %d", cur_index, new_index));
- inner_class_list_h->ushort_at_put(i
- + instanceKlass::inner_class_outer_class_info_offset, new_index);
- }
- cur_index = inner_class_list_h->ushort_at(i
- + instanceKlass::inner_class_inner_name_offset);
- new_index = find_new_index(cur_index);
- if (new_index != 0) {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("inner_name change: %d to %d", cur_index, new_index));
- inner_class_list_h->ushort_at_put(i
- + instanceKlass::inner_class_inner_name_offset, new_index);
- }
- } // end for each inner class
- } // end if we have inner classes
-
- // Attach each method in klass to the new constant pool and update
- // to use new constant pool indices as needed:
- objArrayHandle methods(THREAD, scratch_class->methods());
- for (i = methods->length() - 1; i >= 0; i--) {
- methodHandle method(THREAD, (methodOop)methods->obj_at(i));
- method->set_constants(scratch_cp());
-
- int new_index = find_new_index(method->name_index());
- if (new_index != 0) {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("method-name_index change: %d to %d", method->name_index(),
- new_index));
- method->set_name_index(new_index);
- }
- new_index = find_new_index(method->signature_index());
- if (new_index != 0) {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("method-signature_index change: %d to %d",
- method->signature_index(), new_index));
- method->set_signature_index(new_index);
- }
- new_index = find_new_index(method->generic_signature_index());
- if (new_index != 0) {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("method-generic_signature_index change: %d to %d",
- method->generic_signature_index(), new_index));
- method->set_generic_signature_index(new_index);
- }
-
- // Update constant pool indices in the method's checked exception
- // table to use new constant indices as needed.
- int cext_length = method->checked_exceptions_length();
- if (cext_length > 0) {
- CheckedExceptionElement * cext_table =
- method->checked_exceptions_start();
- for (int j = 0; j < cext_length; j++) {
- int cur_index = cext_table[j].class_cp_index;
- int new_index = find_new_index(cur_index);
- if (new_index != 0) {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("cext-class_cp_index change: %d to %d", cur_index, new_index));
- cext_table[j].class_cp_index = (u2)new_index;
- }
- } // end for each checked exception table entry
- } // end if there are checked exception table entries
-
- // Update each catch type index in the method's exception table
- // to use new constant pool indices as needed. The exception table
- // holds quadruple entries of the form:
- // (beg_bci, end_bci, handler_bci, klass_index)
- const int beg_bci_offset = 0;
- const int end_bci_offset = 1;
- const int handler_bci_offset = 2;
- const int klass_index_offset = 3;
- const int entry_size = 4;
-
- typeArrayHandle ex_table (THREAD, method->exception_table());
- int ext_length = ex_table->length();
- assert(ext_length % entry_size == 0, "exception table format has changed");
-
- for (int j = 0; j < ext_length; j += entry_size) {
- int cur_index = ex_table->int_at(j + klass_index_offset);
- int new_index = find_new_index(cur_index);
- if (new_index != 0) {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("ext-klass_index change: %d to %d", cur_index, new_index));
- ex_table->int_at_put(j + klass_index_offset, new_index);
- }
- } // end for each exception table entry
-
- // Update constant pool indices in the method's local variable
- // table to use new constant indices as needed. The local variable
- // table hold sextuple entries of the form:
- // (start_pc, length, name_index, descriptor_index, signature_index, slot)
- int lvt_length = method->localvariable_table_length();
- if (lvt_length > 0) {
- LocalVariableTableElement * lv_table =
- method->localvariable_table_start();
- for (int j = 0; j < lvt_length; j++) {
- int cur_index = lv_table[j].name_cp_index;
- int new_index = find_new_index(cur_index);
- if (new_index != 0) {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("lvt-name_cp_index change: %d to %d", cur_index, new_index));
- lv_table[j].name_cp_index = (u2)new_index;
- }
- cur_index = lv_table[j].descriptor_cp_index;
- new_index = find_new_index(cur_index);
- if (new_index != 0) {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("lvt-descriptor_cp_index change: %d to %d", cur_index,
- new_index));
- lv_table[j].descriptor_cp_index = (u2)new_index;
- }
- cur_index = lv_table[j].signature_cp_index;
- new_index = find_new_index(cur_index);
- if (new_index != 0) {
- RC_TRACE_WITH_THREAD(0x00080000, THREAD,
- ("lvt-signature_cp_index change: %d to %d", cur_index, new_index));
- lv_table[j].signature_cp_index = (u2)new_index;
- }
- } // end for each local variable table entry
- } // end if there are local variable table entries
-
- rewrite_cp_refs_in_stack_map_table(method, THREAD);
- } // end for each method
- assert(scratch_cp()->is_conc_safe(), "Just checking");
-} // end set_new_constant_pool()
-
-
-// Unevolving classes may point to methods of the_class directly
+#endif
+
+
+// Unevolving classes may point to old methods directly
// from their constant pool caches, itables, and/or vtables. We
// use the SystemDictionary::classes_do() facility and this helper
-// to fix up these pointers.
+// to fix up these pointers. Additional field offsets and vtable indices
+// in the constant pool cache entries are fixed.
//
// Note: We currently don't support updating the vtable in
// arrayKlassOops. See Open Issues in jvmtiRedefineClasses.hpp.
-void VM_RedefineClasses::adjust_cpool_cache_and_vtable(klassOop k_oop,
- oop initiating_loader, TRAPS) {
+void VM_RedefineClasses::adjust_cpool_cache(klassOop k_oop_latest, oop initiating_loader, TRAPS) {
+ klassOop k_oop = k_oop_latest;
+ while (k_oop != NULL) {
+ //tty->print_cr("name=%s", k_oop->klass_part()->name()->as_C_string());
+/*
+ methodOop *matching_old_methods = NEW_RESOURCE_ARRAY(methodOop, _old_methods->length());
+ methodOop *matching_new_methods = NEW_RESOURCE_ARRAY(methodOop, _old_methods->length());
+
+ for (int i=0; i<_matching_methods_length; i++) {
+ matching_old_methods[i] = (methodOop)_old_methods->obj_at(_matching_old_methods[i]);
+ matching_new_methods[i] = (methodOop)_new_methods->obj_at(_matching_new_methods[i]);
+ }*/
+
+ Klass *k = k_oop->klass_part();
+ if (k->oop_is_instance()) {
+ HandleMark hm(THREAD);
+ instanceKlass *ik = (instanceKlass *) k;
+
+ constantPoolHandle other_cp;
+ constantPoolCacheOop cp_cache;
+
+ other_cp = constantPoolHandle(ik->constants());
+
+ for (int i=0; ilength(); i++) {
+ if (other_cp->tag_at(i).is_klass()) {
+ klassOop klass = other_cp->klass_at(i, THREAD);
+ if (klass->klass_part()->new_version() != NULL) {
+
+ // (tw) TODO: check why/if this is necessary
+ other_cp->klass_at_put(i, klass->klass_part()->new_version());
+ }
+ klass = other_cp->klass_at(i, THREAD);
+ assert(klass->klass_part()->new_version() == NULL, "Must be new klass!");
+ }
+ }
+
+ cp_cache = other_cp->cache();
+
+ if (cp_cache != NULL) {
+ cp_cache->adjust_entries(NULL,
+ NULL,
+ 0);
+ }
+ }
+ k_oop = k_oop->klass_part()->old_version();
+ }
+}
+
+void VM_RedefineClasses::update_jmethod_ids() {
+ for (int j = 0; j < _matching_methods_length; ++j) {
+ methodOop old_method = (methodOop)_old_methods->obj_at(_matching_old_methods[j]);
+ TRACE_RC3("matching method %s", old_method->name_and_sig_as_C_string());
+
+ jmethodID jmid = old_method->find_jmethod_id_or_null();
+ if (old_method->new_version() != NULL && jmid == NULL) {
+ // (tw) Have to create jmethodID in this case
+ jmid = old_method->jmethod_id();
+ }
+
+ if (jmid != NULL) {
+ // There is a jmethodID, change it to point to the new method
+ methodHandle new_method_h((methodOop)_new_methods->obj_at(_matching_new_methods[j]));
+ if (old_method->new_version() == NULL) {
+ methodHandle old_method_h((methodOop)_old_methods->obj_at(_matching_old_methods[j]));
+ jmethodID new_jmethod_id = JNIHandles::make_jmethod_id(old_method_h);
+ bool result = instanceKlass::cast(old_method_h->method_holder())->update_jmethod_id(old_method_h(), new_jmethod_id);
+ //TRACE_RC3("Changed jmethodID for old method assigned to %d / result=%d", new_jmethod_id, result);
+ //TRACE_RC3("jmethodID new method: %d jmethodID old method: %d", new_method_h->jmethod_id(), old_method->jmethod_id());
+ } else {
+ jmethodID mid = new_method_h->jmethod_id();
+ bool result = instanceKlass::cast(new_method_h->method_holder())->update_jmethod_id(new_method_h(), jmid);
+ //TRACE_RC3("Changed jmethodID for new method assigned to %d / result=%d", jmid, result);
+
+ }
+ JNIHandles::change_method_associated_with_jmethod_id(jmid, new_method_h);
+ //TRACE_RC3("changing method associated with jmethod id %d to %s", (int)jmid, new_method_h->name()->as_C_string());
+ assert(JNIHandles::resolve_jmethod_id(jmid) == (methodOop)_new_methods->obj_at(_matching_new_methods[j]), "should be replaced");
+ jmethodID mid = ((methodOop)_new_methods->obj_at(_matching_new_methods[j]))->jmethod_id();
+ assert(JNIHandles::resolve_non_null((jobject)mid) == new_method_h(), "must match!");
+
+ //TRACE_RC3("jmethodID new method: %d jmethodID old method: %d", new_method_h->jmethod_id(), old_method->jmethod_id());
+ }
+ }
+}
+
+
+// Deoptimize all compiled code that depends on this class.
+//
+// If the can_redefine_classes capability is obtained in the onload
+// phase then the compiler has recorded all dependencies from startup.
+// In that case we need only deoptimize and throw away all compiled code
+// that depends on the class.
+//
+// If can_redefine_classes is obtained sometime after the onload
+// phase then the dependency information may be incomplete. In that case
+// the first call to RedefineClasses causes all compiled code to be
+// thrown away. As can_redefine_classes has been obtained then
+// all future compilations will record dependencies so second and
+// subsequent calls to RedefineClasses need only throw away code
+// that depends on the class.
+//
+void VM_RedefineClasses::flush_dependent_code(instanceKlassHandle k_h, TRAPS) {
+ assert_locked_or_safepoint(Compile_lock);
+
+ // All dependencies have been recorded from startup or this is a second or
+ // subsequent use of RedefineClasses
+
+ // For now deopt all
+ // (tw) TODO: Improve the dependency system such that we can safely deopt only a subset of the methods
+ if (0 && JvmtiExport::all_dependencies_are_recorded()) {
+ Universe::flush_evol_dependents_on(k_h);
+ } else {
+ CodeCache::mark_all_nmethods_for_deoptimization();
+
+ ResourceMark rm(THREAD);
+ DeoptimizationMarker dm;
+
+ // Deoptimize all activations depending on marked nmethods
+ Deoptimization::deoptimize_dependents();
+
+ // Make the dependent methods not entrant (in VM_Deoptimize they are made zombies)
+ CodeCache::make_marked_nmethods_not_entrant();
+
+ // From now on we know that the dependency information is complete
+ JvmtiExport::set_all_dependencies_are_recorded(true);
+ }
+}
+
+void VM_RedefineClasses::compute_added_deleted_matching_methods() {
+ methodOop old_method;
+ methodOop new_method;
+
+ _matching_old_methods = NEW_RESOURCE_ARRAY(int, _old_methods->length());
+ _matching_new_methods = NEW_RESOURCE_ARRAY(int, _old_methods->length());
+ _added_methods = NEW_RESOURCE_ARRAY(int, _new_methods->length());
+ _deleted_methods = NEW_RESOURCE_ARRAY(int, _old_methods->length());
+
+ _matching_methods_length = 0;
+ _deleted_methods_length = 0;
+ _added_methods_length = 0;
+
+ int nj = 0;
+ int oj = 0;
+ while (true) {
+ if (oj >= _old_methods->length()) {
+ if (nj >= _new_methods->length()) {
+ break; // we've looked at everything, done
+ }
+ // New method at the end
+ new_method = (methodOop) _new_methods->obj_at(nj);
+ _added_methods[_added_methods_length++] = nj;
+ ++nj;
+ } else if (nj >= _new_methods->length()) {
+ // Old method, at the end, is deleted
+ old_method = (methodOop) _old_methods->obj_at(oj);
+ _deleted_methods[_deleted_methods_length++] = oj;
+ ++oj;
+ } else {
+ old_method = (methodOop) _old_methods->obj_at(oj);
+ new_method = (methodOop) _new_methods->obj_at(nj);
+ if (old_method->name() == new_method->name()) {
+ if (old_method->signature() == new_method->signature()) {
+ _matching_old_methods[_matching_methods_length ] = oj;//old_method;
+ _matching_new_methods[_matching_methods_length++] = nj;//new_method;
+ ++nj;
+ ++oj;
+ } else {
+ // added overloaded have already been moved to the end,
+ // so this is a deleted overloaded method
+ _deleted_methods[_deleted_methods_length++] = oj;//old_method;
+ ++oj;
+ }
+ } else { // names don't match
+ if (old_method->name()->fast_compare(new_method->name()) > 0) {
+ // new method
+ _added_methods[_added_methods_length++] = nj;//new_method;
+ ++nj;
+ } else {
+ // deleted method
+ _deleted_methods[_deleted_methods_length++] = oj;//old_method;
+ ++oj;
+ }
+ }
+ }
+ }
+ assert(_matching_methods_length + _deleted_methods_length == _old_methods->length(), "sanity");
+ assert(_matching_methods_length + _added_methods_length == _new_methods->length(), "sanity");
+ TRACE_RC3("Matching methods = %d / deleted methods = %d / added methods = %d", _matching_methods_length, _deleted_methods_length, _added_methods_length);
+}
+
+
+
+// Install the redefinition of a class:
+// - house keeping (flushing breakpoints and caches, deoptimizing
+// dependent compiled code)
+// - adjusting constant pool caches and vtables in other classes
+void VM_RedefineClasses::redefine_single_class(instanceKlassHandle the_new_class, TRAPS) {
+
+ ResourceMark rm(THREAD);
+
+ assert(the_new_class->old_version() != NULL, "Must not be null");
+ assert(the_new_class->old_version()->klass_part()->new_version() == the_new_class(), "Must equal");
+
+ instanceKlassHandle the_old_class = instanceKlassHandle(THREAD, the_new_class->old_version());
+
+#ifndef JVMTI_KERNEL
+ // Remove all breakpoints in methods of this class
+ JvmtiBreakpoints& jvmti_breakpoints = JvmtiCurrentBreakpoints::get_jvmti_breakpoints();
+ jvmti_breakpoints.clearall_in_class_at_safepoint(the_old_class());
+#endif // !JVMTI_KERNEL
+
+ if (the_old_class() == Universe::reflect_invoke_cache()->klass()) {
+ // We are redefining java.lang.reflect.Method. Method.invoke() is
+ // cached and users of the cache care about each active version of
+ // the method so we have to track this previous version.
+ // Do this before methods get switched
+ Universe::reflect_invoke_cache()->add_previous_version(
+ the_old_class->method_with_idnum(Universe::reflect_invoke_cache()->method_idnum()));
+ }
+
+ _old_methods = the_old_class->methods();
+ _new_methods = the_new_class->methods();
+ _the_class_oop = the_old_class();
+ compute_added_deleted_matching_methods();
+
+ // track which methods are EMCP for add_previous_version() call below
+
+ // (tw) TODO: Check if we need the concept of EMCP?
+ BitMap emcp_methods(_old_methods->length());
+ int emcp_method_count = 0;
+ emcp_methods.clear(); // clears 0..(length() - 1)
+
+ // We need to mark methods as old!!
+ check_methods_and_mark_as_obsolete(&emcp_methods, &emcp_method_count);
+ update_jmethod_ids();
+
+ // keep track of previous versions of this class
+ the_new_class->add_previous_version(the_old_class, &emcp_methods,
+ emcp_method_count);
+
+ // TODO:
+ transfer_old_native_function_registrations(the_old_class);
+
+
+
+#ifdef ASSERT
+
+// klassOop systemLookup1 = SystemDictionary::resolve_or_null(the_old_class->name(), the_old_class->class_loader(), the_old_class->protection_domain(), THREAD);
+// assert(systemLookup1 == the_new_class(), "New class must be in system dictionary!");
+
+ //JNIHandles::verify();
+
+// klassOop systemLookup = SystemDictionary::resolve_or_null(the_old_class->name(), the_old_class->class_loader(), the_old_class->protection_domain(), THREAD);
+
+// assert(systemLookup == the_new_class(), "New class must be in system dictionary!");
+ assert(the_new_class->old_version() != NULL, "Must not be null");
+ assert(the_new_class->old_version()->klass_part()->new_version() == the_new_class(), "Must equal");
+
+ for (int i=0; imethods()->length(); i++) {
+ assert(((methodOop)the_new_class->methods()->obj_at(i))->method_holder() == the_new_class(), "method holder must match!");
+ }
+
+ _old_methods->verify();
+ _new_methods->verify();
+
+ the_new_class->vtable()->verify(tty);
+ the_old_class->vtable()->verify(tty);
+
+#endif
+
+ // increment the classRedefinedCount field in the_class and in any
+ // direct and indirect subclasses of the_class
+ increment_class_counter((instanceKlass *)the_old_class()->klass_part(), THREAD);
+
+}
+
+
+void VM_RedefineClasses::check_methods_and_mark_as_obsolete(BitMap *emcp_methods, int * emcp_method_count_p) {
+ TRACE_RC3("Checking matching methods for EMCP");
+ *emcp_method_count_p = 0;
+ int obsolete_count = 0;
+ int old_index = 0;
+ for (int j = 0; j < _matching_methods_length; ++j, ++old_index) {
+ methodOop old_method = (methodOop)_old_methods->obj_at(_matching_old_methods[j]);
+ methodOop new_method = (methodOop)_new_methods->obj_at(_matching_new_methods[j]);
+ methodOop old_array_method;
+
+ // Maintain an old_index into the _old_methods array by skipping
+ // deleted methods
+ while ((old_array_method = (methodOop) _old_methods->obj_at(old_index))
+ != old_method) {
+ ++old_index;
+ }
+
+ if (MethodComparator::methods_EMCP(old_method, new_method)) {
+ // The EMCP definition from JSR-163 requires the bytecodes to be
+ // the same with the exception of constant pool indices which may
+ // differ. However, the constants referred to by those indices
+ // must be the same.
+ //
+ // We use methods_EMCP() for comparison since constant pool
+ // merging can remove duplicate constant pool entries that were
+ // present in the old method and removed from the rewritten new
+ // method. A faster binary comparison function would consider the
+ // old and new methods to be different when they are actually
+ // EMCP.
+
+ // track which methods are EMCP for add_previous_version() call
+ emcp_methods->set_bit(old_index);
+ (*emcp_method_count_p)++;
+
+ // An EMCP method is _not_ obsolete. An obsolete method has a
+ // different jmethodID than the current method. An EMCP method
+ // has the same jmethodID as the current method. Having the
+ // same jmethodID for all EMCP versions of a method allows for
+ // a consistent view of the EMCP methods regardless of which
+ // EMCP method you happen to have in hand. For example, a
+ // breakpoint set in one EMCP method will work for all EMCP
+ // versions of the method including the current one.
+
+ old_method->set_new_version(new_method);
+ new_method->set_old_version(old_method);
+
+ TRACE_RC3("Found EMCP method %s", old_method->name_and_sig_as_C_string());
+
+ // Transfer breakpoints
+ instanceKlass *ik = instanceKlass::cast(old_method->method_holder());
+ for (BreakpointInfo* bp = ik->breakpoints(); bp != NULL; bp = bp->next()) {
+ TRACE_RC2("Checking breakpoint");
+ TRACE_RC2("%d / %d", bp->match(old_method), bp->match(new_method));
+ if (bp->match(old_method)) {
+ assert(bp->match(new_method), "if old method is method, then new method must match too");
+ TRACE_RC2("Found a breakpoint in an old EMCP method");
+ new_method->set_breakpoint(bp->bci());
+ }
+ }
+
+
+
+ } else {
+ // mark obsolete methods as such
+ old_method->set_is_obsolete();
+ obsolete_count++;
+
+ // With tracing we try not to "yack" too much. The position of
+ // this trace assumes there are fewer obsolete methods than
+ // EMCP methods.
+ TRACE_RC3("mark %s(%s) as obsolete",
+ old_method->name()->as_C_string(),
+ old_method->signature()->as_C_string());
+ }
+ old_method->set_is_old();
+ }
+ for (int i = 0; i < _deleted_methods_length; ++i) {
+ methodOop old_method = (methodOop)_old_methods->obj_at(_deleted_methods[i]);
+
+ //assert(old_method->vtable_index() < 0,
+ // "cannot delete methods with vtable entries");;
+
+ // Mark all deleted methods as old and obsolete
+ old_method->set_is_old();
+ old_method->set_is_obsolete();
+ ++obsolete_count;
+ // With tracing we try not to "yack" too much. The position of
+ // this trace assumes there are fewer obsolete methods than
+ // EMCP methods.
+ TRACE_RC3("mark deleted %s(%s) as obsolete",
+ old_method->name()->as_C_string(),
+ old_method->signature()->as_C_string());
+ }
+ //assert((*emcp_method_count_p + obsolete_count) == _old_methods->length(), "sanity check");
+ TRACE_RC3("EMCP_cnt=%d, obsolete_cnt=%d !", *emcp_method_count_p, obsolete_count);
+}
+
+// Increment the classRedefinedCount field in the specific instanceKlass
+// and in all direct and indirect subclasses.
+void VM_RedefineClasses::increment_class_counter(instanceKlass *ik, TRAPS) {
+ oop class_mirror = ik->java_mirror();
+ klassOop class_oop = java_lang_Class::as_klassOop(class_mirror);
+ int new_count = java_lang_Class::classRedefinedCount(class_mirror) + 1;
+ java_lang_Class::set_classRedefinedCount(class_mirror, new_count);
+ TRACE_RC3("updated count for class=%s to %d", ik->external_name(), new_count);
+}
+
+#ifndef PRODUCT
+void VM_RedefineClasses::check_class(klassOop k_oop, TRAPS) {
Klass *k = k_oop->klass_part();
if (k->oop_is_instance()) {
HandleMark hm(THREAD);
instanceKlass *ik = (instanceKlass *) k;
-
- // HotSpot specific optimization! HotSpot does not currently
- // support delegation from the bootstrap class loader to a
- // user-defined class loader. This means that if the bootstrap
- // class loader is the initiating class loader, then it will also
- // be the defining class loader. This also means that classes
- // loaded by the bootstrap class loader cannot refer to classes
- // loaded by a user-defined class loader. Note: a user-defined
- // class loader can delegate to the bootstrap class loader.
- //
- // If the current class being redefined has a user-defined class
- // loader as its defining class loader, then we can skip all
- // classes loaded by the bootstrap class loader.
- bool is_user_defined =
- instanceKlass::cast(_the_class_oop)->class_loader() != NULL;
- if (is_user_defined && ik->class_loader() == NULL) {
- return;
- }
-
- // This is a very busy routine. We don't want too much tracing
- // printed out.
- bool trace_name_printed = false;
-
- // Very noisy: only enable this call if you are trying to determine
- // that a specific class gets found by this routine.
- // RC_TRACE macro has an embedded ResourceMark
- // RC_TRACE_WITH_THREAD(0x00100000, THREAD,
- // ("adjust check: name=%s", ik->external_name()));
- // trace_name_printed = true;
-
- // Fix the vtable embedded in the_class and subclasses of the_class,
- // if one exists. We discard scratch_class and we don't keep an
- // instanceKlass around to hold obsolete methods so we don't have
- // any other instanceKlass embedded vtables to update. The vtable
- // holds the methodOops for virtual (but not final) methods.
- if (ik->vtable_length() > 0 && ik->is_subtype_of(_the_class_oop)) {
- // ik->vtable() creates a wrapper object; rm cleans it up
+ assert(ik->is_newest_version(), "must be latest version in system dictionary");
+
+ if (ik->vtable_length() > 0) {
ResourceMark rm(THREAD);
- ik->vtable()->adjust_method_entries(_matching_old_methods,
- _matching_new_methods,
- _matching_methods_length,
- &trace_name_printed);
- }
-
- // If the current class has an itable and we are either redefining an
- // interface or if the current class is a subclass of the_class, then
- // we potentially have to fix the itable. If we are redefining an
- // interface, then we have to call adjust_method_entries() for
- // every instanceKlass that has an itable since there isn't a
- // subclass relationship between an interface and an instanceKlass.
- if (ik->itable_length() > 0 && (Klass::cast(_the_class_oop)->is_interface()
- || ik->is_subclass_of(_the_class_oop))) {
- // ik->itable() creates a wrapper object; rm cleans it up
- ResourceMark rm(THREAD);
- ik->itable()->adjust_method_entries(_matching_old_methods,
- _matching_new_methods,
- _matching_methods_length,
- &trace_name_printed);
- }
-
- // The constant pools in other classes (other_cp) can refer to
- // methods in the_class. We have to update method information in
- // other_cp's cache. If other_cp has a previous version, then we
- // have to repeat the process for each previous version. The
- // constant pool cache holds the methodOops for non-virtual
- // methods and for virtual, final methods.
- //
- // Special case: if the current class is the_class, then new_cp
- // has already been attached to the_class and old_cp has already
- // been added as a previous version. The new_cp doesn't have any
- // cached references to old methods so it doesn't need to be
- // updated. We can simply start with the previous version(s) in
- // that case.
- constantPoolHandle other_cp;
- constantPoolCacheOop cp_cache;
-
- if (k_oop != _the_class_oop) {
- // this klass' constant pool cache may need adjustment
- other_cp = constantPoolHandle(ik->constants());
- cp_cache = other_cp->cache();
- if (cp_cache != NULL) {
- cp_cache->adjust_method_entries(_matching_old_methods,
- _matching_new_methods,
- _matching_methods_length,
- &trace_name_printed);
+ if (!ik->vtable()->check_no_old_entries()) {
+ TRACE_RC1("size of class: %d\n", k_oop->size());
+ TRACE_RC1("klassVtable::check_no_old_entries failure -- OLD method found -- class: %s", ik->signature_name());
+ assert(false, "OLD method found");
}
- }
- {
- ResourceMark rm(THREAD);
- // PreviousVersionInfo objects returned via PreviousVersionWalker
- // contain a GrowableArray of handles. We have to clean up the
- // GrowableArray _after_ the PreviousVersionWalker destructor
- // has destroyed the handles.
- {
- // the previous versions' constant pool caches may need adjustment
- PreviousVersionWalker pvw(ik);
- for (PreviousVersionInfo * pv_info = pvw.next_previous_version();
- pv_info != NULL; pv_info = pvw.next_previous_version()) {
- other_cp = pv_info->prev_constant_pool_handle();
- cp_cache = other_cp->cache();
- if (cp_cache != NULL) {
- cp_cache->adjust_method_entries(_matching_old_methods,
- _matching_new_methods,
- _matching_methods_length,
- &trace_name_printed);
- }
- }
- } // pvw is cleaned up
- } // rm is cleaned up
- }
-}
-
-void VM_RedefineClasses::update_jmethod_ids() {
- for (int j = 0; j < _matching_methods_length; ++j) {
- methodOop old_method = _matching_old_methods[j];
- jmethodID jmid = old_method->find_jmethod_id_or_null();
- if (jmid != NULL) {
- // There is a jmethodID, change it to point to the new method
- methodHandle new_method_h(_matching_new_methods[j]);
- JNIHandles::change_method_associated_with_jmethod_id(jmid, new_method_h);
- assert(JNIHandles::resolve_jmethod_id(jmid) == _matching_new_methods[j],
- "should be replaced");
+
+ ik->vtable()->verify(tty, true);
}
}
}
-void VM_RedefineClasses::check_methods_and_mark_as_obsolete(
- BitMap *emcp_methods, int * emcp_method_count_p) {
- *emcp_method_count_p = 0;
- int obsolete_count = 0;
- int old_index = 0;
- for (int j = 0; j < _matching_methods_length; ++j, ++old_index) {
- methodOop old_method = _matching_old_methods[j];
- methodOop new_method = _matching_new_methods[j];
- methodOop old_array_method;
-
- // Maintain an old_index into the _old_methods array by skipping
- // deleted methods
- while ((old_array_method = (methodOop) _old_methods->obj_at(old_index))
- != old_method) {
- ++old_index;
+#endif
+
+VM_RedefineClasses::FindAffectedKlassesClosure::FindAffectedKlassesClosure( GrowableArray *original_klasses, GrowableArray *result )
+{
+ assert(original_klasses != NULL && result != NULL, "");
+ this->_original_klasses = original_klasses;
+ this->_result = result;
+ SystemDictionary::classes_do(this);
+}
+
+void VM_RedefineClasses::FindAffectedKlassesClosure::do_object( oop obj )
+{
+ klassOop klass = (klassOop)obj;
+ assert(!_result->contains(klass), "must not occur more than once!");
+ assert(klass->klass_part()->new_version() == NULL, "Only last version is valid entry in system dictionary");
+
+ for(int i=0; i<_original_klasses->length(); i++) {
+ instanceKlassHandle cur = _original_klasses->at(i);
+ if (cur() != klass && klass->klass_part()->is_subtype_of(cur()) && !_original_klasses->contains(klass)) {
+ TRACE_RC3("Found affected class: %s", klass->klass_part()->name()->as_C_string());
+ _result->append(klass);
+ break;
}
-
- if (MethodComparator::methods_EMCP(old_method, new_method)) {
- // The EMCP definition from JSR-163 requires the bytecodes to be
- // the same with the exception of constant pool indices which may
- // differ. However, the constants referred to by those indices
- // must be the same.
- //
- // We use methods_EMCP() for comparison since constant pool
- // merging can remove duplicate constant pool entries that were
- // present in the old method and removed from the rewritten new
- // method. A faster binary comparison function would consider the
- // old and new methods to be different when they are actually
- // EMCP.
- //
- // The old and new methods are EMCP and you would think that we
- // could get rid of one of them here and now and save some space.
- // However, the concept of EMCP only considers the bytecodes and
- // the constant pool entries in the comparison. Other things,
- // e.g., the line number table (LNT) or the local variable table
- // (LVT) don't count in the comparison. So the new (and EMCP)
- // method can have a new LNT that we need so we can't just
- // overwrite the new method with the old method.
- //
- // When this routine is called, we have already attached the new
- // methods to the_class so the old methods are effectively
- // overwritten. However, if an old method is still executing,
- // then the old method cannot be collected until sometime after
- // the old method call has returned. So the overwriting of old
- // methods by new methods will save us space except for those
- // (hopefully few) old methods that are still executing.
- //
- // A method refers to a constMethodOop and this presents another
- // possible avenue to space savings. The constMethodOop in the
- // new method contains possibly new attributes (LNT, LVT, etc).
- // At first glance, it seems possible to save space by replacing
- // the constMethodOop in the old method with the constMethodOop
- // from the new method. The old and new methods would share the
- // same constMethodOop and we would save the space occupied by
- // the old constMethodOop. However, the constMethodOop contains
- // a back reference to the containing method. Sharing the
- // constMethodOop between two methods could lead to confusion in
- // the code that uses the back reference. This would lead to
- // brittle code that could be broken in non-obvious ways now or
- // in the future.
- //
- // Another possibility is to copy the constMethodOop from the new
- // method to the old method and then overwrite the new method with
- // the old method. Since the constMethodOop contains the bytecodes
- // for the method embedded in the oop, this option would change
- // the bytecodes out from under any threads executing the old
- // method and make the thread's bcp invalid. Since EMCP requires
- // that the bytecodes be the same modulo constant pool indices, it
- // is straight forward to compute the correct new bcp in the new
- // constMethodOop from the old bcp in the old constMethodOop. The
- // time consuming part would be searching all the frames in all
- // of the threads to find all of the calls to the old method.
- //
- // It looks like we will have to live with the limited savings
- // that we get from effectively overwriting the old methods
- // when the new methods are attached to the_class.
-
- // track which methods are EMCP for add_previous_version() call
- emcp_methods->set_bit(old_index);
- (*emcp_method_count_p)++;
-
- // An EMCP method is _not_ obsolete. An obsolete method has a
- // different jmethodID than the current method. An EMCP method
- // has the same jmethodID as the current method. Having the
- // same jmethodID for all EMCP versions of a method allows for
- // a consistent view of the EMCP methods regardless of which
- // EMCP method you happen to have in hand. For example, a
- // breakpoint set in one EMCP method will work for all EMCP
- // versions of the method including the current one.
- } else {
- // mark obsolete methods as such
- old_method->set_is_obsolete();
- obsolete_count++;
-
- // obsolete methods need a unique idnum
- u2 num = instanceKlass::cast(_the_class_oop)->next_method_idnum();
- if (num != constMethodOopDesc::UNSET_IDNUM) {
-// u2 old_num = old_method->method_idnum();
- old_method->set_method_idnum(num);
-// TO DO: attach obsolete annotations to obsolete method's new idnum
+ }
+}
+
+jvmtiError VM_RedefineClasses::do_topological_class_sorting( const jvmtiClassDefinition *class_defs, int class_count, GrowableArray *affected, GrowableArray *arr, TRAPS)
+{
+ GrowableArray< Pair > *links = new GrowableArray< Pair >();
+
+ for (int i=0; iclass_loader());
+ Handle protection_domain(THREAD, the_class->protection_domain());
+
+ ClassFileStream st((u1*) class_defs[i].class_bytes,
+ class_defs[i].class_byte_count, (char *)"__VM_RedefineClasses__");
+ ClassFileParser cfp(&st);
+
+ GrowableArray symbolArr;
+ TRACE_RC2("Before find super symbols of class %s", the_class->name()->as_C_string());
+ cfp.findSuperSymbols(the_class->name(), the_class_loader, protection_domain, the_class, symbolArr, THREAD);
+
+ for (int j=0; jas_C_string());
+
+ for (int k=0; klength(); k++) {
+ klassOop curOop = arr->at(k)();
+ // (tw) TODO: Check if we get aliasing problems with different class loaders?
+ if (curOop->klass_part()->name() == sym() /*&& curOop->klass_part()->class_loader() == the_class_loader()*/) {
+ TRACE_RC2("Found class to link");
+ links->append(Pair(curOop, the_class()));
+ break;
+ }
}
- // With tracing we try not to "yack" too much. The position of
- // this trace assumes there are fewer obsolete methods than
- // EMCP methods.
- RC_TRACE(0x00000100, ("mark %s(%s) as obsolete",
- old_method->name()->as_C_string(),
- old_method->signature()->as_C_string()));
}
- old_method->set_is_old();
}
- for (int i = 0; i < _deleted_methods_length; ++i) {
- methodOop old_method = _deleted_methods[i];
-
- assert(old_method->vtable_index() < 0,
- "cannot delete methods with vtable entries");;
-
- // Mark all deleted methods as old and obsolete
- old_method->set_is_old();
- old_method->set_is_obsolete();
- ++obsolete_count;
- // With tracing we try not to "yack" too much. The position of
- // this trace assumes there are fewer obsolete methods than
- // EMCP methods.
- RC_TRACE(0x00000100, ("mark deleted %s(%s) as obsolete",
- old_method->name()->as_C_string(),
- old_method->signature()->as_C_string()));
+
+
+ TRACE_RC1("Identified links between classes! ");
+
+ for (int i=0; ilength(); i++) {
+
+ instanceKlassHandle klass = affected->at(i);
+
+ klassOop superKlass = klass->super();
+ if (affected->contains(superKlass)) {
+ links->append(Pair(superKlass, klass()));
+ }
+
+ objArrayOop superInterfaces = klass->local_interfaces();
+ for (int j=0; jlength(); j++) {
+ klassOop interfaceKlass = (klassOop)superInterfaces->obj_at(j);
+ if (arr->contains(interfaceKlass)) {
+ links->append(Pair(interfaceKlass, klass()));
+ }
+ }
}
- assert((*emcp_method_count_p + obsolete_count) == _old_methods->length(),
- "sanity check");
- RC_TRACE(0x00000100, ("EMCP_cnt=%d, obsolete_cnt=%d", *emcp_method_count_p,
- obsolete_count));
+
+ IF_TRACE_RC2 {
+ TRACE_RC2("Identified links: ");
+ for (int i=0; ilength(); i++) {
+ TRACE_RC2("%s to %s", links->at(i).left()->klass_part()->name()->as_C_string(),
+ links->at(i).right()->klass_part()->name()->as_C_string());
+ }
+ }
+
+ for (int i=0; ilength(); i++) {
+
+ int j;
+ for (j=i; jlength(); j++) {
+
+ int k;
+ for (k=0; klength(); k++) {
+
+ klassOop k1 = links->adr_at(k)->right();
+ klassOop k2 = arr->at(j)();
+ if (k1 == k2) {
+ break;
+ }
+ }
+
+ if (k == links->length()) {
+ break;
+ }
+ }
+
+ if (j == arr->length()) {
+ // circle detected
+ return JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION;
+ }
+
+ for (int k=0; klength(); k++) {
+ if (links->adr_at(k)->left() == arr->at(j)()) {
+ links->at_put(k, links->at(links->length() - 1));
+ links->remove_at(links->length() - 1);
+ k--;
+ }
+ }
+
+ instanceKlassHandle tmp = arr->at(j);
+ arr->at_put(j, arr->at(i));
+ arr->at_put(i, tmp);
+ }
+
+ return JVMTI_ERROR_NONE;
}
+void VM_RedefineClasses::oops_do(OopClosure *closure) {
+
+ if (_updated_oops != NULL) {
+ for (int i=0; i<_updated_oops->length(); i++) {
+ closure->do_oop(_updated_oops->adr_at(i));
+ }
+ }
+}
+
+void VM_RedefineClasses::transfer_special_access_flags(fieldDescriptor *from, fieldDescriptor *to) {
+ to->set_is_field_modification_watched(from->is_field_modification_watched());
+ to->set_is_field_access_watched(from->is_field_access_watched());
+ if (from->is_field_modification_watched() || from->is_field_access_watched()) {
+ TRACE_RC2("Transfered watch for field %s", from->name()->as_C_string());
+ }
+ update_klass_field_access_flag(to);
+}
+
+void VM_RedefineClasses::update_klass_field_access_flag(fieldDescriptor *fd) {
+ instanceKlass* ik = instanceKlass::cast(fd->field_holder());
+ typeArrayOop fields = ik->fields();
+ fields->ushort_at_put(fd->index(), (jushort)fd->access_flags().as_short());
+}
+
+
// This internal class transfers the native function registration from old methods
// to new methods. It is designed to handle both the simple case of unchanged
// native methods and the complex cases of native method prefixes being added and/or
@@ -2831,7 +3022,7 @@
// Same, caused by prefix removal only 3_2_1_m -> 3_2_m
//
class TransferNativeFunctionRegistration {
- private:
+private:
instanceKlassHandle the_class;
int prefix_count;
char** prefixes;
@@ -2844,42 +3035,42 @@
// (2) with the prefix.
// where 'prefix' is the prefix at that 'depth' (first prefix, second prefix,...)
methodOop search_prefix_name_space(int depth, char* name_str, size_t name_len,
- symbolOop signature) {
- symbolOop name_symbol = SymbolTable::probe(name_str, (int)name_len);
- if (name_symbol != NULL) {
- methodOop method = Klass::cast(the_class())->lookup_method(name_symbol, signature);
- if (method != NULL) {
- // Even if prefixed, intermediate methods must exist.
- if (method->is_native()) {
- // Wahoo, we found a (possibly prefixed) version of the method, return it.
- return method;
- }
- if (depth < prefix_count) {
- // Try applying further prefixes (other than this one).
- method = search_prefix_name_space(depth+1, name_str, name_len, signature);
- if (method != NULL) {
- return method; // found
+ symbolOop signature) {
+ symbolOop name_symbol = SymbolTable::probe(name_str, (int)name_len);
+ if (name_symbol != NULL) {
+ methodOop method = Klass::cast(the_class()->klass_part()->new_version())->lookup_method(name_symbol, signature);
+ if (method != NULL) {
+ // Even if prefixed, intermediate methods must exist.
+ if (method->is_native()) {
+ // Wahoo, we found a (possibly prefixed) version of the method, return it.
+ return method;
}
-
- // Try adding this prefix to the method name and see if it matches
- // another method name.
- char* prefix = prefixes[depth];
- size_t prefix_len = strlen(prefix);
- size_t trial_len = name_len + prefix_len;
- char* trial_name_str = NEW_RESOURCE_ARRAY(char, trial_len + 1);
- strcpy(trial_name_str, prefix);
- strcat(trial_name_str, name_str);
- method = search_prefix_name_space(depth+1, trial_name_str, trial_len,
- signature);
- if (method != NULL) {
- // If found along this branch, it was prefixed, mark as such
- method->set_is_prefixed_native();
- return method; // found
+ if (depth < prefix_count) {
+ // Try applying further prefixes (other than this one).
+ method = search_prefix_name_space(depth+1, name_str, name_len, signature);
+ if (method != NULL) {
+ return method; // found
+ }
+
+ // Try adding this prefix to the method name and see if it matches
+ // another method name.
+ char* prefix = prefixes[depth];
+ size_t prefix_len = strlen(prefix);
+ size_t trial_len = name_len + prefix_len;
+ char* trial_name_str = NEW_RESOURCE_ARRAY(char, trial_len + 1);
+ strcpy(trial_name_str, prefix);
+ strcat(trial_name_str, name_str);
+ method = search_prefix_name_space(depth+1, trial_name_str, trial_len,
+ signature);
+ if (method != NULL) {
+ // If found along this branch, it was prefixed, mark as such
+ method->set_is_prefixed_native();
+ return method; // found
+ }
}
}
}
- }
- return NULL; // This whole branch bore nothing
+ return NULL; // This whole branch bore nothing
}
// Return the method name with old prefixes stripped away.
@@ -2904,10 +3095,10 @@
ResourceMark rm;
char* name_str = method_name_without_prefixes(method);
return search_prefix_name_space(0, name_str, strlen(name_str),
- method->signature());
+ method->signature());
}
- public:
+public:
// Construct a native method transfer processor for this class.
TransferNativeFunctionRegistration(instanceKlassHandle _the_class) {
@@ -2918,9 +3109,9 @@
}
// Attempt to transfer any of the old or deleted methods that are native
- void transfer_registrations(methodOop* old_methods, int methods_length) {
+ void transfer_registrations(instanceKlassHandle old_klass, int* old_methods, int methods_length) {
for (int j = 0; j < methods_length; j++) {
- methodOop old_method = old_methods[j];
+ methodOop old_method = (methodOop)old_klass->methods()->obj_at(old_methods[j]);
if (old_method->is_native() && old_method->has_native_function()) {
methodOop new_method = strip_and_search_for_new_native(old_method);
@@ -2929,7 +3120,9 @@
// Redefine does not send events (except CFLH), certainly not this
// behind the scenes re-registration.
new_method->set_native_function(old_method->native_function(),
- !methodOopDesc::native_bind_event_is_interesting);
+ !methodOopDesc::native_bind_event_is_interesting);
+
+ TRACE_RC3("Transfering native function for method %s", old_method->name()->as_C_string());
}
}
}
@@ -2937,481 +3130,8 @@
};
// Don't lose the association between a native method and its JNI function.
-void VM_RedefineClasses::transfer_old_native_function_registrations(instanceKlassHandle the_class) {
- TransferNativeFunctionRegistration transfer(the_class);
- transfer.transfer_registrations(_deleted_methods, _deleted_methods_length);
- transfer.transfer_registrations(_matching_old_methods, _matching_methods_length);
+void VM_RedefineClasses::transfer_old_native_function_registrations(instanceKlassHandle old_klass) {
+ TransferNativeFunctionRegistration transfer(old_klass);
+ transfer.transfer_registrations(old_klass, _deleted_methods, _deleted_methods_length);
+ transfer.transfer_registrations(old_klass, _matching_old_methods, _matching_methods_length);
}
-
-// Deoptimize all compiled code that depends on this class.
-//
-// If the can_redefine_classes capability is obtained in the onload
-// phase then the compiler has recorded all dependencies from startup.
-// In that case we need only deoptimize and throw away all compiled code
-// that depends on the class.
-//
-// If can_redefine_classes is obtained sometime after the onload
-// phase then the dependency information may be incomplete. In that case
-// the first call to RedefineClasses causes all compiled code to be
-// thrown away. As can_redefine_classes has been obtained then
-// all future compilations will record dependencies so second and
-// subsequent calls to RedefineClasses need only throw away code
-// that depends on the class.
-//
-void VM_RedefineClasses::flush_dependent_code(instanceKlassHandle k_h, TRAPS) {
- assert_locked_or_safepoint(Compile_lock);
-
- // All dependencies have been recorded from startup or this is a second or
- // subsequent use of RedefineClasses
- if (JvmtiExport::all_dependencies_are_recorded()) {
- Universe::flush_evol_dependents_on(k_h);
- } else {
- CodeCache::mark_all_nmethods_for_deoptimization();
-
- ResourceMark rm(THREAD);
- DeoptimizationMarker dm;
-
- // Deoptimize all activations depending on marked nmethods
- Deoptimization::deoptimize_dependents();
-
- // Make the dependent methods not entrant (in VM_Deoptimize they are made zombies)
- CodeCache::make_marked_nmethods_not_entrant();
-
- // From now on we know that the dependency information is complete
- JvmtiExport::set_all_dependencies_are_recorded(true);
- }
-}
-
-void VM_RedefineClasses::compute_added_deleted_matching_methods() {
- methodOop old_method;
- methodOop new_method;
-
- _matching_old_methods = NEW_RESOURCE_ARRAY(methodOop, _old_methods->length());
- _matching_new_methods = NEW_RESOURCE_ARRAY(methodOop, _old_methods->length());
- _added_methods = NEW_RESOURCE_ARRAY(methodOop, _new_methods->length());
- _deleted_methods = NEW_RESOURCE_ARRAY(methodOop, _old_methods->length());
-
- _matching_methods_length = 0;
- _deleted_methods_length = 0;
- _added_methods_length = 0;
-
- int nj = 0;
- int oj = 0;
- while (true) {
- if (oj >= _old_methods->length()) {
- if (nj >= _new_methods->length()) {
- break; // we've looked at everything, done
- }
- // New method at the end
- new_method = (methodOop) _new_methods->obj_at(nj);
- _added_methods[_added_methods_length++] = new_method;
- ++nj;
- } else if (nj >= _new_methods->length()) {
- // Old method, at the end, is deleted
- old_method = (methodOop) _old_methods->obj_at(oj);
- _deleted_methods[_deleted_methods_length++] = old_method;
- ++oj;
- } else {
- old_method = (methodOop) _old_methods->obj_at(oj);
- new_method = (methodOop) _new_methods->obj_at(nj);
- if (old_method->name() == new_method->name()) {
- if (old_method->signature() == new_method->signature()) {
- _matching_old_methods[_matching_methods_length ] = old_method;
- _matching_new_methods[_matching_methods_length++] = new_method;
- ++nj;
- ++oj;
- } else {
- // added overloaded have already been moved to the end,
- // so this is a deleted overloaded method
- _deleted_methods[_deleted_methods_length++] = old_method;
- ++oj;
- }
- } else { // names don't match
- if (old_method->name()->fast_compare(new_method->name()) > 0) {
- // new method
- _added_methods[_added_methods_length++] = new_method;
- ++nj;
- } else {
- // deleted method
- _deleted_methods[_deleted_methods_length++] = old_method;
- ++oj;
- }
- }
- }
- }
- assert(_matching_methods_length + _deleted_methods_length == _old_methods->length(), "sanity");
- assert(_matching_methods_length + _added_methods_length == _new_methods->length(), "sanity");
-}
-
-
-
-// Install the redefinition of a class:
-// - house keeping (flushing breakpoints and caches, deoptimizing
-// dependent compiled code)
-// - replacing parts in the_class with parts from scratch_class
-// - adding a weak reference to track the obsolete but interesting
-// parts of the_class
-// - adjusting constant pool caches and vtables in other classes
-// that refer to methods in the_class. These adjustments use the
-// SystemDictionary::classes_do() facility which only allows
-// a helper method to be specified. The interesting parameters
-// that we would like to pass to the helper method are saved in
-// static global fields in the VM operation.
-void VM_RedefineClasses::redefine_single_class(jclass the_jclass,
- instanceKlassHandle scratch_class, TRAPS) {
-
- RC_TIMER_START(_timer_rsc_phase1);
-
- oop the_class_mirror = JNIHandles::resolve_non_null(the_jclass);
- klassOop the_class_oop = java_lang_Class::as_klassOop(the_class_mirror);
- instanceKlassHandle the_class = instanceKlassHandle(THREAD, the_class_oop);
-
-#ifndef JVMTI_KERNEL
- // Remove all breakpoints in methods of this class
- JvmtiBreakpoints& jvmti_breakpoints = JvmtiCurrentBreakpoints::get_jvmti_breakpoints();
- jvmti_breakpoints.clearall_in_class_at_safepoint(the_class_oop);
-#endif // !JVMTI_KERNEL
-
- if (the_class_oop == Universe::reflect_invoke_cache()->klass()) {
- // We are redefining java.lang.reflect.Method. Method.invoke() is
- // cached and users of the cache care about each active version of
- // the method so we have to track this previous version.
- // Do this before methods get switched
- Universe::reflect_invoke_cache()->add_previous_version(
- the_class->method_with_idnum(Universe::reflect_invoke_cache()->method_idnum()));
- }
-
- // Deoptimize all compiled code that depends on this class
- flush_dependent_code(the_class, THREAD);
-
- _old_methods = the_class->methods();
- _new_methods = scratch_class->methods();
- _the_class_oop = the_class_oop;
- compute_added_deleted_matching_methods();
- update_jmethod_ids();
-
- // Attach new constant pool to the original klass. The original
- // klass still refers to the old constant pool (for now).
- scratch_class->constants()->set_pool_holder(the_class());
-
-#if 0
- // In theory, with constant pool merging in place we should be able
- // to save space by using the new, merged constant pool in place of
- // the old constant pool(s). By "pool(s)" I mean the constant pool in
- // the klass version we are replacing now and any constant pool(s) in
- // previous versions of klass. Nice theory, doesn't work in practice.
- // When this code is enabled, even simple programs throw NullPointer
- // exceptions. I'm guessing that this is caused by some constant pool
- // cache difference between the new, merged constant pool and the
- // constant pool that was just being used by the klass. I'm keeping
- // this code around to archive the idea, but the code has to remain
- // disabled for now.
-
- // Attach each old method to the new constant pool. This can be
- // done here since we are past the bytecode verification and
- // constant pool optimization phases.
- for (int i = _old_methods->length() - 1; i >= 0; i--) {
- methodOop method = (methodOop)_old_methods->obj_at(i);
- method->set_constants(scratch_class->constants());
- }
-
- {
- // walk all previous versions of the klass
- instanceKlass *ik = (instanceKlass *)the_class()->klass_part();
- PreviousVersionWalker pvw(ik);
- instanceKlassHandle ikh;
- do {
- ikh = pvw.next_previous_version();
- if (!ikh.is_null()) {
- ik = ikh();
-
- // attach previous version of klass to the new constant pool
- ik->set_constants(scratch_class->constants());
-
- // Attach each method in the previous version of klass to the
- // new constant pool
- objArrayOop prev_methods = ik->methods();
- for (int i = prev_methods->length() - 1; i >= 0; i--) {
- methodOop method = (methodOop)prev_methods->obj_at(i);
- method->set_constants(scratch_class->constants());
- }
- }
- } while (!ikh.is_null());
- }
-#endif
-
- // Replace methods and constantpool
- the_class->set_methods(_new_methods);
- scratch_class->set_methods(_old_methods); // To prevent potential GCing of the old methods,
- // and to be able to undo operation easily.
-
- constantPoolOop old_constants = the_class->constants();
- the_class->set_constants(scratch_class->constants());
- scratch_class->set_constants(old_constants); // See the previous comment.
-#if 0
- // We are swapping the guts of "the new class" with the guts of "the
- // class". Since the old constant pool has just been attached to "the
- // new class", it seems logical to set the pool holder in the old
- // constant pool also. However, doing this will change the observable
- // class hierarchy for any old methods that are still executing. A
- // method can query the identity of its "holder" and this query uses
- // the method's constant pool link to find the holder. The change in
- // holding class from "the class" to "the new class" can confuse
- // things.
- //
- // Setting the old constant pool's holder will also cause
- // verification done during vtable initialization below to fail.
- // During vtable initialization, the vtable's class is verified to be
- // a subtype of the method's holder. The vtable's class is "the
- // class" and the method's holder is gotten from the constant pool
- // link in the method itself. For "the class"'s directly implemented
- // methods, the method holder is "the class" itself (as gotten from
- // the new constant pool). The check works fine in this case. The
- // check also works fine for methods inherited from super classes.
- //
- // Miranda methods are a little more complicated. A miranda method is
- // provided by an interface when the class implementing the interface
- // does not provide its own method. These interfaces are implemented
- // internally as an instanceKlass. These special instanceKlasses
- // share the constant pool of the class that "implements" the
- // interface. By sharing the constant pool, the method holder of a
- // miranda method is the class that "implements" the interface. In a
- // non-redefine situation, the subtype check works fine. However, if
- // the old constant pool's pool holder is modified, then the check
- // fails because there is no class hierarchy relationship between the
- // vtable's class and "the new class".
-
- old_constants->set_pool_holder(scratch_class());
-#endif
-
- // track which methods are EMCP for add_previous_version() call below
- BitMap emcp_methods(_old_methods->length());
- int emcp_method_count = 0;
- emcp_methods.clear(); // clears 0..(length() - 1)
- check_methods_and_mark_as_obsolete(&emcp_methods, &emcp_method_count);
- transfer_old_native_function_registrations(the_class);
-
- // The class file bytes from before any retransformable agents mucked
- // with them was cached on the scratch class, move to the_class.
- // Note: we still want to do this if nothing needed caching since it
- // should get cleared in the_class too.
- the_class->set_cached_class_file(scratch_class->get_cached_class_file_bytes(),
- scratch_class->get_cached_class_file_len());
-
- // Replace inner_classes
- typeArrayOop old_inner_classes = the_class->inner_classes();
- the_class->set_inner_classes(scratch_class->inner_classes());
- scratch_class->set_inner_classes(old_inner_classes);
-
- // Initialize the vtable and interface table after
- // methods have been rewritten
- {
- ResourceMark rm(THREAD);
- // no exception should happen here since we explicitly
- // do not check loader constraints.
- // compare_and_normalize_class_versions has already checked:
- // - classloaders unchanged, signatures unchanged
- // - all instanceKlasses for redefined classes reused & contents updated
- the_class->vtable()->initialize_vtable(false, THREAD);
- the_class->itable()->initialize_itable(false, THREAD);
- assert(!HAS_PENDING_EXCEPTION || (THREAD->pending_exception()->is_a(SystemDictionary::ThreadDeath_klass())), "redefine exception");
- }
-
- // Leave arrays of jmethodIDs and itable index cache unchanged
-
- // Copy the "source file name" attribute from new class version
- the_class->set_source_file_name(scratch_class->source_file_name());
-
- // Copy the "source debug extension" attribute from new class version
- the_class->set_source_debug_extension(
- scratch_class->source_debug_extension());
-
- // Use of javac -g could be different in the old and the new
- if (scratch_class->access_flags().has_localvariable_table() !=
- the_class->access_flags().has_localvariable_table()) {
-
- AccessFlags flags = the_class->access_flags();
- if (scratch_class->access_flags().has_localvariable_table()) {
- flags.set_has_localvariable_table();
- } else {
- flags.clear_has_localvariable_table();
- }
- the_class->set_access_flags(flags);
- }
-
- // Replace class annotation fields values
- typeArrayOop old_class_annotations = the_class->class_annotations();
- the_class->set_class_annotations(scratch_class->class_annotations());
- scratch_class->set_class_annotations(old_class_annotations);
-
- // Replace fields annotation fields values
- objArrayOop old_fields_annotations = the_class->fields_annotations();
- the_class->set_fields_annotations(scratch_class->fields_annotations());
- scratch_class->set_fields_annotations(old_fields_annotations);
-
- // Replace methods annotation fields values
- objArrayOop old_methods_annotations = the_class->methods_annotations();
- the_class->set_methods_annotations(scratch_class->methods_annotations());
- scratch_class->set_methods_annotations(old_methods_annotations);
-
- // Replace methods parameter annotation fields values
- objArrayOop old_methods_parameter_annotations =
- the_class->methods_parameter_annotations();
- the_class->set_methods_parameter_annotations(
- scratch_class->methods_parameter_annotations());
- scratch_class->set_methods_parameter_annotations(old_methods_parameter_annotations);
-
- // Replace methods default annotation fields values
- objArrayOop old_methods_default_annotations =
- the_class->methods_default_annotations();
- the_class->set_methods_default_annotations(
- scratch_class->methods_default_annotations());
- scratch_class->set_methods_default_annotations(old_methods_default_annotations);
-
- // Replace minor version number of class file
- u2 old_minor_version = the_class->minor_version();
- the_class->set_minor_version(scratch_class->minor_version());
- scratch_class->set_minor_version(old_minor_version);
-
- // Replace major version number of class file
- u2 old_major_version = the_class->major_version();
- the_class->set_major_version(scratch_class->major_version());
- scratch_class->set_major_version(old_major_version);
-
- // Replace CP indexes for class and name+type of enclosing method
- u2 old_class_idx = the_class->enclosing_method_class_index();
- u2 old_method_idx = the_class->enclosing_method_method_index();
- the_class->set_enclosing_method_indices(
- scratch_class->enclosing_method_class_index(),
- scratch_class->enclosing_method_method_index());
- scratch_class->set_enclosing_method_indices(old_class_idx, old_method_idx);
-
- // keep track of previous versions of this class
- the_class->add_previous_version(scratch_class, &emcp_methods,
- emcp_method_count);
-
- RC_TIMER_STOP(_timer_rsc_phase1);
- RC_TIMER_START(_timer_rsc_phase2);
-
- // Adjust constantpool caches and vtables for all classes
- // that reference methods of the evolved class.
- SystemDictionary::classes_do(adjust_cpool_cache_and_vtable, THREAD);
-
- if (the_class->oop_map_cache() != NULL) {
- // Flush references to any obsolete methods from the oop map cache
- // so that obsolete methods are not pinned.
- the_class->oop_map_cache()->flush_obsolete_entries();
- }
-
- // increment the classRedefinedCount field in the_class and in any
- // direct and indirect subclasses of the_class
- increment_class_counter((instanceKlass *)the_class()->klass_part(), THREAD);
-
- // RC_TRACE macro has an embedded ResourceMark
- RC_TRACE_WITH_THREAD(0x00000001, THREAD,
- ("redefined name=%s, count=%d (avail_mem=" UINT64_FORMAT "K)",
- the_class->external_name(),
- java_lang_Class::classRedefinedCount(the_class_mirror),
- os::available_memory() >> 10));
-
- RC_TIMER_STOP(_timer_rsc_phase2);
-} // end redefine_single_class()
-
-
-// Increment the classRedefinedCount field in the specific instanceKlass
-// and in all direct and indirect subclasses.
-void VM_RedefineClasses::increment_class_counter(instanceKlass *ik, TRAPS) {
- oop class_mirror = ik->java_mirror();
- klassOop class_oop = java_lang_Class::as_klassOop(class_mirror);
- int new_count = java_lang_Class::classRedefinedCount(class_mirror) + 1;
- java_lang_Class::set_classRedefinedCount(class_mirror, new_count);
-
- if (class_oop != _the_class_oop) {
- // _the_class_oop count is printed at end of redefine_single_class()
- RC_TRACE_WITH_THREAD(0x00000008, THREAD,
- ("updated count in subclass=%s to %d", ik->external_name(), new_count));
- }
-
- for (Klass *subk = ik->subklass(); subk != NULL;
- subk = subk->next_sibling()) {
- klassOop sub = subk->as_klassOop();
- instanceKlass *subik = (instanceKlass *)sub->klass_part();
-
- // recursively do subclasses of the current subclass
- increment_class_counter(subik, THREAD);
- }
-}
-
-#ifndef PRODUCT
-void VM_RedefineClasses::check_class(klassOop k_oop,
- oop initiating_loader, TRAPS) {
- Klass *k = k_oop->klass_part();
- if (k->oop_is_instance()) {
- HandleMark hm(THREAD);
- instanceKlass *ik = (instanceKlass *) k;
-
- if (ik->vtable_length() > 0) {
- ResourceMark rm(THREAD);
- if (!ik->vtable()->check_no_old_entries()) {
- tty->print_cr("klassVtable::check_no_old_entries failure -- OLD method found -- class: %s", ik->signature_name());
- ik->vtable()->dump_vtable();
- dump_methods();
- assert(false, "OLD method found");
- }
- }
- }
-}
-
-void VM_RedefineClasses::dump_methods() {
- int j;
- tty->print_cr("_old_methods --");
- for (j = 0; j < _old_methods->length(); ++j) {
- methodOop m = (methodOop) _old_methods->obj_at(j);
- tty->print("%4d (%5d) ", j, m->vtable_index());
- m->access_flags().print_on(tty);
- tty->print(" -- ");
- m->print_name(tty);
- tty->cr();
- }
- tty->print_cr("_new_methods --");
- for (j = 0; j < _new_methods->length(); ++j) {
- methodOop m = (methodOop) _new_methods->obj_at(j);
- tty->print("%4d (%5d) ", j, m->vtable_index());
- m->access_flags().print_on(tty);
- tty->print(" -- ");
- m->print_name(tty);
- tty->cr();
- }
- tty->print_cr("_matching_(old/new)_methods --");
- for (j = 0; j < _matching_methods_length; ++j) {
- methodOop m = _matching_old_methods[j];
- tty->print("%4d (%5d) ", j, m->vtable_index());
- m->access_flags().print_on(tty);
- tty->print(" -- ");
- m->print_name(tty);
- tty->cr();
- m = _matching_new_methods[j];
- tty->print(" (%5d) ", m->vtable_index());
- m->access_flags().print_on(tty);
- tty->cr();
- }
- tty->print_cr("_deleted_methods --");
- for (j = 0; j < _deleted_methods_length; ++j) {
- methodOop m = _deleted_methods[j];
- tty->print("%4d (%5d) ", j, m->vtable_index());
- m->access_flags().print_on(tty);
- tty->print(" -- ");
- m->print_name(tty);
- tty->cr();
- }
- tty->print_cr("_added_methods --");
- for (j = 0; j < _added_methods_length; ++j) {
- methodOop m = _added_methods[j];
- tty->print("%4d (%5d) ", j, m->vtable_index());
- m->access_flags().print_on(tty);
- tty->print(" -- ");
- m->print_name(tty);
- tty->cr();
- }
-}
-#endif
diff -r f5603a6e5042 src/share/vm/prims/jvmtiRedefineClasses.hpp
--- a/src/share/vm/prims/jvmtiRedefineClasses.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/prims/jvmtiRedefineClasses.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -1,351 +1,50 @@
/*
- * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
+* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* This code is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License version 2 only, as
+* published by the Free Software Foundation.
+*
+* This code is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+* version 2 for more details (a copy is included in the LICENSE file that
+* accompanied this code).
+*
+* You should have received a copy of the GNU General Public License version
+* 2 along with this work; if not, write to the Free Software Foundation,
+* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+*
+* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+* or visit www.oracle.com if you need additional information or have any
+* questions.
+*
+*/
-// Introduction:
-//
-// The RedefineClasses() API is used to change the definition of one or
-// more classes. While the API supports redefining more than one class
-// in a single call, in general, the API is discussed in the context of
-// changing the definition of a single current class to a single new
-// class. For clarity, the current class is will always be called
-// "the_class" and the new class will always be called "scratch_class".
-//
-// The name "the_class" is used because there is only one structure
-// that represents a specific class; redefinition does not replace the
-// structure, but instead replaces parts of the structure. The name
-// "scratch_class" is used because the structure that represents the
-// new definition of a specific class is simply used to carry around
-// the parts of the new definition until they are used to replace the
-// appropriate parts in the_class. Once redefinition of a class is
-// complete, scratch_class is thrown away.
-//
-//
-// Implementation Overview:
-//
-// The RedefineClasses() API is mostly a wrapper around the VM op that
-// does the real work. The work is split in varying degrees between
-// doit_prologue(), doit() and doit_epilogue().
-//
-// 1) doit_prologue() is called by the JavaThread on the way to a
-// safepoint. It does parameter verification and loads scratch_class
-// which involves:
-// - parsing the incoming class definition using the_class' class
-// loader and security context
-// - linking scratch_class
-// - merging constant pools and rewriting bytecodes as needed
-// for the merged constant pool
-// - verifying the bytecodes in scratch_class
-// - setting up the constant pool cache and rewriting bytecodes
-// as needed to use the cache
-// - finally, scratch_class is compared to the_class to verify
-// that it is a valid replacement class
-// - if everything is good, then scratch_class is saved in an
-// instance field in the VM operation for the doit() call
-//
-// Note: A JavaThread must do the above work.
-//
-// 2) doit() is called by the VMThread during a safepoint. It installs
-// the new class definition(s) which involves:
-// - retrieving the scratch_class from the instance field in the
-// VM operation
-// - house keeping (flushing breakpoints and caches, deoptimizing
-// dependent compiled code)
-// - replacing parts in the_class with parts from scratch_class
-// - adding weak reference(s) to track the obsolete but interesting
-// parts of the_class
-// - adjusting constant pool caches and vtables in other classes
-// that refer to methods in the_class. These adjustments use the
-// SystemDictionary::classes_do() facility which only allows
-// a helper method to be specified. The interesting parameters
-// that we would like to pass to the helper method are saved in
-// static global fields in the VM operation.
-// - telling the SystemDictionary to notice our changes
-//
-// Note: the above work must be done by the VMThread to be safe.
-//
-// 3) doit_epilogue() is called by the JavaThread after the VM op
-// is finished and the safepoint is done. It simply cleans up
-// memory allocated in doit_prologue() and used in doit().
-//
-//
-// Constant Pool Details:
-//
-// When the_class is redefined, we cannot just replace the constant
-// pool in the_class with the constant pool from scratch_class because
-// that could confuse obsolete methods that may still be running.
-// Instead, the constant pool from the_class, old_cp, is merged with
-// the constant pool from scratch_class, scratch_cp. The resulting
-// constant pool, merge_cp, replaces old_cp in the_class.
-//
-// The key part of any merging algorithm is the entry comparison
-// function so we have to know the types of entries in a constant pool
-// in order to merge two of them together. Constant pools can contain
-// up to 12 different kinds of entries; the JVM_CONSTANT_Unicode entry
-// is not presently used so we only have to worry about the other 11
-// entry types. For the purposes of constant pool merging, it is
-// helpful to know that the 11 entry types fall into 3 different
-// subtypes: "direct", "indirect" and "double-indirect".
-//
-// Direct CP entries contain data and do not contain references to
-// other CP entries. The following are direct CP entries:
-// JVM_CONSTANT_{Double,Float,Integer,Long,Utf8}
-//
-// Indirect CP entries contain 1 or 2 references to a direct CP entry
-// and no other data. The following are indirect CP entries:
-// JVM_CONSTANT_{Class,NameAndType,String}
-//
-// Double-indirect CP entries contain two references to indirect CP
-// entries and no other data. The following are double-indirect CP
-// entries:
-// JVM_CONSTANT_{Fieldref,InterfaceMethodref,Methodref}
-//
-// When comparing entries between two constant pools, the entry types
-// are compared first and if they match, then further comparisons are
-// made depending on the entry subtype. Comparing direct CP entries is
-// simply a matter of comparing the data associated with each entry.
-// Comparing both indirect and double-indirect CP entries requires
-// recursion.
-//
-// Fortunately, the recursive combinations are limited because indirect
-// CP entries can only refer to direct CP entries and double-indirect
-// CP entries can only refer to indirect CP entries. The following is
-// an example illustration of the deepest set of indirections needed to
-// access the data associated with a JVM_CONSTANT_Fieldref entry:
-//
-// JVM_CONSTANT_Fieldref {
-// class_index => JVM_CONSTANT_Class {
-// name_index => JVM_CONSTANT_Utf8 {
-//
-// }
-// }
-// name_and_type_index => JVM_CONSTANT_NameAndType {
-// name_index => JVM_CONSTANT_Utf8 {
-//
-// }
-// descriptor_index => JVM_CONSTANT_Utf8 {
-//
-// }
-// }
-// }
-//
-// The above illustration is not a data structure definition for any
-// computer language. The curly braces ('{' and '}') are meant to
-// delimit the context of the "fields" in the CP entry types shown.
-// Each indirection from the JVM_CONSTANT_Fieldref entry is shown via
-// "=>", e.g., the class_index is used to indirectly reference a
-// JVM_CONSTANT_Class entry where the name_index is used to indirectly
-// reference a JVM_CONSTANT_Utf8 entry which contains the interesting
-// . In order to understand a JVM_CONSTANT_Fieldref entry, we
-// have to do a total of 5 indirections just to get to the CP entries
-// that contain the interesting pieces of data and then we have to
-// fetch the three pieces of data. This means we have to do a total of
-// (5 + 3) * 2 == 16 dereferences to compare two JVM_CONSTANT_Fieldref
-// entries.
-//
-// Here is the indirection, data and dereference count for each entry
-// type:
-//
-// JVM_CONSTANT_Class 1 indir, 1 data, 2 derefs
-// JVM_CONSTANT_Double 0 indir, 1 data, 1 deref
-// JVM_CONSTANT_Fieldref 2 indir, 3 data, 8 derefs
-// JVM_CONSTANT_Float 0 indir, 1 data, 1 deref
-// JVM_CONSTANT_Integer 0 indir, 1 data, 1 deref
-// JVM_CONSTANT_InterfaceMethodref 2 indir, 3 data, 8 derefs
-// JVM_CONSTANT_Long 0 indir, 1 data, 1 deref
-// JVM_CONSTANT_Methodref 2 indir, 3 data, 8 derefs
-// JVM_CONSTANT_NameAndType 1 indir, 2 data, 4 derefs
-// JVM_CONSTANT_String 1 indir, 1 data, 2 derefs
-// JVM_CONSTANT_Utf8 0 indir, 1 data, 1 deref
-//
-// So different subtypes of CP entries require different amounts of
-// work for a proper comparison.
-//
-// Now that we've talked about the different entry types and how to
-// compare them we need to get back to merging. This is not a merge in
-// the "sort -u" sense or even in the "sort" sense. When we merge two
-// constant pools, we copy all the entries from old_cp to merge_cp,
-// preserving entry order. Next we append all the unique entries from
-// scratch_cp to merge_cp and we track the index changes from the
-// location in scratch_cp to the possibly new location in merge_cp.
-// When we are done, any obsolete code that is still running that
-// uses old_cp should not be able to observe any difference if it
-// were to use merge_cp. As for the new code in scratch_class, it is
-// modified to use the appropriate index values in merge_cp before it
-// is used to replace the code in the_class.
-//
-// There is one small complication in copying the entries from old_cp
-// to merge_cp. Two of the CP entry types are special in that they are
-// lazily resolved. Before explaining the copying complication, we need
-// to digress into CP entry resolution.
-//
-// JVM_CONSTANT_Class and JVM_CONSTANT_String entries are present in
-// the class file, but are not stored in memory as such until they are
-// resolved. The entries are not resolved unless they are used because
-// resolution is expensive. During class file parsing the entries are
-// initially stored in memory as JVM_CONSTANT_ClassIndex and
-// JVM_CONSTANT_StringIndex entries. These special CP entry types
-// indicate that the JVM_CONSTANT_Class and JVM_CONSTANT_String entries
-// have been parsed, but the index values in the entries have not been
-// validated. After the entire constant pool has been parsed, the index
-// values can be validated and then the entries are converted into
-// JVM_CONSTANT_UnresolvedClass and JVM_CONSTANT_UnresolvedString
-// entries. During this conversion process, the UTF8 values that are
-// indirectly referenced by the JVM_CONSTANT_ClassIndex and
-// JVM_CONSTANT_StringIndex entries are changed into symbolOops and the
-// entries are modified to refer to the symbolOops. This optimization
-// eliminates one level of indirection for those two CP entry types and
-// gets the entries ready for verification. During class file parsing
-// it is also possible for JVM_CONSTANT_UnresolvedString entries to be
-// resolved into JVM_CONSTANT_String entries. Verification expects to
-// find JVM_CONSTANT_UnresolvedClass and either JVM_CONSTANT_String or
-// JVM_CONSTANT_UnresolvedString entries and not JVM_CONSTANT_Class
-// entries.
-//
-// Now we can get back to the copying complication. When we copy
-// entries from old_cp to merge_cp, we have to revert any
-// JVM_CONSTANT_Class entries to JVM_CONSTANT_UnresolvedClass entries
-// or verification will fail.
-//
-// It is important to explicitly state that the merging algorithm
-// effectively unresolves JVM_CONSTANT_Class entries that were in the
-// old_cp when they are changed into JVM_CONSTANT_UnresolvedClass
-// entries in the merge_cp. This is done both to make verification
-// happy and to avoid adding more brittleness between RedefineClasses
-// and the constant pool cache. By allowing the constant pool cache
-// implementation to (re)resolve JVM_CONSTANT_UnresolvedClass entries
-// into JVM_CONSTANT_Class entries, we avoid having to embed knowledge
-// about those algorithms in RedefineClasses.
-//
-// Appending unique entries from scratch_cp to merge_cp is straight
-// forward for direct CP entries and most indirect CP entries. For the
-// indirect CP entry type JVM_CONSTANT_NameAndType and for the double-
-// indirect CP entry types, the presence of more than one piece of
-// interesting data makes appending the entries more complicated.
-//
-// For the JVM_CONSTANT_{Double,Float,Integer,Long,Utf8} entry types,
-// the entry is simply copied from scratch_cp to the end of merge_cp.
-// If the index in scratch_cp is different than the destination index
-// in merge_cp, then the change in index value is tracked.
-//
-// Note: the above discussion for the direct CP entries also applies
-// to the JVM_CONSTANT_Unresolved{Class,String} entry types.
-//
-// For the JVM_CONSTANT_{Class,String} entry types, since there is only
-// one data element at the end of the recursion, we know that we have
-// either one or two unique entries. If the JVM_CONSTANT_Utf8 entry is
-// unique then it is appended to merge_cp before the current entry.
-// If the JVM_CONSTANT_Utf8 entry is not unique, then the current entry
-// is updated to refer to the duplicate entry in merge_cp before it is
-// appended to merge_cp. Again, any changes in index values are tracked
-// as needed.
-//
-// Note: the above discussion for JVM_CONSTANT_{Class,String} entry
-// types is theoretical. Since those entry types have already been
-// optimized into JVM_CONSTANT_Unresolved{Class,String} entry types,
-// they are handled as direct CP entries.
-//
-// For the JVM_CONSTANT_NameAndType entry type, since there are two
-// data elements at the end of the recursions, we know that we have
-// between one and three unique entries. Any unique JVM_CONSTANT_Utf8
-// entries are appended to merge_cp before the current entry. For any
-// JVM_CONSTANT_Utf8 entries that are not unique, the current entry is
-// updated to refer to the duplicate entry in merge_cp before it is
-// appended to merge_cp. Again, any changes in index values are tracked
-// as needed.
-//
-// For the JVM_CONSTANT_{Fieldref,InterfaceMethodref,Methodref} entry
-// types, since there are two indirect CP entries and three data
-// elements at the end of the recursions, we know that we have between
-// one and six unique entries. See the JVM_CONSTANT_Fieldref diagram
-// above for an example of all six entries. The uniqueness algorithm
-// for the JVM_CONSTANT_Class and JVM_CONSTANT_NameAndType entries is
-// covered above. Any unique entries are appended to merge_cp before
-// the current entry. For any entries that are not unique, the current
-// entry is updated to refer to the duplicate entry in merge_cp before
-// it is appended to merge_cp. Again, any changes in index values are
-// tracked as needed.
-//
-//
-// Other Details:
-//
-// Details for other parts of RedefineClasses need to be written.
-// This is a placeholder section.
-//
-//
-// Open Issues (in no particular order):
-//
-// - How do we serialize the RedefineClasses() API without deadlocking?
-//
-// - SystemDictionary::parse_stream() was called with a NULL protection
-// domain since the initial version. This has been changed to pass
-// the_class->protection_domain(). This change has been tested with
-// all NSK tests and nothing broke, but what will adding it now break
-// in ways that we don't test?
-//
-// - GenerateOopMap::rewrite_load_or_store() has a comment in its
-// (indirect) use of the Relocator class that the max instruction
-// size is 4 bytes. goto_w and jsr_w are 5 bytes and wide/iinc is
-// 6 bytes. Perhaps Relocator only needs a 4 byte buffer to do
-// what it does to the bytecodes. More investigation is needed.
-//
-// - java.lang.Object methods can be called on arrays. This is
-// implemented via the arrayKlassOop vtable which we don't
-// update. For example, if we redefine java.lang.Object.toString(),
-// then the new version of the method will not be called for array
-// objects.
-//
-// - How do we know if redefine_single_class() and the guts of
-// instanceKlass are out of sync? I don't think this can be
-// automated, but we should probably order the work in
-// redefine_single_class() to match the order of field
-// definitions in instanceKlass. We also need to add some
-// comments about keeping things in sync.
-//
-// - set_new_constant_pool() is huge and we should consider refactoring
-// it into smaller chunks of work.
-//
-// - The exception table update code in set_new_constant_pool() defines
-// const values that are also defined in a local context elsewhere.
-// The same literal values are also used in elsewhere. We need to
-// coordinate a cleanup of these constants with Runtime.
-//
+// New version that allows arbitrary changes to already loaded classes.
+// Modifications done by: Thomas Wuerthinger
-class VM_RedefineClasses: public VM_Operation {
+#define RC_ABORT(error) { _result = error; return false; }
+
+class VM_RedefineClasses: public VM_GC_Operation {
private:
+
// These static fields are needed by SystemDictionary::classes_do()
// facility and the adjust_cpool_cache_and_vtable() helper:
static objArrayOop _old_methods;
static objArrayOop _new_methods;
- static methodOop* _matching_old_methods;
- static methodOop* _matching_new_methods;
- static methodOop* _deleted_methods;
- static methodOop* _added_methods;
+ static int* _matching_old_methods;
+ static int* _matching_new_methods;
+ static int* _deleted_methods;
+ static int* _added_methods;
static int _matching_methods_length;
static int _deleted_methods_length;
static int _added_methods_length;
static klassOop _the_class_oop;
+ static int _revision_number;
+
// The instance fields are used to pass information from
// doit_prologue() to doit() and doit_epilogue().
jint _class_count;
@@ -359,36 +58,29 @@
// _index_map_p contains any entries.
int _index_map_count;
intArray * _index_map_p;
- // ptr to _class_count scratch_classes
- instanceKlassHandle * _scratch_classes;
- jvmtiError _res;
+ GrowableArray* _new_classes;
+ GrowableArray* _updated_oops;
+ jvmtiError _result;
+ int _max_redefinition_flags;
// Performance measurement support. These timers do not cover all
// the work done for JVM/TI RedefineClasses() but they do cover
// the heavy lifting.
- elapsedTimer _timer_rsc_phase1;
- elapsedTimer _timer_rsc_phase2;
- elapsedTimer _timer_vm_op_prologue;
+ elapsedTimer _timer_total;
+ elapsedTimer _timer_prologue;
+ elapsedTimer _timer_class_linking;
+ elapsedTimer _timer_class_loading;
+ elapsedTimer _timer_check_type;
+ elapsedTimer _timer_prepare_redefinition;
+ elapsedTimer _timer_wait_for_locks;
+ elapsedTimer _timer_redefinition;
+ elapsedTimer _timer_vm_op_epilogue;
- // These routines are roughly in call order unless otherwise noted.
-
- // Load the caller's new class definition(s) into _scratch_classes.
- // Constant pool merging work is done here as needed. Also calls
- // compare_and_normalize_class_versions() to verify the class
- // definition(s).
+ jvmtiError check_redefinition_allowed(instanceKlassHandle new_class);
+ jvmtiError find_sorted_affected_classes(GrowableArray *all_affected_klasses);
+ jvmtiError find_class_bytes(instanceKlassHandle the_class, const unsigned char **class_bytes, jint *class_byte_count, jboolean *not_changed);
jvmtiError load_new_class_versions(TRAPS);
- // Verify that the caller provided class definition(s) that meet
- // the restrictions of RedefineClasses. Normalize the order of
- // overloaded methods as needed.
- jvmtiError compare_and_normalize_class_versions(
- instanceKlassHandle the_class, instanceKlassHandle scratch_class);
-
- // Swap annotations[i] with annotations[j]
- // Used by compare_and_normalize_class_versions() when normalizing
- // overloaded methods or changing idnum as when adding or deleting methods.
- void swap_all_method_annotations(int i, int j, instanceKlassHandle scratch_class);
-
// Figure out which new methods match old methods in name and signature,
// which methods have been added, and which are no longer present
void compute_added_deleted_matching_methods();
@@ -396,94 +88,94 @@
// Change jmethodIDs to point to the new methods
void update_jmethod_ids();
- // In addition to marking methods as obsolete, this routine
- // records which methods are EMCP (Equivalent Module Constant
- // Pool) in the emcp_methods BitMap and returns the number of
- // EMCP methods via emcp_method_count_p. This information is
- // used when information about the previous version of the_class
- // is squirreled away.
- void check_methods_and_mark_as_obsolete(BitMap *emcp_methods,
- int * emcp_method_count_p);
- void transfer_old_native_function_registrations(instanceKlassHandle the_class);
+ class FindAffectedKlassesClosure : public ObjectClosure {
- // Unevolving classes may point to methods of the_class directly
- // from their constant pool caches, itables, and/or vtables. We
- // use the SystemDictionary::classes_do() facility and this helper
- // to fix up these pointers.
- static void adjust_cpool_cache_and_vtable(klassOop k_oop, oop loader, TRAPS);
+ private:
+ GrowableArray *_original_klasses;
+ GrowableArray *_result;
+
+ public:
+ FindAffectedKlassesClosure(GrowableArray *original_klasses, GrowableArray *result);
+
+ virtual void do_object(oop obj);
+ };
+
+
+ static jvmtiError do_topological_class_sorting(const jvmtiClassDefinition *class_definitions, int class_count, GrowableArray *affected, GrowableArray *arr, TRAPS);
// Install the redefinition of a class
- void redefine_single_class(jclass the_jclass,
- instanceKlassHandle scratch_class, TRAPS);
+ void redefine_single_class(instanceKlassHandle the_new_class, TRAPS);
// Increment the classRedefinedCount field in the specific instanceKlass
// and in all direct and indirect subclasses.
void increment_class_counter(instanceKlass *ik, TRAPS);
- // 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);
- int find_new_index(int old_index);
- bool is_unresolved_class_mismatch(constantPoolHandle cp1, int index1,
- constantPoolHandle cp2, int index2);
- bool is_unresolved_string_mismatch(constantPoolHandle cp1, int index1,
- constantPoolHandle cp2, int index2);
- void map_index(constantPoolHandle scratch_cp, int old_index, int new_index);
- bool merge_constant_pools(constantPoolHandle old_cp,
- constantPoolHandle scratch_cp, constantPoolHandle *merge_cp_p,
- int *merge_cp_length_p, TRAPS);
- jvmtiError merge_cp_and_rewrite(instanceKlassHandle the_class,
- instanceKlassHandle scratch_class, TRAPS);
- u2 rewrite_cp_ref_in_annotation_data(
- typeArrayHandle annotations_typeArray, int &byte_i_ref,
- const char * trace_mesg, TRAPS);
- bool rewrite_cp_refs(instanceKlassHandle scratch_class, TRAPS);
- bool rewrite_cp_refs_in_annotation_struct(
- typeArrayHandle class_annotations, int &byte_i_ref, TRAPS);
- bool rewrite_cp_refs_in_annotations_typeArray(
- typeArrayHandle annotations_typeArray, int &byte_i_ref, TRAPS);
- bool rewrite_cp_refs_in_class_annotations(
- instanceKlassHandle scratch_class, TRAPS);
- bool rewrite_cp_refs_in_element_value(
- typeArrayHandle class_annotations, int &byte_i_ref, TRAPS);
- bool rewrite_cp_refs_in_fields_annotations(
- instanceKlassHandle scratch_class, TRAPS);
- void rewrite_cp_refs_in_method(methodHandle method,
- methodHandle * new_method_p, TRAPS);
- bool rewrite_cp_refs_in_methods(instanceKlassHandle scratch_class, TRAPS);
- bool rewrite_cp_refs_in_methods_annotations(
- instanceKlassHandle scratch_class, TRAPS);
- bool rewrite_cp_refs_in_methods_default_annotations(
- instanceKlassHandle scratch_class, TRAPS);
- bool rewrite_cp_refs_in_methods_parameter_annotations(
- instanceKlassHandle scratch_class, TRAPS);
- void rewrite_cp_refs_in_stack_map_table(methodHandle method, TRAPS);
- void rewrite_cp_refs_in_verification_type_info(
- address& stackmap_addr_ref, address stackmap_end, u2 frame_i,
- u1 frame_size, TRAPS);
- void set_new_constant_pool(instanceKlassHandle scratch_class,
- constantPoolHandle scratch_cp, int scratch_cp_length, bool shrink, TRAPS);
void flush_dependent_code(instanceKlassHandle k_h, TRAPS);
- static void check_class(klassOop k_oop, oop initiating_loader, TRAPS) PRODUCT_RETURN;
+ static void check_class(klassOop k_oop,/* oop initiating_loader,*/ TRAPS) PRODUCT_RETURN;
- static void dump_methods() PRODUCT_RETURN;
+ static void adjust_cpool_cache(klassOop k_oop, oop initiating_loader, TRAPS);
+
+#ifdef ASSERT
+ static void verify_classes(klassOop k_oop, oop initiating_loader, TRAPS);
+#endif
+
+ int calculate_redefinition_flags(instanceKlassHandle new_version);
+ void calculate_instance_update_information(klassOop new_version);
+ void check_methods_and_mark_as_obsolete(BitMap *emcp_methods, int * emcp_method_count_p);
public:
- VM_RedefineClasses(jint class_count,
- const jvmtiClassDefinition *class_defs,
- JvmtiClassLoadKind class_load_kind);
- VMOp_Type type() const { return VMOp_RedefineClasses; }
+ VM_RedefineClasses(jint class_count, const jvmtiClassDefinition *class_defs, JvmtiClassLoadKind class_load_kind);
+ virtual ~VM_RedefineClasses();
+
+ bool check_arguments();
bool doit_prologue();
void doit();
void doit_epilogue();
+ void rollback();
- bool allow_nested_vm_operations() const { return true; }
- jvmtiError check_error() { return _res; }
+ jvmtiError check_exception() const;
+ VMOp_Type type() const { return VMOp_RedefineClasses; }
+ bool skip_operation() const { return false; }
+ bool allow_nested_vm_operations() const { return true; }
+ jvmtiError check_error() { return _result; }
+
+ void update_active_methods();
+
+ // Checks for type consistency after hierarchy change
+ bool check_type_consistency();
+ void calculate_type_check_information();
+ bool check_field_value_types();
+ void clear_type_check_information();
+ bool check_method_stacks();
+ bool check_loaded_methods();
+ bool check_method(methodOop method);
+ static symbolOop signature_to_class_name(symbolOop signature);
+
+ void method_forwarding();
+
+ void update_array_classes_to_newest_version(klassOop smallest_dimension);
// Modifiable test must be shared between IsModifiableClass query
// and redefine implementation
static bool is_modifiable_class(oop klass_mirror);
-};
+
+ // Method used during garbage collection, the VM operation must iterate over all oops.
+ void oops_do(OopClosure* f);
+
+ // Utility methods for transfering field access flags
+
+ static void transfer_special_access_flags(fieldDescriptor *from, fieldDescriptor *to);
+ static void update_klass_field_access_flag(fieldDescriptor *fd);
+
+ void transfer_old_native_function_registrations(instanceKlassHandle the_class);
+
+ void lock_threads();
+ void unlock_threads();
+
+ template static void do_oop_work(T* p);
+
+ static void swap_marks(oop first, oop second);
+
+};
\ No newline at end of file
diff -r f5603a6e5042 src/share/vm/prims/jvmtiRedefineClassesTrace.hpp
--- a/src/share/vm/prims/jvmtiRedefineClassesTrace.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/prims/jvmtiRedefineClassesTrace.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -22,102 +22,26 @@
*
*/
-// RedefineClasses tracing support via the TraceRedefineClasses
-// option. A bit is assigned to each group of trace messages.
-// Groups of messages are individually selectable. We have to use
-// decimal values on the command line since the command option
-// parsing logic doesn't like non-decimal numerics. The HEX values
-// are used in the actual RC_TRACE() calls for sanity. To achieve
-// the old cumulative behavior, pick the level after the one in
-// which you are interested and subtract one, e.g., 33554431 will
-// print every tracing message.
-//
-// 0x00000000 | 0 - default; no tracing messages
-// 0x00000001 | 1 - name each target class before loading, after
-// loading and after redefinition is completed
-// 0x00000002 | 2 - print info if parsing, linking or
-// verification throws an exception
-// 0x00000004 | 4 - print timer info for the VM operation
-// 0x00000008 | 8 - print subclass counter updates
-// 0x00000010 | 16 - unused
-// 0x00000020 | 32 - unused
-// 0x00000040 | 64 - unused
-// 0x00000080 | 128 - unused
-// 0x00000100 | 256 - previous class weak reference addition
-// 0x00000200 | 512 - previous class weak reference mgmt during
-// class unloading checks (GC)
-// 0x00000400 | 1024 - previous class weak reference mgmt during
-// add previous ops (GC)
-// 0x00000800 | 2048 - previous class breakpoint mgmt
-// 0x00001000 | 4096 - detect calls to obsolete methods
-// 0x00002000 | 8192 - fail a guarantee() in addition to detection
-// 0x00004000 | 16384 - unused
-// 0x00008000 | 32768 - old/new method matching/add/delete
-// 0x00010000 | 65536 - impl details: CP size info
-// 0x00020000 | 131072 - impl details: CP merge pass info
-// 0x00040000 | 262144 - impl details: CP index maps
-// 0x00080000 | 524288 - impl details: modified CP index values
-// 0x00100000 | 1048576 - impl details: vtable updates
-// 0x00200000 | 2097152 - impl details: itable updates
-// 0x00400000 | 4194304 - impl details: constant pool cache updates
-// 0x00800000 | 8388608 - impl details: methodComparator info
-// 0x01000000 | 16777216 - impl details: nmethod evolution info
-// 0x02000000 | 33554432 - impl details: annotation updates
-// 0x04000000 | 67108864 - impl details: StackMapTable updates
-// 0x08000000 | 134217728 - impl details: OopMapCache updates
-// 0x10000000 | 268435456 - unused
-// 0x20000000 | 536870912 - unused
-// 0x40000000 | 1073741824 - unused
-// 0x80000000 | 2147483648 - unused
-//
-// Note: The ResourceMark is to cleanup resource allocated args.
-// The "while (0)" is so we can use semi-colon at end of RC_TRACE().
-#define RC_TRACE(level, args) \
- if ((TraceRedefineClasses & level) != 0) { \
- ResourceMark rm; \
- tty->print("RedefineClasses-0x%x: ", level); \
- tty->print_cr args; \
- } while (0)
+#define IF_TRACE_RC1 if (TraceRedefineClasses >= 1)
+#define IF_TRACE_RC2 if (TraceRedefineClasses >= 2)
+#define IF_TRACE_RC3 if (TraceRedefineClasses >= 3)
+#define IF_TRACE_RC4 if (TraceRedefineClasses >= 4)
+#define IF_TRACE_RC5 if (TraceRedefineClasses >= 5)
-#define RC_TRACE_WITH_THREAD(level, thread, args) \
- if ((TraceRedefineClasses & level) != 0) { \
- ResourceMark rm(thread); \
- tty->print("RedefineClasses-0x%x: ", level); \
- tty->print_cr args; \
- } while (0)
-
-#define RC_TRACE_MESG(args) \
- { \
- ResourceMark rm; \
- tty->print("RedefineClasses: "); \
- tty->print_cr args; \
- } while (0)
-
-// Macro for checking if TraceRedefineClasses has a specific bit
-// enabled. Returns true if the bit specified by level is set.
-#define RC_TRACE_ENABLED(level) ((TraceRedefineClasses & level) != 0)
-
-// Macro for checking if TraceRedefineClasses has one or more bits
-// set in a range of bit values. Returns true if one or more bits
-// is set in the range from low..high inclusive. Assumes that low
-// and high are single bit values.
-//
-// ((high << 1) - 1)
-// Yields a mask that removes bits greater than the high bit value.
-// This algorithm doesn't work with highest bit.
-// ~(low - 1)
-// Yields a mask that removes bits lower than the low bit value.
-#define RC_TRACE_IN_RANGE(low, high) \
-(((TraceRedefineClasses & ((high << 1) - 1)) & ~(low - 1)) != 0)
+#define TRACE_RC1 if (TraceRedefineClasses >= 1) tty->print("TraceRedefineClasses-1: "); if (TraceRedefineClasses >= 1) tty->print_cr
+#define TRACE_RC2 if (TraceRedefineClasses >= 2) tty->print(" TraceRedefineClasses-2: "); if (TraceRedefineClasses >= 2) tty->print_cr
+#define TRACE_RC3 if (TraceRedefineClasses >= 3) tty->print(" TraceRedefineClasses-3: "); if (TraceRedefineClasses >= 3) tty->print_cr
+#define TRACE_RC4 if (TraceRedefineClasses >= 4) tty->print(" TraceRedefineClasses-4: "); if (TraceRedefineClasses >= 4) tty->print_cr
+#define TRACE_RC5 if (TraceRedefineClasses >= 5) tty->print(" TraceRedefineClasses-5: "); if (TraceRedefineClasses >= 5) tty->print_cr
// Timer support macros. Only do timer operations if timer tracing
// is enabled. The "while (0)" is so we can use semi-colon at end of
// the macro.
#define RC_TIMER_START(t) \
- if (RC_TRACE_ENABLED(0x00000004)) { \
+ if (TimeRedefineClasses) { \
t.start(); \
} while (0)
#define RC_TIMER_STOP(t) \
- if (RC_TRACE_ENABLED(0x00000004)) { \
+ if (TimeRedefineClasses) { \
t.stop(); \
} while (0)
diff -r f5603a6e5042 src/share/vm/prims/methodComparator.cpp
--- a/src/share/vm/prims/methodComparator.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/prims/methodComparator.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -37,10 +37,9 @@
if (old_method->code_size() != new_method->code_size())
return false;
if (check_stack_and_locals_size(old_method, new_method) != 0) {
- // RC_TRACE macro has an embedded ResourceMark
- RC_TRACE(0x00800000, ("Methods %s non-comparable with diagnosis %d",
+ TRACE_RC4("Methods %s non-comparable with diagnosis %d",
old_method->name()->as_C_string(),
- check_stack_and_locals_size(old_method, new_method)));
+ check_stack_and_locals_size(old_method, new_method));
return false;
}
@@ -60,6 +59,37 @@
if (! args_same(c_old, c_new))
return false;
}
+
+ // (tw) Added exception table comparison to EMCP comparison
+
+ typeArrayOop ex_old = old_method->constMethod()->exception_table();
+ typeArrayOop ex_new = new_method->constMethod()->exception_table();
+
+ if (ex_old == NULL && ex_new != NULL) return false;
+ if (ex_old != NULL && ex_new == NULL) return false;
+
+ if (ex_old != NULL && ex_new != NULL && ex_old->length() == ex_new->length()) {
+ // Per entry:
+ /* start */
+ /* limit */
+ /* goto pc */
+ /* cp index */
+ for (int i=0; ilength(); i++) {
+ int old_val = ex_old->int_at(i);
+ int new_val = ex_new->int_at(i);
+ if ((i + 1) % 4 == 0) {
+ if (old_val == 0 || new_val == 0) {
+ if (old_val != new_val) return false;
+ } else if ((_old_cp->klass_at_noresolve(old_val) != _new_cp->klass_at_noresolve(new_val)))
+ return false;
+ } else {
+ if (old_val != new_val) {
+ return false;
+ }
+ }
+ }
+ }
+
return true;
}
@@ -109,10 +139,9 @@
// Now we can test all forward jumps
for (int i = 0; i < fwd_jmps.length() / 2; i++) {
if (! bci_map.old_and_new_locations_same(fwd_jmps.at(i*2), fwd_jmps.at(i*2+1))) {
- RC_TRACE(0x00800000,
- ("Fwd jump miss: old dest = %d, calc new dest = %d, act new dest = %d",
+ TRACE_RC4("Fwd jump miss: old dest = %d, calc new dest = %d, act new dest = %d",
fwd_jmps.at(i*2), bci_map.new_bci_for_old(fwd_jmps.at(i*2)),
- fwd_jmps.at(i*2+1)));
+ fwd_jmps.at(i*2+1));
return false;
}
}
diff -r f5603a6e5042 src/share/vm/prims/nativeLookup.cpp
--- a/src/share/vm/prims/nativeLookup.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/prims/nativeLookup.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -1,4 +1,4 @@
-/*
+*
* Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -24,7 +24,7 @@
# include "incls/_precompiled.incl"
# include "incls/_nativeLookup.cpp.incl"
-
+# include "incls/_jvmtiEnv.cpp.incl"
static void mangle_name_on(outputStream* st, symbolOop name, int begin, int end) {
char* bytes = (char*)name->bytes() + begin;
@@ -82,6 +82,39 @@
void JNICALL JVM_RegisterPerfMethods(JNIEnv *env, jclass perfclass);
}
+// Helper function to call redefineClasses from Java Code
+JVM_ENTRY(int, JVM_RedefineClassesHelper(JNIEnv *env, jclass cb, jclass target, jbyteArray bytes))
+ ResourceMark rm(THREAD);
+
+ JavaThread* current_thread = JavaThread::current();
+ jbyte* bytecodes = NULL;
+ const int class_count = 1;
+ jvmtiClassDefinition* class_definitions = NEW_RESOURCE_ARRAY(jvmtiClassDefinition, class_count);
+
+ {
+ ThreadToNativeFromVM ttnfv(thread);
+ bytecodes = env->GetByteArrayElements(bytes, false);
+ class_definitions[0].klass = target;
+ class_definitions[0].class_byte_count = env->GetArrayLength(bytes);
+ class_definitions[0].class_bytes = (unsigned char*)bytecodes;
+ }
+
+ VM_RedefineClasses op(class_count, class_definitions, jvmti_class_load_kind_retransform);
+ VMThread::execute(&op);
+ int result = op.check_error();
+
+ {
+ ThreadToNativeFromVM ttnfv(thread);
+ if (env->ExceptionOccurred()) {
+ return -1;
+ }
+ env->ReleaseByteArrayElements(bytes, bytecodes, 0);
+ }
+
+ return result;
+JVM_END
+
+
static address lookup_special_native(char* jni_name) {
// NB: To ignore the jni prefix and jni postfix strstr is used matching.
if (!JDK_Version::is_gte_jdk14x_version()) {
@@ -135,6 +168,9 @@
return entry;
}
}
+ if(strstr(jni_name, "Java_at_ssw_hotswap_ClassRedefinition_redefineClasses") != NULL) {
+ return CAST_FROM_FN_PTR(address, JVM_RedefineClassesHelper);
+ }
// Otherwise call static method findNative in ClassLoader
KlassHandle klass (THREAD, SystemDictionary::ClassLoader_klass());
diff -r f5603a6e5042 src/share/vm/runtime/arguments.cpp
--- a/src/share/vm/runtime/arguments.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/arguments.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -25,7 +25,7 @@
#include "incls/_precompiled.incl"
#include "incls/_arguments.cpp.incl"
-#define DEFAULT_VENDOR_URL_BUG "http://java.sun.com/webapps/bugreport/crash.jsp"
+#define DEFAULT_VENDOR_URL_BUG "http://ssw.jku.at/dcevm/bugreport/"
#define DEFAULT_JAVA_LAUNCHER "generic"
char** Arguments::_jvm_flags_array = NULL;
@@ -1704,6 +1704,15 @@
status = false;
}
+ // (tw) Must use serial GC
+ if (!UseSerialGC && i >= 1) {
+ jio_fprintf(defaultStream::error_stream(),
+ "Must use the serial GC in the Dynamic Code Evolution VM\n");
+ status = false;
+ } else {
+ UseSerialGC = true;
+ }
+
return status;
}
diff -r f5603a6e5042 src/share/vm/runtime/deoptimization.cpp
--- a/src/share/vm/runtime/deoptimization.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/deoptimization.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -513,6 +513,38 @@
// Cleanup thread deopt data
cleanup_deopt_info(thread, array);
+ // (tw) Redefinition support: Check if we need to transfer method execution points to new versions
+ {
+ ResourceMark res_mark;
+
+ // Verify that the just-unpacked frames match the interpreter's
+ // notions of expression stack and locals
+ vframeArray* cur_array = thread->vframe_array_last();
+ RegisterMap rm(thread, false);
+ rm.set_include_argument_oops(false);
+ for (int i = 0; i < cur_array->frames(); i++) {
+ vframeArrayElement* el = cur_array->element(i);
+ frame* frame = el->iframe();
+ guarantee(frame->is_interpreted_frame(), "Wrong frame type");
+ RegisterMap reg_map(thread);
+ vframe* vf = vframe::new_vframe(frame, ®_map, thread);
+ interpretedVFrame *iframe = (interpretedVFrame *)vf;
+ methodOop method = iframe->method();
+ int bci = iframe->bci();
+ method = method->newest_version();
+ iframe->set_method(method, bci);
+
+ methodOop forward_method = method->forward_method();
+ if (forward_method != NULL && method->is_in_code_section(bci)) {
+ int new_bci = method->calculate_forward_bci(bci, forward_method);
+ if (TraceRedefineClasses >= 2) {
+ tty->print_cr("Transfering execution of %s to new method old_bci=%d new_bci=%d", forward_method->name()->as_C_string(), bci, new_bci);
+ }
+ iframe->set_method(forward_method, new_bci);
+ }
+ }
+ }
+
#ifndef PRODUCT
if (VerifyStack) {
ResourceMark res_mark;
diff -r f5603a6e5042 src/share/vm/runtime/frame.cpp
--- a/src/share/vm/runtime/frame.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/frame.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -357,6 +357,12 @@
*interpreter_frame_method_addr() = method;
}
+// (tw) Sets constant pool cache oop
+void frame::interpreter_frame_set_cache(constantPoolCacheOop cp) {
+ assert(is_interpreted_frame(), "interpreted frame expected");
+ *interpreter_frame_cache_addr() = cp;
+}
+
void frame::interpreter_frame_set_bcx(intptr_t bcx) {
assert(is_interpreted_frame(), "Not an interpreted frame");
if (ProfileInterpreter) {
@@ -372,19 +378,27 @@
// The bcx was just converted from bci to bcp.
// Convert the mdx in parallel.
methodDataOop mdo = interpreter_frame_method()->method_data();
- assert(mdo != NULL, "");
- int mdi = mdx - 1; // We distinguish valid mdi from zero by adding one.
- address mdp = mdo->di_to_dp(mdi);
- interpreter_frame_set_mdx((intptr_t)mdp);
+ if (mdo == NULL) {
+ interpreter_frame_set_mdx(0);
+ } else {
+ assert(mdo != NULL, "");
+ int mdi = mdx - 1; // We distinguish valid mdi from zero by adding one.
+ address mdp = mdo->di_to_dp(mdi);
+ interpreter_frame_set_mdx((intptr_t)mdp);
+ }
}
} else {
if (is_now_bci) {
// The bcx was just converted from bcp to bci.
// Convert the mdx in parallel.
methodDataOop mdo = interpreter_frame_method()->method_data();
- assert(mdo != NULL, "");
- int mdi = mdo->dp_to_di((address)mdx);
- interpreter_frame_set_mdx((intptr_t)mdi + 1); // distinguish valid from 0.
+ if (mdo == NULL) {
+ interpreter_frame_set_mdx(0);
+ } else {
+ assert(mdo != NULL, "");
+ int mdi = mdo->dp_to_di((address)mdx);
+ interpreter_frame_set_mdx((intptr_t)mdi + 1); // distinguish valid from 0.
+ }
}
}
}
diff -r f5603a6e5042 src/share/vm/runtime/frame.hpp
--- a/src/share/vm/runtime/frame.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/frame.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -295,6 +295,7 @@
// Method & constant pool cache
methodOop interpreter_frame_method() const;
void interpreter_frame_set_method(methodOop method);
+ void interpreter_frame_set_cache(constantPoolCacheOop method);
methodOop* interpreter_frame_method_addr() const;
constantPoolCacheOop* interpreter_frame_cache_addr() const;
#ifdef PPC
diff -r f5603a6e5042 src/share/vm/runtime/globals.hpp
--- a/src/share/vm/runtime/globals.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/globals.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -1127,9 +1127,23 @@
product(bool, StressLdcRewrite, false, \
"Force ldc -> ldc_w rewrite during RedefineClasses") \
\
+ product(bool, UseMethodForwardPoints, false, \
+ "Use method forward points") \
+ \
+ product(intx, MethodForwardPointsMaxLocals, 300, \
+ "Maximum number of locals in forwarding method") \
+ \
+ product(intx, MethodForwardPointsMaxStack, 300, \
+ "Maximum number of stack slots in forwarding method") \
+ \
product(intx, TraceRedefineClasses, 0, \
"Trace level for JVMTI RedefineClasses") \
\
+ product(bool, TimeRedefineClasses, false, \
+ "Measure timing for JVMTI RedefineClasses") \
+ \
+ product(bool, AllowAdvancedClassRedefinition, true, \
+ "Allow advanced class redefinition beyond swapping method bodies")\
develop(bool, StressMethodComparator, false, \
"run the MethodComparator on all loaded methods") \
\
diff -r f5603a6e5042 src/share/vm/runtime/interfaceSupport.hpp
--- a/src/share/vm/runtime/interfaceSupport.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/interfaceSupport.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -257,7 +257,7 @@
ThreadToNativeFromVM(JavaThread *thread) : ThreadStateTransition(thread) {
// We are leaving the VM at this point and going directly to native code.
// Block, if we are in the middle of a safepoint synchronization.
- assert(!thread->owns_locks(), "must release all locks when leaving VM");
+ assert(!thread->owns_locks_but_redefine_classes_lock(), "must release all locks when leaving VM");
thread->frame_anchor()->make_walkable(thread);
trans_and_fence(_thread_in_vm, _thread_in_native);
// Check for pending. async. exceptions or suspends.
diff -r f5603a6e5042 src/share/vm/runtime/javaCalls.cpp
--- a/src/share/vm/runtime/javaCalls.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/javaCalls.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -33,7 +33,7 @@
bool clear_pending_exception = true;
guarantee(thread->is_Java_thread(), "crucial check - the VM thread cannot and must not escape to Java code");
- assert(!thread->owns_locks(), "must release all locks when leaving VM");
+ assert(!thread->owns_locks_but_redefine_classes_lock(), "must release all locks when leaving VM");
guarantee(!thread->is_Compiler_thread(), "cannot make java calls from the compiler");
_result = result;
diff -r f5603a6e5042 src/share/vm/runtime/jniHandles.cpp
--- a/src/share/vm/runtime/jniHandles.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/jniHandles.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -96,6 +96,10 @@
}
jmethodID JNIHandles::make_jmethod_id(methodHandle mh) {
+ if (mh->newest_version() != mh()) {
+ methodHandle mh_new(Thread::current(), mh()->newest_version());
+ return (jmethodID) make_weak_global(mh_new);
+ }
return (jmethodID) make_weak_global(mh);
}
diff -r f5603a6e5042 src/share/vm/runtime/mutex.cpp
--- a/src/share/vm/runtime/mutex.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/mutex.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -1195,7 +1195,7 @@
// in increasing rank order (modulo any native ranks)
for (tmp = locks; tmp != NULL; tmp = tmp->next()) {
if (tmp->next() != NULL) {
- assert(tmp->rank() == Mutex::native ||
+ assert(tmp->rank() == Mutex::native || tmp->rank() == Mutex::redefine_classes ||
tmp->rank() <= tmp->next()->rank(), "mutex rank anomaly?");
}
}
@@ -1215,7 +1215,7 @@
// in increasing rank order (modulo any native ranks)
for (tmp = locks; tmp != NULL; tmp = tmp->next()) {
if (tmp->next() != NULL) {
- assert(tmp->rank() == Mutex::native ||
+ assert(tmp->rank() == Mutex::native || tmp->rank() == Mutex::redefine_classes ||
tmp->rank() <= tmp->next()->rank(), "mutex rank anomaly?");
}
}
@@ -1282,6 +1282,7 @@
// already hold Terminator_lock - may happen because of periodic safepoints
if (this->rank() != Mutex::native &&
this->rank() != Mutex::suspend_resume &&
+ this->rank() != Mutex::redefine_classes &&
locks != NULL && locks->rank() <= this->rank() &&
!SafepointSynchronize::is_at_safepoint() &&
this != Interrupt_lock && this != ProfileVM_lock &&
diff -r f5603a6e5042 src/share/vm/runtime/mutex.hpp
--- a/src/share/vm/runtime/mutex.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/mutex.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -102,7 +102,8 @@
barrier = safepoint + 1,
nonleaf = barrier + 1,
max_nonleaf = nonleaf + 900,
- native = max_nonleaf + 1
+ native = max_nonleaf + 1,
+ redefine_classes = native + 1
};
// The WaitSet and EntryList linked lists are composed of ParkEvents.
diff -r f5603a6e5042 src/share/vm/runtime/mutexLocker.cpp
--- a/src/share/vm/runtime/mutexLocker.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/mutexLocker.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -34,6 +34,7 @@
// Consider using GCC's __read_mostly.
Mutex* Patching_lock = NULL;
+Mutex* RedefineClasses_lock = NULL;
Monitor* SystemDictionary_lock = NULL;
Mutex* PackageTable_lock = NULL;
Mutex* CompiledIC_lock = NULL;
@@ -77,6 +78,7 @@
Mutex* DirtyCardQ_FL_lock = NULL;
Monitor* DirtyCardQ_CBL_mon = NULL;
Mutex* Shared_DirtyCardQ_lock = NULL;
+Monitor* RedefinitionSync_lock = NULL;
Mutex* ParGCRareEvent_lock = NULL;
Mutex* EvacFailureStack_lock = NULL;
Mutex* DerivedPointerTableGC_lock = NULL;
@@ -180,6 +182,7 @@
def(HotCardCache_lock , Mutex , special , true );
def(EvacFailureStack_lock , Mutex , nonleaf , true );
}
+ def(RedefinitionSync_lock , Monitor , leaf , false );
def(ParGCRareEvent_lock , Mutex , leaf , true );
def(DerivedPointerTableGC_lock , Mutex, leaf, true );
def(CodeCache_lock , Mutex , special, true );
@@ -253,6 +256,7 @@
def(Debug2_lock , Mutex , nonleaf+4, true );
def(Debug3_lock , Mutex , nonleaf+4, true );
def(ProfileVM_lock , Monitor, nonleaf+4, false); // used for profiling of the VMThread
+ def(RedefineClasses_lock , Mutex, nonleaf+7, false ); // for ensuring that class redefinition is not done in parallel
def(CompileThread_lock , Monitor, nonleaf+5, false );
}
diff -r f5603a6e5042 src/share/vm/runtime/mutexLocker.hpp
--- a/src/share/vm/runtime/mutexLocker.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/mutexLocker.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -25,6 +25,8 @@
// Mutexes used in the VM.
extern Mutex* Patching_lock; // a lock used to guard code patching of compiled code
+extern Monitor* RedefinitionSync_lock; // a lock on synchronized class redefinition
+extern Mutex* RedefineClasses_lock; // a lock on class redefinition
extern Monitor* SystemDictionary_lock; // a lock on the system dictonary
extern Mutex* PackageTable_lock; // a lock on the class loader package table
extern Mutex* CompiledIC_lock; // a lock used to guard compiled IC patching and access
diff -r f5603a6e5042 src/share/vm/runtime/reflection.cpp
--- a/src/share/vm/runtime/reflection.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/reflection.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -449,7 +449,8 @@
// sun/reflect/MagicAccessorImpl subclasses to succeed trivially.
if ( JDK_Version::is_gte_jdk14x_version()
&& UseNewReflection
- && Klass::cast(current_class)->is_subclass_of(SystemDictionary::reflect_MagicAccessorImpl_klass())) {
+ && (Klass::cast(current_class)->is_subclass_of(SystemDictionary::reflect_MagicAccessorImpl_klass()) ||
+ Klass::cast(current_class)->is_subclass_of(SystemDictionary::reflect_MagicAccessorImpl_klass()->klass_part()->newest_version()))) {
return true;
}
@@ -505,6 +506,12 @@
AccessFlags access,
bool classloader_only,
bool protected_restriction) {
+
+ // (tw) Decide accessibility based on active version
+ if (current_class != NULL) {
+ current_class = current_class->klass_part()->active_version();
+ }
+
// Verify that current_class can access a field of field_class, where that
// field's access bits are "access". We assume that we've already verified
// that current_class can access field_class.
@@ -546,7 +553,8 @@
// sun/reflect/MagicAccessorImpl subclasses to succeed trivially.
if ( JDK_Version::is_gte_jdk14x_version()
&& UseNewReflection
- && Klass::cast(current_class)->is_subclass_of(SystemDictionary::reflect_MagicAccessorImpl_klass())) {
+ && (Klass::cast(current_class)->is_subclass_of(SystemDictionary::reflect_MagicAccessorImpl_klass()) ||
+ Klass::cast(current_class)->is_subclass_of(SystemDictionary::reflect_MagicAccessorImpl_klass()->klass_part()->newest_version()))) {
return true;
}
diff -r f5603a6e5042 src/share/vm/runtime/sharedRuntime.cpp
--- a/src/share/vm/runtime/sharedRuntime.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/sharedRuntime.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -511,21 +511,13 @@
//
JRT_LEAF(int, SharedRuntime::rc_trace_method_entry(
JavaThread* thread, methodOopDesc* method))
- assert(RC_TRACE_IN_RANGE(0x00001000, 0x00002000), "wrong call");
+ assert(TraceRedefineClasses >= 4, "wrong call");
if (method->is_obsolete()) {
// We are calling an obsolete method, but this is not necessarily
// an error. Our method could have been redefined just after we
// fetched the methodOop from the constant pool.
-
- // RC_TRACE macro has an embedded ResourceMark
- RC_TRACE_WITH_THREAD(0x00001000, thread,
- ("calling obsolete method '%s'",
- method->name_and_sig_as_C_string()));
- if (RC_TRACE_ENABLED(0x00002000)) {
- // this option is provided to debug calls to obsolete methods
- guarantee(false, "faulting at call to an obsolete method.");
- }
+ TRACE_RC4("calling obsolete method '%s'", method->name_and_sig_as_C_string());
}
return 0;
JRT_END
@@ -986,7 +978,20 @@
if (JvmtiExport::can_hotswap_or_post_breakpoint()) {
int retry_count = 0;
while (!HAS_PENDING_EXCEPTION && callee_method->is_old() &&
- callee_method->method_holder() != SystemDictionary::Object_klass()) {
+ callee_method->method_holder()->klass_part()->newest_version() != SystemDictionary::Object_klass()->klass_part()->newest_version()) {
+
+ // (tw) If we are executing an old method, this is OK!
+ {
+ ResourceMark rm(thread);
+ RegisterMap cbl_map(thread, false);
+ frame caller_frame = thread->last_frame().sender(&cbl_map);
+
+ CodeBlob* caller_cb = caller_frame.cb();
+ guarantee(caller_cb != NULL && caller_cb->is_nmethod(), "must be called from nmethod");
+ nmethod* caller_nm = caller_cb->as_nmethod_or_null();
+ if (caller_nm->method()->is_old()) break;
+ }
+
// If has a pending exception then there is no need to re-try to
// resolve this method.
// If the method has been redefined, we need to try again.
diff -r f5603a6e5042 src/share/vm/runtime/thread.cpp
--- a/src/share/vm/runtime/thread.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/thread.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -109,6 +109,8 @@
_lgrp_id = -1;
_osthread = NULL;
+ _redefine_classes_mutex = new Mutex(Mutex::redefine_classes, "redefine classes lock", false);
+
// allocated data structures
set_resource_area(new ResourceArea());
set_handle_area(new HandleArea(NULL));
@@ -141,6 +143,7 @@
omFreeProvision = 32 ;
omInUseList = NULL ;
omInUseCount = 0 ;
+ _pretend_new_universe = false;
_SR_lock = new Monitor(Mutex::suspend_resume, "SR_lock", true);
_suspend_flags = 0;
@@ -747,6 +750,15 @@
return false;
}
+bool Thread::owns_locks_but_redefine_classes_lock() const {
+ for(Monitor *cur = _owned_locks; cur; cur = cur->next()) {
+ if (cur != RedefineClasses_lock && cur->rank() != Mutex::redefine_classes) {
+ return true;
+ }
+ }
+ return false;
+}
+
#endif
@@ -1397,7 +1409,7 @@
ThreadStateTransition::transition_and_fence(this, _thread_new, _thread_in_vm);
assert(JavaThread::current() == this, "sanity check");
- assert(!Thread::current()->owns_locks(), "sanity check");
+ assert(!Thread::current()->owns_locks_but_redefine_classes_lock(), "sanity check");
DTRACE_THREAD_PROBE(start, this);
@@ -2842,13 +2854,14 @@
// Create a CompilerThread
CompilerThread::CompilerThread(CompileQueue* queue, CompilerCounters* counters)
-: JavaThread(&compiler_thread_entry) {
+: JavaThread(&compiler_thread_entry), _should_bailout(false) {
_env = NULL;
_log = NULL;
_task = NULL;
_queue = queue;
_counters = counters;
_buffer_blob = NULL;
+ _compilation_mutex = new Mutex(Mutex::redefine_classes, "compilationMutex", false);
#ifndef PRODUCT
_ideal_graph_printer = NULL;
@@ -2869,6 +2882,7 @@
int Threads::_number_of_non_daemon_threads = 0;
int Threads::_return_code = 0;
size_t JavaThread::_stack_size_at_create = 0;
+bool Threads::_wait_at_instrumentation_entry = false;
// All JavaThreads
#define ALL_JAVA_THREADS(X) for (JavaThread* X = _thread_list; X; X = X->next())
diff -r f5603a6e5042 src/share/vm/runtime/thread.hpp
--- a/src/share/vm/runtime/thread.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/thread.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -162,11 +162,14 @@
void enter_signal_handler() { _num_nested_signal++; }
void leave_signal_handler() { _num_nested_signal--; }
bool is_inside_signal_handler() const { return _num_nested_signal > 0; }
+ Mutex* redefine_classes_mutex() { return _redefine_classes_mutex; }
private:
// Debug tracing
static void trace(const char* msg, const Thread* const thread) PRODUCT_RETURN;
+ Mutex* _redefine_classes_mutex;
+
// Active_handles points to a block of handles
JNIHandleBlock* _active_handles;
@@ -438,10 +441,15 @@
uintptr_t _self_raw_id; // used by get_thread (mutable)
int _lgrp_id;
+
+ bool _pretend_new_universe;
+
public:
// Stack overflow support
address stack_base() const { assert(_stack_base != NULL,"Sanity check"); return _stack_base; }
+ void set_pretend_new_universe(bool b) { if (_pretend_new_universe != b) { if (TraceRedefineClasses >= 5) tty->print_cr("Changing pretend universe to %d", (int)b); _pretend_new_universe = b; } }
+ bool pretend_new_universe() { return _pretend_new_universe; }
void set_stack_base(address base) { _stack_base = base; }
size_t stack_size() const { return _stack_size; }
void set_stack_size(size_t size) { _stack_size = size; }
@@ -476,6 +484,7 @@
void print_owned_locks() const { print_owned_locks_on(tty); }
Monitor * owned_locks() const { return _owned_locks; }
bool owns_locks() const { return owned_locks() != NULL; }
+ bool owns_locks_but_redefine_classes_lock() const;
bool owns_locks_but_compiled_lock() const;
// Deadlock detection
@@ -1611,6 +1620,8 @@
CompileTask* _task;
CompileQueue* _queue;
BufferBlob* _buffer_blob;
+ bool _should_bailout;
+ Mutex* _compilation_mutex;
public:
@@ -1618,12 +1629,16 @@
CompilerThread(CompileQueue* queue, CompilerCounters* counters);
+ bool should_bailout() const { return _should_bailout; }
+ void set_should_bailout(bool b) { _should_bailout = false; }
+
bool is_Compiler_thread() const { return true; }
// Hide this compiler thread from external view.
bool is_hidden_from_external_view() const { return true; }
CompileQueue* queue() { return _queue; }
CompilerCounters* counters() { return _counters; }
+ Mutex *compilation_mutex() { return _compilation_mutex; }
// Get/set the thread's compilation environment.
ciEnv* env() { return _env; }
@@ -1667,6 +1682,7 @@
static int _number_of_threads;
static int _number_of_non_daemon_threads;
static int _return_code;
+ static bool _wait_at_instrumentation_entry;
public:
// Thread management
@@ -1678,6 +1694,9 @@
static JavaThread* first() { return _thread_list; }
static void threads_do(ThreadClosure* tc);
+ static bool wait_at_instrumentation_entry() { return _wait_at_instrumentation_entry; }
+ static void set_wait_at_instrumentation_entry(bool b) { _wait_at_instrumentation_entry = b; }
+
// Initializes the vm and creates the vm thread
static jint create_vm(JavaVMInitArgs* args, bool* canTryAgain);
static void convert_vm_init_libraries_to_agents();
diff -r f5603a6e5042 src/share/vm/runtime/vframe.cpp
--- a/src/share/vm/runtime/vframe.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/vframe.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -232,6 +232,46 @@
return fr().interpreter_frame_method();
}
+// (tw) Sets interpreter frame method.
+void interpretedVFrame::set_method(methodOop new_method, int new_bci) {
+ methodOop old_method = fr().interpreter_frame_method();
+ int old_stack_size = fr().interpreter_frame_expression_stack_size();
+ if (old_method == new_method) return;
+ u_char *old_bcp = bcp();
+ int old_bci = bci();
+ fr().interpreter_frame_set_method(new_method);
+ fr().interpreter_frame_set_cache(new_method->constants()->cache());
+ u_char *new_bcp = new_method->code_base() + new_bci;
+ assert(new_method->bcp_from(new_bci) == new_bcp, "");
+
+ set_bcp(new_bcp);
+
+ Bytecodes::Code code = Bytecodes::java_code_at(old_bcp);
+ assert(Bytecodes::java_code_at(new_bcp) == code, "must have same bytecode at this position");
+
+ switch (code) {
+ case Bytecodes::_invokevirtual :
+ case Bytecodes::_invokespecial :
+ case Bytecodes::_invokestatic :
+ case Bytecodes::_invokeinterface: {
+ int old_index = Bytes::get_native_u2(old_bcp+1);
+ int new_index = Bytes::get_native_u2(new_bcp+1);
+ new_method->constants()->cache()->entry_at(new_index)->copy_from(old_method->constants()->cache()->entry_at(old_index));
+ break;
+ }
+
+ case Bytecodes::_invokedynamic: {
+ int old_index = Bytes::get_native_u4(old_bcp+1);
+ int new_index = Bytes::get_native_u4(new_bcp+1);
+ new_method->constants()->cache()->secondary_entry_at(new_index)->copy_from(old_method->constants()->cache()->secondary_entry_at(old_index));
+ break;
+ }
+ }
+
+ int new_stack_size = fr().interpreter_frame_expression_stack_size();
+ assert(new_method->validate_bci_from_bcx((intptr_t)new_bcp) == new_bci, "");
+}
+
StackValueCollection* interpretedVFrame::locals() const {
int length = method()->max_locals();
diff -r f5603a6e5042 src/share/vm/runtime/vframe.hpp
--- a/src/share/vm/runtime/vframe.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/vframe.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -150,6 +150,7 @@
StackValueCollection* locals() const;
StackValueCollection* expressions() const;
GrowableArray* monitors() const;
+ void set_method(methodOop method, int new_bci);
void set_locals(StackValueCollection* values) const;
diff -r f5603a6e5042 src/share/vm/runtime/vmThread.cpp
--- a/src/share/vm/runtime/vmThread.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/vmThread.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -624,6 +624,9 @@
void VMThread::oops_do(OopClosure* f, CodeBlobClosure* cf) {
Thread::oops_do(f, cf);
_vm_queue->oops_do(f);
+ if (_cur_vm_operation != NULL) {
+ _cur_vm_operation->oops_do(f);
+ }
}
//------------------------------------------------------------------------------------------------------------------
diff -r f5603a6e5042 src/share/vm/runtime/vm_version.cpp
--- a/src/share/vm/runtime/vm_version.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/runtime/vm_version.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -40,10 +40,11 @@
#error HOTSPOT_BUILD_TARGET must be defined
#endif
+// (tw) added DCE VM build number
#ifdef PRODUCT
- #define VM_RELEASE HOTSPOT_RELEASE_VERSION
+ #define VM_RELEASE "0.2-b02-internal, " HOTSPOT_RELEASE_VERSION
#else
- #define VM_RELEASE HOTSPOT_RELEASE_VERSION "-" HOTSPOT_BUILD_TARGET
+ #define VM_RELEASE "0.2-b02-internal-debug, " HOTSPOT_RELEASE_VERSION "-" HOTSPOT_BUILD_TARGET
#endif
// HOTSPOT_RELEASE_VERSION must follow the release version naming convention
@@ -110,6 +111,12 @@
#ifndef HOTSPOT_VM_DISTRO
#error HOTSPOT_VM_DISTRO must be defined
#endif
+
+// (tw) Set VM name to DCE
+#undef HOTSPOT_VM_DISTRO
+#define HOTSPOT_VM_DISTRO "Dynamic Code Evolution"
+#define VM_RELEASE_HOTSP
+
#define VMNAME HOTSPOT_VM_DISTRO " " VMLP VMTYPE " VM"
const char* Abstract_VM_Version::vm_name() {
diff -r f5603a6e5042 src/share/vm/utilities/exceptions.cpp
--- a/src/share/vm/utilities/exceptions.cpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/utilities/exceptions.cpp Fri Dec 17 13:23:04 2010 +0100
@@ -239,6 +239,8 @@
assert(thread->is_Java_thread(), "can only be called by a Java thread");
assert(!thread->has_pending_exception(), "already has exception");
+ bool old_pretend_value = Thread::current()->pretend_new_universe();
+ Thread::current()->set_pretend_new_universe(false);
Handle h_exception;
// Resolve exception klass
@@ -286,6 +288,8 @@
h_exception = Handle(thread, thread->pending_exception());
thread->clear_pending_exception();
}
+
+ Thread::current()->set_pretend_new_universe(old_pretend_value);
return h_exception;
}
diff -r f5603a6e5042 src/share/vm/utilities/growableArray.hpp
--- a/src/share/vm/utilities/growableArray.hpp Wed Nov 17 22:42:08 2010 -0800
+++ b/src/share/vm/utilities/growableArray.hpp Fri Dec 17 13:23:04 2010 +0100
@@ -124,6 +124,33 @@
assert(on_stack(), "fast ResourceObj path only");
return (void*)resource_allocate_bytes(thread, elementSize * _max);
}
+
+};
+
+template class Pair : public StackObj
+{
+private:
+ E _left;
+ F _right;
+
+public:
+
+ Pair() {
+
+ }
+
+ Pair(E left, F right) {
+ this->_left = left;
+ this->_right = right;
+ }
+
+ E left() {
+ return _left;
+ }
+
+ F right() {
+ return _right;
+ }
};
template class GrowableArray : public GenericGrowableArray {