diff --git a/manifold-core-parent/manifold/src/main/java/manifold/internal/javac/ClassSymbols.java b/manifold-core-parent/manifold/src/main/java/manifold/internal/javac/ClassSymbols.java index 2a615ef7e..82ee574e8 100644 --- a/manifold-core-parent/manifold/src/main/java/manifold/internal/javac/ClassSymbols.java +++ b/manifold-core-parent/manifold/src/main/java/manifold/internal/javac/ClassSymbols.java @@ -20,6 +20,7 @@ import com.sun.tools.javac.api.BasicJavacTask; import com.sun.tools.javac.api.JavacTool; import com.sun.tools.javac.api.JavacTrees; +import com.sun.tools.javac.code.Source; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.tree.JCTree; @@ -29,17 +30,10 @@ import java.io.IOException; import java.io.StringWriter; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -import javax.tools.DiagnosticListener; -import javax.tools.JavaFileManager; -import javax.tools.JavaFileObject; -import javax.tools.StandardJavaFileManager; -import javax.tools.StandardLocation; +import javax.tools.*; import com.sun.tools.javac.util.Options; import manifold.api.fs.IResource; @@ -91,6 +85,7 @@ private ClassSymbols( IModule module ) StringWriter errors = new StringWriter(); BasicJavacTask task = (BasicJavacTask)_javacTool.getTask( errors, _fm, null, makeJavacArgs(), null, null ); + pushModuleSymbol( task ); if( errors.getBuffer().length() > 0 ) { // report errors to console @@ -108,13 +103,7 @@ private ClassSymbols( IModule module ) BasicJavacTask task = (BasicJavacTask)_javacTool.getTask( errors, _wfm, null, makeJavacArgs(), null, null ); if( _wfm instanceof ManifoldJavaFileManager ) { - if( JreUtil.isJava9orLater() ) - { - // Java 9+ requires a module when resolving a symbol, it's always 'noModule' with this particular java task - Stack stack = new Stack(); - stack.push( ReflectUtil.field( Symtab.instance( task.getContext() ), "noModule" ).get() ); - task.getContext().put( ManifoldJavaFileManager.MODULE_CTX, stack ); - } + pushModuleSymbol( task ); ((ManifoldJavaFileManager)_wfm).setContext( task.getContext() ); } @@ -127,32 +116,51 @@ private ClassSymbols( IModule module ) } ); } + private static void pushModuleSymbol( BasicJavacTask task ) + { + if( JreUtil.isJava8() ) + { + return; + } + + // module to be in task's context for JavaDynamicJdk_XX#getTypeElement + String moduleFieldName = JreUtil.isJava9NoModule( JavacPlugin.instance().getContext() ) + ? "noModule" + : "unnamedModule"; + + Context ctx = task.getContext(); + /*ModuleSymbol*/ Object moduleSym; + moduleSym = ReflectUtil.field( Symtab.instance( ctx ), moduleFieldName ).get(); + + Stack stack = new Stack<>(); + stack.push( moduleSym ); + task.getContext().put( ManifoldJavaFileManager.MODULE_CTX, stack ); + } + private List makeJavacArgs() { return new ArrayList() {{ add( "-proc:none" ); add( "-Xprefer:source" ); + add( "-implicit:none" ); + + // Must work with types compatible with the originating compiler's settings (-source/-target/--release). + // Particularly with the --release option, because it produces a class symbol that must reflect the classes available + // in the originating JDK e.g., the structure of java.lang.String (super types, method parameters, etc.) must be + // available in the originating JDK. Otherwise, if we deliver java.lang.String with JDK 21's new super types to + // the originating JDK that is --release 17, it won't compile. - // add "--release 8" if compiling say from Java 11 but targeting Java 8 with "--release 8" which runs against the - // actual Java 8 JRE classes, not the Java 11 ones. Otherwise, there is trouble if we bring in JRE 11 sources here, - // which have Java 11 features that won't compile with source level 8. JavacPlugin javacPlugin = JavacPlugin.instance(); Options options = javacPlugin == null ? null : Options.instance( javacPlugin.getContext() ); String release = options == null ? null : options.get( "--release" ); - if( release == null && JreUtil.isJava9orLater() && options != null ) // must be *building* with Java 9+ - { - release = options.get( "-target" ); - release = release == null ? options.get( "--target" ) : release; - } - if( "8".equals( release ) || "1.8".equals( release ) ) + if( release != null ) { add( "--release" ); - add( "8" ); + add( release ); } - else + else if( javacPlugin != null ) { - add( "-source" ); add( "1.8" ); -// much wrath: add( "-source" ); add( javacPlugin == null ? "1.8" : Source.instance( javacPlugin.getContext() ).name ); + add( "-source" ); add( Source.instance( javacPlugin.getContext() ).name ); } }}; } @@ -182,7 +190,7 @@ private void init() { fm.setLocation( StandardLocation.PLATFORM_CLASS_PATH, extendBootclasspath( fm.getLocation( StandardLocation.PLATFORM_CLASS_PATH ) ) ); } - _fm = fm; + _fm = new MyFm( fm, new Context() ); } catch( IOException e ) { @@ -191,6 +199,26 @@ private void init() } } + static class MyFm extends JavacFileManagerBridge + { + protected MyFm( JavaFileManager fileManager, Context ctx ) + { + super( fileManager, ctx ); + } + + @Override + public JavaFileObject getJavaFileForInput( Location location, String className, JavaFileObject.Kind kind ) throws IOException + { + if( className.contains( "module-info" ) ) + { + // The Java tasks used in here do not need to be modular, keep javac from finding module-info.java classes. + // Note, this is key to making extension classes etc. work with --release mode. + return null; + } + return super.getJavaFileForInput( location, className, kind ); + } + } + private Iterable extendBootclasspath( Iterable existing ) { if( JavacPlugin.instance() == null ) diff --git a/manifold-core-parent/manifold/src/main/java/manifold/internal/javac/JavacPlugin.java b/manifold-core-parent/manifold/src/main/java/manifold/internal/javac/JavacPlugin.java index f8c40f236..4056a17fa 100644 --- a/manifold-core-parent/manifold/src/main/java/manifold/internal/javac/JavacPlugin.java +++ b/manifold-core-parent/manifold/src/main/java/manifold/internal/javac/JavacPlugin.java @@ -24,7 +24,6 @@ import com.sun.source.util.TaskEvent; import com.sun.source.util.TaskListener; import com.sun.tools.javac.api.BasicJavacTask; -import com.sun.tools.javac.code.Source; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.comp.*; @@ -195,42 +194,6 @@ public void init( JavacTask task, String... args ) hijackJavacFileManager(); overrideJavacToolEnter(); task.addTaskListener( this ); - warnings(); - } - - private void warnings() - { - sourceWarning(); - } - - // Is the --source JDK lower than the compiling JDK? - // If so, report a warning if manifold-ext is in use and if the compiling JDK is 17+. Because extension classes on JDK - // classes such as String may have issues because although the --source is specified the compiler still loads the String - // class directly from the compiling JDK, which includes whatever new changes that may be in String, mainly super types - // type refs elsewhere in the class. You see, --source only prevents compiling source code from referencing types, methods, fields, etc. - // from the JDK class that are not in the older --source version of the class -- it is just the newer type information that the - // compiler prohibits the use of, but the String type itself is still there with all the new stuff in it. Because a - // manifold extension on String loads the type to generate the stub source, the generated class has all the new type info - // in it, regardless of whatever --source may be. - private void sourceWarning() - { - if( !isExtensionsEnabled() || !JreUtil.isJava9orLater() ) - { - return; - } - - String source = Source.instance( getContext() ).name; - int sourceValue = new BigDecimal( source ).intValue(); - if( sourceValue < JreUtil.JAVA_VERSION ) - { - getIssueReporter().reportWarning( - "\n" + - "Usage of `manifold-ext` compiling with JDK " + JreUtil.JAVA_VERSION + " may not support source version " + source + " if\n" + - "your application uses extensions on JRE classes like String, List, etc. Generally,\n" + - "with JDK 11 or greater it is best to compile --source X with JDK X. If you experience\n" + - "compile errors corresponding with JRE classes, please consider changing your compiler\n" + - "to JDK " + source + " or changing --source to " + JreUtil.JAVA_VERSION + ". Otherwise, ignore this message.\n" ); - } } private void overrideJavacToolEnter() diff --git a/manifold-util/src/main/java/manifold/util/JreUtil.java b/manifold-util/src/main/java/manifold/util/JreUtil.java index b0c372bf3..9ab513978 100644 --- a/manifold-util/src/main/java/manifold/util/JreUtil.java +++ b/manifold-util/src/main/java/manifold/util/JreUtil.java @@ -23,6 +23,7 @@ public class JreUtil public static final int JAVA_VERSION = getJavaVersion(); private static Boolean _modular; private static Boolean _modularRuntime; + private static Boolean _noModule; private static int getJavaVersion() { @@ -178,6 +179,25 @@ public static boolean isJava9Modular_compiler( Object/*Context*/ ctx ) return _modular; } + public static boolean isJava9NoModule( Object/*Context*/ ctx ) + { + if( _noModule == null ) + { + if( isJava8() ) + { + _noModule = true; + } + else + { + //noinspection ConstantConditions + Object modulesUtil = ReflectUtil.method( ReflectUtil.type( "com.sun.tools.javac.comp.Modules" ), "instance", ReflectUtil.type( "com.sun.tools.javac.util.Context" ) ).invokeStatic( ctx ); + Object defModule = ReflectUtil.method( modulesUtil, "getDefaultModule" ).invoke(); + _noModule = defModule == null || (boolean)ReflectUtil.method( defModule, "isNoModule" ).invoke(); + } + } + return _noModule; + } + public static boolean isJava9Modular_runtime() { if( _modularRuntime == null )