4

I am trying to do the same thing in Java 11 that could be done with -Xbootclasspath/p:path in pre java 9.

As a simple example I modified one of the valueOf methods of java.lang.Integer and compiled the project with:

javac --module-source-path=src/java.base --patch-module java.base=src/java.base -d mods $(find src -name '*.java')

I then ran a simple sample using:

java --patch-module java.base=<pathToMyModifiedJavaBaseClasses> -p lib -m my.moduleA/my.moduleA.Main

That worked an I'm seeing the modifications displayed (a simple print out I did from valueOf).

When I try, however, to do the same thing with java.lang.ClassLoader I get the following error when executing the program (compile works):

Error occurred during initialization of boot layer java.lang.LinkageError: loader 'bootstrap' attempted duplicate class definition for java.lang.invoke.SimpleMethodHandle.

I do not even need to make changes in java.lang.ClassLoader. The sheer existence of that class in my patch folder seems to be provocing this error. (I only wanted to add a field though at the bottom of the class)

NOTE: I just figured that it works when the ClassLoader class is compiled with Eclipse. One of the few differences I know is that the Eclipse compiler does not seem to follow JEP 280 yet. But there are invokedynamic instructions in the bytecode resulting from javac as well, so I doubt that this is the problem.

Haasip Satang
  • 457
  • 3
  • 17
  • Dp share what change have you made to the `ClassLoader` with which you're trying to patch the existing module. – Naman Feb 11 '19 at 17:16
  • @nullpointer I tried various changes, originally I wanted to simply add a field and change something in loadClass. But I just figured that only having the class in the folder is enough already to get this error, no changes needed. – Haasip Satang Feb 11 '19 at 17:32
  • @ZhekaKozlov Thanks for commenting but I believe you are talking about the former `java.endorsed.dirs` functionally, aren't you? I didn't read nowhere that patching java.base is not allowed. Matter of fact is it even works depending on the compiler being used as you can see from the description above. So I'd appreciate if you could share some technical details or link to a spec confirming this "you should not...". – Haasip Satang Feb 12 '19 at 11:02
  • Sorry, forget what I said. `java.base` is indeed not an upgradeable module but this has nothing to do with patching. I confused these two unrelated things. You are allowed to patch `java.base` if you want. – ZhekaKozlov Feb 12 '19 at 12:23

1 Answers1

5

You did already point into the right direction. It works when you compile the class with your current version of Eclipse, because that compiler does not follow JEP 280 yet, so it doesn’t use invokedynamic for string concatenation.

This does not imply that using invokedynamic in ClassLoader is problematic in general. It is only problematic in certain critical code paths executed during the bootstrapping of the java.lang.invoke package and apparently, this class does use string concatenation on this code path.

In case of javac, you can force the use of the old string concatenation code via the option
-XDstringConcat=inline. Looking into the bytecode of the ClassLoader.class as shipped with the JDK, it seems this class has been compiled with this option. In fact, looking at some samples, it seems the entire java.base module has been compiled with that option, in contrast to, e.g. java.desktop, whose classes use invokedynamic for string concatenation.

So the conclusion is, to patch classes in the java.base module (in OpenJDK and derivatives), compile them using the -XDstringConcat=inline option when using javac.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • Compiling with `-XDstringConcat=inline` works. I didn't think that was the only reason since I saw other `invokedynamic` instructions in `ClassLoader`. But as you pointed out they are not executed during bootstrapping and hence do not cause any issue. Thanks. – Haasip Satang Feb 12 '19 at 20:36
  • 3
    Indeed, this flag seems to be added specifically for the java.base module (as well as a couple of other modules): http://hg.openjdk.java.net/jdk/jdk/file/fedc89081b57/make/CompileJavaModules.gmk#l41 – Jorn Vernee Feb 12 '19 at 23:13
  • @Holger: One more question related to patching: Which IDE do you use? Eclipse does not seem to have good support for patching modules yet (at least not when it comes to patching java.base). It shows errors like classes or fields cannot be resolved (but then compiles it anyway), code completion does not work when trying to access a new member added to a patched class from another patched class, etc. All up anyting but really usable yet IMHO. – Haasip Satang Feb 12 '19 at 23:57
  • Unfortunately, I can not recommend any. I’m mostly using Eclipse with non-modular software. I’m also using Netbeans, but I’m not happy with its quality, especially since maintained by the Apache Foundation. I’m still hoping for a better future release, but perhaps, I’ll give IntelliJ a try. As said, at the moment, it’s not my main focus, so I can’t give a substantiated advice. – Holger Feb 13 '19 at 08:31
  • @Holger ok, thanks. Same here, mainly Eclipse. But with modules (especially patches) it's still bad. Just opened a [bug](https://bugs.eclipse.org/bugs/show_bug.cgi?id=544424) – Haasip Satang Feb 13 '19 at 21:59
  • @HaasipSatang There are some warts with eclipse & patching & launching, but for development it is fine. Make sure that: 1) The compiler doesn't use the --release option. 2) The project used to patch is used to patch the target module (Build Path -> Module Dependencies -> Patch with...) 3) does not contain the main method (eclipse doesn't like that). Create an extra project for the main. – Johannes Kuhn Apr 03 '23 at 09:05