mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-04 10:46:27 +00:00
Merge
This commit is contained in:
commit
aa53efc689
@ -28,6 +28,10 @@ javac.opts = -XDignore.symbol.file=true -Xlint:all,-deprecation,-options -Werror
|
||||
javac.source = 9
|
||||
javac.target = 9
|
||||
|
||||
#version used to compile build tools
|
||||
javac.build.source = 8
|
||||
javac.build.target = 8
|
||||
|
||||
langtools.resource.includes = \
|
||||
com/sun/tools/javac/resources/compiler.properties
|
||||
|
||||
|
||||
@ -239,11 +239,12 @@
|
||||
</condition>
|
||||
<replace file=".idea/ant.xml" token="@IDEA_JTREG_HOME@" value="${idea.jtreg.home}"/>
|
||||
<replace file=".idea/ant.xml" token="@IDEA_TARGET_JDK@" value="${idea.target.jdk}"/>
|
||||
<replace file=".idea/workspace.xml" token="@IDEA_TARGET_JDK@" value="${idea.target.jdk}"/>
|
||||
<replace file=".idea/workspace.xml" token="@XPATCH@" value="${xpatch.cmd}"/>
|
||||
<replace file=".idea/workspace.xml" token="@PATH_SEP@" value="${path.separator}"/>
|
||||
<replace dir=".idea/runConfigurations" token="@IDEA_TARGET_JDK@" value="${idea.target.jdk}"/>
|
||||
<replace dir=".idea/runConfigurations" token="@XPATCH@" value="${xpatch.cmd}"/>
|
||||
<mkdir dir=".idea/classes"/>
|
||||
<javac srcdir="make/intellij/src"
|
||||
<javac source="${javac.build.source}"
|
||||
target="${javac.build.target}"
|
||||
srcdir="make/intellij/src"
|
||||
destdir=".idea/classes"/>
|
||||
</target>
|
||||
|
||||
@ -255,10 +256,8 @@
|
||||
<copy todir="${build.tools}/propertiesparser" >
|
||||
<fileset dir="${make.tools.dir}/propertiesparser" includes="**/resources/**"/>
|
||||
</copy>
|
||||
<javac fork="true"
|
||||
source="${javac.source}"
|
||||
target="${javac.target}"
|
||||
executable="${langtools.jdk.home}/bin/javac"
|
||||
<javac source="${javac.build.source}"
|
||||
target="${javac.build.target}"
|
||||
srcdir="${make.tools.dir}"
|
||||
includes="propertiesparser/* anttasks/PropertiesParser* anttasks/PathFileSet*"
|
||||
destdir="${build.tools}"
|
||||
@ -273,10 +272,9 @@
|
||||
</target>
|
||||
|
||||
<target name="-def-pcompile">
|
||||
<javac fork="true"
|
||||
source="${javac.source}"
|
||||
target="${javac.target}"
|
||||
executable="${langtools.jdk.home}/bin/javac"
|
||||
<javac
|
||||
source="${javac.build.source}"
|
||||
target="${javac.build.target}"
|
||||
srcdir="${make.tools.dir}"
|
||||
includes="compileproperties/* anttasks/CompileProperties* anttasks/PathFileSet*"
|
||||
destdir="${build.dir}/toolclasses/"
|
||||
|
||||
22
langtools/make/intellij/runConfigurations/javac.xml
Normal file
22
langtools/make/intellij/runConfigurations/javac.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="javac" type="Application" factoryName="Application">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<option name="MAIN_CLASS_NAME" value="com.sun.tools.javac.Main" />
|
||||
<option name="VM_PARAMETERS" value="@XPATCH@" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="@IDEA_TARGET_JDK@" />
|
||||
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<module name="langtools" />
|
||||
<envs />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method>
|
||||
<option name="Make" enabled="false" />
|
||||
<option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="build-all-classes" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
22
langtools/make/intellij/runConfigurations/javadoc.xml
Normal file
22
langtools/make/intellij/runConfigurations/javadoc.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="javadoc" type="Application" factoryName="Application">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<option name="MAIN_CLASS_NAME" value="com.sun.tools.javadoc.Main" />
|
||||
<option name="VM_PARAMETERS" value="@XPATCH@" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="@IDEA_TARGET_JDK@" />
|
||||
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<module name="langtools" />
|
||||
<envs />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method>
|
||||
<option name="Make" enabled="false" />
|
||||
<option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="build-all-classes" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
22
langtools/make/intellij/runConfigurations/javah.xml
Normal file
22
langtools/make/intellij/runConfigurations/javah.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="javah" type="Application" factoryName="Application">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<option name="MAIN_CLASS_NAME" value="com.sun.tools.javah.Main" />
|
||||
<option name="VM_PARAMETERS" value="@XPATCH@ -XaddExports:jdk.compiler/com.sun.tools.javah=ALL-UNNAMED" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="@IDEA_TARGET_JDK@" />
|
||||
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<module name="langtools" />
|
||||
<envs />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method>
|
||||
<option name="Make" enabled="false" />
|
||||
<option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="build-all-classes" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
22
langtools/make/intellij/runConfigurations/javap.xml
Normal file
22
langtools/make/intellij/runConfigurations/javap.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="javap" type="Application" factoryName="Application">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<option name="MAIN_CLASS_NAME" value="com.sun.tools.javap.Main" />
|
||||
<option name="VM_PARAMETERS" value="@XPATCH@ -XaddExports:jdk.jdeps/com.sun.tools.javap=ALL-UNNAMED" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="@IDEA_TARGET_JDK@" />
|
||||
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<module name="langtools" />
|
||||
<envs />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method>
|
||||
<option name="Make" enabled="false" />
|
||||
<option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="build-all-classes" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
20
langtools/make/intellij/runConfigurations/jshell.xml
Normal file
20
langtools/make/intellij/runConfigurations/jshell.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="jshell" type="Application" factoryName="Application">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<option name="MAIN_CLASS_NAME" value="jdk.internal.jshell.tool.JShellTool" />
|
||||
<option name="VM_PARAMETERS" value="@XPATCH@ -XaddExports:jdk.jshell/jdk.internal.jshell.tool=ALL-UNNAMED" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="@IDEA_TARGET_JDK@" />
|
||||
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<module name="langtools" />
|
||||
<envs />
|
||||
<method>
|
||||
<option name="Make" enabled="false" />
|
||||
<option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="build-all-classes" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
18
langtools/make/intellij/runConfigurations/jtreg__debug_.xml
Normal file
18
langtools/make/intellij/runConfigurations/jtreg__debug_.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="jtreg (debug)" type="Remote" factoryName="Remote" singleton="true">
|
||||
<option name="USE_SOCKET_TRANSPORT" value="true" />
|
||||
<option name="SERVER_MODE" value="true" />
|
||||
<option name="SHMEM_ADDRESS" />
|
||||
<option name="HOST" value="localhost" />
|
||||
<option name="PORT" value="5900" />
|
||||
<RunnerSettings RunnerId="Debug">
|
||||
<option name="DEBUG_PORT" value="5900" />
|
||||
<option name="TRANSPORT" value="0" />
|
||||
<option name="LOCAL" value="false" />
|
||||
</RunnerSettings>
|
||||
<ConfigurationWrapper RunnerId="Debug" />
|
||||
<method>
|
||||
<option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="jtreg-debug" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -0,0 +1,8 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="jtreg (run)" type="AntRunConfiguration" factoryName="Ant Target">
|
||||
<antsettings antfile="file://$PROJECT_DIR$/.idea/build.xml" target="jtreg" />
|
||||
<method>
|
||||
<option name="Make" enabled="false" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
22
langtools/make/intellij/runConfigurations/sjavac.xml
Normal file
22
langtools/make/intellij/runConfigurations/sjavac.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="sjavac" type="Application" factoryName="Application">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<option name="MAIN_CLASS_NAME" value="com.sun.tools.sjavac.Main" />
|
||||
<option name="VM_PARAMETERS" value="@XPATCH@ -XaddExports:jdk.compiler/com.sun.tools.sjavac=ALL-UNNAMED" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="@IDEA_TARGET_JDK@" />
|
||||
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<module name="langtools" />
|
||||
<envs />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method>
|
||||
<option name="Make" enabled="false" />
|
||||
<option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="build-all-classes" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -1,157 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<ignored path=".idea/" />
|
||||
</component>
|
||||
<component name="CompilerWorkspaceConfiguration">
|
||||
<option name="MAKE_PROJECT_ON_SAVE" value="true" />
|
||||
</component>
|
||||
<component name="RunManager" selected="Application.javac">
|
||||
<!-- javac -->
|
||||
<configuration default="false" name="javac" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="com.sun.tools.javac.Main" />
|
||||
<option name="VM_PARAMETERS" value="@XPATCH@" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="@IDEA_TARGET_JDK@" />
|
||||
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<module name="langtools" />
|
||||
<envs />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method>
|
||||
<option name="Make" enabled="false" />
|
||||
<option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="build-all-classes" />
|
||||
</method>
|
||||
</configuration>
|
||||
<!-- javadoc -->
|
||||
<configuration default="false" name="javadoc" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="com.sun.tools.javadoc.Main" />
|
||||
<option name="VM_PARAMETERS" value="@XPATCH@" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="@IDEA_TARGET_JDK@" />
|
||||
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<module name="langtools" />
|
||||
<envs />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method>
|
||||
<option name="Make" enabled="false" />
|
||||
<option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="build-all-classes" />
|
||||
</method>
|
||||
</configuration>
|
||||
<!-- javap -->
|
||||
<configuration default="false" name="javap" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="com.sun.tools.javap.Main" />
|
||||
<option name="VM_PARAMETERS" value="@XPATCH@" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="@IDEA_TARGET_JDK@" />
|
||||
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<module name="langtools" />
|
||||
<envs />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method>
|
||||
<option name="Make" enabled="false" />
|
||||
<option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="build-all-classes" />
|
||||
</method>
|
||||
</configuration>
|
||||
<!-- javah -->
|
||||
<configuration default="false" name="javah" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="com.sun.tools.javah.Main" />
|
||||
<option name="VM_PARAMETERS" value="@XPATCH@" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="@IDEA_TARGET_JDK@" />
|
||||
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<module name="langtools" />
|
||||
<envs />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method>
|
||||
<option name="Make" enabled="false" />
|
||||
<option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="build-all-classes" />
|
||||
</method>
|
||||
</configuration>
|
||||
<!-- sjavac -->
|
||||
<configuration default="false" name="sjavac" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="com.sun.tools.sjavac.Main" />
|
||||
<option name="VM_PARAMETERS" value="@XPATCH@" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="@IDEA_TARGET_JDK@" />
|
||||
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<module name="langtools" />
|
||||
<envs />
|
||||
<RunnerSettings RunnerId="Run" />
|
||||
<ConfigurationWrapper RunnerId="Run" />
|
||||
<method>
|
||||
<option name="Make" enabled="false" />
|
||||
<option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="build-all-classes" />
|
||||
</method>
|
||||
</configuration>
|
||||
<!-- jshell -->
|
||||
<configuration default="false" name="jshell" type="Application" factoryName="Application">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<option name="MAIN_CLASS_NAME" value="jdk.internal.jshell.tool.JShellTool" />
|
||||
<option name="VM_PARAMETERS" value="@XPATCH@ -XaddExports:jdk.jshell/jdk.internal.jshell.tool=ALL-UNNAMED" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="@IDEA_TARGET_JDK@" />
|
||||
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<module name="langtools" />
|
||||
<envs />
|
||||
<method>
|
||||
<option name="Make" enabled="false" />
|
||||
<option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="build-all-classes" />
|
||||
</method>
|
||||
</configuration>
|
||||
<!-- jtreg run -->
|
||||
<configuration default="false" name="jtreg (run)" type="AntRunConfiguration" factoryName="Ant Target">
|
||||
<antsettings antfile="file://$PROJECT_DIR$/.idea/build.xml" target="jtreg" />
|
||||
<method>
|
||||
<option name="Make" enabled="false" />
|
||||
</method>
|
||||
</configuration>
|
||||
<!-- jtreg debug -->
|
||||
<configuration default="false" name="jtreg (debug)" type="Remote" factoryName="Remote" singleton="true">
|
||||
<option name="USE_SOCKET_TRANSPORT" value="true" />
|
||||
<option name="SERVER_MODE" value="true" />
|
||||
<option name="SHMEM_ADDRESS" />
|
||||
<option name="HOST" value="localhost" />
|
||||
<option name="PORT" value="5900" />
|
||||
<RunnerSettings RunnerId="Debug">
|
||||
<option name="DEBUG_PORT" value="5900" />
|
||||
<option name="TRANSPORT" value="0" />
|
||||
<option name="LOCAL" value="false" />
|
||||
</RunnerSettings>
|
||||
<ConfigurationWrapper RunnerId="Debug" />
|
||||
<method>
|
||||
<option name="AntTarget" enabled="true" antfile="file://$PROJECT_DIR$/.idea/build.xml" target="jtreg-debug" />
|
||||
</method>
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
<component name="StructureViewFactory">
|
||||
<option name="ACTIVE_ACTIONS" value=",ALPHA_COMPARATOR" />
|
||||
</component>
|
||||
|
||||
@ -37,6 +37,9 @@ import com.sun.tools.javac.tree.TreeInfo;
|
||||
import com.sun.tools.javac.tree.TreeScanner;
|
||||
import com.sun.tools.javac.util.Assert;
|
||||
|
||||
/**This analyzer guards against complex messages (i.e. those that use string concatenation) passed
|
||||
* to various Assert.check methods.
|
||||
*/
|
||||
public class AssertCheckAnalyzer extends AbstractCodingRulesAnalyzer {
|
||||
|
||||
enum AssertOverloadKind {
|
||||
|
||||
@ -41,6 +41,7 @@ import static com.sun.tools.javac.code.Flags.STATIC;
|
||||
import static com.sun.tools.javac.code.Flags.SYNTHETIC;
|
||||
import static com.sun.tools.javac.code.Kinds.Kind.*;
|
||||
|
||||
/**This analyzer guards against non-final static fields.*/
|
||||
public class MutableFieldsAnalyzer extends AbstractCodingRulesAnalyzer {
|
||||
|
||||
public MutableFieldsAnalyzer(JavacTask task) {
|
||||
|
||||
@ -365,18 +365,22 @@ public class ArgumentAttr extends JCTree.Visitor {
|
||||
|
||||
@Override
|
||||
Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
|
||||
for (Map.Entry<ResultInfo, Type> _entry : speculativeTypes.entrySet()) {
|
||||
DeferredAttrContext deferredAttrContext = _entry.getKey().checkContext.deferredAttrContext();
|
||||
if (deferredAttrContext.phase == phase && deferredAttrContext.msym == msym) {
|
||||
return _entry.getValue();
|
||||
if (pertinentToApplicability) {
|
||||
for (Map.Entry<ResultInfo, Type> _entry : speculativeTypes.entrySet()) {
|
||||
DeferredAttrContext deferredAttrContext = _entry.getKey().checkContext.deferredAttrContext();
|
||||
if (deferredAttrContext.phase == phase && deferredAttrContext.msym == msym) {
|
||||
return _entry.getValue();
|
||||
}
|
||||
}
|
||||
return Type.noType;
|
||||
} else {
|
||||
return super.speculativeType(msym, phase);
|
||||
}
|
||||
return Type.noType;
|
||||
}
|
||||
|
||||
@Override
|
||||
JCTree speculativeTree(DeferredAttrContext deferredAttrContext) {
|
||||
return speculativeTree;
|
||||
return pertinentToApplicability ? speculativeTree : super.speculativeTree(deferredAttrContext);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -2678,17 +2678,31 @@ public class Attr extends JCTree.Visitor {
|
||||
class ExpressionLambdaReturnContext extends FunctionalReturnContext {
|
||||
|
||||
JCExpression expr;
|
||||
boolean expStmtExpected;
|
||||
|
||||
ExpressionLambdaReturnContext(JCExpression expr, CheckContext enclosingContext) {
|
||||
super(enclosingContext);
|
||||
this.expr = expr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void report(DiagnosticPosition pos, JCDiagnostic details) {
|
||||
if (expStmtExpected) {
|
||||
enclosingContext.report(pos, diags.fragment(Fragments.StatExprExpected));
|
||||
} else {
|
||||
super.report(pos, details);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean compatible(Type found, Type req, Warner warn) {
|
||||
//a void return is compatible with an expression statement lambda
|
||||
return TreeInfo.isExpressionStatement(expr) && req.hasTag(VOID) ||
|
||||
super.compatible(found, req, warn);
|
||||
if (req.hasTag(VOID)) {
|
||||
expStmtExpected = true;
|
||||
return TreeInfo.isExpressionStatement(expr);
|
||||
} else {
|
||||
return super.compatible(found, req, warn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -35,6 +35,7 @@ import com.sun.tools.javac.resources.CompilerProperties.Fragments;
|
||||
import com.sun.tools.javac.tree.*;
|
||||
import com.sun.tools.javac.util.*;
|
||||
import com.sun.tools.javac.util.DefinedBy.Api;
|
||||
import com.sun.tools.javac.util.GraphUtils.DependencyKind;
|
||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
|
||||
import com.sun.tools.javac.code.Symbol.*;
|
||||
import com.sun.tools.javac.comp.Attr.ResultInfo;
|
||||
@ -44,9 +45,10 @@ import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;
|
||||
import com.sun.tools.javac.util.Log.DeferredDiagnosticHandler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@ -172,6 +174,7 @@ public class DeferredAttr extends JCTree.Visitor {
|
||||
public JCExpression tree;
|
||||
Env<AttrContext> env;
|
||||
AttrMode mode;
|
||||
boolean pertinentToApplicability = true;
|
||||
SpeculativeCache speculativeCache;
|
||||
|
||||
DeferredType(JCExpression tree, Env<AttrContext> env) {
|
||||
@ -290,6 +293,7 @@ public class DeferredAttr extends JCTree.Visitor {
|
||||
resultInfo.checkContext.deferredAttrContext();
|
||||
Assert.check(deferredAttrContext != emptyDeferredAttrContext);
|
||||
if (deferredStuckPolicy.isStuck()) {
|
||||
pertinentToApplicability = false;
|
||||
deferredAttrContext.addDeferredAttrNode(this, resultInfo, deferredStuckPolicy);
|
||||
return Type.noType;
|
||||
} else {
|
||||
@ -574,28 +578,11 @@ public class DeferredAttr extends JCTree.Visitor {
|
||||
*/
|
||||
void complete() {
|
||||
while (!deferredAttrNodes.isEmpty()) {
|
||||
Map<Type, Set<Type>> depVarsMap = new LinkedHashMap<>();
|
||||
List<Type> stuckVars = List.nil();
|
||||
boolean progress = false;
|
||||
//scan a defensive copy of the node list - this is because a deferred
|
||||
//attribution round can add new nodes to the list
|
||||
for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
|
||||
if (!deferredAttrNode.process(this)) {
|
||||
List<Type> restStuckVars =
|
||||
List.from(deferredAttrNode.deferredStuckPolicy.stuckVars())
|
||||
.intersect(inferenceContext.restvars());
|
||||
stuckVars = stuckVars.prependList(restStuckVars);
|
||||
//update dependency map
|
||||
for (Type t : List.from(deferredAttrNode.deferredStuckPolicy.depVars())
|
||||
.intersect(inferenceContext.restvars())) {
|
||||
Set<Type> prevDeps = depVarsMap.get(t);
|
||||
if (prevDeps == null) {
|
||||
prevDeps = new LinkedHashSet<>();
|
||||
depVarsMap.put(t, prevDeps);
|
||||
}
|
||||
prevDeps.addAll(restStuckVars);
|
||||
}
|
||||
} else {
|
||||
if (deferredAttrNode.process(this)) {
|
||||
deferredAttrNodes.remove(deferredAttrNode);
|
||||
progress = true;
|
||||
}
|
||||
@ -610,7 +597,9 @@ public class DeferredAttr extends JCTree.Visitor {
|
||||
//remove all variables that have already been instantiated
|
||||
//from the list of stuck variables
|
||||
try {
|
||||
inferenceContext.solveAny(stuckVars, depVarsMap, warn);
|
||||
//find stuck expression to unstuck
|
||||
DeferredAttrNode toUnstuck = pickDeferredNode();
|
||||
inferenceContext.solveAny(List.from(toUnstuck.deferredStuckPolicy.stuckVars()), warn);
|
||||
inferenceContext.notifyChange();
|
||||
} catch (Infer.GraphStrategy.NodeNotFoundException ex) {
|
||||
//this means that we are in speculative mode and the
|
||||
@ -632,6 +621,59 @@ public class DeferredAttr extends JCTree.Visitor {
|
||||
}
|
||||
return dac.parent.insideOverloadPhase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick the deferred node to be unstuck. The chosen node is the first strongly connected
|
||||
* component containing exactly one node found in the dependency graph induced by deferred nodes.
|
||||
* If no such component is found, the first deferred node is returned.
|
||||
*/
|
||||
DeferredAttrNode pickDeferredNode() {
|
||||
List<StuckNode> nodes = deferredAttrNodes.stream()
|
||||
.map(StuckNode::new)
|
||||
.collect(List.collector());
|
||||
//init stuck expression graph; a deferred node A depends on a deferred node B iff
|
||||
//the intersection between A's input variable and B's output variable is non-empty.
|
||||
for (StuckNode sn1 : nodes) {
|
||||
for (Type t : sn1.data.deferredStuckPolicy.stuckVars()) {
|
||||
for (StuckNode sn2 : nodes) {
|
||||
if (sn1 != sn2 && sn2.data.deferredStuckPolicy.depVars().contains(t)) {
|
||||
sn1.deps.add(sn2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//compute tarjan on the stuck graph
|
||||
List<? extends StuckNode> csn = GraphUtils.tarjan(nodes).get(0);
|
||||
return csn.length() == 1 ? csn.get(0).data : deferredAttrNodes.get(0);
|
||||
}
|
||||
|
||||
class StuckNode extends GraphUtils.TarjanNode<DeferredAttrNode, StuckNode> {
|
||||
|
||||
Set<StuckNode> deps = new HashSet<>();
|
||||
|
||||
StuckNode(DeferredAttrNode data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependencyKind[] getSupportedDependencyKinds() {
|
||||
return new DependencyKind[] { Infer.DependencyKind.STUCK };
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends StuckNode> getDependenciesByKind(DependencyKind dk) {
|
||||
if (dk == Infer.DependencyKind.STUCK) {
|
||||
return deps;
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<? extends StuckNode> getAllDependencies() {
|
||||
return deps;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -2345,10 +2345,11 @@ public class Flow {
|
||||
// assigned before reading their value
|
||||
public void visitSelect(JCFieldAccess tree) {
|
||||
super.visitSelect(tree);
|
||||
JCTree sel = TreeInfo.skipParens(tree.selected);
|
||||
if (enforceThisDotInit &&
|
||||
tree.selected.hasTag(IDENT) &&
|
||||
((JCIdent)tree.selected).name == names._this &&
|
||||
tree.sym.kind == VAR) {
|
||||
sel.hasTag(IDENT) &&
|
||||
((JCIdent)sel).name == names._this &&
|
||||
tree.sym.kind == VAR) {
|
||||
checkInit(tree.pos(), (VarSymbol)tree.sym);
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,11 +52,9 @@ import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
@ -1655,12 +1653,10 @@ public class Infer {
|
||||
class GraphSolver {
|
||||
|
||||
InferenceContext inferenceContext;
|
||||
Map<Type, Set<Type>> stuckDeps;
|
||||
Warner warn;
|
||||
|
||||
GraphSolver(InferenceContext inferenceContext, Map<Type, Set<Type>> stuckDeps, Warner warn) {
|
||||
GraphSolver(InferenceContext inferenceContext, Warner warn) {
|
||||
this.inferenceContext = inferenceContext;
|
||||
this.stuckDeps = stuckDeps;
|
||||
this.warn = warn;
|
||||
}
|
||||
|
||||
@ -1671,7 +1667,7 @@ public class Infer {
|
||||
*/
|
||||
void solve(GraphStrategy sstrategy) {
|
||||
doIncorporation(inferenceContext, warn); //initial propagation of bounds
|
||||
InferenceGraph inferenceGraph = new InferenceGraph(stuckDeps);
|
||||
InferenceGraph inferenceGraph = new InferenceGraph();
|
||||
while (!sstrategy.done()) {
|
||||
if (dependenciesFolder != null) {
|
||||
//add this graph to the pending queue
|
||||
@ -1720,12 +1716,12 @@ public class Infer {
|
||||
*/
|
||||
class Node extends GraphUtils.TarjanNode<ListBuffer<Type>, Node> implements DottableNode<ListBuffer<Type>, Node> {
|
||||
|
||||
/** map listing all dependencies (grouped by kind) */
|
||||
EnumMap<DependencyKind, Set<Node>> deps;
|
||||
/** node dependencies */
|
||||
Set<Node> deps;
|
||||
|
||||
Node(Type ivar) {
|
||||
super(ListBuffer.of(ivar));
|
||||
this.deps = new EnumMap<>(DependencyKind.class);
|
||||
this.deps = new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1734,76 +1730,53 @@ public class Infer {
|
||||
}
|
||||
|
||||
public Iterable<? extends Node> getAllDependencies() {
|
||||
return getDependencies(DependencyKind.values());
|
||||
return deps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Node> getDependenciesByKind(GraphUtils.DependencyKind dk) {
|
||||
return getDependencies((DependencyKind)dk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all dependencies with given kind(s).
|
||||
*/
|
||||
protected Set<Node> getDependencies(DependencyKind... depKinds) {
|
||||
Set<Node> buf = new LinkedHashSet<>();
|
||||
for (DependencyKind dk : depKinds) {
|
||||
Set<Node> depsByKind = deps.get(dk);
|
||||
if (depsByKind != null) {
|
||||
buf.addAll(depsByKind);
|
||||
}
|
||||
if (dk == DependencyKind.BOUND) {
|
||||
return deps;
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds dependency with given kind.
|
||||
*/
|
||||
protected void addDependency(DependencyKind dk, Node depToAdd) {
|
||||
Set<Node> depsByKind = deps.get(dk);
|
||||
if (depsByKind == null) {
|
||||
depsByKind = new LinkedHashSet<>();
|
||||
deps.put(dk, depsByKind);
|
||||
}
|
||||
depsByKind.add(depToAdd);
|
||||
protected void addDependency(Node depToAdd) {
|
||||
deps.add(depToAdd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add multiple dependencies of same given kind.
|
||||
*/
|
||||
protected void addDependencies(DependencyKind dk, Set<Node> depsToAdd) {
|
||||
protected void addDependencies(Set<Node> depsToAdd) {
|
||||
for (Node n : depsToAdd) {
|
||||
addDependency(dk, n);
|
||||
addDependency(n);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a dependency, regardless of its kind.
|
||||
*/
|
||||
protected Set<DependencyKind> removeDependency(Node n) {
|
||||
Set<DependencyKind> removedKinds = new HashSet<>();
|
||||
for (DependencyKind dk : DependencyKind.values()) {
|
||||
Set<Node> depsByKind = deps.get(dk);
|
||||
if (depsByKind == null) continue;
|
||||
if (depsByKind.remove(n)) {
|
||||
removedKinds.add(dk);
|
||||
}
|
||||
}
|
||||
return removedKinds;
|
||||
protected boolean removeDependency(Node n) {
|
||||
return deps.remove(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute closure of a give node, by recursively walking
|
||||
* through all its dependencies (of given kinds)
|
||||
*/
|
||||
protected Set<Node> closure(DependencyKind... depKinds) {
|
||||
protected Set<Node> closure() {
|
||||
boolean progress = true;
|
||||
Set<Node> closure = new HashSet<>();
|
||||
closure.add(this);
|
||||
while (progress) {
|
||||
progress = false;
|
||||
for (Node n1 : new HashSet<>(closure)) {
|
||||
progress = closure.addAll(n1.getDependencies(depKinds));
|
||||
progress = closure.addAll(n1.deps);
|
||||
}
|
||||
}
|
||||
return closure;
|
||||
@ -1815,9 +1788,8 @@ public class Infer {
|
||||
*/
|
||||
protected boolean isLeaf() {
|
||||
//no deps, or only one self dep
|
||||
Set<Node> allDeps = getDependencies(DependencyKind.BOUND, DependencyKind.STUCK);
|
||||
if (allDeps.isEmpty()) return true;
|
||||
for (Node n : allDeps) {
|
||||
if (deps.isEmpty()) return true;
|
||||
for (Node n : deps) {
|
||||
if (n != this) {
|
||||
return false;
|
||||
}
|
||||
@ -1834,24 +1806,15 @@ public class Infer {
|
||||
for (Node n : nodes) {
|
||||
Assert.check(n.data.length() == 1, "Attempt to merge a compound node!");
|
||||
data.appendList(n.data);
|
||||
for (DependencyKind dk : DependencyKind.values()) {
|
||||
addDependencies(dk, n.getDependencies(dk));
|
||||
}
|
||||
addDependencies(n.deps);
|
||||
}
|
||||
//update deps
|
||||
EnumMap<DependencyKind, Set<Node>> deps2 = new EnumMap<>(DependencyKind.class);
|
||||
for (DependencyKind dk : DependencyKind.values()) {
|
||||
for (Node d : getDependencies(dk)) {
|
||||
Set<Node> depsByKind = deps2.get(dk);
|
||||
if (depsByKind == null) {
|
||||
depsByKind = new LinkedHashSet<>();
|
||||
deps2.put(dk, depsByKind);
|
||||
}
|
||||
if (data.contains(d.data.first())) {
|
||||
depsByKind.add(this);
|
||||
} else {
|
||||
depsByKind.add(d);
|
||||
}
|
||||
Set<Node> deps2 = new HashSet<>();
|
||||
for (Node d : deps) {
|
||||
if (data.contains(d.data.first())) {
|
||||
deps2.add(this);
|
||||
} else {
|
||||
deps2.add(d);
|
||||
}
|
||||
}
|
||||
deps = deps2;
|
||||
@ -1862,9 +1825,9 @@ public class Infer {
|
||||
* topology.
|
||||
*/
|
||||
private void graphChanged(Node from, Node to) {
|
||||
for (DependencyKind dk : removeDependency(from)) {
|
||||
if (removeDependency(from)) {
|
||||
if (to != null) {
|
||||
addDependency(dk, to);
|
||||
addDependency(to);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1880,22 +1843,19 @@ public class Infer {
|
||||
public Properties dependencyAttributes(Node sink, GraphUtils.DependencyKind dk) {
|
||||
Properties p = new Properties();
|
||||
p.put("style", ((DependencyKind)dk).dotSyle);
|
||||
if (dk == DependencyKind.STUCK) return p;
|
||||
else {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
String sep = "";
|
||||
for (Type from : data) {
|
||||
UndetVar uv = (UndetVar)inferenceContext.asUndetVar(from);
|
||||
for (Type bound : uv.getBounds(InferenceBound.values())) {
|
||||
if (bound.containsAny(List.from(sink.data))) {
|
||||
buf.append(sep);
|
||||
buf.append(bound);
|
||||
sep = ",";
|
||||
}
|
||||
StringBuilder buf = new StringBuilder();
|
||||
String sep = "";
|
||||
for (Type from : data) {
|
||||
UndetVar uv = (UndetVar)inferenceContext.asUndetVar(from);
|
||||
for (Type bound : uv.getBounds(InferenceBound.values())) {
|
||||
if (bound.containsAny(List.from(sink.data))) {
|
||||
buf.append(sep);
|
||||
buf.append(bound);
|
||||
sep = ",";
|
||||
}
|
||||
}
|
||||
p.put("label", "\"" + buf.toString() + "\"");
|
||||
}
|
||||
p.put("label", "\"" + buf.toString() + "\"");
|
||||
return p;
|
||||
}
|
||||
}
|
||||
@ -1903,8 +1863,8 @@ public class Infer {
|
||||
/** the nodes in the inference graph */
|
||||
ArrayList<Node> nodes;
|
||||
|
||||
InferenceGraph(Map<Type, Set<Type>> optDeps) {
|
||||
initNodes(optDeps);
|
||||
InferenceGraph() {
|
||||
initNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1946,7 +1906,7 @@ public class Infer {
|
||||
* in the graph. For each component containing more than one node, a super node is
|
||||
* created, effectively replacing the original cyclic nodes.
|
||||
*/
|
||||
void initNodes(Map<Type, Set<Type>> stuckDeps) {
|
||||
void initNodes() {
|
||||
//add nodes
|
||||
nodes = new ArrayList<>();
|
||||
for (Type t : inferenceContext.restvars()) {
|
||||
@ -1955,17 +1915,12 @@ public class Infer {
|
||||
//add dependencies
|
||||
for (Node n_i : nodes) {
|
||||
Type i = n_i.data.first();
|
||||
Set<Type> optDepsByNode = stuckDeps.get(i);
|
||||
for (Node n_j : nodes) {
|
||||
Type j = n_j.data.first();
|
||||
UndetVar uv_i = (UndetVar)inferenceContext.asUndetVar(i);
|
||||
if (Type.containsAny(uv_i.getBounds(InferenceBound.values()), List.of(j))) {
|
||||
//update i's bound dependencies
|
||||
n_i.addDependency(DependencyKind.BOUND, n_j);
|
||||
}
|
||||
if (optDepsByNode != null && optDepsByNode.contains(j)) {
|
||||
//update i's stuck dependencies
|
||||
n_i.addDependency(DependencyKind.STUCK, n_j);
|
||||
n_i.addDependency(n_j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -469,15 +469,11 @@ class InferenceContext {
|
||||
}
|
||||
}
|
||||
|
||||
private void solve(GraphStrategy ss, Warner warn) {
|
||||
solve(ss, new HashMap<Type, Set<Type>>(), warn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Solve with given graph strategy.
|
||||
*/
|
||||
private void solve(GraphStrategy ss, Map<Type, Set<Type>> stuckDeps, Warner warn) {
|
||||
GraphSolver s = infer.new GraphSolver(this, stuckDeps, warn);
|
||||
private void solve(GraphStrategy ss, Warner warn) {
|
||||
GraphSolver s = infer.new GraphSolver(this, warn);
|
||||
s.solve(ss);
|
||||
}
|
||||
|
||||
@ -506,12 +502,12 @@ class InferenceContext {
|
||||
/**
|
||||
* Solve at least one variable in given list.
|
||||
*/
|
||||
public void solveAny(List<Type> varsToSolve, Map<Type, Set<Type>> optDeps, Warner warn) {
|
||||
public void solveAny(List<Type> varsToSolve, Warner warn) {
|
||||
solve(infer.new BestLeafSolver(varsToSolve.intersect(restvars())) {
|
||||
public boolean done() {
|
||||
return instvars().intersect(varsToSolve).nonEmpty();
|
||||
}
|
||||
}, optDeps, warn);
|
||||
}, warn);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -380,13 +380,17 @@ public abstract class BaseFileManager implements JavaFileManager {
|
||||
dest = CharBuffer.allocate(newCapacity).put(dest);
|
||||
} else if (result.isMalformed() || result.isUnmappable()) {
|
||||
// bad character in input
|
||||
StringBuilder unmappable = new StringBuilder();
|
||||
int len = result.length();
|
||||
|
||||
log.error(new SimpleDiagnosticPosition(dest.limit()),
|
||||
"illegal.char.for.encoding",
|
||||
charset == null ? encodingName : charset.name());
|
||||
for (int i = 0; i < len; i++) {
|
||||
unmappable.append(String.format("%02X", inbuf.get()));
|
||||
}
|
||||
|
||||
// skip past the coding error
|
||||
inbuf.position(inbuf.position() + result.length());
|
||||
String charsetName = charset == null ? encodingName : charset.name();
|
||||
|
||||
log.error(dest.limit(),
|
||||
Errors.IllegalCharForEncoding(unmappable.toString(), charsetName));
|
||||
|
||||
// undo the flip() to prepare the output buffer
|
||||
// for more translation
|
||||
|
||||
@ -1138,7 +1138,7 @@ public class Locations {
|
||||
}
|
||||
}
|
||||
|
||||
if (warn && false) { // temp disable
|
||||
if (warn && false) { // temp disable, when enabled, massage examples.not-yet.txt suitably.
|
||||
log.warning(Warnings.LocnUnknownFileOnModulePath(p));
|
||||
}
|
||||
return null;
|
||||
|
||||
@ -522,8 +522,9 @@ compiler.err.icls.cant.have.static.decl=\
|
||||
compiler.err.illegal.char=\
|
||||
illegal character: ''{0}''
|
||||
|
||||
# 0: string, 1: string
|
||||
compiler.err.illegal.char.for.encoding=\
|
||||
unmappable character for encoding {0}
|
||||
unmappable character (0x{0}) for encoding {1}
|
||||
|
||||
# 0: set of modifier, 1: set of modifier
|
||||
compiler.err.illegal.combination.of.modifiers=\
|
||||
@ -779,6 +780,10 @@ compiler.misc.incompatible.ret.type.in.lambda=\
|
||||
bad return type in lambda expression\n\
|
||||
{0}
|
||||
|
||||
compiler.misc.stat.expr.expected=\
|
||||
lambda body is not compatible with a void functional interface\n\
|
||||
(consider using a block lambda body, or use a statement expression instead)
|
||||
|
||||
# 0: type
|
||||
compiler.misc.incompatible.ret.type.in.mref=\
|
||||
bad return type in method reference\n\
|
||||
@ -2747,9 +2752,6 @@ compiler.err.module.name.mismatch=\
|
||||
compiler.err.module.decl.sb.in.module-info.java=\
|
||||
module declarations should be in a file named module-info.java
|
||||
|
||||
compiler.err.unexpected.after.module=\
|
||||
unexpected input after module declaration
|
||||
|
||||
compiler.err.module-info.with.xmodule.sourcepath=\
|
||||
illegal combination of -Xmodule and module-info on sourcepath
|
||||
|
||||
|
||||
@ -714,43 +714,43 @@ public abstract class Configuration {
|
||||
}
|
||||
|
||||
public String getText(String key) {
|
||||
try {
|
||||
//Check the doclet specific properties file.
|
||||
return getDocletSpecificMsg().getText(key);
|
||||
} catch (Exception e) {
|
||||
//Check the shared properties file.
|
||||
return message.getText(key);
|
||||
// Check the doclet specific properties file.
|
||||
MessageRetriever docletMessage = getDocletSpecificMsg();
|
||||
if (docletMessage.containsKey(key)) {
|
||||
return docletMessage.getText(key);
|
||||
}
|
||||
// Check the shared properties file.
|
||||
return message.getText(key);
|
||||
}
|
||||
|
||||
public String getText(String key, String a1) {
|
||||
try {
|
||||
//Check the doclet specific properties file.
|
||||
return getDocletSpecificMsg().getText(key, a1);
|
||||
} catch (Exception e) {
|
||||
//Check the shared properties file.
|
||||
return message.getText(key, a1);
|
||||
// Check the doclet specific properties file.
|
||||
MessageRetriever docletMessage = getDocletSpecificMsg();
|
||||
if (docletMessage.containsKey(key)) {
|
||||
return docletMessage.getText(key, a1);
|
||||
}
|
||||
// Check the shared properties file.
|
||||
return message.getText(key, a1);
|
||||
}
|
||||
|
||||
public String getText(String key, String a1, String a2) {
|
||||
try {
|
||||
//Check the doclet specific properties file.
|
||||
return getDocletSpecificMsg().getText(key, a1, a2);
|
||||
} catch (Exception e) {
|
||||
//Check the shared properties file.
|
||||
return message.getText(key, a1, a2);
|
||||
// Check the doclet specific properties file.
|
||||
MessageRetriever docletMessage = getDocletSpecificMsg();
|
||||
if (docletMessage.containsKey(key)) {
|
||||
return docletMessage.getText(key, a1, a2);
|
||||
}
|
||||
// Check the shared properties file.
|
||||
return message.getText(key, a1, a2);
|
||||
}
|
||||
|
||||
public String getText(String key, String a1, String a2, String a3) {
|
||||
try {
|
||||
//Check the doclet specific properties file.
|
||||
return getDocletSpecificMsg().getText(key, a1, a2, a3);
|
||||
} catch (Exception e) {
|
||||
//Check the shared properties file.
|
||||
return message.getText(key, a1, a2, a3);
|
||||
// Check the doclet specific properties file.
|
||||
MessageRetriever docletMessage = getDocletSpecificMsg();
|
||||
if (docletMessage.containsKey(key)) {
|
||||
return docletMessage.getText(key, a1, a2, a3);
|
||||
}
|
||||
// Check the shared properties file.
|
||||
return message.getText(key, a1, a2, a3);
|
||||
}
|
||||
|
||||
public abstract Content newContent();
|
||||
|
||||
@ -83,6 +83,34 @@ public class MessageRetriever {
|
||||
this.resourcelocation = resourcelocation;
|
||||
}
|
||||
|
||||
private ResourceBundle initRB() {
|
||||
ResourceBundle bundle = messageRB;
|
||||
if (bundle == null) {
|
||||
try {
|
||||
messageRB = bundle =
|
||||
ResourceBundle.getBundle(resourcelocation, configuration.getLocale());
|
||||
} catch (MissingResourceException e) {
|
||||
throw new Error("Fatal: Resource (" + resourcelocation
|
||||
+ ") for javadoc doclets is missing.");
|
||||
}
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given <code>key</code> can be retrieved
|
||||
* from this <code>MessageRetriever</code>
|
||||
*
|
||||
* @param key
|
||||
* the resource <code>key</code>
|
||||
* @return <code>true</code> if the given <code>key</code> is
|
||||
* contained in the underlying <code>ResourceBundle</code>.
|
||||
*/
|
||||
public boolean containsKey(String key) {
|
||||
ResourceBundle bundle = initRB();
|
||||
return bundle.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and format message string from resource
|
||||
*
|
||||
@ -92,15 +120,8 @@ public class MessageRetriever {
|
||||
* exist in the properties file.
|
||||
*/
|
||||
public String getText(String key, Object... args) throws MissingResourceException {
|
||||
if (messageRB == null) {
|
||||
try {
|
||||
messageRB = ResourceBundle.getBundle(resourcelocation);
|
||||
} catch (MissingResourceException e) {
|
||||
throw new Error("Fatal: Resource (" + resourcelocation +
|
||||
") for javadoc doclets is missing.");
|
||||
}
|
||||
}
|
||||
String message = messageRB.getString(key);
|
||||
ResourceBundle bundle = initRB();
|
||||
String message = bundle.getString(key);
|
||||
return MessageFormat.format(message, args);
|
||||
}
|
||||
|
||||
|
||||
@ -894,43 +894,43 @@ public abstract class Configuration {
|
||||
}
|
||||
|
||||
public String getText(String key) {
|
||||
try {
|
||||
//Check the doclet specific properties file.
|
||||
return getDocletSpecificMsg().getText(key);
|
||||
} catch (Exception e) {
|
||||
//Check the shared properties file.
|
||||
return message.getText(key);
|
||||
// Check the doclet specific properties file.
|
||||
MessageRetriever docletMessage = getDocletSpecificMsg();
|
||||
if (docletMessage.containsKey(key)) {
|
||||
return docletMessage.getText(key);
|
||||
}
|
||||
// Check the shared properties file.
|
||||
return message.getText(key);
|
||||
}
|
||||
|
||||
public String getText(String key, String a1) {
|
||||
try {
|
||||
//Check the doclet specific properties file.
|
||||
return getDocletSpecificMsg().getText(key, a1);
|
||||
} catch (MissingResourceException e) {
|
||||
//Check the shared properties file.
|
||||
return message.getText(key, a1);
|
||||
// Check the doclet specific properties file.
|
||||
MessageRetriever docletMessage = getDocletSpecificMsg();
|
||||
if (docletMessage.containsKey(key)) {
|
||||
return docletMessage.getText(key, a1);
|
||||
}
|
||||
// Check the shared properties file.
|
||||
return message.getText(key, a1);
|
||||
}
|
||||
|
||||
public String getText(String key, String a1, String a2) {
|
||||
try {
|
||||
//Check the doclet specific properties file.
|
||||
return getDocletSpecificMsg().getText(key, a1, a2);
|
||||
} catch (MissingResourceException e) {
|
||||
//Check the shared properties file.
|
||||
return message.getText(key, a1, a2);
|
||||
// Check the doclet specific properties file.
|
||||
MessageRetriever docletMessage = getDocletSpecificMsg();
|
||||
if (docletMessage.containsKey(key)) {
|
||||
return docletMessage.getText(key, a1, a2);
|
||||
}
|
||||
// Check the shared properties file.
|
||||
return message.getText(key, a1, a2);
|
||||
}
|
||||
|
||||
public String getText(String key, String a1, String a2, String a3) {
|
||||
try {
|
||||
//Check the doclet specific properties file.
|
||||
return getDocletSpecificMsg().getText(key, a1, a2, a3);
|
||||
} catch (MissingResourceException e) {
|
||||
//Check the shared properties file.
|
||||
return message.getText(key, a1, a2, a3);
|
||||
// Check the doclet specific properties file.
|
||||
MessageRetriever docletMessage = getDocletSpecificMsg();
|
||||
if (docletMessage.containsKey(key)) {
|
||||
return docletMessage.getText(key, a1, a2, a3);
|
||||
}
|
||||
// Check the shared properties file.
|
||||
return message.getText(key, a1, a2, a3);
|
||||
}
|
||||
|
||||
public abstract Content newContent();
|
||||
|
||||
@ -86,15 +86,32 @@ public class MessageRetriever {
|
||||
this.resourcelocation = resourcelocation;
|
||||
}
|
||||
|
||||
private void initRB() {
|
||||
if (messageRB == null) {
|
||||
private ResourceBundle initRB() {
|
||||
ResourceBundle bundle = messageRB;
|
||||
if (bundle == null) {
|
||||
try {
|
||||
messageRB = ResourceBundle.getBundle(resourcelocation, configuration.getLocale());
|
||||
messageRB = bundle =
|
||||
ResourceBundle.getBundle(resourcelocation, configuration.getLocale());
|
||||
} catch (MissingResourceException e) {
|
||||
throw new Error("Fatal: Resource (" + resourcelocation
|
||||
+ ") for javadoc doclets is missing.");
|
||||
}
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given <code>key</code> can be retrieved
|
||||
* from this <code>MessageRetriever</code>
|
||||
*
|
||||
* @param key
|
||||
* the resource <code>key</code>
|
||||
* @return <code>true</code> if the given <code>key</code> is
|
||||
* contained in the underlying <code>ResourceBundle</code>.
|
||||
*/
|
||||
public boolean containsKey(String key) {
|
||||
ResourceBundle bundle = initRB();
|
||||
return bundle.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,8 +124,8 @@ public class MessageRetriever {
|
||||
* exist in the properties file.
|
||||
*/
|
||||
public String getText(String key, Object... args) throws MissingResourceException {
|
||||
initRB();
|
||||
String message = messageRB.getString(key);
|
||||
ResourceBundle bundle = initRB();
|
||||
String message = bundle.getString(key);
|
||||
return MessageFormat.format(message, args);
|
||||
}
|
||||
|
||||
|
||||
@ -33,6 +33,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.sun.tools.classfile.Dependency.Filter;
|
||||
@ -561,13 +562,10 @@ public class Dependencies {
|
||||
}
|
||||
|
||||
static abstract class BasicDependencyFinder implements Finder {
|
||||
private Map<String,Location> locations = new HashMap<>();
|
||||
private Map<String,Location> locations = new ConcurrentHashMap<>();
|
||||
|
||||
Location getLocation(String className) {
|
||||
Location l = locations.get(className);
|
||||
if (l == null)
|
||||
locations.put(className, l = new SimpleLocation(className));
|
||||
return l;
|
||||
return locations.computeIfAbsent(className, cn -> new SimpleLocation(cn));
|
||||
}
|
||||
|
||||
class Visitor implements ConstantPool.Visitor<Void,Void>, Type.Visitor<Void, Void> {
|
||||
|
||||
@ -25,23 +25,24 @@
|
||||
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import static com.sun.tools.jdeps.JdepsConfiguration.*;
|
||||
|
||||
import com.sun.tools.classfile.Dependency.Location;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.sun.tools.classfile.Dependency.Location;
|
||||
|
||||
/**
|
||||
* Dependency Analyzer.
|
||||
*/
|
||||
@ -52,6 +53,7 @@ public class Analyzer {
|
||||
*/
|
||||
public enum Type {
|
||||
SUMMARY,
|
||||
MODULE, // equivalent to summary in addition, print module descriptor
|
||||
PACKAGE,
|
||||
CLASS,
|
||||
VERBOSE
|
||||
@ -62,9 +64,11 @@ public class Analyzer {
|
||||
* Only the accepted dependencies are recorded.
|
||||
*/
|
||||
interface Filter {
|
||||
boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive);
|
||||
boolean accepts(Location origin, Archive originArchive,
|
||||
Location target, Archive targetArchive);
|
||||
}
|
||||
|
||||
protected final JdepsConfiguration configuration;
|
||||
protected final Type type;
|
||||
protected final Filter filter;
|
||||
protected final Map<Archive, Dependences> results = new HashMap<>();
|
||||
@ -78,7 +82,8 @@ public class Analyzer {
|
||||
* @param type Type of the dependency analysis
|
||||
* @param filter
|
||||
*/
|
||||
public Analyzer(Type type, Filter filter) {
|
||||
Analyzer(JdepsConfiguration config, Type type, Filter filter) {
|
||||
this.configuration = config;
|
||||
this.type = type;
|
||||
this.filter = filter;
|
||||
}
|
||||
@ -86,16 +91,10 @@ public class Analyzer {
|
||||
/**
|
||||
* Performs the dependency analysis on the given archives.
|
||||
*/
|
||||
public boolean run(Stream<? extends Archive> archives) {
|
||||
return run(archives.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the dependency analysis on the given archives.
|
||||
*/
|
||||
public boolean run(Iterable<? extends Archive> archives) {
|
||||
// build a map from Location to Archive
|
||||
buildLocationArchiveMap(archives);
|
||||
boolean run(Iterable<? extends Archive> archives,
|
||||
Map<Location, Archive> locationMap)
|
||||
{
|
||||
this.locationToArchive.putAll(locationMap);
|
||||
|
||||
// traverse and analyze all dependencies
|
||||
for (Archive archive : archives) {
|
||||
@ -106,40 +105,50 @@ public class Analyzer {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void buildLocationArchiveMap(Iterable<? extends Archive> archives) {
|
||||
// build a map from Location to Archive
|
||||
for (Archive archive: archives) {
|
||||
archive.getClasses()
|
||||
.forEach(l -> locationToArchive.putIfAbsent(l, archive));
|
||||
}
|
||||
/**
|
||||
* Returns the analyzed archives
|
||||
*/
|
||||
Set<Archive> archives() {
|
||||
return results.keySet();
|
||||
}
|
||||
|
||||
public boolean hasDependences(Archive archive) {
|
||||
/**
|
||||
* Returns true if the given archive has dependences.
|
||||
*/
|
||||
boolean hasDependences(Archive archive) {
|
||||
if (results.containsKey(archive)) {
|
||||
return results.get(archive).dependencies().size() > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Set<String> dependences(Archive source) {
|
||||
/**
|
||||
* Returns the dependences, either class name or package name
|
||||
* as specified in the given verbose level, from the given source.
|
||||
*/
|
||||
Set<String> dependences(Archive source) {
|
||||
if (!results.containsKey(source)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Dependences result = results.get(source);
|
||||
return result.dependencies().stream()
|
||||
.map(Dep::target)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
return results.get(source).dependencies()
|
||||
.stream()
|
||||
.map(Dep::target)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public Stream<Archive> requires(Archive source) {
|
||||
/**
|
||||
* Returns the direct dependences of the given source
|
||||
*/
|
||||
Stream<Archive> requires(Archive source) {
|
||||
if (!results.containsKey(source)) {
|
||||
return Stream.empty();
|
||||
}
|
||||
Dependences result = results.get(source);
|
||||
return result.requires().stream().filter(a -> !a.isEmpty());
|
||||
return results.get(source).requires()
|
||||
.stream();
|
||||
}
|
||||
|
||||
public interface Visitor {
|
||||
interface Visitor {
|
||||
/**
|
||||
* Visits a recorded dependency from origin to target which can be
|
||||
* a fully-qualified classname, a package name, a module or
|
||||
@ -153,7 +162,7 @@ public class Analyzer {
|
||||
* Visit the dependencies of the given source.
|
||||
* If the requested level is SUMMARY, it will visit the required archives list.
|
||||
*/
|
||||
public void visitDependences(Archive source, Visitor v, Type level) {
|
||||
void visitDependences(Archive source, Visitor v, Type level) {
|
||||
if (level == Type.SUMMARY) {
|
||||
final Dependences result = results.get(source);
|
||||
final Set<Archive> reqs = result.requires();
|
||||
@ -187,7 +196,7 @@ public class Analyzer {
|
||||
}
|
||||
}
|
||||
|
||||
public void visitDependences(Archive source, Visitor v) {
|
||||
void visitDependences(Archive source, Visitor v) {
|
||||
visitDependences(source, v, type);
|
||||
}
|
||||
|
||||
@ -224,14 +233,28 @@ public class Analyzer {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the archive that contains the given location.
|
||||
*/
|
||||
Archive findArchive(Location t) {
|
||||
// local in this archive
|
||||
if (archive.getClasses().contains(t))
|
||||
return archive;
|
||||
|
||||
return locationToArchive.computeIfAbsent(t, _k -> NOT_FOUND);
|
||||
Archive target;
|
||||
if (locationToArchive.containsKey(t)) {
|
||||
target = locationToArchive.get(t);
|
||||
} else {
|
||||
// special case JDK removed API
|
||||
target = configuration.findClass(t)
|
||||
.orElseGet(() -> REMOVED_JDK_INTERNALS.contains(t)
|
||||
? REMOVED_JDK_INTERNALS
|
||||
: NOT_FOUND);
|
||||
}
|
||||
return locationToArchive.computeIfAbsent(t, _k -> target);
|
||||
}
|
||||
|
||||
// return classname or package name depedning on the level
|
||||
// return classname or package name depending on the level
|
||||
private String getLocationName(Location o) {
|
||||
if (level == Type.CLASS || level == Type.VERBOSE) {
|
||||
return o.getClassName();
|
||||
@ -345,4 +368,66 @@ public class Analyzer {
|
||||
target, targetArchive.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static final JdkInternals REMOVED_JDK_INTERNALS = new JdkInternals();
|
||||
|
||||
private static class JdkInternals extends Module {
|
||||
private final String BUNDLE = "com.sun.tools.jdeps.resources.jdkinternals";
|
||||
|
||||
private final Set<String> jdkinternals;
|
||||
private final Set<String> jdkUnsupportedClasses;
|
||||
private JdkInternals() {
|
||||
super("JDK removed internal API");
|
||||
|
||||
try {
|
||||
ResourceBundle rb = ResourceBundle.getBundle(BUNDLE);
|
||||
this.jdkinternals = rb.keySet();
|
||||
} catch (MissingResourceException e) {
|
||||
throw new InternalError("Cannot find jdkinternals resource bundle");
|
||||
}
|
||||
|
||||
this.jdkUnsupportedClasses = getUnsupportedClasses();
|
||||
}
|
||||
|
||||
public boolean contains(Location location) {
|
||||
if (jdkUnsupportedClasses.contains(location.getName() + ".class")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String cn = location.getClassName();
|
||||
int i = cn.lastIndexOf('.');
|
||||
String pn = i > 0 ? cn.substring(0, i) : "";
|
||||
return jdkinternals.contains(cn) || jdkinternals.contains(pn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExported(String pn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private Set<String> getUnsupportedClasses() {
|
||||
// jdk.unsupported may not be observable
|
||||
Optional<Module> om = Profile.FULL_JRE.findModule(JDK_UNSUPPORTED);
|
||||
if (om.isPresent()) {
|
||||
return om.get().reader().entries();
|
||||
}
|
||||
|
||||
// find from local run-time image
|
||||
SystemModuleFinder system = new SystemModuleFinder();
|
||||
if (system.find(JDK_UNSUPPORTED).isPresent()) {
|
||||
try {
|
||||
return system.getClassReader(JDK_UNSUPPORTED).entries();
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,6 +38,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Represents the source of the class files.
|
||||
@ -86,6 +87,10 @@ public class Archive {
|
||||
return Module.UNNAMED_MODULE;
|
||||
}
|
||||
|
||||
public boolean contains(String entry) {
|
||||
return reader.entries().contains(entry);
|
||||
}
|
||||
|
||||
public void addClass(Location origin) {
|
||||
deps.computeIfAbsent(origin, _k -> new HashSet<>());
|
||||
}
|
||||
@ -98,6 +103,15 @@ public class Archive {
|
||||
return deps.keySet();
|
||||
}
|
||||
|
||||
public Stream<Location> getDependencies() {
|
||||
return deps.values().stream()
|
||||
.flatMap(Set::stream);
|
||||
}
|
||||
|
||||
public boolean hasDependences() {
|
||||
return getDependencies().count() > 0;
|
||||
}
|
||||
|
||||
public void visitDependences(Visitor v) {
|
||||
for (Map.Entry<Location,Set<Location>> e: deps.entrySet()) {
|
||||
for (Location target : e.getValue()) {
|
||||
|
||||
@ -173,7 +173,7 @@ public class ClassFileReader {
|
||||
|
||||
static boolean isClass(Path file) {
|
||||
String fn = file.getFileName().toString();
|
||||
return fn.endsWith(".class") && !fn.equals(MODULE_INFO);
|
||||
return fn.endsWith(".class");
|
||||
}
|
||||
|
||||
class FileIterator implements Iterator<ClassFile> {
|
||||
@ -306,7 +306,7 @@ public class ClassFileReader {
|
||||
protected Set<String> scan() {
|
||||
try (JarFile jf = new JarFile(path.toFile())) {
|
||||
return jf.stream().map(JarEntry::getName)
|
||||
.filter(n -> n.endsWith(".class") && !n.endsWith(MODULE_INFO))
|
||||
.filter(n -> n.endsWith(".class"))
|
||||
.collect(Collectors.toSet());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
@ -409,7 +409,7 @@ public class ClassFileReader {
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry e = entries.nextElement();
|
||||
String name = e.getName();
|
||||
if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
|
||||
if (name.endsWith(".class")) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,360 +24,285 @@
|
||||
*/
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import static com.sun.tools.jdeps.Module.*;
|
||||
import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
|
||||
import static java.util.stream.Collectors.*;
|
||||
|
||||
import com.sun.tools.classfile.AccessFlags;
|
||||
import com.sun.tools.classfile.ClassFile;
|
||||
import com.sun.tools.classfile.ConstantPoolException;
|
||||
import com.sun.tools.classfile.Dependencies;
|
||||
import com.sun.tools.classfile.Dependency;
|
||||
import com.sun.tools.classfile.Dependency.Location;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.sun.tools.jdeps.Module.*;
|
||||
import static com.sun.tools.jdeps.ModulePaths.SystemModulePath.JAVA_BASE;
|
||||
/**
|
||||
* Parses class files and finds dependences
|
||||
*/
|
||||
class DependencyFinder {
|
||||
private static Finder API_FINDER = new Finder(true);
|
||||
private static Finder CLASS_FINDER = new Finder(false);
|
||||
|
||||
public class DependencyFinder {
|
||||
private final List<Archive> roots = new ArrayList<>();
|
||||
private final List<Archive> classpaths = new ArrayList<>();
|
||||
private final List<Module> modulepaths = new ArrayList<>();
|
||||
private final List<String> classes = new ArrayList<>();
|
||||
private final boolean compileTimeView;
|
||||
private final JdepsConfiguration configuration;
|
||||
private final JdepsFilter filter;
|
||||
|
||||
DependencyFinder(boolean compileTimeView) {
|
||||
this.compileTimeView = compileTimeView;
|
||||
private final Map<Finder, Deque<Archive>> parsedArchives = new ConcurrentHashMap<>();
|
||||
private final Map<Location, Archive> parsedClasses = new ConcurrentHashMap<>();
|
||||
|
||||
private final ExecutorService pool = Executors.newFixedThreadPool(2);
|
||||
private final Deque<FutureTask<Set<Location>>> tasks = new ConcurrentLinkedDeque<>();
|
||||
|
||||
DependencyFinder(JdepsConfiguration configuration,
|
||||
JdepsFilter filter) {
|
||||
this.configuration = configuration;
|
||||
this.filter = filter;
|
||||
this.parsedArchives.put(API_FINDER, new ConcurrentLinkedDeque<>());
|
||||
this.parsedArchives.put(CLASS_FINDER, new ConcurrentLinkedDeque<>());
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds a class name to the root set
|
||||
*/
|
||||
void addClassName(String cn) {
|
||||
classes.add(cn);
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds the archive of the given path to the root set
|
||||
*/
|
||||
void addRoot(Path path) {
|
||||
addRoot(Archive.getInstance(path));
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds the given archive to the root set
|
||||
*/
|
||||
void addRoot(Archive archive) {
|
||||
Objects.requireNonNull(archive);
|
||||
if (!roots.contains(archive))
|
||||
roots.add(archive);
|
||||
Map<Location, Archive> locationToArchive() {
|
||||
return parsedClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an archive specified in the classpath.
|
||||
* Returns the modules of all dependencies found
|
||||
*/
|
||||
void addClassPathArchive(Path path) {
|
||||
addClassPathArchive(Archive.getInstance(path));
|
||||
Stream<Archive> getDependences(Archive source) {
|
||||
return source.getDependencies()
|
||||
.map(this::locationToArchive)
|
||||
.filter(a -> a != source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an archive specified in the classpath.
|
||||
*/
|
||||
void addClassPathArchive(Archive archive) {
|
||||
Objects.requireNonNull(archive);
|
||||
classpaths.add(archive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an archive specified in the modulepath.
|
||||
*/
|
||||
void addModule(Module m) {
|
||||
Objects.requireNonNull(m);
|
||||
modulepaths.add(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root set.
|
||||
*/
|
||||
List<Archive> roots() {
|
||||
return roots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream of all archives including the root set, module paths,
|
||||
* and classpath.
|
||||
* Returns the location to archive map; or NOT_FOUND.
|
||||
*
|
||||
* This only returns the archives with classes parsed.
|
||||
* Location represents a parsed class.
|
||||
*/
|
||||
Stream<Archive> archives() {
|
||||
Stream<Archive> archives = Stream.concat(roots.stream(), modulepaths.stream());
|
||||
archives = Stream.concat(archives, classpaths.stream());
|
||||
return archives.filter(a -> !a.isEmpty())
|
||||
.distinct();
|
||||
Archive locationToArchive(Location location) {
|
||||
return parsedClasses.containsKey(location)
|
||||
? parsedClasses.get(location)
|
||||
: configuration.findClass(location).orElse(NOT_FOUND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds dependencies
|
||||
*
|
||||
* @param apiOnly API only
|
||||
* @param maxDepth depth of transitive dependency analysis; zero indicates
|
||||
* @throws IOException
|
||||
* Returns a map from an archive to its required archives
|
||||
*/
|
||||
void findDependencies(JdepsFilter filter, boolean apiOnly, int maxDepth)
|
||||
throws IOException
|
||||
{
|
||||
Dependency.Finder finder =
|
||||
apiOnly ? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED)
|
||||
: Dependencies.getClassDependencyFinder();
|
||||
Map<Archive, Set<Archive>> dependences() {
|
||||
Map<Archive, Set<Archive>> map = new HashMap<>();
|
||||
parsedArchives.values().stream()
|
||||
.flatMap(Deque::stream)
|
||||
.filter(a -> !a.isEmpty())
|
||||
.forEach(source -> {
|
||||
Set<Archive> deps = getDependences(source).collect(toSet());
|
||||
if (!deps.isEmpty()) {
|
||||
map.put(source, deps);
|
||||
}
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
// list of archives to be analyzed
|
||||
Set<Archive> roots = new LinkedHashSet<>(this.roots);
|
||||
boolean isParsed(Location location) {
|
||||
return parsedClasses.containsKey(location);
|
||||
}
|
||||
|
||||
// include java.base in root set
|
||||
roots.add(JAVA_BASE);
|
||||
/**
|
||||
* Parses all class files from the given archive stream and returns
|
||||
* all target locations.
|
||||
*/
|
||||
public Set<Location> parse(Stream<? extends Archive> archiveStream) {
|
||||
archiveStream.forEach(archive -> parse(archive, CLASS_FINDER));
|
||||
return waitForTasksCompleted();
|
||||
}
|
||||
|
||||
// If -include pattern specified, classes may be in module path or class path.
|
||||
// To get compile time view analysis, all classes are analyzed.
|
||||
// add all modules except JDK modules to root set
|
||||
modulepaths.stream()
|
||||
.filter(filter::matches)
|
||||
.forEach(roots::add);
|
||||
/**
|
||||
* Parses the exported API class files from the given archive stream and
|
||||
* returns all target locations.
|
||||
*/
|
||||
public Set<Location> parseExportedAPIs(Stream<? extends Archive> archiveStream) {
|
||||
archiveStream.forEach(archive -> parse(archive, API_FINDER));
|
||||
return waitForTasksCompleted();
|
||||
}
|
||||
|
||||
// add classpath to the root set
|
||||
classpaths.stream()
|
||||
.filter(filter::matches)
|
||||
.forEach(roots::add);
|
||||
|
||||
// transitive dependency
|
||||
int depth = maxDepth > 0 ? maxDepth : Integer.MAX_VALUE;
|
||||
|
||||
// Work queue of names of classfiles to be searched.
|
||||
// Entries will be unique, and for classes that do not yet have
|
||||
// dependencies in the results map.
|
||||
ConcurrentLinkedDeque<String> deque = new ConcurrentLinkedDeque<>();
|
||||
ConcurrentSkipListSet<String> doneClasses = new ConcurrentSkipListSet<>();
|
||||
|
||||
TaskExecutor executor = new TaskExecutor(finder, filter, apiOnly, deque, doneClasses);
|
||||
/**
|
||||
* Parses the named class from the given archive and
|
||||
* returns all target locations the named class references.
|
||||
*/
|
||||
public Set<Location> parse(Archive archive, String name) {
|
||||
try {
|
||||
// get the immediate dependencies of the input files
|
||||
for (Archive source : roots) {
|
||||
executor.task(source, deque);
|
||||
}
|
||||
executor.waitForTasksCompleted();
|
||||
|
||||
List<Archive> archives = Stream.concat(Stream.concat(roots.stream(),
|
||||
modulepaths.stream()),
|
||||
classpaths.stream())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Additional pass to find archive where dependences are identified
|
||||
// and also any specified classes, if any.
|
||||
// If -R is specified, perform transitive dependency analysis.
|
||||
Deque<String> unresolved = new LinkedList<>(classes);
|
||||
do {
|
||||
String name;
|
||||
while ((name = unresolved.poll()) != null) {
|
||||
if (doneClasses.contains(name)) {
|
||||
continue;
|
||||
}
|
||||
if (compileTimeView) {
|
||||
final String cn = name + ".class";
|
||||
// parse all classes in the source archive
|
||||
Optional<Archive> source = archives.stream()
|
||||
.filter(a -> a.reader().entries().contains(cn))
|
||||
.findFirst();
|
||||
trace("%s compile time view %s%n", name, source.map(Archive::getName).orElse(" not found"));
|
||||
if (source.isPresent()) {
|
||||
executor.runTask(source.get(), deque);
|
||||
}
|
||||
}
|
||||
ClassFile cf = null;
|
||||
for (Archive archive : archives) {
|
||||
cf = archive.reader().getClassFile(name);
|
||||
|
||||
if (cf != null) {
|
||||
String classFileName;
|
||||
try {
|
||||
classFileName = cf.getName();
|
||||
} catch (ConstantPoolException e) {
|
||||
throw new Dependencies.ClassFileError(e);
|
||||
}
|
||||
if (!doneClasses.contains(classFileName)) {
|
||||
// if name is a fully-qualified class name specified
|
||||
// from command-line, this class might already be parsed
|
||||
doneClasses.add(classFileName);
|
||||
for (Dependency d : finder.findDependencies(cf)) {
|
||||
if (depth == 0) {
|
||||
// ignore the dependency
|
||||
archive.addClass(d.getOrigin());
|
||||
break;
|
||||
} else if (filter.accepts(d) && filter.accept(archive)) {
|
||||
// continue analysis on non-JDK classes
|
||||
archive.addClass(d.getOrigin(), d.getTarget());
|
||||
String cn = d.getTarget().getName();
|
||||
if (!doneClasses.contains(cn) && !deque.contains(cn)) {
|
||||
deque.add(cn);
|
||||
}
|
||||
} else {
|
||||
// ensure that the parsed class is added the archive
|
||||
archive.addClass(d.getOrigin());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cf == null) {
|
||||
doneClasses.add(name);
|
||||
}
|
||||
}
|
||||
unresolved = deque;
|
||||
deque = new ConcurrentLinkedDeque<>();
|
||||
} while (!unresolved.isEmpty() && depth-- > 0);
|
||||
} finally {
|
||||
executor.shutdown();
|
||||
return parse(archive, CLASS_FINDER, name);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TaskExecutor creates FutureTask to analyze all classes in a given archive
|
||||
* Parses the exported API of the named class from the given archive and
|
||||
* returns all target locations the named class references.
|
||||
*/
|
||||
private class TaskExecutor {
|
||||
final ExecutorService pool;
|
||||
final Dependency.Finder finder;
|
||||
final JdepsFilter filter;
|
||||
final boolean apiOnly;
|
||||
final Set<String> doneClasses;
|
||||
final Map<Archive, FutureTask<Void>> tasks = new HashMap<>();
|
||||
|
||||
TaskExecutor(Dependency.Finder finder,
|
||||
JdepsFilter filter,
|
||||
boolean apiOnly,
|
||||
ConcurrentLinkedDeque<String> deque,
|
||||
Set<String> doneClasses) {
|
||||
this.pool = Executors.newFixedThreadPool(2);
|
||||
this.finder = finder;
|
||||
this.filter = filter;
|
||||
this.apiOnly = apiOnly;
|
||||
this.doneClasses = doneClasses;
|
||||
public Set<Location> parseExportedAPIs(Archive archive, String name)
|
||||
{
|
||||
try {
|
||||
return parse(archive, API_FINDER, name);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new task to analyze class files in the given archive.
|
||||
* The dependences are added to the given deque for analysis.
|
||||
*/
|
||||
FutureTask<Void> task(Archive archive, final ConcurrentLinkedDeque<String> deque) {
|
||||
trace("parsing %s %s%n", archive.getName(), archive.path());
|
||||
FutureTask<Void> task = new FutureTask<Void>(new Callable<Void>() {
|
||||
public Void call() throws Exception {
|
||||
for (ClassFile cf : archive.reader().getClassFiles()) {
|
||||
String classFileName;
|
||||
try {
|
||||
classFileName = cf.getName();
|
||||
} catch (ConstantPoolException e) {
|
||||
throw new Dependencies.ClassFileError(e);
|
||||
}
|
||||
private Optional<FutureTask<Set<Location>>> parse(Archive archive, Finder finder) {
|
||||
if (parsedArchives.get(finder).contains(archive))
|
||||
return Optional.empty();
|
||||
|
||||
// tests if this class matches the -include
|
||||
String cn = classFileName.replace('/', '.');
|
||||
if (!filter.matches(cn))
|
||||
continue;
|
||||
parsedArchives.get(finder).add(archive);
|
||||
|
||||
// if -apionly is specified, analyze only exported and public types
|
||||
if (apiOnly && !(isExported(archive, cn) && cf.access_flags.is(AccessFlags.ACC_PUBLIC)))
|
||||
continue;
|
||||
|
||||
if (!doneClasses.contains(classFileName)) {
|
||||
doneClasses.add(classFileName);
|
||||
}
|
||||
|
||||
for (Dependency d : finder.findDependencies(cf)) {
|
||||
if (filter.accepts(d) && filter.accept(archive)) {
|
||||
String name = d.getTarget().getName();
|
||||
if (!doneClasses.contains(name) && !deque.contains(name)) {
|
||||
deque.add(name);
|
||||
}
|
||||
archive.addClass(d.getOrigin(), d.getTarget());
|
||||
} else {
|
||||
// ensure that the parsed class is added the archive
|
||||
archive.addClass(d.getOrigin());
|
||||
}
|
||||
}
|
||||
trace("parsing %s %s%n", archive.getName(), archive.path());
|
||||
FutureTask<Set<Location>> task = new FutureTask<>(new Callable<>() {
|
||||
public Set<Location> call() throws Exception {
|
||||
Set<Location> targets = new HashSet<>();
|
||||
for (ClassFile cf : archive.reader().getClassFiles()) {
|
||||
String classFileName;
|
||||
try {
|
||||
classFileName = cf.getName();
|
||||
} catch (ConstantPoolException e) {
|
||||
throw new Dependencies.ClassFileError(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
tasks.put(archive, task);
|
||||
pool.submit(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
/*
|
||||
* This task will parse all class files of the given archive, if it's a new task.
|
||||
* This method waits until the task is completed.
|
||||
*/
|
||||
void runTask(Archive archive, final ConcurrentLinkedDeque<String> deque) {
|
||||
if (tasks.containsKey(archive))
|
||||
return;
|
||||
|
||||
FutureTask<Void> task = task(archive, deque);
|
||||
try {
|
||||
// wait for completion
|
||||
task.get();
|
||||
} catch (InterruptedException|ExecutionException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Waits until all submitted tasks are completed.
|
||||
*/
|
||||
void waitForTasksCompleted() {
|
||||
try {
|
||||
for (FutureTask<Void> t : tasks.values()) {
|
||||
if (t.isDone())
|
||||
// filter source class/archive
|
||||
String cn = classFileName.replace('/', '.');
|
||||
if (!finder.accept(archive, cn, cf.access_flags))
|
||||
continue;
|
||||
|
||||
// wait for completion
|
||||
t.get();
|
||||
// tests if this class matches the -include
|
||||
if (!filter.matches(cn))
|
||||
continue;
|
||||
|
||||
for (Dependency d : finder.findDependencies(cf)) {
|
||||
if (filter.accepts(d)) {
|
||||
archive.addClass(d.getOrigin(), d.getTarget());
|
||||
targets.add(d.getTarget());
|
||||
} else {
|
||||
// ensure that the parsed class is added the archive
|
||||
archive.addClass(d.getOrigin());
|
||||
}
|
||||
parsedClasses.putIfAbsent(d.getOrigin(), archive);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException|ExecutionException e) {
|
||||
throw new Error(e);
|
||||
|
||||
return targets;
|
||||
}
|
||||
});
|
||||
tasks.add(task);
|
||||
pool.submit(task);
|
||||
return Optional.of(task);
|
||||
}
|
||||
|
||||
private Set<Location> parse(Archive archive, Finder finder, String name)
|
||||
throws IOException
|
||||
{
|
||||
ClassFile cf = archive.reader().getClassFile(name);
|
||||
if (cf == null) {
|
||||
throw new IllegalArgumentException(archive.getName() + " does not contain " + name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Shutdown the executor service.
|
||||
*/
|
||||
void shutdown() {
|
||||
pool.shutdown();
|
||||
Set<Location> targets = new HashSet<>();
|
||||
String cn;
|
||||
try {
|
||||
cn = cf.getName().replace('/', '.');
|
||||
} catch (ConstantPoolException e) {
|
||||
throw new Dependencies.ClassFileError(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the given class name is exported by the given archive.
|
||||
*
|
||||
* All packages are exported in unnamed module.
|
||||
*/
|
||||
private boolean isExported(Archive archive, String classname) {
|
||||
int i = classname.lastIndexOf('.');
|
||||
String pn = i > 0 ? classname.substring(0, i) : "";
|
||||
return archive.getModule().isExported(pn);
|
||||
if (!finder.accept(archive, cn, cf.access_flags))
|
||||
return targets;
|
||||
|
||||
// tests if this class matches the -include
|
||||
if (!filter.matches(cn))
|
||||
return targets;
|
||||
|
||||
// skip checking filter.matches
|
||||
for (Dependency d : finder.findDependencies(cf)) {
|
||||
if (filter.accepts(d)) {
|
||||
targets.add(d.getTarget());
|
||||
archive.addClass(d.getOrigin(), d.getTarget());
|
||||
} else {
|
||||
// ensure that the parsed class is added the archive
|
||||
archive.addClass(d.getOrigin());
|
||||
}
|
||||
parsedClasses.putIfAbsent(d.getOrigin(), archive);
|
||||
}
|
||||
return targets;
|
||||
}
|
||||
|
||||
/*
|
||||
* Waits until all submitted tasks are completed.
|
||||
*/
|
||||
private Set<Location> waitForTasksCompleted() {
|
||||
try {
|
||||
Set<Location> targets = new HashSet<>();
|
||||
FutureTask<Set<Location>> task;
|
||||
while ((task = tasks.poll()) != null) {
|
||||
// wait for completion
|
||||
if (!task.isDone())
|
||||
targets.addAll(task.get());
|
||||
}
|
||||
return targets;
|
||||
} catch (InterruptedException|ExecutionException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Shutdown the executor service.
|
||||
*/
|
||||
void shutdown() {
|
||||
pool.shutdown();
|
||||
}
|
||||
|
||||
private interface SourceFilter {
|
||||
boolean accept(Archive archive, String cn, AccessFlags accessFlags);
|
||||
}
|
||||
|
||||
private static class Finder implements Dependency.Finder, SourceFilter {
|
||||
private final Dependency.Finder finder;
|
||||
private final boolean apiOnly;
|
||||
Finder(boolean apiOnly) {
|
||||
this.apiOnly = apiOnly;
|
||||
this.finder = apiOnly
|
||||
? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED)
|
||||
: Dependencies.getClassDependencyFinder();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(Archive archive, String cn, AccessFlags accessFlags) {
|
||||
int i = cn.lastIndexOf('.');
|
||||
String pn = i > 0 ? cn.substring(0, i) : "";
|
||||
|
||||
// if -apionly is specified, analyze only exported and public types
|
||||
// All packages are exported in unnamed module.
|
||||
return apiOnly ? archive.getModule().isExported(pn) &&
|
||||
accessFlags.is(AccessFlags.ACC_PUBLIC)
|
||||
: true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {
|
||||
return finder.findDependencies(classfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,390 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import com.sun.tools.classfile.Dependency.Location;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.sun.tools.jdeps.Analyzer.Type.CLASS;
|
||||
import static com.sun.tools.jdeps.Analyzer.Type.VERBOSE;
|
||||
import static com.sun.tools.jdeps.Module.trace;
|
||||
import static java.util.stream.Collectors.*;
|
||||
|
||||
/**
|
||||
* Dependency Analyzer.
|
||||
*
|
||||
* Type of filters:
|
||||
* source filter: -include <pattern>
|
||||
* target filter: -package, -regex, -requires
|
||||
*
|
||||
* The initial archive set for analysis includes
|
||||
* 1. archives specified in the command line arguments
|
||||
* 2. observable modules matching the source filter
|
||||
* 3. classpath archives matching the source filter or target filter
|
||||
* 4. -addmods and -m root modules
|
||||
*/
|
||||
public class DepsAnalyzer {
|
||||
final JdepsConfiguration configuration;
|
||||
final JdepsFilter filter;
|
||||
final JdepsWriter writer;
|
||||
final Analyzer.Type verbose;
|
||||
final boolean apiOnly;
|
||||
|
||||
final DependencyFinder finder;
|
||||
final Analyzer analyzer;
|
||||
final List<Archive> rootArchives = new ArrayList<>();
|
||||
|
||||
// parsed archives
|
||||
final Set<Archive> archives = new LinkedHashSet<>();
|
||||
|
||||
public DepsAnalyzer(JdepsConfiguration config,
|
||||
JdepsFilter filter,
|
||||
JdepsWriter writer,
|
||||
Analyzer.Type verbose,
|
||||
boolean apiOnly) {
|
||||
this.configuration = config;
|
||||
this.filter = filter;
|
||||
this.writer = writer;
|
||||
this.verbose = verbose;
|
||||
this.apiOnly = apiOnly;
|
||||
|
||||
this.finder = new DependencyFinder(config, filter);
|
||||
this.analyzer = new Analyzer(configuration, verbose, filter);
|
||||
|
||||
// determine initial archives to be analyzed
|
||||
this.rootArchives.addAll(configuration.initialArchives());
|
||||
|
||||
// if -include pattern is specified, add the matching archives on
|
||||
// classpath to the root archives
|
||||
if (filter.hasIncludePattern() || filter.hasTargetFilter()) {
|
||||
configuration.getModules().values().stream()
|
||||
.filter(source -> filter.include(source) && filter.matches(source))
|
||||
.forEach(this.rootArchives::add);
|
||||
}
|
||||
|
||||
// class path archives
|
||||
configuration.classPathArchives().stream()
|
||||
.filter(filter::matches)
|
||||
.forEach(this.rootArchives::add);
|
||||
|
||||
// Include the root modules for analysis
|
||||
this.rootArchives.addAll(configuration.rootModules());
|
||||
|
||||
trace("analyze root archives: %s%n", this.rootArchives);
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform runtime dependency analysis
|
||||
*/
|
||||
public boolean run() throws IOException {
|
||||
return run(false, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform compile-time view or run-time view dependency analysis.
|
||||
*
|
||||
* @param compileTimeView
|
||||
* @param maxDepth depth of recursive analysis. depth == 0 if -R is set
|
||||
*/
|
||||
public boolean run(boolean compileTimeView, int maxDepth) throws IOException {
|
||||
try {
|
||||
// parse each packaged module or classpath archive
|
||||
if (apiOnly) {
|
||||
finder.parseExportedAPIs(rootArchives.stream());
|
||||
} else {
|
||||
finder.parse(rootArchives.stream());
|
||||
}
|
||||
archives.addAll(rootArchives);
|
||||
|
||||
int depth = maxDepth > 0 ? maxDepth : Integer.MAX_VALUE;
|
||||
|
||||
// transitive analysis
|
||||
if (depth > 1) {
|
||||
if (compileTimeView)
|
||||
transitiveArchiveDeps(depth-1);
|
||||
else
|
||||
transitiveDeps(depth-1);
|
||||
}
|
||||
|
||||
Set<Archive> archives = archives();
|
||||
|
||||
// analyze the dependencies collected
|
||||
analyzer.run(archives, finder.locationToArchive());
|
||||
|
||||
writer.generateOutput(archives, analyzer);
|
||||
} finally {
|
||||
finder.shutdown();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the archives for reporting that has matching dependences.
|
||||
*
|
||||
* If -requires is set, they should be excluded.
|
||||
*/
|
||||
Set<Archive> archives() {
|
||||
if (filter.requiresFilter().isEmpty()) {
|
||||
return archives.stream()
|
||||
.filter(filter::include)
|
||||
.filter(Archive::hasDependences)
|
||||
.collect(Collectors.toSet());
|
||||
} else {
|
||||
// use the archives that have dependences and not specified in -requires
|
||||
return archives.stream()
|
||||
.filter(filter::include)
|
||||
.filter(source -> !filter.requiresFilter().contains(source))
|
||||
.filter(source ->
|
||||
source.getDependencies()
|
||||
.map(finder::locationToArchive)
|
||||
.anyMatch(a -> a != source))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dependences, either class name or package name
|
||||
* as specified in the given verbose level.
|
||||
*/
|
||||
Stream<String> dependences() {
|
||||
return analyzer.archives().stream()
|
||||
.map(analyzer::dependences)
|
||||
.flatMap(Set::stream)
|
||||
.distinct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the archives that contains the given locations and
|
||||
* not parsed and analyzed.
|
||||
*/
|
||||
private Set<Archive> unresolvedArchives(Stream<Location> locations) {
|
||||
return locations.filter(l -> !finder.isParsed(l))
|
||||
.distinct()
|
||||
.map(configuration::findClass)
|
||||
.flatMap(Optional::stream)
|
||||
.filter(filter::include)
|
||||
.collect(toSet());
|
||||
}
|
||||
|
||||
/*
|
||||
* Recursively analyzes entire module/archives.
|
||||
*/
|
||||
private void transitiveArchiveDeps(int depth) throws IOException {
|
||||
Stream<Location> deps = archives.stream()
|
||||
.flatMap(Archive::getDependencies);
|
||||
|
||||
// start with the unresolved archives
|
||||
Set<Archive> unresolved = unresolvedArchives(deps);
|
||||
do {
|
||||
// parse all unresolved archives
|
||||
Set<Location> targets = apiOnly
|
||||
? finder.parseExportedAPIs(unresolved.stream())
|
||||
: finder.parse(unresolved.stream());
|
||||
archives.addAll(unresolved);
|
||||
|
||||
// Add dependencies to the next batch for analysis
|
||||
unresolved = unresolvedArchives(targets.stream());
|
||||
} while (!unresolved.isEmpty() && depth-- > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Recursively analyze the class dependences
|
||||
*/
|
||||
private void transitiveDeps(int depth) throws IOException {
|
||||
Stream<Location> deps = archives.stream()
|
||||
.flatMap(Archive::getDependencies);
|
||||
|
||||
Deque<Location> unresolved = deps.collect(Collectors.toCollection(LinkedList::new));
|
||||
ConcurrentLinkedDeque<Location> deque = new ConcurrentLinkedDeque<>();
|
||||
do {
|
||||
Location target;
|
||||
while ((target = unresolved.poll()) != null) {
|
||||
if (finder.isParsed(target))
|
||||
continue;
|
||||
|
||||
Archive archive = configuration.findClass(target).orElse(null);
|
||||
if (archive != null && filter.include(archive)) {
|
||||
archives.add(archive);
|
||||
|
||||
String name = target.getName();
|
||||
Set<Location> targets = apiOnly
|
||||
? finder.parseExportedAPIs(archive, name)
|
||||
: finder.parse(archive, name);
|
||||
|
||||
// build unresolved dependencies
|
||||
targets.stream()
|
||||
.filter(t -> !finder.isParsed(t))
|
||||
.forEach(deque::add);
|
||||
}
|
||||
}
|
||||
unresolved = deque;
|
||||
deque = new ConcurrentLinkedDeque<>();
|
||||
} while (!unresolved.isEmpty() && depth-- > 0);
|
||||
}
|
||||
|
||||
// ----- for testing purpose -----
|
||||
|
||||
public static enum Info {
|
||||
REQUIRES,
|
||||
REQUIRES_PUBLIC,
|
||||
EXPORTED_API,
|
||||
MODULE_PRIVATE,
|
||||
QUALIFIED_EXPORTED_API,
|
||||
INTERNAL_API,
|
||||
JDK_INTERNAL_API
|
||||
}
|
||||
|
||||
public static class Node {
|
||||
public final String name;
|
||||
public final String source;
|
||||
public final Info info;
|
||||
Node(String name, Info info) {
|
||||
this(name, name, info);
|
||||
}
|
||||
Node(String name, String source, Info info) {
|
||||
this.name = name;
|
||||
this.source = source;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (info != Info.REQUIRES && info != Info.REQUIRES_PUBLIC)
|
||||
sb.append(source).append("/");
|
||||
|
||||
sb.append(name);
|
||||
if (info == Info.QUALIFIED_EXPORTED_API)
|
||||
sb.append(" (qualified)");
|
||||
else if (info == Info.JDK_INTERNAL_API)
|
||||
sb.append(" (JDK internal)");
|
||||
else if (info == Info.INTERNAL_API)
|
||||
sb.append(" (internal)");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Node))
|
||||
return false;
|
||||
|
||||
Node other = (Node)o;
|
||||
return this.name.equals(other.name) &&
|
||||
this.source.equals(other.source) &&
|
||||
this.info.equals(other.info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = name.hashCode();
|
||||
result = 31 * result + source.hashCode();
|
||||
result = 31 * result + info.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a graph of module dependences.
|
||||
*
|
||||
* Each Node represents a module and each edge is a dependence.
|
||||
* No analysis on "requires public".
|
||||
*/
|
||||
public Graph<Node> moduleGraph() {
|
||||
Graph.Builder<Node> builder = new Graph.Builder<>();
|
||||
|
||||
archives().stream()
|
||||
.forEach(m -> {
|
||||
Node u = new Node(m.getName(), Info.REQUIRES);
|
||||
builder.addNode(u);
|
||||
analyzer.requires(m)
|
||||
.map(req -> new Node(req.getName(), Info.REQUIRES))
|
||||
.forEach(v -> builder.addEdge(u, v));
|
||||
});
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a graph of dependences.
|
||||
*
|
||||
* Each Node represents a class or package per the specified verbose level.
|
||||
* Each edge indicates
|
||||
*/
|
||||
public Graph<Node> dependenceGraph() {
|
||||
Graph.Builder<Node> builder = new Graph.Builder<>();
|
||||
|
||||
archives().stream()
|
||||
.map(analyzer.results::get)
|
||||
.filter(deps -> !deps.dependencies().isEmpty())
|
||||
.flatMap(deps -> deps.dependencies().stream())
|
||||
.forEach(d -> addEdge(builder, d));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void addEdge(Graph.Builder<Node> builder, Analyzer.Dep dep) {
|
||||
Archive source = dep.originArchive();
|
||||
Archive target = dep.targetArchive();
|
||||
String pn = dep.target();
|
||||
if ((verbose == CLASS || verbose == VERBOSE)) {
|
||||
int i = dep.target().lastIndexOf('.');
|
||||
pn = i > 0 ? dep.target().substring(0, i) : "";
|
||||
}
|
||||
final Info info;
|
||||
if (source == target) {
|
||||
info = Info.MODULE_PRIVATE;
|
||||
} else if (!target.getModule().isNamed()) {
|
||||
info = Info.EXPORTED_API;
|
||||
} else if (target.getModule().isExported(pn)) {
|
||||
info = Info.EXPORTED_API;
|
||||
} else {
|
||||
Module module = target.getModule();
|
||||
|
||||
if (!source.getModule().isJDK() && module.isJDK())
|
||||
info = Info.JDK_INTERNAL_API;
|
||||
// qualified exports or inaccessible
|
||||
else if (module.isExported(pn, source.getModule().name()))
|
||||
info = Info.QUALIFIED_EXPORTED_API;
|
||||
else
|
||||
info = Info.INTERNAL_API;
|
||||
}
|
||||
|
||||
Node u = new Node(dep.origin(), source.getName(), info);
|
||||
Node v = new Node(dep.target(), target.getName(), info);
|
||||
builder.addEdge(u, v);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class Graph<T> {
|
||||
private final Set<T> nodes;
|
||||
private final Map<T, Set<T>> edges;
|
||||
|
||||
public Graph(Set<T> nodes, Map<T, Set<T>> edges) {
|
||||
this.nodes = Collections.unmodifiableSet(nodes);
|
||||
this.edges = Collections.unmodifiableMap(edges);
|
||||
}
|
||||
|
||||
public Set<T> nodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public Map<T, Set<T>> edges() {
|
||||
return edges;
|
||||
}
|
||||
|
||||
public Set<T> adjacentNodes(T u) {
|
||||
return edges.get(u);
|
||||
}
|
||||
|
||||
public boolean contains(T u) {
|
||||
return nodes.contains(u);
|
||||
}
|
||||
|
||||
public Set<Edge<T>> edgesFrom(T u) {
|
||||
return edges.get(u).stream()
|
||||
.map(v -> new Edge<T>(u, v))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Graph after transitive reduction
|
||||
*/
|
||||
public Graph<T> reduce() {
|
||||
Builder<T> builder = new Builder<>();
|
||||
nodes.stream()
|
||||
.forEach(u -> {
|
||||
builder.addNode(u);
|
||||
edges.get(u).stream()
|
||||
.filter(v -> !pathExists(u, v, false))
|
||||
.forEach(v -> builder.addEdge(u, v));
|
||||
});
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Graph after transitive reduction. All edges in
|
||||
* the given g takes precedence over this graph.
|
||||
*
|
||||
* @throw IllegalArgumentException g must be a subgraph this graph
|
||||
*/
|
||||
public Graph<T> reduce(Graph<T> g) {
|
||||
boolean subgraph = nodes.containsAll(g.nodes) &&
|
||||
g.edges.keySet().stream()
|
||||
.allMatch(u -> adjacentNodes(u).containsAll(g.adjacentNodes(u)));
|
||||
if (!subgraph) {
|
||||
throw new IllegalArgumentException(g + " is not a subgraph of " + this);
|
||||
}
|
||||
|
||||
Builder<T> builder = new Builder<>();
|
||||
nodes.stream()
|
||||
.forEach(u -> {
|
||||
builder.addNode(u);
|
||||
// filter the edge if there exists a path from u to v in the given g
|
||||
// or there exists another path from u to v in this graph
|
||||
edges.get(u).stream()
|
||||
.filter(v -> !g.pathExists(u, v) && !pathExists(u, v, false))
|
||||
.forEach(v -> builder.addEdge(u, v));
|
||||
});
|
||||
|
||||
// add the overlapped edges from this graph and the given g
|
||||
g.edges().keySet().stream()
|
||||
.forEach(u -> g.adjacentNodes(u).stream()
|
||||
.filter(v -> isAdjacent(u, v))
|
||||
.forEach(v -> builder.addEdge(u, v)));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns nodes sorted in topological order.
|
||||
*/
|
||||
public Stream<T> orderedNodes() {
|
||||
TopoSorter<T> sorter = new TopoSorter<>(this);
|
||||
return sorter.result.stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse this graph and performs the given action in topological order
|
||||
*/
|
||||
public void ordered(Consumer<T> action) {
|
||||
TopoSorter<T> sorter = new TopoSorter<>(this);
|
||||
sorter.ordered(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses this graph and performs the given action in reverse topological order
|
||||
*/
|
||||
public void reverse(Consumer<T> action) {
|
||||
TopoSorter<T> sorter = new TopoSorter<>(this);
|
||||
sorter.reverse(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a transposed graph from this graph
|
||||
*/
|
||||
public Graph<T> transpose() {
|
||||
Builder<T> builder = new Builder<>();
|
||||
builder.addNodes(nodes);
|
||||
// reverse edges
|
||||
edges.keySet().forEach(u -> {
|
||||
edges.get(u).stream()
|
||||
.forEach(v -> builder.addEdge(v, u));
|
||||
});
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all nodes reachable from the given set of roots.
|
||||
*/
|
||||
public Set<T> dfs(Set<T> roots) {
|
||||
Deque<T> deque = new LinkedList<>(roots);
|
||||
Set<T> visited = new HashSet<>();
|
||||
while (!deque.isEmpty()) {
|
||||
T u = deque.pop();
|
||||
if (!visited.contains(u)) {
|
||||
visited.add(u);
|
||||
if (contains(u)) {
|
||||
adjacentNodes(u).stream()
|
||||
.filter(v -> !visited.contains(v))
|
||||
.forEach(deque::push);
|
||||
}
|
||||
}
|
||||
}
|
||||
return visited;
|
||||
}
|
||||
|
||||
private boolean isAdjacent(T u, T v) {
|
||||
return edges.containsKey(u) && edges.get(u).contains(v);
|
||||
}
|
||||
|
||||
private boolean pathExists(T u, T v) {
|
||||
return pathExists(u, v, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there exists a path from u to v in this graph.
|
||||
* If includeAdjacent is false, it returns true if there exists
|
||||
* another path from u to v of distance > 1
|
||||
*/
|
||||
private boolean pathExists(T u, T v, boolean includeAdjacent) {
|
||||
if (!nodes.contains(u) || !nodes.contains(v)) {
|
||||
return false;
|
||||
}
|
||||
if (includeAdjacent && isAdjacent(u, v)) {
|
||||
return true;
|
||||
}
|
||||
Deque<T> stack = new LinkedList<>();
|
||||
Set<T> visited = new HashSet<>();
|
||||
stack.push(u);
|
||||
while (!stack.isEmpty()) {
|
||||
T node = stack.pop();
|
||||
if (node.equals(v)) {
|
||||
return true;
|
||||
}
|
||||
if (!visited.contains(node)) {
|
||||
visited.add(node);
|
||||
edges.get(node).stream()
|
||||
.filter(e -> includeAdjacent || !node.equals(u) || !e.equals(v))
|
||||
.forEach(e -> stack.push(e));
|
||||
}
|
||||
}
|
||||
assert !visited.contains(v);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void printGraph(PrintWriter out) {
|
||||
out.println("graph for " + nodes);
|
||||
nodes.stream()
|
||||
.forEach(u -> adjacentNodes(u).stream()
|
||||
.forEach(v -> out.format(" %s -> %s%n", u, v)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return nodes.toString();
|
||||
}
|
||||
|
||||
static class Edge<T> {
|
||||
final T u;
|
||||
final T v;
|
||||
Edge(T u, T v) {
|
||||
this.u = u;
|
||||
this.v = v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s -> %s", u, v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || !(o instanceof Edge))
|
||||
return false;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Edge<T> edge = (Edge<T>) o;
|
||||
|
||||
return u.equals(edge.u) && v.equals(edge.v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = u.hashCode();
|
||||
result = 31 * result + v.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static class Builder<T> {
|
||||
final Set<T> nodes = new HashSet<>();
|
||||
final Map<T, Set<T>> edges = new HashMap<>();
|
||||
|
||||
public void addNode(T node) {
|
||||
if (nodes.contains(node)) {
|
||||
return;
|
||||
}
|
||||
nodes.add(node);
|
||||
edges.computeIfAbsent(node, _e -> new HashSet<>());
|
||||
}
|
||||
|
||||
public void addNodes(Set<T> nodes) {
|
||||
nodes.addAll(nodes);
|
||||
}
|
||||
|
||||
public void addEdge(T u, T v) {
|
||||
addNode(u);
|
||||
addNode(v);
|
||||
edges.get(u).add(v);
|
||||
}
|
||||
|
||||
public Graph<T> build() {
|
||||
return new Graph<T>(nodes, edges);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Topological sort
|
||||
*/
|
||||
static class TopoSorter<T> {
|
||||
final Deque<T> result = new LinkedList<>();
|
||||
final Deque<T> nodes;
|
||||
final Graph<T> graph;
|
||||
TopoSorter(Graph<T> graph) {
|
||||
this.graph = graph;
|
||||
this.nodes = new LinkedList<>(graph.nodes);
|
||||
sort();
|
||||
}
|
||||
|
||||
public void ordered(Consumer<T> action) {
|
||||
result.iterator().forEachRemaining(action);
|
||||
}
|
||||
|
||||
public void reverse(Consumer<T> action) {
|
||||
result.descendingIterator().forEachRemaining(action);
|
||||
}
|
||||
|
||||
private void sort() {
|
||||
Deque<T> visited = new LinkedList<>();
|
||||
Deque<T> done = new LinkedList<>();
|
||||
T node;
|
||||
while ((node = nodes.poll()) != null) {
|
||||
if (!visited.contains(node)) {
|
||||
visit(node, visited, done);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void visit(T node, Deque<T> visited, Deque<T> done) {
|
||||
if (visited.contains(node)) {
|
||||
if (!done.contains(node)) {
|
||||
throw new IllegalArgumentException("Cyclic detected: " +
|
||||
node + " " + graph.edges().get(node));
|
||||
}
|
||||
return;
|
||||
}
|
||||
visited.add(node);
|
||||
graph.edges().get(node).stream()
|
||||
.forEach(x -> visit(x, visited, done));
|
||||
done.add(node);
|
||||
result.addLast(node);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DotGraph {
|
||||
static final String ORANGE = "#e76f00";
|
||||
static final String BLUE = "#437291";
|
||||
static final String GRAY = "#dddddd";
|
||||
|
||||
static final String REEXPORTS = "";
|
||||
static final String REQUIRES = "style=\"dashed\"";
|
||||
static final String REQUIRES_BASE = "color=\"" + GRAY + "\"";
|
||||
|
||||
static final Set<String> javaModules = modules(name ->
|
||||
(name.startsWith("java.") && !name.equals("java.smartcardio")));
|
||||
static final Set<String> jdkModules = modules(name ->
|
||||
(name.startsWith("java.") ||
|
||||
name.startsWith("jdk.") ||
|
||||
name.startsWith("javafx.")) && !javaModules.contains(name));
|
||||
|
||||
private static Set<String> modules(Predicate<String> predicate) {
|
||||
return ModuleFinder.ofSystem().findAll()
|
||||
.stream()
|
||||
.map(ModuleReference::descriptor)
|
||||
.map(ModuleDescriptor::name)
|
||||
.filter(predicate)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
static void printAttributes(PrintWriter out) {
|
||||
out.format(" size=\"25,25\";%n");
|
||||
out.format(" nodesep=.5;%n");
|
||||
out.format(" ranksep=1.5;%n");
|
||||
out.format(" pencolor=transparent;%n");
|
||||
out.format(" node [shape=plaintext, fontname=\"DejaVuSans\", fontsize=36, margin=\".2,.2\"];%n");
|
||||
out.format(" edge [penwidth=4, color=\"#999999\", arrowhead=open, arrowsize=2];%n");
|
||||
}
|
||||
|
||||
static void printNodes(PrintWriter out, Graph<String> graph) {
|
||||
out.format(" subgraph se {%n");
|
||||
graph.nodes().stream()
|
||||
.filter(javaModules::contains)
|
||||
.forEach(mn -> out.format(" \"%s\" [fontcolor=\"%s\", group=%s];%n",
|
||||
mn, ORANGE, "java"));
|
||||
out.format(" }%n");
|
||||
graph.nodes().stream()
|
||||
.filter(jdkModules::contains)
|
||||
.forEach(mn -> out.format(" \"%s\" [fontcolor=\"%s\", group=%s];%n",
|
||||
mn, BLUE, "jdk"));
|
||||
|
||||
graph.nodes().stream()
|
||||
.filter(mn -> !javaModules.contains(mn) && !jdkModules.contains(mn))
|
||||
.forEach(mn -> out.format(" \"%s\";%n", mn));
|
||||
}
|
||||
|
||||
static void printEdges(PrintWriter out, Graph<String> graph,
|
||||
String node, Set<String> requiresPublic) {
|
||||
graph.adjacentNodes(node).forEach(dn -> {
|
||||
String attr = dn.equals("java.base") ? REQUIRES_BASE
|
||||
: (requiresPublic.contains(dn) ? REEXPORTS : REQUIRES);
|
||||
out.format(" \"%s\" -> \"%s\" [%s];%n", node, dn, attr);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import static com.sun.tools.jdeps.JdepsFilter.DEFAULT_FILTER;
|
||||
import static com.sun.tools.jdeps.Module.trace;
|
||||
import static com.sun.tools.jdeps.Graph.*;
|
||||
|
||||
import java.lang.module.ModuleDescriptor.Requires;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Inverse transitive dependency analysis (compile-time view)
|
||||
*/
|
||||
public class InverseDepsAnalyzer extends DepsAnalyzer {
|
||||
// the end points for the resulting paths to be reported
|
||||
private final Map<Archive, Set<Archive>> endPoints = new HashMap<>();
|
||||
// target archives for inverse transitive dependence analysis
|
||||
private final Set<Archive> targets = new HashSet<>();
|
||||
|
||||
public InverseDepsAnalyzer(JdepsConfiguration config,
|
||||
JdepsFilter filter,
|
||||
JdepsWriter writer,
|
||||
Analyzer.Type verbose,
|
||||
boolean apiOnly) {
|
||||
super(config, filter, writer, verbose, apiOnly);
|
||||
}
|
||||
|
||||
public boolean run() throws IOException {
|
||||
try {
|
||||
if (apiOnly) {
|
||||
finder.parseExportedAPIs(rootArchives.stream());
|
||||
} else {
|
||||
finder.parse(rootArchives.stream());
|
||||
}
|
||||
archives.addAll(rootArchives);
|
||||
|
||||
Set<Archive> archives = archives();
|
||||
|
||||
// If -package or -regex is specified, the archives that reference
|
||||
// the matching types are used as the targets for inverse
|
||||
// transitive analysis. If -requires is specified, the
|
||||
// specified modules are the targets.
|
||||
|
||||
if (filter.requiresFilter().isEmpty()) {
|
||||
targets.addAll(archives);
|
||||
} else {
|
||||
filter.requiresFilter().stream()
|
||||
.map(configuration::findModule)
|
||||
.flatMap(Optional::stream)
|
||||
.forEach(targets::add);
|
||||
}
|
||||
|
||||
// If -package or -regex is specified, the end points are
|
||||
// the matching archives. If -requires is specified,
|
||||
// the end points are the modules specified in -requires.
|
||||
if (filter.requiresFilter().isEmpty()) {
|
||||
Map<Archive, Set<Archive>> dependences = finder.dependences();
|
||||
targets.forEach(source -> endPoints.put(source, dependences.get(source)));
|
||||
} else {
|
||||
targets.forEach(t -> endPoints.put(t, Collections.emptySet()));
|
||||
}
|
||||
|
||||
analyzer.run(archives, finder.locationToArchive());
|
||||
|
||||
// print the first-level of dependencies
|
||||
if (writer != null) {
|
||||
writer.generateOutput(archives, analyzer);
|
||||
}
|
||||
|
||||
} finally {
|
||||
finder.shutdown();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target archives determined from the dependency analysis.
|
||||
*
|
||||
* Inverse transitive dependency will find all nodes that depend
|
||||
* upon the returned set of archives directly and indirectly.
|
||||
*/
|
||||
public Set<Archive> targets() {
|
||||
return Collections.unmodifiableSet(targets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all inverse transitive dependencies using the given requires set
|
||||
* as the targets, if non-empty. If the given requires set is empty,
|
||||
* use the archives depending the packages specified in -regex or -p options.
|
||||
*/
|
||||
public Set<Deque<Archive>> inverseDependences() throws IOException {
|
||||
// create a new dependency finder to do the analysis
|
||||
DependencyFinder dependencyFinder = new DependencyFinder(configuration, DEFAULT_FILTER);
|
||||
try {
|
||||
// parse all archives in unnamed module to get compile-time dependences
|
||||
Stream<Archive> archives =
|
||||
Stream.concat(configuration.initialArchives().stream(),
|
||||
configuration.classPathArchives().stream());
|
||||
if (apiOnly) {
|
||||
dependencyFinder.parseExportedAPIs(archives);
|
||||
} else {
|
||||
dependencyFinder.parse(archives);
|
||||
}
|
||||
|
||||
Graph.Builder<Archive> builder = new Graph.Builder<>();
|
||||
// include all target nodes
|
||||
targets().forEach(builder::addNode);
|
||||
|
||||
// transpose the module graph - may filter JDK module
|
||||
configuration.getModules().values().stream()
|
||||
.filter(filter::include)
|
||||
.forEach(m -> {
|
||||
builder.addNode(m);
|
||||
m.descriptor().requires().stream()
|
||||
.map(Requires::name)
|
||||
.map(configuration::findModule) // must be present
|
||||
.forEach(v -> builder.addEdge(v.get(), m));
|
||||
});
|
||||
|
||||
// add the dependences from the analysis
|
||||
Map<Archive, Set<Archive>> dependences = dependencyFinder.dependences();
|
||||
dependences.entrySet().stream()
|
||||
.forEach(e -> {
|
||||
Archive u = e.getKey();
|
||||
builder.addNode(u);
|
||||
e.getValue().forEach(v -> builder.addEdge(v, u));
|
||||
});
|
||||
|
||||
// transposed dependence graph.
|
||||
Graph<Archive> graph = builder.build();
|
||||
trace("targets: %s%n", targets());
|
||||
|
||||
// Traverse from the targets and find all paths
|
||||
// rebuild a graph with all nodes that depends on targets
|
||||
// targets directly and indirectly
|
||||
return targets().stream()
|
||||
.map(t -> findPaths(graph, t))
|
||||
.flatMap(Set::stream)
|
||||
.collect(Collectors.toSet());
|
||||
} finally {
|
||||
dependencyFinder.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all paths reachable from the given targets.
|
||||
*/
|
||||
private Set<Deque<Archive>> findPaths(Graph<Archive> graph, Archive target) {
|
||||
|
||||
// path is in reversed order
|
||||
Deque<Archive> path = new LinkedList<>();
|
||||
path.push(target);
|
||||
|
||||
Set<Edge<Archive>> visited = new HashSet<>();
|
||||
|
||||
Deque<Edge<Archive>> deque = new LinkedList<>();
|
||||
deque.addAll(graph.edgesFrom(target));
|
||||
if (deque.isEmpty()) {
|
||||
return makePaths(path).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
Set<Deque<Archive>> allPaths = new HashSet<>();
|
||||
while (!deque.isEmpty()) {
|
||||
Edge<Archive> edge = deque.pop();
|
||||
|
||||
if (visited.contains(edge))
|
||||
continue;
|
||||
|
||||
Archive node = edge.v;
|
||||
path.addLast(node);
|
||||
visited.add(edge);
|
||||
|
||||
Set<Edge<Archive>> unvisitedDeps = graph.edgesFrom(node)
|
||||
.stream()
|
||||
.filter(e -> !visited.contains(e))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
trace("visiting %s %s (%s)%n", edge, path, unvisitedDeps);
|
||||
if (unvisitedDeps.isEmpty()) {
|
||||
makePaths(path).forEach(allPaths::add);
|
||||
path.removeLast();
|
||||
}
|
||||
|
||||
// push unvisited adjacent edges
|
||||
unvisitedDeps.stream().forEach(deque::push);
|
||||
|
||||
|
||||
// when the adjacent edges of a node are visited, pop it from the path
|
||||
while (!path.isEmpty()) {
|
||||
if (visited.containsAll(graph.edgesFrom(path.peekLast())))
|
||||
path.removeLast();
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return allPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend end point to the path
|
||||
*/
|
||||
private Stream<Deque<Archive>> makePaths(Deque<Archive> path) {
|
||||
Set<Archive> nodes = endPoints.get(path.peekFirst());
|
||||
if (nodes == null || nodes.isEmpty()) {
|
||||
return Stream.of(new LinkedList<>(path));
|
||||
} else {
|
||||
return nodes.stream().map(n -> {
|
||||
Deque<Archive> newPath = new LinkedList<>();
|
||||
newPath.addFirst(n);
|
||||
newPath.addAll(path);
|
||||
return newPath;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,608 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import static com.sun.tools.jdeps.Module.trace;
|
||||
import static java.util.stream.Collectors.*;
|
||||
|
||||
import com.sun.tools.classfile.Dependency;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReader;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.net.URI;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class JdepsConfiguration {
|
||||
// the token for "all modules on the module path"
|
||||
public static final String ALL_MODULE_PATH = "ALL-MODULE-PATH";
|
||||
public static final String ALL_DEFAULT = "ALL-DEFAULT";
|
||||
public static final String MODULE_INFO = "module-info.class";
|
||||
|
||||
private final SystemModuleFinder system;
|
||||
private final ModuleFinder finder;
|
||||
|
||||
private final Map<String, Module> nameToModule = new LinkedHashMap<>();
|
||||
private final Map<String, Module> packageToModule = new HashMap<>();
|
||||
private final Map<String, List<Archive>> packageToUnnamedModule = new HashMap<>();
|
||||
|
||||
private final List<Archive> classpathArchives = new ArrayList<>();
|
||||
private final List<Archive> initialArchives = new ArrayList<>();
|
||||
private final Set<Module> rootModules = new HashSet<>();
|
||||
private final Configuration configuration;
|
||||
|
||||
private JdepsConfiguration(SystemModuleFinder systemModulePath,
|
||||
ModuleFinder finder,
|
||||
Set<String> roots,
|
||||
List<Path> classpaths,
|
||||
List<Archive> initialArchives,
|
||||
boolean allDefaultModules)
|
||||
throws IOException
|
||||
{
|
||||
trace("root: %s%n", roots);
|
||||
|
||||
this.system = systemModulePath;
|
||||
this.finder = finder;
|
||||
|
||||
// build root set for resolution
|
||||
Set<String> mods = new HashSet<>(roots);
|
||||
|
||||
// add default modules to the root set
|
||||
// unnamed module
|
||||
if (!initialArchives.isEmpty() || !classpaths.isEmpty() ||
|
||||
roots.isEmpty() || allDefaultModules) {
|
||||
mods.addAll(systemModulePath.defaultSystemRoots());
|
||||
}
|
||||
|
||||
this.configuration = Configuration.empty()
|
||||
.resolveRequires(finder, ModuleFinder.of(), mods);
|
||||
|
||||
this.configuration.modules().stream()
|
||||
.map(ResolvedModule::reference)
|
||||
.forEach(this::addModuleReference);
|
||||
|
||||
// packages in unnamed module
|
||||
initialArchives.forEach(archive -> {
|
||||
addPackagesInUnnamedModule(archive);
|
||||
this.initialArchives.add(archive);
|
||||
});
|
||||
|
||||
// classpath archives
|
||||
for (Path p : classpaths) {
|
||||
if (Files.exists(p)) {
|
||||
Archive archive = Archive.getInstance(p);
|
||||
addPackagesInUnnamedModule(archive);
|
||||
classpathArchives.add(archive);
|
||||
}
|
||||
}
|
||||
|
||||
// all roots specified in -addmods or -m are included
|
||||
// as the initial set for analysis.
|
||||
roots.stream()
|
||||
.map(nameToModule::get)
|
||||
.forEach(this.rootModules::add);
|
||||
|
||||
initProfiles();
|
||||
|
||||
trace("resolved modules: %s%n", nameToModule.keySet().stream()
|
||||
.sorted().collect(joining("\n", "\n", "")));
|
||||
}
|
||||
|
||||
private void initProfiles() {
|
||||
// other system modules are not observed and not added in nameToModule map
|
||||
Map<String, Module> systemModules =
|
||||
system.moduleNames()
|
||||
.collect(toMap(Function.identity(), (mn) -> {
|
||||
Module m = nameToModule.get(mn);
|
||||
if (m == null) {
|
||||
ModuleReference mref = finder.find(mn).get();
|
||||
m = toModule(mref);
|
||||
}
|
||||
return m;
|
||||
}));
|
||||
Profile.init(systemModules);
|
||||
}
|
||||
|
||||
private void addModuleReference(ModuleReference mref) {
|
||||
Module module = toModule(mref);
|
||||
nameToModule.put(mref.descriptor().name(), module);
|
||||
mref.descriptor().packages()
|
||||
.forEach(pn -> packageToModule.putIfAbsent(pn, module));
|
||||
}
|
||||
|
||||
private void addPackagesInUnnamedModule(Archive archive) {
|
||||
archive.reader().entries().stream()
|
||||
.filter(e -> e.endsWith(".class") && !e.equals(MODULE_INFO))
|
||||
.map(this::toPackageName)
|
||||
.distinct()
|
||||
.forEach(pn -> packageToUnnamedModule
|
||||
.computeIfAbsent(pn, _n -> new ArrayList<>()).add(archive));
|
||||
}
|
||||
|
||||
private String toPackageName(String name) {
|
||||
int i = name.lastIndexOf('/');
|
||||
return i > 0 ? name.replace('/', '.').substring(0, i) : "";
|
||||
}
|
||||
|
||||
public Optional<Module> findModule(String name) {
|
||||
Objects.requireNonNull(name);
|
||||
Module m = nameToModule.get(name);
|
||||
return m!= null ? Optional.of(m) : Optional.empty();
|
||||
|
||||
}
|
||||
|
||||
public Optional<ModuleDescriptor> findModuleDescriptor(String name) {
|
||||
Objects.requireNonNull(name);
|
||||
Module m = nameToModule.get(name);
|
||||
return m!= null ? Optional.of(m.descriptor()) : Optional.empty();
|
||||
}
|
||||
|
||||
boolean isSystem(Module m) {
|
||||
return system.find(m.name()).isPresent();
|
||||
}
|
||||
|
||||
boolean isValidToken(String name) {
|
||||
return ALL_MODULE_PATH.equals(name) || ALL_DEFAULT.equals(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the modules that the given module can read
|
||||
*/
|
||||
public Stream<Module> reads(Module module) {
|
||||
return configuration.findModule(module.name()).get()
|
||||
.reads().stream()
|
||||
.map(ResolvedModule::name)
|
||||
.map(nameToModule::get);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of packages that split between resolved module and
|
||||
* unnamed module
|
||||
*/
|
||||
public Map<String, Set<String>> splitPackages() {
|
||||
Set<String> splitPkgs = packageToModule.keySet().stream()
|
||||
.filter(packageToUnnamedModule::containsKey)
|
||||
.collect(toSet());
|
||||
if (splitPkgs.isEmpty())
|
||||
return Collections.emptyMap();
|
||||
|
||||
return splitPkgs.stream().collect(toMap(Function.identity(), (pn) -> {
|
||||
Set<String> sources = new LinkedHashSet<>();
|
||||
sources.add(packageToModule.get(pn).getModule().location().toString());
|
||||
packageToUnnamedModule.get(pn).stream()
|
||||
.map(Archive::getPathName)
|
||||
.forEach(sources::add);
|
||||
return sources;
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an optional archive containing the given Location
|
||||
*/
|
||||
public Optional<Archive> findClass(Dependency.Location location) {
|
||||
String name = location.getName();
|
||||
int i = name.lastIndexOf('/');
|
||||
String pn = i > 0 ? name.substring(0, i).replace('/', '.') : "";
|
||||
Archive archive = packageToModule.get(pn);
|
||||
if (archive != null) {
|
||||
return archive.contains(name + ".class")
|
||||
? Optional.of(archive)
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
if (packageToUnnamedModule.containsKey(pn)) {
|
||||
return packageToUnnamedModule.get(pn).stream()
|
||||
.filter(a -> a.contains(name + ".class"))
|
||||
.findFirst();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of Modules that can be found in the specified
|
||||
* module paths.
|
||||
*/
|
||||
public Map<String, Module> getModules() {
|
||||
return nameToModule;
|
||||
}
|
||||
|
||||
public Stream<Module> resolve(Set<String> roots) {
|
||||
if (roots.isEmpty()) {
|
||||
return nameToModule.values().stream();
|
||||
} else {
|
||||
return Configuration.empty()
|
||||
.resolveRequires(finder, ModuleFinder.of(), roots)
|
||||
.modules().stream()
|
||||
.map(ResolvedModule::name)
|
||||
.map(nameToModule::get);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Archive> classPathArchives() {
|
||||
return classpathArchives;
|
||||
}
|
||||
|
||||
public List<Archive> initialArchives() {
|
||||
return initialArchives;
|
||||
}
|
||||
|
||||
public Set<Module> rootModules() {
|
||||
return rootModules;
|
||||
}
|
||||
|
||||
public Module toModule(ModuleReference mref) {
|
||||
try {
|
||||
String mn = mref.descriptor().name();
|
||||
URI location = mref.location().orElseThrow(FileNotFoundException::new);
|
||||
ModuleDescriptor md = mref.descriptor();
|
||||
Module.Builder builder = new Module.Builder(md, system.find(mn).isPresent());
|
||||
|
||||
final ClassFileReader reader;
|
||||
if (location.getScheme().equals("jrt")) {
|
||||
reader = system.getClassReader(mn);
|
||||
} else {
|
||||
reader = ClassFileReader.newInstance(Paths.get(location));
|
||||
}
|
||||
|
||||
builder.classes(reader);
|
||||
builder.location(location);
|
||||
|
||||
return builder.build();
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static class SystemModuleFinder implements ModuleFinder {
|
||||
private static final String JAVA_HOME = System.getProperty("java.home");
|
||||
private static final String JAVA_SE = "java.se";
|
||||
|
||||
private final FileSystem fileSystem;
|
||||
private final Path root;
|
||||
private final Map<String, ModuleReference> systemModules;
|
||||
|
||||
SystemModuleFinder() {
|
||||
if (Files.isRegularFile(Paths.get(JAVA_HOME, "lib", "modules"))) {
|
||||
// jrt file system
|
||||
this.fileSystem = FileSystems.getFileSystem(URI.create("jrt:/"));
|
||||
this.root = fileSystem.getPath("/modules");
|
||||
this.systemModules = walk(root);
|
||||
} else {
|
||||
// exploded image
|
||||
this.fileSystem = FileSystems.getDefault();
|
||||
root = Paths.get(JAVA_HOME, "modules");
|
||||
this.systemModules = ModuleFinder.ofSystem().findAll().stream()
|
||||
.collect(toMap(mref -> mref.descriptor().name(), Function.identity()));
|
||||
}
|
||||
}
|
||||
|
||||
SystemModuleFinder(String javaHome) throws IOException {
|
||||
if (javaHome == null) {
|
||||
// -system none
|
||||
this.fileSystem = null;
|
||||
this.root = null;
|
||||
this.systemModules = Collections.emptyMap();
|
||||
} else {
|
||||
if (Files.isRegularFile(Paths.get(javaHome, "lib", "modules")))
|
||||
throw new IllegalArgumentException("Invalid java.home: " + javaHome);
|
||||
|
||||
// alternate java.home
|
||||
Map<String, String> env = new HashMap<>();
|
||||
env.put("java.home", javaHome);
|
||||
// a remote run-time image
|
||||
this.fileSystem = FileSystems.newFileSystem(URI.create("jrt:/"), env);
|
||||
this.root = fileSystem.getPath("/modules");
|
||||
this.systemModules = walk(root);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, ModuleReference> walk(Path root) {
|
||||
try {
|
||||
return Files.walk(root, 1)
|
||||
.filter(path -> !path.equals(root))
|
||||
.map(this::toModuleReference)
|
||||
.collect(toMap(mref -> mref.descriptor().name(),
|
||||
Function.identity()));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private ModuleReference toModuleReference(Path path) {
|
||||
Path minfo = path.resolve(MODULE_INFO);
|
||||
try (InputStream in = Files.newInputStream(minfo);
|
||||
BufferedInputStream bin = new BufferedInputStream(in)) {
|
||||
|
||||
ModuleDescriptor descriptor = dropHashes(ModuleDescriptor.read(bin));
|
||||
String mn = descriptor.name();
|
||||
URI uri = URI.create("jrt:/" + path.getFileName().toString());
|
||||
Supplier<ModuleReader> readerSupplier = new Supplier<>() {
|
||||
@Override
|
||||
public ModuleReader get() {
|
||||
return new ModuleReader() {
|
||||
@Override
|
||||
public Optional<URI> find(String name) throws IOException {
|
||||
return name.equals(mn)
|
||||
? Optional.of(uri) : Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return new ModuleReference(descriptor, uri, readerSupplier);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private ModuleDescriptor dropHashes(ModuleDescriptor md) {
|
||||
ModuleDescriptor.Builder builder = new ModuleDescriptor.Builder(md.name());
|
||||
md.requires().forEach(builder::requires);
|
||||
md.exports().forEach(builder::exports);
|
||||
md.provides().values().stream().forEach(builder::provides);
|
||||
md.uses().stream().forEach(builder::uses);
|
||||
builder.conceals(md.conceals());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ModuleReference> findAll() {
|
||||
return systemModules.values().stream().collect(toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ModuleReference> find(String mn) {
|
||||
return systemModules.containsKey(mn)
|
||||
? Optional.of(systemModules.get(mn)) : Optional.empty();
|
||||
}
|
||||
|
||||
public Stream<String> moduleNames() {
|
||||
return systemModules.values().stream()
|
||||
.map(mref -> mref.descriptor().name());
|
||||
}
|
||||
|
||||
public ClassFileReader getClassReader(String modulename) throws IOException {
|
||||
Path mp = root.resolve(modulename);
|
||||
if (Files.exists(mp) && Files.isDirectory(mp)) {
|
||||
return ClassFileReader.newInstance(fileSystem, mp);
|
||||
} else {
|
||||
throw new FileNotFoundException(mp.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> defaultSystemRoots() {
|
||||
Set<String> roots = new HashSet<>();
|
||||
boolean hasJava = false;
|
||||
if (systemModules.containsKey(JAVA_SE)) {
|
||||
// java.se is a system module
|
||||
hasJava = true;
|
||||
roots.add(JAVA_SE);
|
||||
}
|
||||
|
||||
for (ModuleReference mref : systemModules.values()) {
|
||||
String mn = mref.descriptor().name();
|
||||
if (hasJava && mn.startsWith("java."))
|
||||
continue;
|
||||
|
||||
// add as root if observable and exports at least one package
|
||||
ModuleDescriptor descriptor = mref.descriptor();
|
||||
for (ModuleDescriptor.Exports e : descriptor.exports()) {
|
||||
if (!e.isQualified()) {
|
||||
roots.add(mn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
final SystemModuleFinder systemModulePath;
|
||||
final Set<String> rootModules = new HashSet<>();
|
||||
final List<Archive> initialArchives = new ArrayList<>();
|
||||
final List<Path> paths = new ArrayList<>();
|
||||
final List<Path> classPaths = new ArrayList<>();
|
||||
|
||||
ModuleFinder upgradeModulePath;
|
||||
ModuleFinder appModulePath;
|
||||
boolean addAllApplicationModules;
|
||||
boolean addAllDefaultModules;
|
||||
|
||||
public Builder() {
|
||||
this.systemModulePath = new SystemModuleFinder();
|
||||
}
|
||||
|
||||
public Builder(String javaHome) throws IOException {
|
||||
this.systemModulePath = SystemModuleFinder.JAVA_HOME.equals(javaHome)
|
||||
? new SystemModuleFinder()
|
||||
: new SystemModuleFinder(javaHome);
|
||||
}
|
||||
|
||||
public Builder upgradeModulePath(String upgradeModulePath) {
|
||||
this.upgradeModulePath = createModulePathFinder(upgradeModulePath);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder appModulePath(String modulePath) {
|
||||
this.appModulePath = createModulePathFinder(modulePath);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addmods(Set<String> addmods) {
|
||||
for (String mn : addmods) {
|
||||
switch (mn) {
|
||||
case ALL_MODULE_PATH:
|
||||
this.addAllApplicationModules = true;
|
||||
break;
|
||||
case ALL_DEFAULT:
|
||||
this.addAllDefaultModules = true;
|
||||
break;
|
||||
default:
|
||||
this.rootModules.add(mn);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is for -check option to find all target modules specified
|
||||
* in qualified exports.
|
||||
*
|
||||
* Include all system modules and modules found on modulepath
|
||||
*/
|
||||
public Builder allModules() {
|
||||
systemModulePath.moduleNames()
|
||||
.forEach(this.rootModules::add);
|
||||
this.addAllApplicationModules = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addRoot(Path path) {
|
||||
Archive archive = Archive.getInstance(path);
|
||||
if (archive.contains(MODULE_INFO)) {
|
||||
paths.add(path);
|
||||
} else {
|
||||
initialArchives.add(archive);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addClassPath(String classPath) {
|
||||
this.classPaths.addAll(getClassPaths(classPath));
|
||||
return this;
|
||||
}
|
||||
|
||||
public JdepsConfiguration build() throws IOException {
|
||||
ModuleFinder finder = systemModulePath;
|
||||
if (upgradeModulePath != null) {
|
||||
finder = ModuleFinder.compose(upgradeModulePath, systemModulePath);
|
||||
}
|
||||
if (appModulePath != null) {
|
||||
finder = ModuleFinder.compose(finder, appModulePath);
|
||||
}
|
||||
if (!paths.isEmpty()) {
|
||||
ModuleFinder otherModulePath = ModuleFinder.of(paths.toArray(new Path[0]));
|
||||
|
||||
finder = ModuleFinder.compose(finder, otherModulePath);
|
||||
// add modules specified on command-line (convenience) as root set
|
||||
otherModulePath.findAll().stream()
|
||||
.map(mref -> mref.descriptor().name())
|
||||
.forEach(rootModules::add);
|
||||
}
|
||||
if (addAllApplicationModules && appModulePath != null) {
|
||||
appModulePath.findAll().stream()
|
||||
.map(mref -> mref.descriptor().name())
|
||||
.forEach(rootModules::add);
|
||||
}
|
||||
|
||||
return new JdepsConfiguration(systemModulePath,
|
||||
finder,
|
||||
rootModules,
|
||||
classPaths,
|
||||
initialArchives,
|
||||
addAllDefaultModules);
|
||||
}
|
||||
|
||||
private static ModuleFinder createModulePathFinder(String mpaths) {
|
||||
if (mpaths == null) {
|
||||
return null;
|
||||
} else {
|
||||
String[] dirs = mpaths.split(File.pathSeparator);
|
||||
Path[] paths = new Path[dirs.length];
|
||||
int i = 0;
|
||||
for (String dir : dirs) {
|
||||
paths[i++] = Paths.get(dir);
|
||||
}
|
||||
return ModuleFinder.of(paths);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the list of Archive specified in cpaths and not included
|
||||
* initialArchives
|
||||
*/
|
||||
private List<Path> getClassPaths(String cpaths) {
|
||||
if (cpaths.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Path> paths = new ArrayList<>();
|
||||
for (String p : cpaths.split(File.pathSeparator)) {
|
||||
if (p.length() > 0) {
|
||||
// wildcard to parse all JAR files e.g. -classpath dir/*
|
||||
int i = p.lastIndexOf(".*");
|
||||
if (i > 0) {
|
||||
Path dir = Paths.get(p.substring(0, i));
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.jar")) {
|
||||
for (Path entry : stream) {
|
||||
paths.add(entry);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
} else {
|
||||
paths.add(Paths.get(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -41,22 +41,27 @@ import java.util.stream.Stream;
|
||||
* 2. -filter:package to filter out same-package dependencies
|
||||
* This filter is applied when jdeps parses the class files
|
||||
* and filtered dependencies are not stored in the Analyzer.
|
||||
* 3. -module specifies to match target dependencies from the given module
|
||||
* 3. -requires specifies to match target dependence from the given module
|
||||
* This gets expanded into package lists to be filtered.
|
||||
* 4. -filter:archive to filter out same-archive dependencies
|
||||
* This filter is applied later in the Analyzer as the
|
||||
* containing archive of a target class may not be known until
|
||||
* the entire archive
|
||||
*/
|
||||
class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
|
||||
public class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
|
||||
|
||||
public static final JdepsFilter DEFAULT_FILTER =
|
||||
new JdepsFilter.Builder().filter(true, true).build();
|
||||
|
||||
private final Dependency.Filter filter;
|
||||
private final Pattern filterPattern;
|
||||
private final boolean filterSamePackage;
|
||||
private final boolean filterSameArchive;
|
||||
private final boolean findJDKInternals;
|
||||
private final Pattern includePattern;
|
||||
private final Set<String> includePackages;
|
||||
private final Set<String> excludeModules;
|
||||
private final Pattern includeSystemModules;
|
||||
|
||||
private final Set<String> requires;
|
||||
|
||||
private JdepsFilter(Dependency.Filter filter,
|
||||
Pattern filterPattern,
|
||||
@ -64,16 +69,16 @@ class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
|
||||
boolean filterSameArchive,
|
||||
boolean findJDKInternals,
|
||||
Pattern includePattern,
|
||||
Set<String> includePackages,
|
||||
Set<String> excludeModules) {
|
||||
Pattern includeSystemModules,
|
||||
Set<String> requires) {
|
||||
this.filter = filter;
|
||||
this.filterPattern = filterPattern;
|
||||
this.filterSamePackage = filterSamePackage;
|
||||
this.filterSameArchive = filterSameArchive;
|
||||
this.findJDKInternals = findJDKInternals;
|
||||
this.includePattern = includePattern;
|
||||
this.includePackages = includePackages;
|
||||
this.excludeModules = excludeModules;
|
||||
this.includeSystemModules = includeSystemModules;
|
||||
this.requires = requires;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -82,12 +87,7 @@ class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
|
||||
* @param cn fully-qualified name
|
||||
*/
|
||||
public boolean matches(String cn) {
|
||||
if (includePackages.isEmpty() && includePattern == null)
|
||||
return true;
|
||||
|
||||
int i = cn.lastIndexOf('.');
|
||||
String pn = i > 0 ? cn.substring(0, i) : "";
|
||||
if (includePackages.contains(pn))
|
||||
if (includePattern == null)
|
||||
return true;
|
||||
|
||||
if (includePattern != null)
|
||||
@ -97,29 +97,39 @@ class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the given source includes classes specified in includePattern
|
||||
* or includePackages filters.
|
||||
* Tests if the given source includes classes specified in -include option
|
||||
*
|
||||
* This method can be used to determine if the given source should eagerly
|
||||
* be processed.
|
||||
*/
|
||||
public boolean matches(Archive source) {
|
||||
if (!includePackages.isEmpty() && source.getModule().isNamed()) {
|
||||
boolean found = source.getModule().packages()
|
||||
.stream()
|
||||
.filter(pn -> includePackages.contains(pn))
|
||||
.findAny().isPresent();
|
||||
if (found)
|
||||
return true;
|
||||
if (includePattern != null) {
|
||||
return source.reader().entries().stream()
|
||||
.map(name -> name.replace('/', '.'))
|
||||
.filter(name -> !name.equals("module-info.class"))
|
||||
.anyMatch(this::matches);
|
||||
}
|
||||
if (!includePackages.isEmpty() || includePattern != null) {
|
||||
return source.reader().entries()
|
||||
.stream()
|
||||
.map(name -> name.replace('/', '.'))
|
||||
.filter(this::matches)
|
||||
.findAny().isPresent();
|
||||
}
|
||||
return false;
|
||||
return hasTargetFilter();
|
||||
}
|
||||
|
||||
public boolean include(Archive source) {
|
||||
Module module = source.getModule();
|
||||
// skip system module by default; or if includeSystemModules is set
|
||||
// only include the ones matching the pattern
|
||||
return !module.isSystem() || (includeSystemModules != null &&
|
||||
includeSystemModules.matcher(module.name()).matches());
|
||||
}
|
||||
|
||||
public boolean hasIncludePattern() {
|
||||
return includePattern != null || includeSystemModules != null;
|
||||
}
|
||||
|
||||
public boolean hasTargetFilter() {
|
||||
return filter != null;
|
||||
}
|
||||
|
||||
public Set<String> requiresFilter() {
|
||||
return requires;
|
||||
}
|
||||
|
||||
// ----- Dependency.Filter -----
|
||||
@ -164,42 +174,35 @@ class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if dependency should be recorded for the given source.
|
||||
*/
|
||||
public boolean accept(Archive source) {
|
||||
return !excludeModules.contains(source.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("exclude modules: ")
|
||||
.append(excludeModules.stream().sorted().collect(Collectors.joining(",")))
|
||||
.append("\n");
|
||||
sb.append("include pattern: ").append(includePattern).append("\n");
|
||||
sb.append("filter same archive: ").append(filterSameArchive).append("\n");
|
||||
sb.append("filter same package: ").append(filterSamePackage).append("\n");
|
||||
sb.append("requires: ").append(requires).append("\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static class Builder {
|
||||
Dependency.Filter filter;
|
||||
public static class Builder {
|
||||
static Pattern SYSTEM_MODULE_PATTERN = Pattern.compile("java\\..*|jdk\\..*|javafx\\..*");
|
||||
Pattern filterPattern;
|
||||
Pattern regex;
|
||||
boolean filterSamePackage;
|
||||
boolean filterSameArchive;
|
||||
boolean findJDKInterals;
|
||||
// source filters
|
||||
Pattern includePattern;
|
||||
Set<String> includePackages = new HashSet<>();
|
||||
Set<String> includeModules = new HashSet<>();
|
||||
Set<String> excludeModules = new HashSet<>();
|
||||
Pattern includeSystemModules;
|
||||
Set<String> requires = new HashSet<>();
|
||||
Set<String> targetPackages = new HashSet<>();
|
||||
|
||||
public Builder packages(Set<String> packageNames) {
|
||||
this.filter = Dependencies.getPackageFilter(packageNames, false);
|
||||
this.targetPackages.addAll(packageNames);
|
||||
return this;
|
||||
}
|
||||
public Builder regex(Pattern regex) {
|
||||
this.filter = Dependencies.getRegexFilter(regex);
|
||||
this.regex = regex;
|
||||
return this;
|
||||
}
|
||||
public Builder filter(Pattern regex) {
|
||||
@ -211,6 +214,13 @@ class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
|
||||
this.filterSameArchive = sameArchive;
|
||||
return this;
|
||||
}
|
||||
public Builder requires(String name, Set<String> packageNames) {
|
||||
this.requires.add(name);
|
||||
this.targetPackages.addAll(packageNames);
|
||||
|
||||
includeIfSystemModule(name);
|
||||
return this;
|
||||
}
|
||||
public Builder findJDKInternals(boolean value) {
|
||||
this.findJDKInterals = value;
|
||||
return this;
|
||||
@ -219,30 +229,33 @@ class JdepsFilter implements Dependency.Filter, Analyzer.Filter {
|
||||
this.includePattern = regex;
|
||||
return this;
|
||||
}
|
||||
public Builder includePackage(String pn) {
|
||||
this.includePackages.add(pn);
|
||||
public Builder includeSystemModules(Pattern regex) {
|
||||
this.includeSystemModules = regex;
|
||||
return this;
|
||||
}
|
||||
public Builder includeModules(Set<String> includes) {
|
||||
this.includeModules.addAll(includes);
|
||||
return this;
|
||||
}
|
||||
public Builder excludeModules(Set<String> excludes) {
|
||||
this.excludeModules.addAll(excludes);
|
||||
public Builder includeIfSystemModule(String name) {
|
||||
if (includeSystemModules == null &&
|
||||
SYSTEM_MODULE_PATTERN.matcher(name).matches()) {
|
||||
this.includeSystemModules = SYSTEM_MODULE_PATTERN;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
JdepsFilter build() {
|
||||
public JdepsFilter build() {
|
||||
Dependency.Filter filter = null;
|
||||
if (regex != null)
|
||||
filter = Dependencies.getRegexFilter(regex);
|
||||
else if (!targetPackages.isEmpty()) {
|
||||
filter = Dependencies.getPackageFilter(targetPackages, false);
|
||||
}
|
||||
return new JdepsFilter(filter,
|
||||
filterPattern,
|
||||
filterSamePackage,
|
||||
filterSameArchive,
|
||||
findJDKInterals,
|
||||
includePattern,
|
||||
includePackages,
|
||||
excludeModules.stream()
|
||||
.filter(mn -> !includeModules.contains(mn))
|
||||
.collect(Collectors.toSet()));
|
||||
includeSystemModules,
|
||||
requires);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -25,31 +25,29 @@
|
||||
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
|
||||
import static com.sun.tools.jdeps.Analyzer.Type.*;
|
||||
import static com.sun.tools.jdeps.JdepsWriter.*;
|
||||
import static com.sun.tools.jdeps.ModuleAnalyzer.Graph;
|
||||
import static com.sun.tools.jdeps.JdepsConfiguration.ALL_MODULE_PATH;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.module.ResolutionException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@ -58,7 +56,12 @@ import java.util.stream.Stream;
|
||||
* Implementation for the jdeps tool for static class dependency analysis.
|
||||
*/
|
||||
class JdepsTask {
|
||||
static class BadArgs extends Exception {
|
||||
static interface BadArguments {
|
||||
String getKey();
|
||||
Object[] getArgs();
|
||||
boolean showUsage();
|
||||
}
|
||||
static class BadArgs extends Exception implements BadArguments {
|
||||
static final long serialVersionUID = 8765093759964640721L;
|
||||
BadArgs(String key, Object... args) {
|
||||
super(JdepsTask.getMessage(key, args));
|
||||
@ -73,6 +76,44 @@ class JdepsTask {
|
||||
final String key;
|
||||
final Object[] args;
|
||||
boolean showUsage;
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showUsage() {
|
||||
return showUsage;
|
||||
}
|
||||
}
|
||||
|
||||
static class UncheckedBadArgs extends RuntimeException implements BadArguments {
|
||||
static final long serialVersionUID = -1L;
|
||||
final BadArgs cause;
|
||||
UncheckedBadArgs(BadArgs cause) {
|
||||
super(cause);
|
||||
this.cause = cause;
|
||||
}
|
||||
@Override
|
||||
public String getKey() {
|
||||
return cause.key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getArgs() {
|
||||
return cause.args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showUsage() {
|
||||
return cause.showUsage;
|
||||
}
|
||||
}
|
||||
|
||||
static abstract class Option {
|
||||
@ -126,7 +167,7 @@ class JdepsTask {
|
||||
if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) {
|
||||
throw new BadArgs("err.invalid.path", arg);
|
||||
}
|
||||
task.options.dotOutputDir = arg;
|
||||
task.options.dotOutputDir = Paths.get(arg);;
|
||||
}
|
||||
},
|
||||
new Option(false, "-s", "-summary") {
|
||||
@ -136,8 +177,9 @@ class JdepsTask {
|
||||
}
|
||||
},
|
||||
new Option(false, "-v", "-verbose",
|
||||
"-verbose:package",
|
||||
"-verbose:class") {
|
||||
"-verbose:module",
|
||||
"-verbose:package",
|
||||
"-verbose:class") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
switch (opt) {
|
||||
case "-v":
|
||||
@ -146,6 +188,9 @@ class JdepsTask {
|
||||
task.options.filterSameArchive = false;
|
||||
task.options.filterSamePackage = false;
|
||||
break;
|
||||
case "-verbose:module":
|
||||
task.options.verbose = MODULE;
|
||||
break;
|
||||
case "-verbose:package":
|
||||
task.options.verbose = PACKAGE;
|
||||
break;
|
||||
@ -157,6 +202,80 @@ class JdepsTask {
|
||||
}
|
||||
}
|
||||
},
|
||||
new Option(false, "-apionly") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.apiOnly = true;
|
||||
}
|
||||
},
|
||||
new Option(true, "-check") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
Set<String> mods = Set.of(arg.split(","));
|
||||
task.options.checkModuleDeps = mods;
|
||||
task.options.addmods.addAll(mods);
|
||||
}
|
||||
},
|
||||
new Option(true, "-genmoduleinfo") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
Path p = Paths.get(arg);
|
||||
if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) {
|
||||
throw new BadArgs("err.invalid.path", arg);
|
||||
}
|
||||
task.options.genModuleInfo = Paths.get(arg);
|
||||
}
|
||||
},
|
||||
new Option(false, "-jdkinternals") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.findJDKInternals = true;
|
||||
task.options.verbose = CLASS;
|
||||
if (task.options.includePattern == null) {
|
||||
task.options.includePattern = Pattern.compile(".*");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// ---- paths option ----
|
||||
new Option(true, "-cp", "-classpath") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.classpath = arg;
|
||||
}
|
||||
},
|
||||
new Option(true, "-mp", "-modulepath") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
task.options.modulePath = arg;
|
||||
}
|
||||
},
|
||||
new Option(true, "-upgrademodulepath") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
task.options.upgradeModulePath = arg;
|
||||
}
|
||||
},
|
||||
new Option(true, "-system") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
if (arg.equals("none")) {
|
||||
task.options.systemModulePath = null;
|
||||
} else {
|
||||
Path path = Paths.get(arg);
|
||||
if (Files.isRegularFile(path.resolve("lib").resolve("modules")))
|
||||
task.options.systemModulePath = arg;
|
||||
else
|
||||
throw new BadArgs("err.invalid.path", arg);
|
||||
}
|
||||
}
|
||||
},
|
||||
new Option(true, "-addmods") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
Set<String> mods = Set.of(arg.split(","));
|
||||
task.options.addmods.addAll(mods);
|
||||
}
|
||||
},
|
||||
new Option(true, "-m") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
task.options.rootModule = arg;
|
||||
task.options.addmods.add(arg);
|
||||
}
|
||||
},
|
||||
|
||||
// ---- Target filtering options ----
|
||||
new Option(true, "-p", "-package") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.packageNames.add(arg);
|
||||
@ -167,7 +286,7 @@ class JdepsTask {
|
||||
task.options.regex = Pattern.compile(arg);
|
||||
}
|
||||
},
|
||||
new Option(true, "-module") {
|
||||
new Option(true, "-requires") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.requires.add(arg);
|
||||
}
|
||||
@ -198,28 +317,27 @@ class JdepsTask {
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// ---- Source filtering options ----
|
||||
new Option(true, "-include") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
task.options.includePattern = Pattern.compile(arg);
|
||||
}
|
||||
},
|
||||
|
||||
// Another alternative to list modules in -addmods option
|
||||
new HiddenOption(true, "-include-system-modules") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
task.options.includeSystemModulePattern = Pattern.compile(arg);
|
||||
}
|
||||
},
|
||||
|
||||
new Option(false, "-P", "-profile") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
task.options.showProfile = true;
|
||||
task.options.showModule = false;
|
||||
}
|
||||
},
|
||||
new Option(false, "-M") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
task.options.showModule = true;
|
||||
task.options.showProfile = false;
|
||||
}
|
||||
},
|
||||
new Option(false, "-apionly") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.apiOnly = true;
|
||||
}
|
||||
},
|
||||
|
||||
new Option(false, "-R", "-recursive") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.depth = 0;
|
||||
@ -228,15 +346,17 @@ class JdepsTask {
|
||||
task.options.filterSamePackage = false;
|
||||
}
|
||||
},
|
||||
new Option(true, "-genmoduleinfo") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
Path p = Paths.get(arg);
|
||||
if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) {
|
||||
throw new BadArgs("err.invalid.path", arg);
|
||||
}
|
||||
task.options.genModuleInfo = arg;
|
||||
|
||||
new Option(false, "-I", "-inverse") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.inverse = true;
|
||||
// equivalent to the inverse of compile-time view analysis
|
||||
task.options.compileTimeView = true;
|
||||
task.options.filterSamePackage = true;
|
||||
task.options.filterSameArchive = true;
|
||||
}
|
||||
},
|
||||
|
||||
new Option(false, "-ct", "-compile-time") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.compileTimeView = true;
|
||||
@ -245,62 +365,11 @@ class JdepsTask {
|
||||
task.options.depth = 0;
|
||||
}
|
||||
},
|
||||
new Option(false, "-jdkinternals") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.findJDKInternals = true;
|
||||
task.options.verbose = CLASS;
|
||||
if (task.options.includePattern == null) {
|
||||
task.options.includePattern = Pattern.compile(".*");
|
||||
}
|
||||
}
|
||||
},
|
||||
new Option(true, "-cp", "-classpath") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.classpath = arg;
|
||||
}
|
||||
},
|
||||
new Option(true, "-mp", "-modulepath") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
task.options.modulePath = arg;
|
||||
task.options.showModule = true;
|
||||
}
|
||||
},
|
||||
new Option(true, "-upgrademodulepath") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
task.options.upgradeModulePath = arg;
|
||||
task.options.showModule = true;
|
||||
}
|
||||
},
|
||||
new Option(true, "-m") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
task.options.rootModule = arg;
|
||||
task.options.includes.add(arg);
|
||||
task.options.showModule = true;
|
||||
}
|
||||
},
|
||||
new Option(false, "-check") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
task.options.checkModuleDeps = true;
|
||||
}
|
||||
},
|
||||
new HiddenOption(true, "-include-modules") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
Arrays.stream(arg.split(","))
|
||||
.forEach(task.options.includes::add);
|
||||
task.options.showModule = true;
|
||||
}
|
||||
},
|
||||
new HiddenOption(true, "-exclude-modules") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
Arrays.stream(arg.split(","))
|
||||
.forEach(task.options.excludes::add);
|
||||
task.options.showModule = true;
|
||||
}
|
||||
},
|
||||
|
||||
new Option(false, "-q", "-quiet") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.nowarning = true;
|
||||
}
|
||||
task.options.nowarning = true;
|
||||
}
|
||||
},
|
||||
|
||||
new Option(false, "-version") {
|
||||
@ -318,7 +387,11 @@ class JdepsTask {
|
||||
task.options.showLabel = true;
|
||||
}
|
||||
},
|
||||
|
||||
new HiddenOption(false, "-hide-module") {
|
||||
void process(JdepsTask task, String opt, String arg) {
|
||||
task.options.showModule = false;
|
||||
}
|
||||
},
|
||||
new HiddenOption(true, "-depth") {
|
||||
void process(JdepsTask task, String opt, String arg) throws BadArgs {
|
||||
try {
|
||||
@ -332,7 +405,7 @@ class JdepsTask {
|
||||
|
||||
private static final String PROGNAME = "jdeps";
|
||||
private final Options options = new Options();
|
||||
private final List<String> classes = new ArrayList<>();
|
||||
private final List<String> inputArgs = new ArrayList<>();
|
||||
|
||||
private PrintWriter log;
|
||||
void setLog(PrintWriter out) {
|
||||
@ -342,13 +415,13 @@ class JdepsTask {
|
||||
/**
|
||||
* Result codes.
|
||||
*/
|
||||
static final int EXIT_OK = 0, // Completed with no errors.
|
||||
EXIT_ERROR = 1, // Completed but reported errors.
|
||||
EXIT_CMDERR = 2, // Bad command-line arguments
|
||||
EXIT_SYSERR = 3, // System error or resource exhaustion.
|
||||
EXIT_ABNORMAL = 4;// terminated abnormally
|
||||
static final int EXIT_OK = 0, // Completed with no errors.
|
||||
EXIT_ERROR = 1, // Completed but reported errors.
|
||||
EXIT_CMDERR = 2, // Bad command-line arguments
|
||||
EXIT_SYSERR = 3, // System error or resource exhaustion.
|
||||
EXIT_ABNORMAL = 4; // terminated abnormally
|
||||
|
||||
int run(String[] args) {
|
||||
int run(String... args) {
|
||||
if (log == null) {
|
||||
log = new PrintWriter(System.out);
|
||||
}
|
||||
@ -360,15 +433,11 @@ class JdepsTask {
|
||||
if (options.version || options.fullVersion) {
|
||||
showVersion(options.fullVersion);
|
||||
}
|
||||
if (options.rootModule != null && !classes.isEmpty()) {
|
||||
reportError("err.invalid.module.option", options.rootModule, classes);
|
||||
return EXIT_CMDERR;
|
||||
if (!inputArgs.isEmpty() && options.rootModule != null) {
|
||||
reportError("err.invalid.arg.for.option", "-m");
|
||||
}
|
||||
if (options.checkModuleDeps && options.rootModule == null) {
|
||||
reportError("err.root.module.not.set");
|
||||
return EXIT_CMDERR;
|
||||
}
|
||||
if (classes.isEmpty() && options.rootModule == null && options.includePattern == null) {
|
||||
if (inputArgs.isEmpty() && options.addmods.isEmpty() && options.includePattern == null
|
||||
&& options.includeSystemModulePattern == null && options.checkModuleDeps == null) {
|
||||
if (options.help || options.version || options.fullVersion) {
|
||||
return EXIT_OK;
|
||||
} else {
|
||||
@ -377,19 +446,10 @@ class JdepsTask {
|
||||
}
|
||||
}
|
||||
if (options.genModuleInfo != null) {
|
||||
if (options.dotOutputDir != null || !options.classpath.isEmpty() || options.hasFilter()) {
|
||||
if (options.dotOutputDir != null || options.classpath != null || options.hasFilter()) {
|
||||
showHelp();
|
||||
return EXIT_CMDERR;
|
||||
}
|
||||
// default to compile time view analysis
|
||||
options.compileTimeView = true;
|
||||
for (String fn : classes) {
|
||||
Path p = Paths.get(fn);
|
||||
if (!Files.exists(p) || !fn.endsWith(".jar")) {
|
||||
reportError("err.genmoduleinfo.not.jarfile", fn);
|
||||
return EXIT_CMDERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options.numFilters() > 1) {
|
||||
@ -397,6 +457,16 @@ class JdepsTask {
|
||||
return EXIT_CMDERR;
|
||||
}
|
||||
|
||||
if (options.inverse && options.depth != 1) {
|
||||
reportError("err.invalid.inverse.option", "-R");
|
||||
return EXIT_CMDERR;
|
||||
}
|
||||
|
||||
if (options.inverse && options.numFilters() == 0) {
|
||||
reportError("err.invalid.filters");
|
||||
return EXIT_CMDERR;
|
||||
}
|
||||
|
||||
if ((options.findJDKInternals) && (options.hasFilter() || options.showSummary)) {
|
||||
showHelp();
|
||||
return EXIT_CMDERR;
|
||||
@ -405,12 +475,15 @@ class JdepsTask {
|
||||
showHelp();
|
||||
return EXIT_CMDERR;
|
||||
}
|
||||
if (options.checkModuleDeps != null && !inputArgs.isEmpty()) {
|
||||
reportError("err.invalid.module.option", inputArgs, "-check");
|
||||
}
|
||||
|
||||
boolean ok = run();
|
||||
return ok ? EXIT_OK : EXIT_ERROR;
|
||||
} catch (BadArgs e) {
|
||||
reportError(e.key, e.args);
|
||||
if (e.showUsage) {
|
||||
} catch (BadArgs|UncheckedBadArgs e) {
|
||||
reportError(e.getKey(), e.getArgs());
|
||||
if (e.showUsage()) {
|
||||
log.println(getMessage("main.usage.summary", PROGNAME));
|
||||
}
|
||||
return EXIT_CMDERR;
|
||||
@ -419,176 +492,217 @@ class JdepsTask {
|
||||
return EXIT_CMDERR;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return EXIT_ABNORMAL;
|
||||
return EXIT_CMDERR;
|
||||
} finally {
|
||||
log.flush();
|
||||
}
|
||||
}
|
||||
|
||||
private ModulePaths modulePaths;
|
||||
private boolean run() throws BadArgs, IOException {
|
||||
DependencyFinder dependencyFinder =
|
||||
new DependencyFinder(options.compileTimeView);
|
||||
boolean run() throws IOException {
|
||||
JdepsConfiguration config = buildConfig();
|
||||
|
||||
buildArchive(dependencyFinder);
|
||||
|
||||
if (options.rootModule != null &&
|
||||
(options.checkModuleDeps || (options.dotOutputDir != null &&
|
||||
options.verbose == SUMMARY))) {
|
||||
// -dotfile -s prints the configuration of the given root
|
||||
// -checkModuleDeps prints the suggested module-info.java
|
||||
return analyzeModules(dependencyFinder);
|
||||
}
|
||||
|
||||
// otherwise analyze the dependencies
|
||||
if (options.genModuleInfo != null) {
|
||||
return genModuleInfo(dependencyFinder);
|
||||
} else {
|
||||
return analyzeDeps(dependencyFinder);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildArchive(DependencyFinder dependencyFinder)
|
||||
throws BadArgs, IOException
|
||||
{
|
||||
// If -genmoduleinfo is specified, the input arguments must be JAR files
|
||||
// Treat them as automatic modules for analysis
|
||||
List<Path> jarfiles = options.genModuleInfo != null
|
||||
? classes.stream().map(Paths::get)
|
||||
.collect(Collectors.toList())
|
||||
: Collections.emptyList();
|
||||
// Set module paths
|
||||
this.modulePaths = new ModulePaths(options.upgradeModulePath, options.modulePath, jarfiles);
|
||||
|
||||
// add modules to dependency finder for analysis
|
||||
Map<String, Module> modules = modulePaths.getModules();
|
||||
modules.values().stream()
|
||||
.forEach(dependencyFinder::addModule);
|
||||
|
||||
// If -m option is set, add the specified module and its transitive dependences
|
||||
// to the root set
|
||||
if (options.rootModule != null) {
|
||||
modulePaths.dependences(options.rootModule)
|
||||
.forEach(dependencyFinder::addRoot);
|
||||
}
|
||||
// detect split packages
|
||||
config.splitPackages().entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.forEach(e -> System.out.format("split package: %s %s%n", e.getKey(),
|
||||
e.getValue().toString()));
|
||||
|
||||
// check if any module specified in -requires is missing
|
||||
Optional<String> req = options.requires.stream()
|
||||
.filter(mn -> !modules.containsKey(mn))
|
||||
.findFirst();
|
||||
if (req.isPresent()) {
|
||||
throw new BadArgs("err.module.not.found", req.get());
|
||||
Stream.concat(options.addmods.stream(), options.requires.stream())
|
||||
.filter(mn -> !config.isValidToken(mn))
|
||||
.forEach(mn -> config.findModule(mn).orElseThrow(() ->
|
||||
new UncheckedBadArgs(new BadArgs("err.module.not.found", mn))));
|
||||
|
||||
// -genmoduleinfo
|
||||
if (options.genModuleInfo != null) {
|
||||
return genModuleInfo(config);
|
||||
}
|
||||
|
||||
// classpath
|
||||
for (Path p : getClassPaths(options.classpath)) {
|
||||
if (Files.exists(p)) {
|
||||
dependencyFinder.addClassPathArchive(p);
|
||||
}
|
||||
// -check
|
||||
if (options.checkModuleDeps != null) {
|
||||
return new ModuleAnalyzer(config, log, options.checkModuleDeps).run();
|
||||
}
|
||||
|
||||
// if -genmoduleinfo is not set, the input arguments are considered as
|
||||
// unnamed module. Add them to the root set
|
||||
if (options.genModuleInfo == null) {
|
||||
// add root set
|
||||
for (String s : classes) {
|
||||
Path p = Paths.get(s);
|
||||
if (Files.exists(p)) {
|
||||
// add to the initial root set
|
||||
dependencyFinder.addRoot(p);
|
||||
} else {
|
||||
if (isValidClassName(s)) {
|
||||
dependencyFinder.addClassName(s);
|
||||
} else {
|
||||
warning("warn.invalid.arg", s);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options.dotOutputDir != null &&
|
||||
(options.verbose == SUMMARY || options.verbose == MODULE) &&
|
||||
!options.addmods.isEmpty() && inputArgs.isEmpty()) {
|
||||
return new ModuleAnalyzer(config, log).genDotFiles(options.dotOutputDir);
|
||||
}
|
||||
|
||||
if (options.inverse) {
|
||||
return analyzeInverseDeps(config);
|
||||
} else {
|
||||
return analyzeDeps(config);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean analyzeDeps(DependencyFinder dependencyFinder) throws IOException {
|
||||
JdepsFilter filter = dependencyFilter();
|
||||
private JdepsConfiguration buildConfig() throws IOException {
|
||||
JdepsConfiguration.Builder builder =
|
||||
new JdepsConfiguration.Builder(options.systemModulePath);
|
||||
|
||||
// parse classfiles and find all dependencies
|
||||
findDependencies(dependencyFinder, filter, options.apiOnly);
|
||||
builder.upgradeModulePath(options.upgradeModulePath)
|
||||
.appModulePath(options.modulePath)
|
||||
.addmods(options.addmods);
|
||||
|
||||
// analyze the dependencies collected
|
||||
Analyzer analyzer = new Analyzer(options.verbose, filter);
|
||||
analyzer.run(dependencyFinder.archives());
|
||||
if (options.checkModuleDeps != null) {
|
||||
// check all system modules in the image
|
||||
builder.allModules();
|
||||
}
|
||||
|
||||
if (options.classpath != null)
|
||||
builder.addClassPath(options.classpath);
|
||||
|
||||
// build the root set of archives to be analyzed
|
||||
for (String s : inputArgs) {
|
||||
Path p = Paths.get(s);
|
||||
if (Files.exists(p)) {
|
||||
builder.addRoot(p);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private boolean analyzeDeps(JdepsConfiguration config) throws IOException {
|
||||
// output result
|
||||
final JdepsWriter writer;
|
||||
if (options.dotOutputDir != null) {
|
||||
Path dir = Paths.get(options.dotOutputDir);
|
||||
Files.createDirectories(dir);
|
||||
writer = new DotFileWriter(dir, options.verbose,
|
||||
writer = new DotFileWriter(options.dotOutputDir,
|
||||
options.verbose,
|
||||
options.showProfile,
|
||||
options.showModule,
|
||||
options.showLabel);
|
||||
} else {
|
||||
writer = new SimpleWriter(log, options.verbose,
|
||||
writer = new SimpleWriter(log,
|
||||
options.verbose,
|
||||
options.showProfile,
|
||||
options.showModule);
|
||||
}
|
||||
|
||||
// Targets for reporting - include the root sets and other analyzed archives
|
||||
final List<Archive> targets;
|
||||
if (options.rootModule == null) {
|
||||
// no module as the root set
|
||||
targets = dependencyFinder.archives()
|
||||
.filter(filter::accept)
|
||||
.filter(a -> !a.getModule().isNamed())
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
// named modules in topological order
|
||||
Stream<Module> modules = dependencyFinder.archives()
|
||||
.filter(a -> a.getModule().isNamed())
|
||||
.map(Archive::getModule);
|
||||
Graph<Module> graph = ModuleAnalyzer.graph(modulePaths, modules.toArray(Module[]::new));
|
||||
// then add unnamed module
|
||||
targets = graph.orderedNodes()
|
||||
.filter(filter::accept)
|
||||
.collect(Collectors.toList());
|
||||
// analyze the dependencies
|
||||
DepsAnalyzer analyzer = new DepsAnalyzer(config,
|
||||
dependencyFilter(config),
|
||||
writer,
|
||||
options.verbose,
|
||||
options.apiOnly);
|
||||
|
||||
// in case any reference not found
|
||||
dependencyFinder.archives()
|
||||
.filter(a -> !a.getModule().isNamed())
|
||||
.forEach(targets::add);
|
||||
}
|
||||
boolean ok = analyzer.run(options.compileTimeView, options.depth);
|
||||
|
||||
// print skipped entries, if any
|
||||
analyzer.archives()
|
||||
.forEach(archive -> archive.reader()
|
||||
.skippedEntries().stream()
|
||||
.forEach(name -> warning("warn.skipped.entry",
|
||||
name, archive.getPathName())));
|
||||
|
||||
writer.generateOutput(targets, analyzer);
|
||||
if (options.findJDKInternals && !options.nowarning) {
|
||||
showReplacements(targets, analyzer);
|
||||
Map<String, String> jdkInternals = analyzer.dependences()
|
||||
.collect(Collectors.toMap(Function.identity(), this::replacementFor));
|
||||
|
||||
if (!jdkInternals.isEmpty()) {
|
||||
log.println();
|
||||
warning("warn.replace.useJDKInternals", getMessage("jdeps.wiki.url"));
|
||||
|
||||
if (jdkInternals.values().stream().anyMatch(repl -> repl != null)) {
|
||||
log.println();
|
||||
log.format("%-40s %s%n", "JDK Internal API", "Suggested Replacement");
|
||||
log.format("%-40s %s%n", "----------------", "---------------------");
|
||||
jdkInternals.entrySet().stream()
|
||||
.filter(e -> e.getValue() != null)
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.forEach(e -> log.format("%-40s %s%n", e.getKey(), e.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
return ok;
|
||||
}
|
||||
|
||||
private JdepsFilter dependencyFilter() {
|
||||
// Filter specified by -filter, -package, -regex, and -module options
|
||||
JdepsFilter.Builder builder = new JdepsFilter.Builder();
|
||||
private boolean analyzeInverseDeps(JdepsConfiguration config) throws IOException {
|
||||
JdepsWriter writer = new SimpleWriter(log,
|
||||
options.verbose,
|
||||
options.showProfile,
|
||||
options.showModule);
|
||||
|
||||
// Exclude JDK modules from analysis and reporting if -m specified.
|
||||
modulePaths.getModules().values().stream()
|
||||
.filter(m -> m.isJDK())
|
||||
.map(Module::name)
|
||||
.forEach(options.excludes::add);
|
||||
InverseDepsAnalyzer analyzer = new InverseDepsAnalyzer(config,
|
||||
dependencyFilter(config),
|
||||
writer,
|
||||
options.verbose,
|
||||
options.apiOnly);
|
||||
boolean ok = analyzer.run();
|
||||
|
||||
log.println();
|
||||
if (!options.requires.isEmpty())
|
||||
log.format("Inverse transitive dependences on %s%n", options.requires);
|
||||
else
|
||||
log.format("Inverse transitive dependences matching %s%n",
|
||||
options.regex != null
|
||||
? options.regex.toString()
|
||||
: "packages " + options.packageNames);
|
||||
|
||||
analyzer.inverseDependences().stream()
|
||||
.sorted(Comparator.comparing(this::sortPath))
|
||||
.forEach(path -> log.println(path.stream()
|
||||
.map(Archive::getName)
|
||||
.collect(Collectors.joining(" <- "))));
|
||||
return ok;
|
||||
}
|
||||
|
||||
private String sortPath(Deque<Archive> path) {
|
||||
return path.peekFirst().getName();
|
||||
}
|
||||
|
||||
private boolean genModuleInfo(JdepsConfiguration config) throws IOException {
|
||||
ModuleInfoBuilder builder
|
||||
= new ModuleInfoBuilder(config, inputArgs, options.genModuleInfo);
|
||||
boolean ok = builder.run();
|
||||
|
||||
builder.modules().forEach(module -> {
|
||||
if (module.packages().contains("")) {
|
||||
reportError("ERROR: %s contains unnamed package. " +
|
||||
"module-info.java not generated%n", module.getPathName());
|
||||
}
|
||||
});
|
||||
|
||||
if (!ok && !options.nowarning) {
|
||||
log.println("Missing dependencies");
|
||||
builder.visitMissingDeps(
|
||||
new Analyzer.Visitor() {
|
||||
@Override
|
||||
public void visitDependence(String origin, Archive originArchive,
|
||||
String target, Archive targetArchive) {
|
||||
if (targetArchive == NOT_FOUND)
|
||||
log.format(" %-50s -> %-50s %s%n",
|
||||
origin, target, targetArchive.getName());
|
||||
}
|
||||
});
|
||||
|
||||
log.println("ERROR: missing dependencies (check \"requires NOT_FOUND;\")");
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a filter used during dependency analysis
|
||||
*/
|
||||
private JdepsFilter dependencyFilter(JdepsConfiguration config) {
|
||||
// Filter specified by -filter, -package, -regex, and -requires options
|
||||
JdepsFilter.Builder builder = new JdepsFilter.Builder();
|
||||
|
||||
// source filters
|
||||
builder.includePattern(options.includePattern);
|
||||
builder.includeModules(options.includes);
|
||||
builder.excludeModules(options.excludes);
|
||||
builder.includeSystemModules(options.includeSystemModulePattern);
|
||||
|
||||
builder.filter(options.filterSamePackage, options.filterSameArchive);
|
||||
builder.findJDKInternals(options.findJDKInternals);
|
||||
|
||||
// -module
|
||||
// -requires
|
||||
if (!options.requires.isEmpty()) {
|
||||
Map<String, Module> modules = modulePaths.getModules();
|
||||
builder.packages(options.requires.stream()
|
||||
.map(modules::get)
|
||||
.flatMap(m -> m.packages().stream())
|
||||
.collect(Collectors.toSet()));
|
||||
options.requires.stream()
|
||||
.forEach(mn -> {
|
||||
Module m = config.findModule(mn).get();
|
||||
builder.requires(mn, m.packages());
|
||||
});
|
||||
}
|
||||
// -regex
|
||||
if (options.regex != null)
|
||||
@ -600,62 +714,14 @@ class JdepsTask {
|
||||
if (options.filterRegex != null)
|
||||
builder.filter(options.filterRegex);
|
||||
|
||||
// check if system module is set
|
||||
config.rootModules().stream()
|
||||
.map(Module::name)
|
||||
.forEach(builder::includeIfSystemModule);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void findDependencies(DependencyFinder dependencyFinder,
|
||||
JdepsFilter filter,
|
||||
boolean apiOnly)
|
||||
throws IOException
|
||||
{
|
||||
dependencyFinder.findDependencies(filter, apiOnly, options.depth);
|
||||
|
||||
// print skipped entries, if any
|
||||
for (Archive a : dependencyFinder.roots()) {
|
||||
for (String name : a.reader().skippedEntries()) {
|
||||
warning("warn.skipped.entry", name, a.getPathName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean genModuleInfo(DependencyFinder dependencyFinder) throws IOException {
|
||||
ModuleInfoBuilder builder = new ModuleInfoBuilder(modulePaths, dependencyFinder);
|
||||
boolean result = builder.run(options.verbose, options.nowarning);
|
||||
builder.build(Paths.get(options.genModuleInfo));
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean analyzeModules(DependencyFinder dependencyFinder)
|
||||
throws IOException
|
||||
{
|
||||
ModuleAnalyzer analyzer = new ModuleAnalyzer(modulePaths,
|
||||
dependencyFinder,
|
||||
options.rootModule);
|
||||
if (options.checkModuleDeps) {
|
||||
return analyzer.run();
|
||||
}
|
||||
if (options.dotOutputDir != null && options.verbose == SUMMARY) {
|
||||
Path dir = Paths.get(options.dotOutputDir);
|
||||
Files.createDirectories(dir);
|
||||
analyzer.genDotFile(dir);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isValidClassName(String name) {
|
||||
if (!Character.isJavaIdentifierStart(name.charAt(0))) {
|
||||
return false;
|
||||
}
|
||||
for (int i=1; i < name.length(); i++) {
|
||||
char c = name.charAt(i);
|
||||
if (c != '.' && !Character.isJavaIdentifierPart(c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void handleOptions(String[] args) throws BadArgs {
|
||||
// process options
|
||||
for (int i=0; i < args.length; i++) {
|
||||
@ -684,7 +750,7 @@ class JdepsTask {
|
||||
if (name.charAt(0) == '-') {
|
||||
throw new BadArgs("err.option.after.class", name).showUsage(true);
|
||||
}
|
||||
classes.add(name);
|
||||
inputArgs.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -703,7 +769,7 @@ class JdepsTask {
|
||||
log.println(getMessage("error.prefix") + " " + getMessage(key, args));
|
||||
}
|
||||
|
||||
private void warning(String key, Object... args) {
|
||||
void warning(String key, Object... args) {
|
||||
log.println(getMessage("warn.prefix") + " " + getMessage(key, args));
|
||||
}
|
||||
|
||||
@ -749,7 +815,7 @@ class JdepsTask {
|
||||
boolean version;
|
||||
boolean fullVersion;
|
||||
boolean showProfile;
|
||||
boolean showModule;
|
||||
boolean showModule = true;
|
||||
boolean showSummary;
|
||||
boolean apiOnly;
|
||||
boolean showLabel;
|
||||
@ -761,22 +827,23 @@ class JdepsTask {
|
||||
boolean filterSamePackage = true;
|
||||
boolean filterSameArchive = false;
|
||||
Pattern filterRegex;
|
||||
String dotOutputDir;
|
||||
String genModuleInfo;
|
||||
String classpath = "";
|
||||
Path dotOutputDir;
|
||||
Path genModuleInfo;
|
||||
String classpath;
|
||||
int depth = 1;
|
||||
Set<String> requires = new HashSet<>();
|
||||
Set<String> packageNames = new HashSet<>();
|
||||
Pattern regex; // apply to the dependences
|
||||
Pattern includePattern; // apply to classes
|
||||
Pattern includePattern;
|
||||
Pattern includeSystemModulePattern;
|
||||
boolean inverse = false;
|
||||
boolean compileTimeView = false;
|
||||
boolean checkModuleDeps = false;
|
||||
Set<String> checkModuleDeps;
|
||||
String systemModulePath = System.getProperty("java.home");
|
||||
String upgradeModulePath;
|
||||
String modulePath;
|
||||
String rootModule;
|
||||
// modules to be included or excluded
|
||||
Set<String> includes = new HashSet<>();
|
||||
Set<String> excludes = new HashSet<>();
|
||||
Set<String> addmods = new HashSet<>();
|
||||
|
||||
boolean hasFilter() {
|
||||
return numFilters() > 0;
|
||||
@ -789,11 +856,8 @@ class JdepsTask {
|
||||
if (packageNames.size() > 0) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
boolean isRootModule() {
|
||||
return rootModule != null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ResourceBundleHelper {
|
||||
static final ResourceBundle versionRB;
|
||||
static final ResourceBundle bundle;
|
||||
@ -819,35 +883,6 @@ class JdepsTask {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the list of Archive specified in cpaths and not included
|
||||
* initialArchives
|
||||
*/
|
||||
private List<Path> getClassPaths(String cpaths) throws IOException
|
||||
{
|
||||
if (cpaths.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Path> paths = new ArrayList<>();
|
||||
for (String p : cpaths.split(File.pathSeparator)) {
|
||||
if (p.length() > 0) {
|
||||
// wildcard to parse all JAR files e.g. -classpath dir/*
|
||||
int i = p.lastIndexOf(".*");
|
||||
if (i > 0) {
|
||||
Path dir = Paths.get(p.substring(0, i));
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.jar")) {
|
||||
for (Path entry : stream) {
|
||||
paths.add(entry);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
paths.add(Paths.get(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the recommended replacement API for the given classname;
|
||||
* or return null if replacement API is not known.
|
||||
@ -865,32 +900,5 @@ class JdepsTask {
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
private void showReplacements(List<Archive> archives, Analyzer analyzer) {
|
||||
Map<String,String> jdkinternals = new TreeMap<>();
|
||||
boolean useInternals = false;
|
||||
for (Archive source : archives) {
|
||||
useInternals = useInternals || analyzer.hasDependences(source);
|
||||
for (String cn : analyzer.dependences(source)) {
|
||||
String repl = replacementFor(cn);
|
||||
if (repl != null) {
|
||||
jdkinternals.putIfAbsent(cn, repl);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (useInternals) {
|
||||
log.println();
|
||||
warning("warn.replace.useJDKInternals", getMessage("jdeps.wiki.url"));
|
||||
}
|
||||
if (!jdkinternals.isEmpty()) {
|
||||
log.println();
|
||||
log.format("%-40s %s%n", "JDK Internal API", "Suggested Replacement");
|
||||
log.format("%-40s %s%n", "----------------", "---------------------");
|
||||
for (Map.Entry<String,String> e : jdkinternals.entrySet()) {
|
||||
log.format("%-40s %s%n", e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -24,23 +24,33 @@
|
||||
*/
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import static com.sun.tools.jdeps.Analyzer.Type.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.module.ModuleDescriptor.Requires;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.sun.tools.jdeps.Analyzer.Type.*;
|
||||
|
||||
public abstract class JdepsWriter {
|
||||
public static JdepsWriter newDotWriter(Path outputdir, Analyzer.Type type) {
|
||||
return new DotFileWriter(outputdir, type, false, true, false);
|
||||
}
|
||||
|
||||
public static JdepsWriter newSimpleWriter(PrintWriter writer, Analyzer.Type type) {
|
||||
return new SimpleWriter(writer, type, false, true);
|
||||
}
|
||||
|
||||
final Analyzer.Type type;
|
||||
final boolean showProfile;
|
||||
final boolean showModule;
|
||||
|
||||
JdepsWriter(Analyzer.Type type, boolean showProfile, boolean showModule) {
|
||||
private JdepsWriter(Analyzer.Type type, boolean showProfile, boolean showModule) {
|
||||
this.type = type;
|
||||
this.showProfile = showProfile;
|
||||
this.showModule = showModule;
|
||||
@ -48,7 +58,7 @@ public abstract class JdepsWriter {
|
||||
|
||||
abstract void generateOutput(Collection<Archive> archives, Analyzer analyzer) throws IOException;
|
||||
|
||||
public static class DotFileWriter extends JdepsWriter {
|
||||
static class DotFileWriter extends JdepsWriter {
|
||||
final boolean showLabel;
|
||||
final Path outputDir;
|
||||
DotFileWriter(Path dir, Analyzer.Type type,
|
||||
@ -62,8 +72,10 @@ public abstract class JdepsWriter {
|
||||
void generateOutput(Collection<Archive> archives, Analyzer analyzer)
|
||||
throws IOException
|
||||
{
|
||||
Files.createDirectories(outputDir);
|
||||
|
||||
// output individual .dot file for each archive
|
||||
if (type != SUMMARY) {
|
||||
if (type != SUMMARY && type != MODULE) {
|
||||
archives.stream()
|
||||
.filter(analyzer::hasDependences)
|
||||
.forEach(archive -> {
|
||||
@ -85,13 +97,13 @@ public abstract class JdepsWriter {
|
||||
{
|
||||
// If verbose mode (-v or -verbose option),
|
||||
// the summary.dot file shows package-level dependencies.
|
||||
Analyzer.Type summaryType =
|
||||
(type == PACKAGE || type == SUMMARY) ? SUMMARY : PACKAGE;
|
||||
boolean isSummary = type == PACKAGE || type == SUMMARY || type == MODULE;
|
||||
Analyzer.Type summaryType = isSummary ? SUMMARY : PACKAGE;
|
||||
Path summary = outputDir.resolve("summary.dot");
|
||||
try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary));
|
||||
SummaryDotFile dotfile = new SummaryDotFile(sw, summaryType)) {
|
||||
for (Archive archive : archives) {
|
||||
if (type == PACKAGE || type == SUMMARY) {
|
||||
if (isSummary) {
|
||||
if (showLabel) {
|
||||
// build labels listing package-level dependencies
|
||||
analyzer.visitDependences(archive, dotfile.labelBuilder(), PACKAGE);
|
||||
@ -208,19 +220,22 @@ public abstract class JdepsWriter {
|
||||
void generateOutput(Collection<Archive> archives, Analyzer analyzer) {
|
||||
RawOutputFormatter depFormatter = new RawOutputFormatter(writer);
|
||||
RawSummaryFormatter summaryFormatter = new RawSummaryFormatter(writer);
|
||||
for (Archive archive : archives) {
|
||||
// print summary
|
||||
if (showModule && archive.getModule().isNamed()) {
|
||||
summaryFormatter.showModuleRequires(archive.getModule());
|
||||
} else {
|
||||
archives.stream()
|
||||
.filter(analyzer::hasDependences)
|
||||
.sorted(Comparator.comparing(Archive::getName))
|
||||
.forEach(archive -> {
|
||||
if (showModule && archive.getModule().isNamed() && type != SUMMARY) {
|
||||
// print module-info except -summary
|
||||
summaryFormatter.printModuleDescriptor(archive.getModule());
|
||||
}
|
||||
// print summary
|
||||
analyzer.visitDependences(archive, summaryFormatter, SUMMARY);
|
||||
}
|
||||
|
||||
if (analyzer.hasDependences(archive) && type != SUMMARY) {
|
||||
// print the class-level or package-level dependences
|
||||
analyzer.visitDependences(archive, depFormatter);
|
||||
}
|
||||
}
|
||||
if (analyzer.hasDependences(archive) && type != SUMMARY) {
|
||||
// print the class-level or package-level dependences
|
||||
analyzer.visitDependences(archive, depFormatter);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class RawOutputFormatter implements Analyzer.Visitor {
|
||||
@ -269,20 +284,16 @@ public abstract class JdepsWriter {
|
||||
writer.format("%n");
|
||||
}
|
||||
|
||||
public void showModuleRequires(Module module) {
|
||||
public void printModuleDescriptor(Module module) {
|
||||
if (!module.isNamed())
|
||||
return;
|
||||
|
||||
writer.format("module %s", module.name());
|
||||
if (module.isAutomatic())
|
||||
writer.format(" (automatic)");
|
||||
writer.println();
|
||||
module.requires().keySet()
|
||||
writer.format("%s%s%n", module.name(), module.isAutomatic() ? " automatic" : "");
|
||||
writer.format(" [%s]%n", module.location());
|
||||
module.descriptor().requires()
|
||||
.stream()
|
||||
.sorted()
|
||||
.forEach(req -> writer.format(" requires %s%s%n",
|
||||
module.requires.get(req) ? "public " : "",
|
||||
req));
|
||||
.sorted(Comparator.comparing(Requires::name))
|
||||
.forEach(req -> writer.format(" requires %s%n", req));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -307,7 +318,7 @@ public abstract class JdepsWriter {
|
||||
}
|
||||
|
||||
// exported API
|
||||
boolean jdkunsupported = Module.isJDKUnsupported(module, pn);
|
||||
boolean jdkunsupported = Module.JDK_UNSUPPORTED.equals(module.name());
|
||||
if (module.isExported(pn) && !jdkunsupported) {
|
||||
return showProfileOrModule(module);
|
||||
}
|
||||
|
||||
@ -25,67 +25,56 @@
|
||||
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* JDeps internal representation of module for dependency analysis.
|
||||
* Jdeps internal representation of module for dependency analysis.
|
||||
*/
|
||||
class Module extends Archive {
|
||||
static final boolean traceOn = Boolean.getBoolean("jdeps.debug");
|
||||
static final Module UNNAMED_MODULE = new UnnamedModule();
|
||||
static final String JDK_UNSUPPORTED = "jdk.unsupported";
|
||||
|
||||
static final boolean DEBUG = Boolean.getBoolean("jdeps.debug");
|
||||
static void trace(String fmt, Object... args) {
|
||||
trace(DEBUG, fmt, args);
|
||||
}
|
||||
|
||||
static void trace(boolean traceOn, String fmt, Object... args) {
|
||||
if (traceOn) {
|
||||
System.err.format(fmt, args);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the given package name is JDK critical internal API
|
||||
* in jdk.unsupported module
|
||||
*/
|
||||
static boolean isJDKUnsupported(Module m, String pn) {
|
||||
return JDK_UNSUPPORTED.equals(m.name()) || unsupported.contains(pn);
|
||||
};
|
||||
private final ModuleDescriptor descriptor;
|
||||
private final Map<String, Set<String>> exports;
|
||||
private final boolean isSystem;
|
||||
private final URI location;
|
||||
|
||||
protected final ModuleDescriptor descriptor;
|
||||
protected final Map<String, Boolean> requires;
|
||||
protected final Map<String, Set<String>> exports;
|
||||
protected final Set<String> packages;
|
||||
protected final boolean isJDK;
|
||||
protected final URI location;
|
||||
protected Module(String name) {
|
||||
super(name);
|
||||
this.descriptor = null;
|
||||
this.location = null;
|
||||
this.exports = Collections.emptyMap();
|
||||
this.isSystem = true;
|
||||
}
|
||||
|
||||
private Module(String name,
|
||||
URI location,
|
||||
ModuleDescriptor descriptor,
|
||||
Map<String, Boolean> requires,
|
||||
Map<String, Set<String>> exports,
|
||||
Set<String> packages,
|
||||
boolean isJDK,
|
||||
boolean isSystem,
|
||||
ClassFileReader reader) {
|
||||
super(name, location, reader);
|
||||
this.descriptor = descriptor;
|
||||
this.location = location;
|
||||
this.requires = Collections.unmodifiableMap(requires);
|
||||
this.exports = Collections.unmodifiableMap(exports);
|
||||
this.packages = Collections.unmodifiableSet(packages);
|
||||
this.isJDK = isJDK;
|
||||
this.isSystem = isSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,31 +100,35 @@ class Module extends Archive {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public boolean isJDK() {
|
||||
return isJDK;
|
||||
public URI location() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public Map<String, Boolean> requires() {
|
||||
return requires;
|
||||
public boolean isJDK() {
|
||||
String mn = name();
|
||||
return isSystem &&
|
||||
(mn.startsWith("java.") || mn.startsWith("jdk.") || mn.startsWith("javafx."));
|
||||
}
|
||||
|
||||
public boolean isSystem() {
|
||||
return isSystem;
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> exports() {
|
||||
return exports;
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> provides() {
|
||||
return descriptor.provides().entrySet().stream()
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().providers()));
|
||||
}
|
||||
|
||||
public Set<String> packages() {
|
||||
return packages;
|
||||
return descriptor.packages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the package of the given name is exported.
|
||||
*/
|
||||
public boolean isExported(String pn) {
|
||||
if (JDK_UNSUPPORTED.equals(this.name())) {
|
||||
return false;
|
||||
}
|
||||
return exports.containsKey(pn) ? exports.get(pn).isEmpty() : false;
|
||||
}
|
||||
|
||||
@ -159,11 +152,6 @@ class Module extends Archive {
|
||||
return isExported(pn) || exports.containsKey(pn) && exports.get(pn).contains(target);
|
||||
}
|
||||
|
||||
private final static String JDK_UNSUPPORTED = "jdk.unsupported";
|
||||
|
||||
// temporary until jdk.unsupported module
|
||||
private final static List<String> unsupported = Arrays.asList("sun.misc", "sun.reflect");
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name();
|
||||
@ -171,21 +159,19 @@ class Module extends Archive {
|
||||
|
||||
public final static class Builder {
|
||||
final String name;
|
||||
final Map<String, Boolean> requires = new HashMap<>();
|
||||
final Map<String, Set<String>> exports = new HashMap<>();
|
||||
final Set<String> packages = new HashSet<>();
|
||||
final boolean isJDK;
|
||||
final ModuleDescriptor descriptor;
|
||||
final boolean isSystem;
|
||||
ClassFileReader reader;
|
||||
ModuleDescriptor descriptor;
|
||||
URI location;
|
||||
|
||||
public Builder(String name) {
|
||||
this(name, false);
|
||||
public Builder(ModuleDescriptor md) {
|
||||
this(md, false);
|
||||
}
|
||||
|
||||
public Builder(String name, boolean isJDK) {
|
||||
this.name = name;
|
||||
this.isJDK = isJDK;
|
||||
public Builder(ModuleDescriptor md, boolean isSystem) {
|
||||
this.name = md.name();
|
||||
this.descriptor = md;
|
||||
this.isSystem = isSystem;
|
||||
}
|
||||
|
||||
public Builder location(URI location) {
|
||||
@ -193,48 +179,30 @@ class Module extends Archive {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder descriptor(ModuleDescriptor md) {
|
||||
this.descriptor = md;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder require(String d, boolean reexport) {
|
||||
requires.put(d, reexport);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder packages(Set<String> pkgs) {
|
||||
packages.addAll(pkgs);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder export(String p, Set<String> ms) {
|
||||
Objects.requireNonNull(p);
|
||||
Objects.requireNonNull(ms);
|
||||
exports.put(p, new HashSet<>(ms));
|
||||
return this;
|
||||
}
|
||||
public Builder classes(ClassFileReader reader) {
|
||||
this.reader = reader;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Module build() {
|
||||
if (descriptor.isAutomatic() && isJDK) {
|
||||
if (descriptor.isAutomatic() && isSystem) {
|
||||
throw new InternalError("JDK module: " + name + " can't be automatic module");
|
||||
}
|
||||
|
||||
return new Module(name, location, descriptor, requires, exports, packages, isJDK, reader);
|
||||
Map<String, Set<String>> exports = new HashMap<>();
|
||||
|
||||
descriptor.exports().stream()
|
||||
.forEach(exp -> exports.computeIfAbsent(exp.source(), _k -> new HashSet<>())
|
||||
.addAll(exp.targets()));
|
||||
|
||||
return new Module(name, location, descriptor, exports, isSystem, reader);
|
||||
}
|
||||
}
|
||||
|
||||
final static Module UNNAMED_MODULE = new UnnamedModule();
|
||||
private static class UnnamedModule extends Module {
|
||||
private UnnamedModule() {
|
||||
super("unnamed", null, null,
|
||||
Collections.emptyMap(),
|
||||
Collections.emptyMap(),
|
||||
Collections.emptySet(),
|
||||
false, null);
|
||||
}
|
||||
|
||||
@ -260,10 +228,7 @@ class Module extends Archive {
|
||||
}
|
||||
|
||||
private static class StrictModule extends Module {
|
||||
private static final String SERVICES_PREFIX = "META-INF/services/";
|
||||
private final Map<String, Set<String>> provides;
|
||||
private final Module module;
|
||||
private final JarFile jarfile;
|
||||
private final ModuleDescriptor md;
|
||||
|
||||
/**
|
||||
* Converts the given automatic module to a strict module.
|
||||
@ -272,114 +237,26 @@ class Module extends Archive {
|
||||
* declare service providers, if specified in META-INF/services configuration file
|
||||
*/
|
||||
private StrictModule(Module m, Map<String, Boolean> requires) {
|
||||
super(m.name(), m.location, m.descriptor, requires, m.exports, m.packages, m.isJDK, m.reader());
|
||||
this.module = m;
|
||||
try {
|
||||
this.jarfile = new JarFile(m.path().toFile(), false);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
this.provides = providers(jarfile);
|
||||
super(m.name(), m.location, m.descriptor, m.exports, m.isSystem, m.reader());
|
||||
|
||||
ModuleDescriptor.Builder builder = new ModuleDescriptor.Builder(m.name());
|
||||
requires.keySet().forEach(mn -> {
|
||||
if (requires.get(mn).equals(Boolean.TRUE)) {
|
||||
builder.requires(ModuleDescriptor.Requires.Modifier.PUBLIC, mn);
|
||||
} else {
|
||||
builder.requires(mn);
|
||||
}
|
||||
});
|
||||
m.descriptor.exports().forEach(e -> builder.exports(e));
|
||||
m.descriptor.uses().forEach(s -> builder.uses(s));
|
||||
m.descriptor.provides().values().forEach(p -> builder.provides(p));
|
||||
builder.conceals(m.descriptor.conceals());
|
||||
this.md = builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Set<String>> provides() {
|
||||
return provides;
|
||||
}
|
||||
|
||||
private Map<String, Set<String>> providers(JarFile jf) {
|
||||
Map<String, Set<String>> provides = new HashMap<>();
|
||||
// map names of service configuration files to service names
|
||||
Set<String> serviceNames = jf.stream()
|
||||
.map(e -> e.getName())
|
||||
.filter(e -> e.startsWith(SERVICES_PREFIX))
|
||||
.distinct()
|
||||
.map(this::toServiceName)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// parse each service configuration file
|
||||
for (String sn : serviceNames) {
|
||||
JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn);
|
||||
Set<String> providerClasses = new HashSet<>();
|
||||
try (InputStream in = jf.getInputStream(entry)) {
|
||||
BufferedReader reader
|
||||
= new BufferedReader(new InputStreamReader(in, "UTF-8"));
|
||||
String cn;
|
||||
while ((cn = nextLine(reader)) != null) {
|
||||
if (isJavaIdentifier(cn)) {
|
||||
providerClasses.add(cn);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
if (!providerClasses.isEmpty())
|
||||
provides.put(sn, providerClasses);
|
||||
}
|
||||
|
||||
return provides;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a container with the service type corresponding to the name of
|
||||
* a services configuration file.
|
||||
*
|
||||
* For example, if called with "META-INF/services/p.S" then this method
|
||||
* returns a container with the value "p.S".
|
||||
*/
|
||||
private Optional<String> toServiceName(String cf) {
|
||||
assert cf.startsWith(SERVICES_PREFIX);
|
||||
int index = cf.lastIndexOf("/") + 1;
|
||||
if (index < cf.length()) {
|
||||
String prefix = cf.substring(0, index);
|
||||
if (prefix.equals(SERVICES_PREFIX)) {
|
||||
String sn = cf.substring(index);
|
||||
if (isJavaIdentifier(sn))
|
||||
return Optional.of(sn);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next line from the given reader and trims it of comments and
|
||||
* leading/trailing white space.
|
||||
*
|
||||
* Returns null if the reader is at EOF.
|
||||
*/
|
||||
private String nextLine(BufferedReader reader) throws IOException {
|
||||
String ln = reader.readLine();
|
||||
if (ln != null) {
|
||||
int ci = ln.indexOf('#');
|
||||
if (ci >= 0)
|
||||
ln = ln.substring(0, ci);
|
||||
ln = ln.trim();
|
||||
}
|
||||
return ln;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given identifier is a legal Java identifier.
|
||||
*/
|
||||
private static boolean isJavaIdentifier(String id) {
|
||||
int n = id.length();
|
||||
if (n == 0)
|
||||
return false;
|
||||
if (!Character.isJavaIdentifierStart(id.codePointAt(0)))
|
||||
return false;
|
||||
int cp = id.codePointAt(0);
|
||||
int i = Character.charCount(cp);
|
||||
for (; i < n; i += Character.charCount(cp)) {
|
||||
cp = id.codePointAt(i);
|
||||
if (!Character.isJavaIdentifierPart(cp) && id.charAt(i) != '.')
|
||||
return false;
|
||||
}
|
||||
if (cp == '.')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
public ModuleDescriptor descriptor() {
|
||||
return md;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -24,15 +24,24 @@
|
||||
*/
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import static com.sun.tools.jdeps.Analyzer.Type.CLASS;
|
||||
import static com.sun.tools.jdeps.JdepsTask.*;
|
||||
import static com.sun.tools.jdeps.Analyzer.NOT_FOUND;
|
||||
import static com.sun.tools.jdeps.Module.trace;
|
||||
import static com.sun.tools.jdeps.JdepsFilter.DEFAULT_FILTER;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Exports;
|
||||
import java.lang.module.ModuleDescriptor.Provides;
|
||||
import java.lang.module.ModuleDescriptor.Requires;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@ -40,168 +49,159 @@ import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
||||
public class ModuleInfoBuilder {
|
||||
final ModulePaths modulePaths;
|
||||
final JdepsConfiguration configuration;
|
||||
final Path outputdir;
|
||||
|
||||
final DependencyFinder dependencyFinder;
|
||||
final JdepsFilter filter;
|
||||
final Analyzer analyzer;
|
||||
final Map<Module, Module> strictModules = new HashMap<>();
|
||||
ModuleInfoBuilder(ModulePaths modulePaths, DependencyFinder finder) {
|
||||
this.modulePaths = modulePaths;
|
||||
this.dependencyFinder = finder;
|
||||
this.filter = new JdepsFilter.Builder().filter(true, true).build();
|
||||
this.analyzer = new Analyzer(CLASS, filter);
|
||||
final Map<Module, Module> strictModules;
|
||||
public ModuleInfoBuilder(JdepsConfiguration configuration,
|
||||
List<String> args,
|
||||
Path outputdir) {
|
||||
this.configuration = configuration;
|
||||
this.outputdir = outputdir;
|
||||
|
||||
this.dependencyFinder = new DependencyFinder(configuration, DEFAULT_FILTER);
|
||||
this.analyzer = new Analyzer(configuration, Analyzer.Type.CLASS, DEFAULT_FILTER);
|
||||
|
||||
// add targets to modulepath if it has module-info.class
|
||||
List<Path> paths = args.stream()
|
||||
.map(fn -> Paths.get(fn))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// automatic module to convert to strict module
|
||||
this.strictModules = ModuleFinder.of(paths.toArray(new Path[0]))
|
||||
.findAll().stream()
|
||||
.map(configuration::toModule)
|
||||
.collect(Collectors.toMap(Function.identity(), Function.identity()));
|
||||
|
||||
Optional<Module> om = strictModules.keySet().stream()
|
||||
.filter(m -> !m.descriptor().isAutomatic())
|
||||
.findAny();
|
||||
if (om.isPresent()) {
|
||||
throw new UncheckedBadArgs(new BadArgs("err.genmoduleinfo.not.jarfile",
|
||||
om.get().getPathName()));
|
||||
}
|
||||
if (strictModules.isEmpty()) {
|
||||
throw new UncheckedBadArgs(new BadArgs("err.invalid.path", args));
|
||||
}
|
||||
}
|
||||
|
||||
private Stream<Module> automaticModules() {
|
||||
return modulePaths.getModules().values()
|
||||
.stream()
|
||||
.filter(Module::isAutomatic);
|
||||
public boolean run() throws IOException {
|
||||
try {
|
||||
// pass 1: find API dependencies
|
||||
Map<Archive, Set<Archive>> requiresPublic = computeRequiresPublic();
|
||||
|
||||
// pass 2: analyze all class dependences
|
||||
dependencyFinder.parse(automaticModules().stream());
|
||||
|
||||
analyzer.run(automaticModules(), dependencyFinder.locationToArchive());
|
||||
|
||||
// computes requires and requires public
|
||||
automaticModules().forEach(m -> {
|
||||
Map<String, Boolean> requires;
|
||||
if (requiresPublic.containsKey(m)) {
|
||||
requires = requiresPublic.get(m).stream()
|
||||
.map(Archive::getModule)
|
||||
.collect(Collectors.toMap(Module::name, (v) -> Boolean.TRUE));
|
||||
} else {
|
||||
requires = new HashMap<>();
|
||||
}
|
||||
analyzer.requires(m)
|
||||
.map(Archive::getModule)
|
||||
.forEach(d -> requires.putIfAbsent(d.name(), Boolean.FALSE));
|
||||
|
||||
strictModules.put(m, m.toStrictModule(requires));
|
||||
});
|
||||
|
||||
// generate module-info.java
|
||||
descriptors().forEach(md -> writeModuleInfo(outputdir, md));
|
||||
|
||||
// find any missing dependences
|
||||
return automaticModules().stream()
|
||||
.flatMap(analyzer::requires)
|
||||
.allMatch(m -> !m.equals(NOT_FOUND));
|
||||
} finally {
|
||||
dependencyFinder.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stream of resulting modules
|
||||
*/
|
||||
Stream<Module> modules() {
|
||||
return strictModules.values().stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stream of resulting ModuleDescriptors
|
||||
*/
|
||||
public Stream<ModuleDescriptor> descriptors() {
|
||||
return strictModules.values().stream().map(Module::descriptor);
|
||||
}
|
||||
|
||||
void visitMissingDeps(Analyzer.Visitor visitor) {
|
||||
automaticModules().stream()
|
||||
.filter(m -> analyzer.requires(m).anyMatch(d -> d.equals(NOT_FOUND)))
|
||||
.forEach(m -> {
|
||||
analyzer.visitDependences(m, visitor, Analyzer.Type.VERBOSE);
|
||||
});
|
||||
}
|
||||
void writeModuleInfo(Path dir, ModuleDescriptor descriptor) {
|
||||
String mn = descriptor.name();
|
||||
Path srcFile = dir.resolve(mn).resolve("module-info.java");
|
||||
try {
|
||||
Files.createDirectories(srcFile.getParent());
|
||||
System.out.println("writing to " + srcFile);
|
||||
try (PrintWriter pw = new PrintWriter(Files.newOutputStream(srcFile))) {
|
||||
printModuleInfo(pw, descriptor);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void printModuleInfo(PrintWriter writer, ModuleDescriptor descriptor) {
|
||||
writer.format("module %s {%n", descriptor.name());
|
||||
|
||||
Map<String, Module> modules = configuration.getModules();
|
||||
// first print the JDK modules
|
||||
descriptor.requires().stream()
|
||||
.filter(req -> !req.name().equals("java.base")) // implicit requires
|
||||
.sorted(Comparator.comparing(Requires::name))
|
||||
.forEach(req -> writer.format(" requires %s;%n", req));
|
||||
|
||||
descriptor.exports().stream()
|
||||
.peek(exp -> {
|
||||
if (exp.targets().size() > 0)
|
||||
throw new InternalError(descriptor.name() + " qualified exports: " + exp);
|
||||
})
|
||||
.sorted(Comparator.comparing(Exports::source))
|
||||
.forEach(exp -> writer.format(" exports %s;%n", exp.source()));
|
||||
|
||||
descriptor.provides().values().stream()
|
||||
.sorted(Comparator.comparing(Provides::service))
|
||||
.forEach(p -> p.providers().stream()
|
||||
.sorted()
|
||||
.forEach(impl -> writer.format(" provides %s with %s;%n", p.service(), impl)));
|
||||
|
||||
writer.println("}");
|
||||
}
|
||||
|
||||
|
||||
private Set<Module> automaticModules() {
|
||||
return strictModules.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute 'requires public' dependences by analyzing API dependencies
|
||||
*/
|
||||
Map<Module, Set<Module>> computeRequiresPublic() throws IOException {
|
||||
dependencyFinder.findDependencies(filter, true /* api only */, 1);
|
||||
Analyzer pass1 = new Analyzer(Analyzer.Type.CLASS, filter);
|
||||
private Map<Archive, Set<Archive>> computeRequiresPublic() throws IOException {
|
||||
// parse the input modules
|
||||
dependencyFinder.parseExportedAPIs(automaticModules().stream());
|
||||
|
||||
pass1.run(dependencyFinder.archives());
|
||||
|
||||
return automaticModules().collect(Collectors.toMap(Function.identity(),
|
||||
source -> pass1.requires(source)
|
||||
.map(Archive::getModule)
|
||||
.collect(Collectors.toSet())));
|
||||
}
|
||||
|
||||
boolean run(Analyzer.Type verbose, boolean quiet) throws IOException {
|
||||
// add all automatic modules to the root set
|
||||
automaticModules().forEach(dependencyFinder::addRoot);
|
||||
|
||||
// pass 1: find API dependencies
|
||||
Map<Module, Set<Module>> requiresPublic = computeRequiresPublic();
|
||||
|
||||
// pass 2: analyze all class dependences
|
||||
dependencyFinder.findDependencies(filter, false /* all classes */, 1);
|
||||
analyzer.run(dependencyFinder.archives());
|
||||
|
||||
// computes requires and requires public
|
||||
automaticModules().forEach(m -> {
|
||||
Map<String, Boolean> requires;
|
||||
if (requiresPublic.containsKey(m)) {
|
||||
requires = requiresPublic.get(m)
|
||||
.stream()
|
||||
.collect(Collectors.toMap(Archive::getName, (v) -> Boolean.TRUE));
|
||||
} else {
|
||||
requires = new HashMap<>();
|
||||
}
|
||||
analyzer.requires(m)
|
||||
.forEach(d -> requires.putIfAbsent(d.getName(), Boolean.FALSE));
|
||||
|
||||
trace("strict module %s requires %s%n", m.name(), requires);
|
||||
strictModules.put(m, m.toStrictModule(requires));
|
||||
});
|
||||
|
||||
// find any missing dependences
|
||||
Optional<Module> missingDeps = automaticModules()
|
||||
.filter(this::missingDep)
|
||||
.findAny();
|
||||
if (missingDeps.isPresent()) {
|
||||
automaticModules()
|
||||
.filter(this::missingDep)
|
||||
.forEach(m -> {
|
||||
System.err.format("Missing dependencies from %s%n", m.name());
|
||||
analyzer.visitDependences(m,
|
||||
new Analyzer.Visitor() {
|
||||
@Override
|
||||
public void visitDependence(String origin, Archive originArchive,
|
||||
String target, Archive targetArchive) {
|
||||
if (targetArchive == NOT_FOUND)
|
||||
System.err.format(" %-50s -> %-50s %s%n",
|
||||
origin, target, targetArchive.getName());
|
||||
}
|
||||
}, verbose);
|
||||
System.err.println();
|
||||
});
|
||||
|
||||
System.err.println("ERROR: missing dependencies (check \"requires NOT_FOUND;\")");
|
||||
}
|
||||
return missingDeps.isPresent() ? false : true;
|
||||
}
|
||||
|
||||
private boolean missingDep(Archive m) {
|
||||
return analyzer.requires(m).filter(a -> a.equals(NOT_FOUND))
|
||||
.findAny().isPresent();
|
||||
}
|
||||
|
||||
void build(Path dir) throws IOException {
|
||||
ModuleInfoWriter writer = new ModuleInfoWriter(dir);
|
||||
writer.generateOutput(strictModules.values(), analyzer);
|
||||
}
|
||||
|
||||
private class ModuleInfoWriter {
|
||||
private final Path outputDir;
|
||||
ModuleInfoWriter(Path dir) {
|
||||
this.outputDir = dir;
|
||||
}
|
||||
|
||||
void generateOutput(Iterable<Module> modules, Analyzer analyzer) throws IOException {
|
||||
// generate module-info.java file for each archive
|
||||
for (Module m : modules) {
|
||||
if (m.packages().contains("")) {
|
||||
System.err.format("ERROR: %s contains unnamed package. " +
|
||||
"module-info.java not generated%n", m.getPathName());
|
||||
continue;
|
||||
}
|
||||
|
||||
String mn = m.getName();
|
||||
Path srcFile = outputDir.resolve(mn).resolve("module-info.java");
|
||||
Files.createDirectories(srcFile.getParent());
|
||||
System.out.println("writing to " + srcFile);
|
||||
try (PrintWriter pw = new PrintWriter(Files.newOutputStream(srcFile))) {
|
||||
printModuleInfo(pw, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void printModuleInfo(PrintWriter writer, Module m) {
|
||||
writer.format("module %s {%n", m.name());
|
||||
|
||||
Map<String, Module> modules = modulePaths.getModules();
|
||||
Map<String, Boolean> requires = m.requires();
|
||||
// first print the JDK modules
|
||||
requires.keySet().stream()
|
||||
.filter(mn -> !mn.equals("java.base")) // implicit requires
|
||||
.filter(mn -> modules.containsKey(mn) && modules.get(mn).isJDK())
|
||||
.sorted()
|
||||
.forEach(mn -> {
|
||||
String modifier = requires.get(mn) ? "public " : "";
|
||||
writer.format(" requires %s%s;%n", modifier, mn);
|
||||
});
|
||||
|
||||
// print requires non-JDK modules
|
||||
requires.keySet().stream()
|
||||
.filter(mn -> !modules.containsKey(mn) || !modules.get(mn).isJDK())
|
||||
.sorted()
|
||||
.forEach(mn -> {
|
||||
String modifier = requires.get(mn) ? "public " : "";
|
||||
writer.format(" requires %s%s;%n", modifier, mn);
|
||||
});
|
||||
|
||||
m.packages().stream()
|
||||
.sorted()
|
||||
.forEach(pn -> writer.format(" exports %s;%n", pn));
|
||||
|
||||
m.provides().entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.forEach(e -> {
|
||||
String service = e.getKey();
|
||||
e.getValue().stream()
|
||||
.sorted()
|
||||
.forEach(impl -> writer.format(" provides %s with %s;%n", service, impl));
|
||||
});
|
||||
|
||||
writer.println("}");
|
||||
}
|
||||
return dependencyFinder.dependences();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,206 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystemNotFoundException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.ProviderNotFoundException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
|
||||
|
||||
public class ModulePaths {
|
||||
final ModuleFinder finder;
|
||||
final Map<String, Module> modules = new LinkedHashMap<>();
|
||||
|
||||
public ModulePaths(String upgradeModulePath, String modulePath) {
|
||||
this(upgradeModulePath, modulePath, Collections.emptyList());
|
||||
}
|
||||
|
||||
public ModulePaths(String upgradeModulePath, String modulePath, List<Path> jars) {
|
||||
ModuleFinder finder = ModuleFinder.ofSystem();
|
||||
if (upgradeModulePath != null) {
|
||||
finder = ModuleFinder.compose(createModulePathFinder(upgradeModulePath), finder);
|
||||
}
|
||||
if (jars.size() > 0) {
|
||||
finder = ModuleFinder.compose(finder, ModuleFinder.of(jars.toArray(new Path[0])));
|
||||
}
|
||||
if (modulePath != null) {
|
||||
finder = ModuleFinder.compose(finder, createModulePathFinder(modulePath));
|
||||
}
|
||||
this.finder = finder;
|
||||
|
||||
// add modules from modulepaths
|
||||
finder.findAll().stream().forEach(mref ->
|
||||
modules.computeIfAbsent(mref.descriptor().name(), mn -> toModule(mn, mref))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of Modules that can be found in the specified
|
||||
* module paths.
|
||||
*/
|
||||
Map<String, Module> getModules() {
|
||||
return modules;
|
||||
}
|
||||
|
||||
Set<Module> dependences(String... roots) {
|
||||
Configuration cf = configuration(roots);
|
||||
return cf.modules().stream()
|
||||
.map(ResolvedModule::name)
|
||||
.map(modules::get)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
Configuration configuration(String... roots) {
|
||||
return Configuration.empty().resolveRequires(finder, ModuleFinder.empty(), Set.of(roots));
|
||||
}
|
||||
|
||||
private static ModuleFinder createModulePathFinder(String mpaths) {
|
||||
if (mpaths == null) {
|
||||
return null;
|
||||
} else {
|
||||
String[] dirs = mpaths.split(File.pathSeparator);
|
||||
Path[] paths = new Path[dirs.length];
|
||||
int i = 0;
|
||||
for (String dir : dirs) {
|
||||
paths[i++] = Paths.get(dir);
|
||||
}
|
||||
return ModuleFinder.of(paths);
|
||||
}
|
||||
}
|
||||
|
||||
private static Module toModule(String mn, ModuleReference mref) {
|
||||
return SystemModulePath.find(mn)
|
||||
.orElse(toModule(new Module.Builder(mn), mref));
|
||||
}
|
||||
|
||||
private static Module toModule(Module.Builder builder, ModuleReference mref) {
|
||||
ModuleDescriptor md = mref.descriptor();
|
||||
builder.descriptor(md);
|
||||
for (ModuleDescriptor.Requires req : md.requires()) {
|
||||
builder.require(req.name(), req.modifiers().contains(PUBLIC));
|
||||
}
|
||||
for (ModuleDescriptor.Exports exp : md.exports()) {
|
||||
builder.export(exp.source(), exp.targets());
|
||||
}
|
||||
builder.packages(md.packages());
|
||||
|
||||
try {
|
||||
URI location = mref.location()
|
||||
.orElseThrow(FileNotFoundException::new);
|
||||
builder.location(location);
|
||||
builder.classes(getClassReader(location, md.name()));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
static class SystemModulePath {
|
||||
final static Module JAVA_BASE;
|
||||
|
||||
private final static FileSystem fs;
|
||||
private final static Path root;
|
||||
private final static Map<String, Module> installed = new HashMap<>();
|
||||
static {
|
||||
if (isJrtAvailable()) {
|
||||
// jrt file system
|
||||
fs = FileSystems.getFileSystem(URI.create("jrt:/"));
|
||||
root = fs.getPath("/modules");
|
||||
} else {
|
||||
// exploded image
|
||||
String javahome = System.getProperty("java.home");
|
||||
fs = FileSystems.getDefault();
|
||||
root = Paths.get(javahome, "modules");
|
||||
}
|
||||
|
||||
ModuleFinder.ofSystem().findAll().stream()
|
||||
.forEach(mref ->
|
||||
installed.computeIfAbsent(mref.descriptor().name(),
|
||||
mn -> toModule(new Module.Builder(mn, true), mref))
|
||||
);
|
||||
JAVA_BASE = installed.get("java.base");
|
||||
|
||||
Profile.init(installed);
|
||||
}
|
||||
|
||||
private static boolean isJrtAvailable() {
|
||||
try {
|
||||
FileSystems.getFileSystem(URI.create("jrt:/"));
|
||||
return true;
|
||||
} catch (ProviderNotFoundException | FileSystemNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<Module> find(String mn) {
|
||||
return installed.containsKey(mn) ? Optional.of(installed.get(mn))
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
public static boolean contains(Module m) {
|
||||
return installed.containsValue(m);
|
||||
}
|
||||
|
||||
public static ClassFileReader getClassReader(String modulename) throws IOException {
|
||||
Path mp = root.resolve(modulename);
|
||||
if (Files.exists(mp) && Files.isDirectory(mp)) {
|
||||
return ClassFileReader.newInstance(fs, mp);
|
||||
} else {
|
||||
throw new FileNotFoundException(mp.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ModuleClassReader that only reads classes for the given modulename.
|
||||
*/
|
||||
public static ClassFileReader getClassReader(URI location, String modulename)
|
||||
throws IOException {
|
||||
if (location.getScheme().equals("jrt")) {
|
||||
return SystemModulePath.getClassReader(modulename);
|
||||
} else {
|
||||
Path path = Paths.get(location);
|
||||
return ClassFileReader.newInstance(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -26,9 +26,13 @@
|
||||
package com.sun.tools.jdeps;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@ -44,17 +48,18 @@ enum Profile {
|
||||
// need a way to determine JRE modules
|
||||
SE_JRE("Java SE JRE", 4, "java.se", "jdk.charsets",
|
||||
"jdk.crypto.ec", "jdk.crypto.pkcs11",
|
||||
"jdk.crypto.mscapi", "jdk.crypto.ucrypto", "jdk.jvmstat",
|
||||
"jdk.crypto.mscapi", "jdk.crypto.ucrypto",
|
||||
"jdk.localedata", "jdk.scripting.nashorn", "jdk.zipfs"),
|
||||
FULL_JRE("Full JRE", 5, "java.se.ee", "jdk.charsets",
|
||||
"jdk.crypto.ec", "jdk.crypto.pkcs11",
|
||||
"jdk.crypto.mscapi", "jdk.crypto.ucrypto", "jdk.jvmstat",
|
||||
"jdk.localedata", "jdk.scripting.nashorn", "jdk.zipfs");
|
||||
"jdk.localedata", "jdk.scripting.nashorn",
|
||||
"jdk.unsupported", "jdk.zipfs");
|
||||
|
||||
final String name;
|
||||
final int profile;
|
||||
final String[] mnames;
|
||||
final Set<Module> modules = new HashSet<>();
|
||||
final Map<String, Module> modules = new HashMap<>();
|
||||
|
||||
Profile(String name, int profile, String... mnames) {
|
||||
this.name = name;
|
||||
@ -75,12 +80,18 @@ enum Profile {
|
||||
return JDK.isEmpty() ? 0 : Profile.values().length;
|
||||
}
|
||||
|
||||
Optional<Module> findModule(String name) {
|
||||
return modules.containsKey(name)
|
||||
? Optional.of(modules.get(name))
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Profile for the given package name; null if not found.
|
||||
*/
|
||||
public static Profile getProfile(String pn) {
|
||||
for (Profile p : Profile.values()) {
|
||||
for (Module m : p.modules) {
|
||||
for (Module m : p.modules.values()) {
|
||||
if (m.packages().contains(pn)) {
|
||||
return p;
|
||||
}
|
||||
@ -94,7 +105,7 @@ enum Profile {
|
||||
*/
|
||||
public static Profile getProfile(Module m) {
|
||||
for (Profile p : Profile.values()) {
|
||||
if (p.modules.contains(m)) {
|
||||
if (p.modules.containsValue(m)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
@ -102,34 +113,28 @@ enum Profile {
|
||||
}
|
||||
|
||||
private final static Set<Module> JDK = new HashSet<>();
|
||||
static synchronized void init(Map<String, Module> installed) {
|
||||
for (Profile p : Profile.values()) {
|
||||
for (String mn : p.mnames) {
|
||||
// this includes platform-dependent module that may not exist
|
||||
Module m = installed.get(mn);
|
||||
if (m != null) {
|
||||
p.addModule(installed, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
static synchronized void init(Map<String, Module> systemModules) {
|
||||
Arrays.stream(Profile.values()).forEach(p ->
|
||||
// this includes platform-dependent module that may not exist
|
||||
Arrays.stream(p.mnames)
|
||||
.filter(systemModules::containsKey)
|
||||
.map(systemModules::get)
|
||||
.forEach(m -> p.addModule(systemModules, m)));
|
||||
|
||||
// JDK modules should include full JRE plus other jdk.* modules
|
||||
// Just include all installed modules. Assume jdeps is running
|
||||
// in JDK image
|
||||
JDK.addAll(installed.values());
|
||||
JDK.addAll(systemModules.values());
|
||||
}
|
||||
|
||||
private void addModule(Map<String, Module> installed, Module m) {
|
||||
modules.add(m);
|
||||
for (String n : m.requires().keySet()) {
|
||||
Module d = installed.get(n);
|
||||
if (d == null) {
|
||||
throw new InternalError("module " + n + " required by " +
|
||||
m.name() + " doesn't exist");
|
||||
}
|
||||
modules.add(d);
|
||||
}
|
||||
private void addModule(Map<String, Module> systemModules, Module module) {
|
||||
modules.put(module.name(), module);
|
||||
module.descriptor().requires().stream()
|
||||
.map(ModuleDescriptor.Requires::name)
|
||||
.map(systemModules::get)
|
||||
.forEach(m -> modules.put(m.name(), m));
|
||||
}
|
||||
|
||||
// for debugging
|
||||
public static void main(String[] args) throws IOException {
|
||||
// find platform modules
|
||||
@ -139,14 +144,6 @@ enum Profile {
|
||||
for (Profile p : Profile.values()) {
|
||||
String profileName = p.name;
|
||||
System.out.format("%2d: %-10s %s%n", p.profile, profileName, p.modules);
|
||||
for (Module m: p.modules) {
|
||||
System.out.format("module %s%n", m.name());
|
||||
System.out.format(" requires %s%n", m.requires());
|
||||
for (Map.Entry<String,Set<String>> e: m.exports().entrySet()) {
|
||||
System.out.format(" exports %s %s%n", e.getKey(),
|
||||
e.getValue().isEmpty() ? "" : "to " + e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("All JDK modules:-");
|
||||
JDK.stream().sorted(Comparator.comparing(Module::name))
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
main.usage.summary=\
|
||||
Usage: {0} <options> [-m <module-name> | <classes...>]\n\
|
||||
Usage: {0} <options> <path ...>]\n\
|
||||
use -h, -? or -help for a list of possible options
|
||||
|
||||
main.usage=\
|
||||
Usage: {0} <options> [-m <module-name> | <classes...>]\n\
|
||||
If -m <module-name> is specified, the specified module will be analyzed\n\
|
||||
otherwise, <classes> can be a pathname to a .class file, a directory,\n\
|
||||
a JAR file, or a fully-qualified class name.\n\
|
||||
Usage: {0} <options> <path ...>]\n\
|
||||
<path> can be a pathname to a .class file, a directory, a JAR file.\n\
|
||||
\n\
|
||||
Possible options include:
|
||||
|
||||
@ -14,134 +12,156 @@ error.prefix=Error:
|
||||
warn.prefix=Warning:
|
||||
|
||||
main.opt.h=\
|
||||
\ -h -? -help Print this usage message
|
||||
\ -h -? -help Print this usage message
|
||||
|
||||
main.opt.version=\
|
||||
\ -version Version information
|
||||
\ -version Version information
|
||||
|
||||
main.opt.v=\
|
||||
\ -v -verbose Print all class level dependencies\n\
|
||||
\ Equivalent to -verbose:class -filter:none.\n\
|
||||
\ -verbose:package Print package-level dependencies excluding\n\
|
||||
\ dependencies within the same package by default\n\
|
||||
\ -verbose:class Print class-level dependencies excluding\n\
|
||||
\ dependencies within the same package by default
|
||||
|
||||
main.opt.f=\
|
||||
\ -f <regex> -filter <regex> Filter dependences matching the given pattern\n\
|
||||
\ If given multiple times, the last one will be used.\n\
|
||||
\ -filter:package Filter dependences within the same package (default)\n\
|
||||
\ -filter:module Filter dependences within the same module\n\
|
||||
\ -filter:archive Filter dependences within the same archive\n\
|
||||
\ -filter:none No -filter:package and -filter:archive filtering\n\
|
||||
\ Filtering specified via the -filter option still applies.
|
||||
\ -v -verbose Print all class level dependencies\n\
|
||||
\ Equivalent to -verbose:class -filter:none.\n\
|
||||
\ -verbose:package Print package-level dependencies excluding\n\
|
||||
\ dependencies within the same package by default\n\
|
||||
\ -verbose:class Print class-level dependencies excluding\n\
|
||||
\ dependencies within the same package by default
|
||||
|
||||
main.opt.s=\
|
||||
\ -s -summary Print dependency summary only.\n\
|
||||
\ If -s option is used with -m, the module descriptor of\n\
|
||||
\ the given module will be read to generate the graph.
|
||||
\ -s -summary Print dependency summary only.
|
||||
|
||||
main.opt.p=\
|
||||
\ -p <pkgname> Finds dependences matching the given package name\n\
|
||||
\ -package <pkgname> (may be given multiple times).
|
||||
main.opt.f=\
|
||||
\ -f <regex> -filter <regex> Filter dependences matching the given\n\
|
||||
\ pattern. If given multiple times, the last\n\
|
||||
\ one will be used.\n\
|
||||
\ -filter:package Filter dependences within the same package.\n\
|
||||
\ This is the default.\n\
|
||||
\ -filter:archive Filter dependences within the same archive.\n\
|
||||
\ -filter:module Filter dependences within the same module.\n\
|
||||
\ -filter:none No -filter:package and -filter:archive\n\
|
||||
\ filtering. Filtering specified via the\n\
|
||||
\ -filter option still applies.\n\
|
||||
|
||||
main.opt.p=\n\
|
||||
\Options to filter dependencies:\n\
|
||||
\ -p <pkgname> -package <pkgname> Finds dependences matching the given package\n\
|
||||
\ name (may be given multiple times).
|
||||
|
||||
main.opt.e=\
|
||||
\ -e <regex>\n\
|
||||
\ -regex <regex> Finds dependences matching the given pattern.
|
||||
\ -e <regex> -regex <regex> Finds dependences matching the given pattern.
|
||||
|
||||
main.opt.module=\
|
||||
\ -module <module-name> Finds dependences matching the given module name\n\
|
||||
\ (may be given multiple times).\n\
|
||||
\ -package, -regex, -requires are mutual exclusive.
|
||||
main.opt.requires=\
|
||||
\ -requires <module-name> Finds dependences matching the given module\n\
|
||||
\ name (may be given multiple times).\n\
|
||||
\ -package, -regex, -requires are mutual exclusive.
|
||||
|
||||
main.opt.include=\
|
||||
\ -include <regex> Restrict analysis to classes matching pattern\n\
|
||||
\ This option filters the list of classes to\n\
|
||||
\ be analyzed. It can be used together with\n\
|
||||
\ -p and -e which apply pattern to the dependences
|
||||
main.opt.include=\n\
|
||||
\Options to filter classes to be analyzed:\n\
|
||||
\ -include <regex> Restrict analysis to classes matching pattern\n\
|
||||
\ This option filters the list of classes to\n\
|
||||
\ be analyzed. It can be used together with\n\
|
||||
\ -p and -e which apply pattern to the dependences
|
||||
|
||||
main.opt.P=\
|
||||
\ -P -profile Show profile containing a package
|
||||
|
||||
main.opt.M=\
|
||||
\ -M Show module containing a package
|
||||
\ -P -profile Show profile containing a package
|
||||
|
||||
main.opt.cp=\
|
||||
\ -cp <path> -classpath <path> Specify where to find class files
|
||||
\ -cp <path> -classpath <path> Specify where to find class files
|
||||
|
||||
main.opt.mp=\
|
||||
\ -mp <module path>...\n\
|
||||
\ -modulepath <module path>... Specify module path
|
||||
\ -modulepath <module path>... Specify module path
|
||||
|
||||
main.opt.upgrademodulepath=\
|
||||
\ -upgrademodulepath <module path>... Specify upgrade module path
|
||||
|
||||
main.opt.system=\
|
||||
\ -system <java-home> Specify an alternate system module path
|
||||
|
||||
main.opt.addmods=\
|
||||
\ -addmods <module-name>[,<module-name>...]\n\
|
||||
\ Adds modules to the root set for analysis
|
||||
|
||||
main.opt.m=\
|
||||
\ -m <module-name> Specify the name of the module and its transitive\n\
|
||||
\ dependences to be analyzed.
|
||||
\ -m <module-name> Specify the root module for analysis
|
||||
|
||||
main.opt.R=\
|
||||
\ -R -recursive Recursively traverse all run-time dependencies.\n\
|
||||
\ The -R option implies -filter:none. If -p, -e, -f\n\
|
||||
\ option is specified, only the matching dependences\n\
|
||||
\ are analyzed.
|
||||
\ -R -recursive Recursively traverse all run-time dependencies.\n\
|
||||
\ The -R option implies -filter:none. If -p,\n\
|
||||
\ -e, -foption is specified, only the matching\n\
|
||||
\ dependences are analyzed.
|
||||
|
||||
main.opt.I=\
|
||||
\ -I -inverse Analyzes the dependences per other given options\n\
|
||||
\ and then find all artifacts that directly\n\
|
||||
\ and indirectly depend on the matching nodes.\n\
|
||||
\ This is equivalent to the inverse of\n\
|
||||
\ compile-time view analysis and print\n\
|
||||
\ dependency summary. This option must use\n\
|
||||
\ with -requires, -package or -regex option.
|
||||
|
||||
main.opt.ct=\
|
||||
\ -ct -compile-time Compile-time view of transitive dependencies\n\
|
||||
\ i.e. compile-time view of -R option. If a dependence\n\
|
||||
\ is found from a directory, a JAR file or a module,\n\
|
||||
\ all class files in that containing archive are analyzed.
|
||||
\ -ct -compile-time Compile-time view of transitive dependencies\n\
|
||||
\ i.e. compile-time view of -R option.\n\
|
||||
\ Analyzes the dependences per other given options\n\
|
||||
\ If a dependence is found from a directory,\n\
|
||||
\ a JAR file or a module, all classes in that \n\
|
||||
\ containing archive are analyzed.
|
||||
|
||||
main.opt.apionly=\
|
||||
\ -apionly Restrict analysis to APIs i.e. dependences\n\
|
||||
\ from the signature of public and protected\n\
|
||||
\ members of public classes including field\n\
|
||||
\ type, method parameter types, returned type,\n\
|
||||
\ checked exception types etc
|
||||
\ -apionly Restrict analysis to APIs i.e. dependences\n\
|
||||
\ from the signature of public and protected\n\
|
||||
\ members of public classes including field\n\
|
||||
\ type, method parameter types, returned type,\n\
|
||||
\ checked exception types etc.
|
||||
|
||||
main.opt.genmoduleinfo=\
|
||||
\ -genmoduleinfo <dir> Generate module-info.java under the specified directory.\n\
|
||||
\ The specified JAR files will be analyzed.\n\
|
||||
\ This option cannot be used with -dotoutput or -cp.
|
||||
\ -genmoduleinfo <dir> Generate module-info.java under the specified\n\
|
||||
\ directory. The specified JAR files will be\n\
|
||||
\ analyzed. This option cannot be used with\n\
|
||||
\ -dotoutput or -cp.
|
||||
|
||||
main.opt.check=\
|
||||
\ -check Analyze the dependence of a given module specified via\n\
|
||||
\ -m option. It prints out the resulting module dependency\n\
|
||||
\ graph after transition reduction and also identifies any\n\
|
||||
\ unused qualified exports.
|
||||
\ -check <module-name>[,<module-name>...\n\
|
||||
\ Analyze the dependence of the specified modules\n\
|
||||
\ It prints the module descriptor, the resulting\n\
|
||||
\ module dependences after analysis and the\n\
|
||||
\ graph after transition reduction. It also\n\
|
||||
\ identifies any unused qualified exports.
|
||||
|
||||
|
||||
main.opt.dotoutput=\
|
||||
\ -dotoutput <dir> Destination directory for DOT file output
|
||||
\ -dotoutput <dir> Destination directory for DOT file output
|
||||
|
||||
main.opt.jdkinternals=\
|
||||
\ -jdkinternals Finds class-level dependences on JDK internal APIs.\n\
|
||||
\ By default, it analyzes all classes on -classpath\n\
|
||||
\ and input files unless -include option is specified.\n\
|
||||
\ This option cannot be used with -p, -e and -s options.\n\
|
||||
\ WARNING: JDK internal APIs may not be accessible in\n\
|
||||
\ the next release.
|
||||
\ -jdkinternals Finds class-level dependences on JDK internal\n\
|
||||
\ APIs. By default, it analyzes all classes\n\
|
||||
\ on -classpath and input files unless -include\n\
|
||||
\ option is specified. This option cannot be\n\
|
||||
\ used with -p, -e and -s options.\n\
|
||||
\ WARNING: JDK internal APIs are inaccessible.
|
||||
|
||||
main.opt.depth=\
|
||||
\ -depth=<depth> Specify the depth of the transitive\n\
|
||||
\ dependency analysis
|
||||
\ -depth=<depth> Specify the depth of the transitive\n\
|
||||
\ dependency analysis
|
||||
|
||||
main.opt.q=\
|
||||
\ -q -quiet Do not show missing dependencies from -genmoduleinfo output.
|
||||
\ -q -quiet Do not show missing dependencies from \n\
|
||||
\ -genmoduleinfo output.
|
||||
|
||||
err.unknown.option=unknown option: {0}
|
||||
err.missing.arg=no value given for {0}
|
||||
err.invalid.arg.for.option=invalid argument for option: {0}
|
||||
err.option.after.class=option must be specified before classes: {0}
|
||||
err.genmoduleinfo.not.jarfile={0} not valid for -genmoduleinfo option (must be JAR file)
|
||||
err.genmoduleinfo.not.jarfile={0} not valid for -genmoduleinfo option (must be non-modular JAR file)
|
||||
err.profiles.msg=No profile information
|
||||
err.exception.message={0}
|
||||
err.invalid.path=invalid path: {0}
|
||||
err.invalid.module.option=-m {0} is set but {1} is specified.
|
||||
err.invalid.module.option=Cannot set {0} with {1} option.
|
||||
err.invalid.filters=Only one of -package (-p), -regex (-e), -requires option can be set
|
||||
err.module.not.found=module not found: {0}
|
||||
err.root.module.not.set=-m is not set
|
||||
warn.invalid.arg=Invalid classname or pathname not exist: {0}
|
||||
err.root.module.not.set=root module set empty
|
||||
err.invalid.inverse.option={0} cannot be used with -inverse option
|
||||
err.inverse.filter.not.set={0} cannot be used with -inverse option
|
||||
warn.invalid.arg=Path not exist: {0}
|
||||
warn.split.package=package {0} defined in {1} {2}
|
||||
warn.replace.useJDKInternals=\
|
||||
JDK internal APIs are unsupported and private to JDK implementation that are\n\
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
// No translation needed
|
||||
# No translation needed
|
||||
com.sun.crypto.provider.SunJCE=Use java.security.Security.getProvider(provider-name) @since 1.3
|
||||
com.sun.image.codec=Use javax.imageio @since 1.4
|
||||
com.sun.org.apache.xml.internal.security=Use java.xml.crypto @since 1.6
|
||||
com.sun.org.apache.xml.internal.security.utils.Base64=Use java.util.Base64 @since 1.8
|
||||
com.sun.org.apache.xml.internal.resolver=Use javax.xml.catalog @since 9
|
||||
@ -9,17 +8,34 @@ com.sun.net.ssl.internal.ssl.Provider=Use java.security.Security.getProvider(pro
|
||||
com.sun.rowset=Use javax.sql.rowset.RowSetProvider @since 1.7
|
||||
com.sun.tools.javac.tree=Use com.sun.source @since 1.6
|
||||
com.sun.tools.javac=Use javax.tools and javax.lang.model @since 1.6
|
||||
java.awt.peer=Should not use. See https://bugs.openjdk.java.net/browse/JDK-8037739
|
||||
java.awt.dnd.peer=Should not use. See https://bugs.openjdk.java.net/browse/JDK-8037739
|
||||
jdk.internal.ref.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
|
||||
sun.awt.image.codec=Use javax.imageio @since 1.4
|
||||
sun.misc.BASE64Encoder=Use java.util.Base64 @since 1.8
|
||||
sun.misc.BASE64Decoder=Use java.util.Base64 @since 1.8
|
||||
sun.misc.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
|
||||
sun.misc.Service=Use java.util.ServiceLoader @since 1.6
|
||||
sun.awt.CausedFocusEvent=Use java.awt.event.FocusEvent::getCause @since 9
|
||||
sun.font.FontUtilities=See java.awt.Font.textRequiresLayout @since 9
|
||||
sun.reflect.Reflection=See StackWalker API @since 9
|
||||
sun.reflect.ReflectionFactory=See http://openjdk.java.net/jeps/260
|
||||
sun.misc.Unsafe=See http://openjdk.java.net/jeps/260
|
||||
sun.misc.Signal=See http://openjdk.java.net/jeps/260
|
||||
sun.misc.SignalHandler=See http://openjdk.java.net/jeps/260
|
||||
sun.security.action=Use java.security.PrivilegedAction @since 1.1
|
||||
sun.security.krb5=Use com.sun.security.jgss
|
||||
sun.security.provider.PolicyFile=Use java.security.Policy.getInstance("JavaPolicy", new URIParameter(uri)) @since 1.6
|
||||
sun.security.provider.Sun=Use java.security.Security.getProvider(provider-name) @since 1.3
|
||||
sun.security.util.SecurityConstants=Use appropriate java.security.Permission subclass @since 1.1
|
||||
sun.security.x509.X500Name=Use javax.security.auth.x500.X500Principal @since 1.4
|
||||
sun.tools.jar=Use java.util.jar or jar tool @since 1.2
|
||||
jdk.internal.ref.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
|
||||
sun.tools.jar=Use java.util.jar or jar tool @since 1.2\
|
||||
# Internal APIs removed in JDK 9
|
||||
com.apple.eawt=Use java.awt.desktop and JEP 272 @since 9
|
||||
com.apple.concurrent=Removed. See https://bugs.openjdk.java.net/browse/JDK-8148187
|
||||
com.sun.image.codec=Use javax.imageio @since 1.4
|
||||
sun.misc.BASE64Encoder=Use java.util.Base64 @since 1.8
|
||||
sun.misc.BASE64Decoder=Use java.util.Base64 @since 1.8
|
||||
sun.misc.Cleaner=Use java.lang.ref.PhantomReference @since 1.2 or java.lang.ref.Cleaner @since 9
|
||||
sun.misc.Service=Use java.util.ServiceLoader @since 1.6
|
||||
sun.misc=Removed. See http://openjdk.java.net/jeps/260
|
||||
sun.reflect=Removed. See http://openjdk.java.net/jeps/260
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,7 +25,6 @@
|
||||
|
||||
package jdk.internal.jshell.tool;
|
||||
|
||||
import jdk.jshell.SourceCodeAnalysis.CompletionInfo;
|
||||
import jdk.jshell.SourceCodeAnalysis.QualifiedNames;
|
||||
import jdk.jshell.SourceCodeAnalysis.Suggestion;
|
||||
|
||||
@ -36,6 +35,7 @@ import java.io.PrintStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -44,6 +44,9 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.prefs.BackingStoreException;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.jline.NoInterruptUnixTerminal;
|
||||
import jdk.internal.jline.Terminal;
|
||||
@ -54,10 +57,13 @@ import jdk.internal.jline.console.ConsoleReader;
|
||||
import jdk.internal.jline.console.KeyMap;
|
||||
import jdk.internal.jline.console.UserInterruptException;
|
||||
import jdk.internal.jline.console.completer.Completer;
|
||||
import jdk.internal.jline.extra.EditingHistory;
|
||||
import jdk.internal.jshell.tool.StopDetectingInputStream.State;
|
||||
|
||||
class ConsoleIOContext extends IOContext {
|
||||
|
||||
private static final String HISTORY_LINE_PREFIX = "HISTORY_LINE_";
|
||||
|
||||
final JShellTool repl;
|
||||
final StopDetectingInputStream input;
|
||||
final ConsoleReader in;
|
||||
@ -80,9 +86,14 @@ class ConsoleIOContext extends IOContext {
|
||||
in = new ConsoleReader(cmdin, cmdout, term);
|
||||
in.setExpandEvents(false);
|
||||
in.setHandleUserInterrupt(true);
|
||||
in.setHistory(history = new EditingHistory(repl.prefs) {
|
||||
@Override protected CompletionInfo analyzeCompletion(String input) {
|
||||
return repl.analysis.analyzeCompletion(input);
|
||||
List<String> persistenHistory = Stream.of(repl.prefs.keys())
|
||||
.filter(key -> key.startsWith(HISTORY_LINE_PREFIX))
|
||||
.sorted()
|
||||
.map(key -> repl.prefs.get(key, null))
|
||||
.collect(Collectors.toList());
|
||||
in.setHistory(history = new EditingHistory(in, persistenHistory) {
|
||||
@Override protected boolean isComplete(CharSequence input) {
|
||||
return repl.analysis.analyzeCompletion(input.toString()).completeness.isComplete;
|
||||
}
|
||||
});
|
||||
in.setBellEnabled(true);
|
||||
@ -150,8 +161,6 @@ class ConsoleIOContext extends IOContext {
|
||||
}
|
||||
});
|
||||
bind(DOCUMENTATION_SHORTCUT, (ActionListener) evt -> documentation(repl));
|
||||
bind(CTRL_UP, (ActionListener) evt -> moveHistoryToSnippet(((EditingHistory) in.getHistory())::previousSnippet));
|
||||
bind(CTRL_DOWN, (ActionListener) evt -> moveHistoryToSnippet(((EditingHistory) in.getHistory())::nextSnippet));
|
||||
for (FixComputer computer : FIX_COMPUTERS) {
|
||||
for (String shortcuts : SHORTCUT_FIXES) {
|
||||
bind(shortcuts + computer.shortcut, (ActionListener) evt -> fixes(computer));
|
||||
@ -181,7 +190,24 @@ class ConsoleIOContext extends IOContext {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
history.save();
|
||||
//save history:
|
||||
try {
|
||||
for (String key : repl.prefs.keys()) {
|
||||
if (key.startsWith(HISTORY_LINE_PREFIX))
|
||||
repl.prefs.remove(key);
|
||||
}
|
||||
Collection<? extends String> savedHistory = history.save();
|
||||
if (!savedHistory.isEmpty()) {
|
||||
int len = (int) Math.ceil(Math.log10(savedHistory.size()+1));
|
||||
String format = HISTORY_LINE_PREFIX + "%0" + len + "d";
|
||||
int index = 0;
|
||||
for (String historyLine : savedHistory) {
|
||||
repl.prefs.put(String.format(format, index++), historyLine);
|
||||
}
|
||||
}
|
||||
} catch (BackingStoreException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
in.shutdown();
|
||||
try {
|
||||
in.getTerminal().restore();
|
||||
@ -190,30 +216,6 @@ class ConsoleIOContext extends IOContext {
|
||||
}
|
||||
}
|
||||
|
||||
private void moveHistoryToSnippet(Supplier<Boolean> action) {
|
||||
if (!action.get()) {
|
||||
try {
|
||||
in.beep();
|
||||
} catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
//could use:
|
||||
//in.resetPromptLine(in.getPrompt(), in.getHistory().current().toString(), -1);
|
||||
//but that would mean more re-writing on the screen, (and prints an additional
|
||||
//empty line), so using setBuffer directly:
|
||||
Method setBuffer = in.getClass().getDeclaredMethod("setBuffer", String.class);
|
||||
|
||||
setBuffer.setAccessible(true);
|
||||
setBuffer.invoke(in, in.getHistory().current().toString());
|
||||
in.flush();
|
||||
} catch (ReflectiveOperationException | IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void bind(String shortcut, Object action) {
|
||||
KeyMap km = in.getKeys();
|
||||
for (int i = 0; i < shortcut.length(); i++) {
|
||||
@ -227,8 +229,6 @@ class ConsoleIOContext extends IOContext {
|
||||
}
|
||||
|
||||
private static final String DOCUMENTATION_SHORTCUT = "\033\133\132"; //Shift-TAB
|
||||
private static final String CTRL_UP = "\033\133\061\073\065\101"; //Ctrl-UP
|
||||
private static final String CTRL_DOWN = "\033\133\061\073\065\102"; //Ctrl-DOWN
|
||||
private static final String[] SHORTCUT_FIXES = {
|
||||
"\033\015", //Alt-Enter (Linux)
|
||||
"\033\133\061\067\176", //F6/Alt-F1 (Mac)
|
||||
|
||||
@ -1,382 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.jshell.tool;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
import java.util.prefs.BackingStoreException;
|
||||
import java.util.prefs.Preferences;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.jline.console.history.History;
|
||||
import jdk.internal.jline.console.history.History.Entry;
|
||||
import jdk.internal.jline.console.history.MemoryHistory;
|
||||
import jdk.jshell.SourceCodeAnalysis.CompletionInfo;
|
||||
|
||||
/*Public for tests (HistoryTest).
|
||||
*/
|
||||
public abstract class EditingHistory implements History {
|
||||
|
||||
private final Preferences prefs;
|
||||
private final History fullHistory;
|
||||
private History currentDelegate;
|
||||
|
||||
protected EditingHistory(Preferences prefs) {
|
||||
this.prefs = prefs;
|
||||
this.fullHistory = new MemoryHistory();
|
||||
this.currentDelegate = fullHistory;
|
||||
load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return currentDelegate.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return currentDelegate.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int index() {
|
||||
return currentDelegate.index();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
if (currentDelegate != fullHistory)
|
||||
throw new IllegalStateException("narrowed");
|
||||
currentDelegate.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence get(int index) {
|
||||
return currentDelegate.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(CharSequence line) {
|
||||
NarrowingHistoryLine currentLine = null;
|
||||
int origIndex = fullHistory.index();
|
||||
int fullSize;
|
||||
try {
|
||||
fullHistory.moveToEnd();
|
||||
fullSize = fullHistory.index();
|
||||
if (currentDelegate == fullHistory) {
|
||||
if (origIndex < fullHistory.index()) {
|
||||
for (Entry entry : fullHistory) {
|
||||
if (!(entry.value() instanceof NarrowingHistoryLine))
|
||||
continue;
|
||||
int[] cluster = ((NarrowingHistoryLine) entry.value()).span;
|
||||
if (cluster[0] == origIndex && cluster[1] > cluster[0]) {
|
||||
currentDelegate = new MemoryHistory();
|
||||
for (int i = cluster[0]; i <= cluster[1]; i++) {
|
||||
currentDelegate.add(fullHistory.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fullHistory.moveToEnd();
|
||||
while (fullHistory.previous()) {
|
||||
CharSequence c = fullHistory.current();
|
||||
if (c instanceof NarrowingHistoryLine) {
|
||||
currentLine = (NarrowingHistoryLine) c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
fullHistory.moveTo(origIndex);
|
||||
}
|
||||
if (currentLine == null || currentLine.span[1] != (-1)) {
|
||||
line = currentLine = new NarrowingHistoryLine(line, fullSize);
|
||||
}
|
||||
StringBuilder complete = new StringBuilder();
|
||||
for (int i = currentLine.span[0]; i < fullSize; i++) {
|
||||
complete.append(fullHistory.get(i));
|
||||
}
|
||||
complete.append(line);
|
||||
if (analyzeCompletion(complete.toString()).completeness.isComplete) {
|
||||
currentLine.span[1] = fullSize; //TODO: +1?
|
||||
currentDelegate = fullHistory;
|
||||
}
|
||||
fullHistory.add(line);
|
||||
}
|
||||
|
||||
protected abstract CompletionInfo analyzeCompletion(String input);
|
||||
|
||||
@Override
|
||||
public void set(int index, CharSequence item) {
|
||||
if (currentDelegate != fullHistory)
|
||||
throw new IllegalStateException("narrowed");
|
||||
currentDelegate.set(index, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence remove(int i) {
|
||||
if (currentDelegate != fullHistory)
|
||||
throw new IllegalStateException("narrowed");
|
||||
return currentDelegate.remove(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence removeFirst() {
|
||||
if (currentDelegate != fullHistory)
|
||||
throw new IllegalStateException("narrowed");
|
||||
return currentDelegate.removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence removeLast() {
|
||||
if (currentDelegate != fullHistory)
|
||||
throw new IllegalStateException("narrowed");
|
||||
return currentDelegate.removeLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(CharSequence item) {
|
||||
if (currentDelegate != fullHistory)
|
||||
throw new IllegalStateException("narrowed");
|
||||
currentDelegate.replace(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<Entry> entries(int index) {
|
||||
return currentDelegate.entries(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<Entry> entries() {
|
||||
return currentDelegate.entries();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry> iterator() {
|
||||
return currentDelegate.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence current() {
|
||||
return currentDelegate.current();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean previous() {
|
||||
return currentDelegate.previous();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean next() {
|
||||
return currentDelegate.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToFirst() {
|
||||
return currentDelegate.moveToFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveToLast() {
|
||||
return currentDelegate.moveToLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveTo(int index) {
|
||||
return currentDelegate.moveTo(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveToEnd() {
|
||||
currentDelegate.moveToEnd();
|
||||
}
|
||||
|
||||
public boolean previousSnippet() {
|
||||
for (int i = index() - 1; i >= 0; i--) {
|
||||
if (get(i) instanceof NarrowingHistoryLine) {
|
||||
moveTo(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean nextSnippet() {
|
||||
for (int i = index() + 1; i < size(); i++) {
|
||||
if (get(i) instanceof NarrowingHistoryLine) {
|
||||
moveTo(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (index() < size()) {
|
||||
moveToEnd();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final String HISTORY_LINE_PREFIX = "HISTORY_LINE_";
|
||||
private static final String HISTORY_SNIPPET_START = "HISTORY_SNIPPET";
|
||||
|
||||
public final void load() {
|
||||
try {
|
||||
Set<Integer> snippetsStart = new HashSet<>();
|
||||
for (String start : prefs.get(HISTORY_SNIPPET_START, "").split(";")) {
|
||||
if (!start.isEmpty())
|
||||
snippetsStart.add(Integer.parseInt(start));
|
||||
}
|
||||
List<String> keys = Stream.of(prefs.keys()).sorted().collect(Collectors.toList());
|
||||
NarrowingHistoryLine currentHistoryLine = null;
|
||||
int currentLine = 0;
|
||||
for (String key : keys) {
|
||||
if (!key.startsWith(HISTORY_LINE_PREFIX))
|
||||
continue;
|
||||
CharSequence line = prefs.get(key, "");
|
||||
if (snippetsStart.contains(currentLine)) {
|
||||
class PersistentNarrowingHistoryLine extends NarrowingHistoryLine implements PersistentEntryMarker {
|
||||
public PersistentNarrowingHistoryLine(CharSequence delegate, int start) {
|
||||
super(delegate, start);
|
||||
}
|
||||
}
|
||||
line = currentHistoryLine = new PersistentNarrowingHistoryLine(line, currentLine);
|
||||
} else {
|
||||
class PersistentLine implements CharSequence, PersistentEntryMarker {
|
||||
private final CharSequence delegate;
|
||||
public PersistentLine(CharSequence delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
@Override public int length() {
|
||||
return delegate.length();
|
||||
}
|
||||
@Override public char charAt(int index) {
|
||||
return delegate.charAt(index);
|
||||
}
|
||||
@Override public CharSequence subSequence(int start, int end) {
|
||||
return delegate.subSequence(start, end);
|
||||
}
|
||||
@Override public String toString() {
|
||||
return delegate.toString();
|
||||
}
|
||||
}
|
||||
line = new PersistentLine(line);
|
||||
}
|
||||
if (currentHistoryLine != null)
|
||||
currentHistoryLine.span[1] = currentLine;
|
||||
currentLine++;
|
||||
fullHistory.add(line);
|
||||
}
|
||||
currentLine = 0;
|
||||
} catch (BackingStoreException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
try {
|
||||
for (String key : prefs.keys()) {
|
||||
if (key.startsWith(HISTORY_LINE_PREFIX))
|
||||
prefs.remove(key);
|
||||
}
|
||||
Iterator<Entry> entries = fullHistory.iterator();
|
||||
if (entries.hasNext()) {
|
||||
int len = (int) Math.ceil(Math.log10(fullHistory.size()+1));
|
||||
String format = HISTORY_LINE_PREFIX + "%0" + len + "d";
|
||||
StringBuilder snippetStarts = new StringBuilder();
|
||||
String snippetStartDelimiter = "";
|
||||
while (entries.hasNext()) {
|
||||
Entry entry = entries.next();
|
||||
prefs.put(String.format(format, entry.index()), entry.value().toString());
|
||||
if (entry.value() instanceof NarrowingHistoryLine) {
|
||||
snippetStarts.append(snippetStartDelimiter);
|
||||
snippetStarts.append(entry.index());
|
||||
snippetStartDelimiter = ";";
|
||||
}
|
||||
}
|
||||
prefs.put(HISTORY_SNIPPET_START, snippetStarts.toString());
|
||||
}
|
||||
} catch (BackingStoreException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> currentSessionEntries() {
|
||||
List<String> result = new ArrayList<>();
|
||||
|
||||
for (Entry e : fullHistory) {
|
||||
if (!(e.value() instanceof PersistentEntryMarker)) {
|
||||
result.add(e.value().toString());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void fullHistoryReplace(String source) {
|
||||
fullHistory.replace(source);
|
||||
}
|
||||
|
||||
private class NarrowingHistoryLine implements CharSequence {
|
||||
private final CharSequence delegate;
|
||||
private final int[] span;
|
||||
|
||||
public NarrowingHistoryLine(CharSequence delegate, int start) {
|
||||
this.delegate = delegate;
|
||||
this.span = new int[] {start, -1};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return delegate.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
return delegate.charAt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
return delegate.subSequence(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return delegate.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private interface PersistentEntryMarker {}
|
||||
}
|
||||
|
||||
@ -47,6 +47,9 @@ class Feedback {
|
||||
// Patern for substituted fields within a customized format string
|
||||
private static final Pattern FIELD_PATTERN = Pattern.compile("\\{(.*?)\\}");
|
||||
|
||||
// Internal field name for truncation length
|
||||
private static final String TRUNCATION_FIELD = "<truncation>";
|
||||
|
||||
// Current mode
|
||||
private Mode mode = new Mode("", false); // initial value placeholder during start-up
|
||||
|
||||
@ -103,6 +106,10 @@ class Feedback {
|
||||
return new Setter(messageHandler, at).setFormat();
|
||||
}
|
||||
|
||||
public boolean setTruncation(MessageHandler messageHandler, ArgTokenizer at) {
|
||||
return new Setter(messageHandler, at).setTruncation();
|
||||
}
|
||||
|
||||
public boolean setNewMode(MessageHandler messageHandler, ArgTokenizer at) {
|
||||
return new Setter(messageHandler, at).setNewMode();
|
||||
}
|
||||
@ -251,13 +258,42 @@ class Feedback {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// Compute the display output given full context and values
|
||||
String format(FormatCase fc, FormatAction fa, FormatWhen fw,
|
||||
FormatResolve fr, FormatUnresolved fu, FormatErrors fe,
|
||||
String name, String type, String value, String unresolved, List<String> errorLines) {
|
||||
// Convert the context into a bit representation used as selectors for store field formats
|
||||
long bits = bits(fc, fa, fw, fr, fu, fe);
|
||||
String fname = name==null? "" : name;
|
||||
String ftype = type==null? "" : type;
|
||||
String fvalue = value==null? "" : value;
|
||||
// Compute the representation of value
|
||||
String fvalue;
|
||||
if (value==null) {
|
||||
fvalue = "";
|
||||
} else {
|
||||
// Retrieve the truncation length
|
||||
String truncField = format(TRUNCATION_FIELD, bits);
|
||||
if (truncField.isEmpty()) {
|
||||
// No truncation set, use whole value
|
||||
fvalue = value;
|
||||
} else {
|
||||
// Convert truncation length to int
|
||||
// this is safe since it has been tested before it is set
|
||||
int trunc = Integer.parseUnsignedInt(truncField);
|
||||
if (value.length() > trunc) {
|
||||
if (trunc <= 5) {
|
||||
// Very short truncations have no room for "..."
|
||||
fvalue = value.substring(0, trunc);
|
||||
} else {
|
||||
// Normal truncation, make total length equal truncation length
|
||||
fvalue = value.substring(0, trunc - 4) + " ...";
|
||||
}
|
||||
} else {
|
||||
// Within truncation length, use whole value
|
||||
fvalue = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
String funresolved = unresolved==null? "" : unresolved;
|
||||
String errors = errorLines.stream()
|
||||
.map(el -> String.format(
|
||||
@ -564,7 +600,7 @@ class Feedback {
|
||||
return valid;
|
||||
}
|
||||
|
||||
// For /set newmode <new-mode> [command|quiet [<old-mode>]]
|
||||
// For /set newmode <new-mode> [-command|-quiet [<old-mode>]]
|
||||
boolean setNewMode() {
|
||||
String umode = at.next();
|
||||
if (umode == null) {
|
||||
@ -575,8 +611,8 @@ class Feedback {
|
||||
errorat("jshell.err.feedback.expected.mode.name", umode);
|
||||
valid = false;
|
||||
}
|
||||
String[] fluffOpt = at.next("command", "quiet");
|
||||
boolean fluff = fluffOpt == null || fluffOpt.length != 1 || "command".equals(fluffOpt[0]);
|
||||
String[] fluffOpt = at.next("-command", "-quiet");
|
||||
boolean fluff = fluffOpt == null || fluffOpt.length != 1 || "-command".equals(fluffOpt[0]);
|
||||
if (fluffOpt != null && fluffOpt.length != 1) {
|
||||
errorat("jshell.err.feedback.command.quiet");
|
||||
valid = false;
|
||||
@ -619,7 +655,32 @@ class Feedback {
|
||||
errorat("jshell.err.feedback.expected.field");
|
||||
valid = false;
|
||||
}
|
||||
String format = valid? nextFormat() : null;
|
||||
String format = valid ? nextFormat() : null;
|
||||
return installFormat(m, field, format, "/help /set format");
|
||||
}
|
||||
|
||||
// For /set truncation <mode> <length> <selector>...
|
||||
boolean setTruncation() {
|
||||
Mode m = nextMode();
|
||||
String length = at.next();
|
||||
if (length == null) {
|
||||
errorat("jshell.err.truncation.expected.length");
|
||||
valid = false;
|
||||
} else {
|
||||
try {
|
||||
// Assure that integer format is correct
|
||||
Integer.parseUnsignedInt(length);
|
||||
} catch (NumberFormatException ex) {
|
||||
errorat("jshell.err.truncation.length.not.integer", length);
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
// install length into an internal format field
|
||||
return installFormat(m, TRUNCATION_FIELD, length, "/help /set truncation");
|
||||
}
|
||||
|
||||
// install the format of a field under parsed selectors
|
||||
boolean installFormat(Mode m, String field, String format, String help) {
|
||||
String slRaw;
|
||||
List<SelectorList> slList = new ArrayList<>();
|
||||
while (valid && (slRaw = at.next()) != null) {
|
||||
@ -629,8 +690,10 @@ class Feedback {
|
||||
}
|
||||
if (valid) {
|
||||
if (slList.isEmpty()) {
|
||||
// No selectors specified, then always the format
|
||||
m.set(field, ALWAYS, format);
|
||||
} else {
|
||||
// Set the format of the field for specified selector
|
||||
slList.stream()
|
||||
.forEach(sl -> m.set(field,
|
||||
sl.cases.getSet(), sl.actions.getSet(), sl.whens.getSet(),
|
||||
@ -638,7 +701,7 @@ class Feedback {
|
||||
format));
|
||||
}
|
||||
} else {
|
||||
fluffmsg("jshell.msg.see", "/help /set format");
|
||||
fluffmsg("jshell.msg.see", help);
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@ public class JShellTool implements MessageHandler {
|
||||
private static final String LINE_SEP = System.getProperty("line.separator");
|
||||
private static final Pattern LINEBREAK = Pattern.compile("\\R");
|
||||
private static final Pattern HISTORY_ALL_START_FILENAME = Pattern.compile(
|
||||
"((?<cmd>(all|history|start))(\\z|\\p{javaWhitespace}+))?(?<filename>.*)");
|
||||
"((?<cmd>(-all|-history|-start))(\\z|\\p{javaWhitespace}+))?(?<filename>.*)");
|
||||
private static final String RECORD_SEPARATOR = "\u241E";
|
||||
private static final String RB_NAME_PREFIX = "jdk.internal.jshell.tool.resources";
|
||||
private static final String VERSION_RB_NAME = RB_NAME_PREFIX + ".version";
|
||||
@ -879,8 +879,8 @@ public class JShellTool implements MessageHandler {
|
||||
}
|
||||
|
||||
private static final CompletionProvider EMPTY_COMPLETION_PROVIDER = new FixedCompletionProvider();
|
||||
private static final CompletionProvider KEYWORD_COMPLETION_PROVIDER = new FixedCompletionProvider("all ", "start ", "history ");
|
||||
private static final CompletionProvider RELOAD_OPTIONS_COMPLETION_PROVIDER = new FixedCompletionProvider("restore", "quiet");
|
||||
private static final CompletionProvider KEYWORD_COMPLETION_PROVIDER = new FixedCompletionProvider("-all ", "-start ", "-history ");
|
||||
private static final CompletionProvider RELOAD_OPTIONS_COMPLETION_PROVIDER = new FixedCompletionProvider("-restore", "-quiet");
|
||||
private static final CompletionProvider FILE_COMPLETION_PROVIDER = fileCompletions(p -> true);
|
||||
private final Map<String, Command> commands = new LinkedHashMap<>();
|
||||
private void registerCommand(Command cmd) {
|
||||
@ -917,11 +917,10 @@ public class JShellTool implements MessageHandler {
|
||||
p.getFileName().toString().endsWith(".jar"));
|
||||
}
|
||||
|
||||
private CompletionProvider editCompletion() {
|
||||
private CompletionProvider snippetCompletion(Supplier<List<? extends Snippet>> snippetsSupplier) {
|
||||
return (prefix, cursor, anchor) -> {
|
||||
anchor[0] = 0;
|
||||
return state.snippets()
|
||||
.stream()
|
||||
return snippetsSupplier.get() .stream()
|
||||
.flatMap(k -> (k instanceof DeclarationSnippet)
|
||||
? Stream.of(String.valueOf(k.id()), ((DeclarationSnippet) k).name())
|
||||
: Stream.of(String.valueOf(k.id())))
|
||||
@ -931,11 +930,11 @@ public class JShellTool implements MessageHandler {
|
||||
};
|
||||
}
|
||||
|
||||
private CompletionProvider editKeywordCompletion() {
|
||||
private CompletionProvider snippetKeywordCompletion(Supplier<List<? extends Snippet>> snippetsSupplier) {
|
||||
return (code, cursor, anchor) -> {
|
||||
List<Suggestion> result = new ArrayList<>();
|
||||
result.addAll(KEYWORD_COMPLETION_PROVIDER.completionSuggestions(code, cursor, anchor));
|
||||
result.addAll(editCompletion().completionSuggestions(code, cursor, anchor));
|
||||
result.addAll(snippetCompletion(snippetsSupplier).completionSuggestions(code, cursor, anchor));
|
||||
return result;
|
||||
};
|
||||
}
|
||||
@ -963,18 +962,51 @@ public class JShellTool implements MessageHandler {
|
||||
};
|
||||
}
|
||||
|
||||
// Snippet lists
|
||||
|
||||
List<Snippet> allSnippets() {
|
||||
return state.snippets();
|
||||
}
|
||||
|
||||
List<Snippet> dropableSnippets() {
|
||||
return state.snippets().stream()
|
||||
.filter(sn -> state.status(sn).isActive)
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
List<VarSnippet> allVarSnippets() {
|
||||
return state.snippets().stream()
|
||||
.filter(sn -> sn.kind() == Snippet.Kind.VAR)
|
||||
.map(sn -> (VarSnippet) sn)
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
List<MethodSnippet> allMethodSnippets() {
|
||||
return state.snippets().stream()
|
||||
.filter(sn -> sn.kind() == Snippet.Kind.METHOD)
|
||||
.map(sn -> (MethodSnippet) sn)
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
List<TypeDeclSnippet> allTypeSnippets() {
|
||||
return state.snippets().stream()
|
||||
.filter(sn -> sn.kind() == Snippet.Kind.TYPE_DECL)
|
||||
.map(sn -> (TypeDeclSnippet) sn)
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
// Table of commands -- with command forms, argument kinds, helpKey message, implementation, ...
|
||||
|
||||
{
|
||||
registerCommand(new Command("/list",
|
||||
arg -> cmdList(arg),
|
||||
editKeywordCompletion()));
|
||||
snippetKeywordCompletion(this::allSnippets)));
|
||||
registerCommand(new Command("/edit",
|
||||
arg -> cmdEdit(arg),
|
||||
editCompletion()));
|
||||
snippetCompletion(this::allSnippets)));
|
||||
registerCommand(new Command("/drop",
|
||||
arg -> cmdDrop(arg),
|
||||
editCompletion(),
|
||||
snippetCompletion(this::dropableSnippets),
|
||||
CommandKind.REPLAY));
|
||||
registerCommand(new Command("/save",
|
||||
arg -> cmdSave(arg),
|
||||
@ -983,14 +1015,14 @@ public class JShellTool implements MessageHandler {
|
||||
arg -> cmdOpen(arg),
|
||||
FILE_COMPLETION_PROVIDER));
|
||||
registerCommand(new Command("/vars",
|
||||
arg -> cmdVars(),
|
||||
EMPTY_COMPLETION_PROVIDER));
|
||||
arg -> cmdVars(arg),
|
||||
snippetKeywordCompletion(this::allVarSnippets)));
|
||||
registerCommand(new Command("/methods",
|
||||
arg -> cmdMethods(),
|
||||
EMPTY_COMPLETION_PROVIDER));
|
||||
registerCommand(new Command("/classes",
|
||||
arg -> cmdClasses(),
|
||||
EMPTY_COMPLETION_PROVIDER));
|
||||
arg -> cmdMethods(arg),
|
||||
snippetKeywordCompletion(this::allMethodSnippets)));
|
||||
registerCommand(new Command("/types",
|
||||
arg -> cmdTypes(arg),
|
||||
snippetKeywordCompletion(this::allTypeSnippets)));
|
||||
registerCommand(new Command("/imports",
|
||||
arg -> cmdImports(),
|
||||
EMPTY_COMPLETION_PROVIDER));
|
||||
@ -1019,7 +1051,7 @@ public class JShellTool implements MessageHandler {
|
||||
EMPTY_COMPLETION_PROVIDER));
|
||||
registerCommand(new Command("/set",
|
||||
arg -> cmdSet(arg),
|
||||
new FixedCompletionProvider("format", "feedback", "prompt", "newmode", "start", "editor")));
|
||||
new FixedCompletionProvider(SET_SUBCOMMANDS)));
|
||||
registerCommand(new Command("/?",
|
||||
"help.quest",
|
||||
arg -> cmdHelp(arg),
|
||||
@ -1094,7 +1126,7 @@ public class JShellTool implements MessageHandler {
|
||||
// --- Command implementations ---
|
||||
|
||||
private static final String[] SET_SUBCOMMANDS = new String[]{
|
||||
"format", "feedback", "newmode", "prompt", "editor", "start"};
|
||||
"format", "truncation", "feedback", "newmode", "prompt", "editor", "start"};
|
||||
|
||||
final boolean cmdSet(String arg) {
|
||||
ArgTokenizer at = new ArgTokenizer("/set ", arg.trim());
|
||||
@ -1105,6 +1137,8 @@ public class JShellTool implements MessageHandler {
|
||||
switch (which) {
|
||||
case "format":
|
||||
return feedback.setFormat(this, at);
|
||||
case "truncation":
|
||||
return feedback.setTruncation(this, at);
|
||||
case "feedback":
|
||||
return feedback.setFeedback(this, at);
|
||||
case "newmode":
|
||||
@ -1310,7 +1344,7 @@ public class JShellTool implements MessageHandler {
|
||||
/**
|
||||
* Avoid parameterized varargs possible heap pollution warning.
|
||||
*/
|
||||
private interface SnippetPredicate extends Predicate<Snippet> { }
|
||||
private interface SnippetPredicate<T extends Snippet> extends Predicate<T> { }
|
||||
|
||||
/**
|
||||
* Apply filters to a stream until one that is non-empty is found.
|
||||
@ -1320,10 +1354,11 @@ public class JShellTool implements MessageHandler {
|
||||
* @param filters Filters to attempt
|
||||
* @return The non-empty filtered Stream, or null
|
||||
*/
|
||||
private static Stream<Snippet> nonEmptyStream(Supplier<Stream<Snippet>> supplier,
|
||||
SnippetPredicate... filters) {
|
||||
for (SnippetPredicate filt : filters) {
|
||||
Iterator<Snippet> iterator = supplier.get().filter(filt).iterator();
|
||||
@SafeVarargs
|
||||
private static <T extends Snippet> Stream<T> nonEmptyStream(Supplier<Stream<T>> supplier,
|
||||
SnippetPredicate<T>... filters) {
|
||||
for (SnippetPredicate<T> filt : filters) {
|
||||
Iterator<T> iterator = supplier.get().filter(filt).iterator();
|
||||
if (iterator.hasNext()) {
|
||||
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
|
||||
}
|
||||
@ -1352,24 +1387,40 @@ public class JShellTool implements MessageHandler {
|
||||
* Convert a user argument to a Stream of snippets referenced by that argument
|
||||
* (or lack of argument).
|
||||
*
|
||||
* @param arg The user's argument to the command, maybe be the empty string
|
||||
* @param snippets the base list of possible snippets
|
||||
* @param arg the user's argument to the command, maybe be the empty string
|
||||
* @param allowAll if true, allow the use of '-all' and '-start'
|
||||
* @return a Stream of referenced snippets or null if no matches to specific arg
|
||||
*/
|
||||
private Stream<Snippet> argToSnippets(String arg, boolean allowAll) {
|
||||
List<Snippet> snippets = state.snippets();
|
||||
if (allowAll && arg.equals("all")) {
|
||||
private <T extends Snippet> Stream<T> argToSnippets(List<T> snippets, String arg, boolean allowAll) {
|
||||
return argToSnippets(snippets, this::mainActive, arg, allowAll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a user argument to a Stream of snippets referenced by that argument
|
||||
* (or lack of argument).
|
||||
*
|
||||
* @param snippets the base list of possible snippets
|
||||
* @param defFilter the filter to apply to the arguments if no argument
|
||||
* @param arg the user's argument to the command, maybe be the empty string
|
||||
* @param allowAll if true, allow the use of '-all' and '-start'
|
||||
* @return a Stream of referenced snippets or null if no matches to specific arg
|
||||
*/
|
||||
private <T extends Snippet> Stream<T> argToSnippets(List<T> snippets,
|
||||
Predicate<Snippet> defFilter, String arg, boolean allowAll) {
|
||||
if (allowAll && arg.equals("-all")) {
|
||||
// all snippets including start-up, failed, and overwritten
|
||||
return snippets.stream();
|
||||
} else if (allowAll && arg.equals("start")) {
|
||||
} else if (allowAll && arg.equals("-start")) {
|
||||
// start-up snippets
|
||||
return snippets.stream()
|
||||
.filter(this::inStartUp);
|
||||
} else if (arg.isEmpty()) {
|
||||
// Default is all active user snippets
|
||||
return snippets.stream()
|
||||
.filter(this::mainActive);
|
||||
.filter(defFilter);
|
||||
} else {
|
||||
Stream<Snippet> result =
|
||||
Stream<T> result =
|
||||
nonEmptyStream(
|
||||
() -> snippets.stream(),
|
||||
// look for active user declarations matching the name
|
||||
@ -1383,12 +1434,39 @@ public class JShellTool implements MessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a user argument to a Stream of snippets referenced by that
|
||||
* argument, printing an informative message if no matches. Allow '-all' and
|
||||
* '-start'.
|
||||
*
|
||||
* @param snippets the base list of possible snippets
|
||||
* @param defFilter the filter to apply to the arguments if no argument
|
||||
* @param arg the user's argument to the command, maybe be the empty string
|
||||
* @param cmd the name of the command (for use in a help message
|
||||
* @return a Stream of referenced snippets or null if no matches to specific
|
||||
* arg
|
||||
*/
|
||||
private <T extends Snippet> Stream<T> argToSnippetsWithMessage(List<T> snippets,
|
||||
Predicate<Snippet> defFilter, String arg, String cmd) {
|
||||
Stream<T> stream = argToSnippets(snippets, defFilter, arg, true);
|
||||
if (stream == null) {
|
||||
errormsg("jshell.err.def.or.id.not.found", arg);
|
||||
// Check if there are any definitions at all
|
||||
if (argToSnippets(snippets, "", false).iterator().hasNext()) {
|
||||
fluffmsg("jshell.msg.try.command.without.args", cmd);
|
||||
} else {
|
||||
hardmsg("jshell.msg.no.active");
|
||||
}
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
private boolean cmdDrop(String arg) {
|
||||
if (arg.isEmpty()) {
|
||||
errormsg("jshell.err.drop.arg");
|
||||
return false;
|
||||
}
|
||||
Stream<Snippet> stream = argToSnippets(arg, false);
|
||||
Stream<Snippet> stream = argToSnippets(dropableSnippets(), arg, false);
|
||||
if (stream == null) {
|
||||
errormsg("jshell.err.def.or.id.not.found", arg);
|
||||
fluffmsg("jshell.msg.see.classes.etc");
|
||||
@ -1415,10 +1493,9 @@ public class JShellTool implements MessageHandler {
|
||||
}
|
||||
|
||||
private boolean cmdEdit(String arg) {
|
||||
Stream<Snippet> stream = argToSnippets(arg, true);
|
||||
Stream<Snippet> stream = argToSnippetsWithMessage(state.snippets(),
|
||||
this::mainActive, arg, "/edit");
|
||||
if (stream == null) {
|
||||
errormsg("jshell.err.def.or.id.not.found", arg);
|
||||
fluffmsg("jshell.msg.see.classes.etc");
|
||||
return false;
|
||||
}
|
||||
Set<String> srcSet = new LinkedHashSet<>();
|
||||
@ -1518,18 +1595,12 @@ public class JShellTool implements MessageHandler {
|
||||
}
|
||||
|
||||
private boolean cmdList(String arg) {
|
||||
if (arg.equals("history")) {
|
||||
if (arg.equals("-history")) {
|
||||
return cmdHistory();
|
||||
}
|
||||
Stream<Snippet> stream = argToSnippets(arg, true);
|
||||
Stream<Snippet> stream = argToSnippetsWithMessage(state.snippets(),
|
||||
this::mainActive, arg, "/list");
|
||||
if (stream == null) {
|
||||
errormsg("jshell.err.def.or.id.not.found", arg);
|
||||
// Check if there are any definitions at all
|
||||
if (argToSnippets("", false).iterator().hasNext()) {
|
||||
fluffmsg("jshell.msg.try.list.without.args");
|
||||
} else {
|
||||
hardmsg("jshell.msg.no.active");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1601,13 +1672,13 @@ public class JShellTool implements MessageHandler {
|
||||
Iterable<String> history = replayableHistory;
|
||||
boolean echo = true;
|
||||
if (arg.length() > 0) {
|
||||
if ("restore".startsWith(arg)) {
|
||||
if ("-restore".startsWith(arg)) {
|
||||
if (replayableHistoryPrevious == null) {
|
||||
errormsg("jshell.err.reload.no.previous");
|
||||
return false;
|
||||
}
|
||||
history = replayableHistoryPrevious;
|
||||
} else if ("quiet".startsWith(arg)) {
|
||||
} else if ("-quiet".startsWith(arg)) {
|
||||
echo = false;
|
||||
} else {
|
||||
errormsg("jshell.err.arg", "/reload", arg);
|
||||
@ -1634,13 +1705,13 @@ public class JShellTool implements MessageHandler {
|
||||
boolean saveStart = false;
|
||||
String cmd = mat.group("cmd");
|
||||
if (cmd != null) switch (cmd) {
|
||||
case "all":
|
||||
saveAll = "all";
|
||||
case "-all":
|
||||
saveAll = "-all";
|
||||
break;
|
||||
case "history":
|
||||
case "-history":
|
||||
useHistory = true;
|
||||
break;
|
||||
case "start":
|
||||
case "-start":
|
||||
saveStart = true;
|
||||
break;
|
||||
}
|
||||
@ -1660,7 +1731,7 @@ public class JShellTool implements MessageHandler {
|
||||
} else if (saveStart) {
|
||||
writer.append(DEFAULT_STARTUP);
|
||||
} else {
|
||||
Stream<Snippet> stream = argToSnippets(saveAll, true);
|
||||
Stream<Snippet> stream = argToSnippets(state.snippets(), saveAll, true);
|
||||
if (stream != null) {
|
||||
for (Snippet sn : stream.collect(toList())) {
|
||||
writer.write(sn.source());
|
||||
@ -1678,25 +1749,42 @@ public class JShellTool implements MessageHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean cmdVars() {
|
||||
for (VarSnippet vk : state.variables()) {
|
||||
private boolean cmdVars(String arg) {
|
||||
Stream<VarSnippet> stream = argToSnippetsWithMessage(allVarSnippets(),
|
||||
this::isActive, arg, "/vars");
|
||||
if (stream == null) {
|
||||
return false;
|
||||
}
|
||||
stream.forEachOrdered(vk ->
|
||||
{
|
||||
String val = state.status(vk) == Status.VALID
|
||||
? state.varValue(vk)
|
||||
: "jshell.msg.vars.not.active";
|
||||
hard(" %s %s = %s", vk.typeName(), vk.name(), val);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean cmdMethods() {
|
||||
for (MethodSnippet mk : state.methods()) {
|
||||
hard(" %s %s", mk.name(), mk.signature());
|
||||
private boolean cmdMethods(String arg) {
|
||||
Stream<MethodSnippet> stream = argToSnippetsWithMessage(allMethodSnippets(),
|
||||
this::isActive, arg, "/methods");
|
||||
if (stream == null) {
|
||||
return false;
|
||||
}
|
||||
stream.forEachOrdered(mk
|
||||
-> hard(" %s %s", mk.name(), mk.signature())
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean cmdClasses() {
|
||||
for (TypeDeclSnippet ck : state.types()) {
|
||||
private boolean cmdTypes(String arg) {
|
||||
Stream<TypeDeclSnippet> stream = argToSnippetsWithMessage(allTypeSnippets(),
|
||||
this::isActive, arg, "/types");
|
||||
if (stream == null) {
|
||||
return false;
|
||||
}
|
||||
stream.forEachOrdered(ck
|
||||
-> {
|
||||
String kind;
|
||||
switch (ck.subKind()) {
|
||||
case INTERFACE_SUBKIND:
|
||||
@ -1717,7 +1805,7 @@ public class JShellTool implements MessageHandler {
|
||||
break;
|
||||
}
|
||||
hard(" %s %s", kind, ck.name());
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -34,11 +34,11 @@ jshell.err.opt.unknown = Unknown option: {0}
|
||||
|
||||
jshell.msg.terminated =\
|
||||
State engine terminated.\n\
|
||||
Restore definitions with: /reload restore
|
||||
Restore definitions with: /reload -restore
|
||||
|
||||
jshell.msg.use.one.of = Use one of: {0}
|
||||
jshell.err.def.or.id.not.found = No definition or id found named: {0}
|
||||
jshell.msg.see.classes.etc = See /classes, /methods, /vars, or /list
|
||||
jshell.err.def.or.id.not.found = No applicable definition or id found named: {0}
|
||||
jshell.msg.see.classes.etc = See /types, /methods, /vars, or /list
|
||||
jshell.err.arg = Invalid ''{0}'' argument: {1}
|
||||
jshell.msg.see = See {0} for help.
|
||||
|
||||
@ -57,7 +57,7 @@ jshell.msg.set.editor.set = Editor set to: {0}
|
||||
jshell.err.cant.launch.editor = Cannot launch editor -- unexpected exception: {0}
|
||||
jshell.msg.try.set.editor = Try /set editor to use external editor.
|
||||
|
||||
jshell.msg.try.list.without.args = Try ''/list'' without arguments.
|
||||
jshell.msg.try.command.without.args = Try ''{0}'' without arguments.
|
||||
jshell.msg.no.active = There are no active definitions.
|
||||
|
||||
jshell.msg.resetting = Resetting...
|
||||
@ -102,7 +102,7 @@ jshell.msg.help.for.help = Type /help for help.
|
||||
|
||||
jshell.err.feedback.expected.new.feedback.mode = Expected new feedback mode -- {0}
|
||||
jshell.err.feedback.expected.mode.name = Expected a new feedback mode name. ''{0}'' is a known feedback mode -- {1}
|
||||
jshell.err.feedback.command.quiet = Specify either ''command'' or ''quiet'' -- {0}
|
||||
jshell.err.feedback.command.quiet = Specify either ''-command'' or ''-quiet'' -- {0}
|
||||
jshell.err.feedback.expected.field = Expected field name missing -- {0}
|
||||
jshell.err.feedback.expected.mode = Expected a feedback mode -- {0}
|
||||
jshell.err.feedback.does.not.match.mode = Does not match any current feedback mode: {0} -- {1}
|
||||
@ -117,6 +117,9 @@ jshell.msg.feedback.new.mode = Created new feedback mode: {0}
|
||||
jshell.msg.feedback.mode = Feedback mode: {0}
|
||||
jshell.msg.feedback.mode.following = The feedback mode should be one of the following:
|
||||
|
||||
jshell.err.truncation.expected.length = Expected truncation length -- {0}
|
||||
jshell.err.truncation.length.not.integer = Truncation length must be an integer: {0} -- {1}
|
||||
|
||||
jshell.console.see.more = <press tab to see more>
|
||||
jshell.console.do.nothing = Do nothing
|
||||
jshell.console.choice = Choice: \
|
||||
@ -148,15 +151,15 @@ where possible options include:\n\
|
||||
\ -version Version information\n
|
||||
|
||||
help.list.summary = list the source you have typed
|
||||
help.list.args = [all|start|<name or id>]
|
||||
help.list.args = [<name or id>|-all|-start]
|
||||
help.list =\
|
||||
Show the source of snippets, prefaced with the snippet id.\n\
|
||||
\n\
|
||||
/list\n\t\
|
||||
List the currently active snippets of code that you typed or read with /open\n\n\
|
||||
/list start\n\t\
|
||||
/list -start\n\t\
|
||||
List the automatically evaluated start-up snippets\n\n\
|
||||
/list all\n\t\
|
||||
/list -all\n\t\
|
||||
List all snippets including failed, overwritten, dropped, and start-up\n\n\
|
||||
/list <name>\n\t\
|
||||
List snippets with the specified name (preference for active snippets)\n\n\
|
||||
@ -188,18 +191,18 @@ Drop a snippet -- making it inactive.\n\
|
||||
Drop the snippet with the specified snippet id
|
||||
|
||||
help.save.summary = Save snippet source to a file.
|
||||
help.save.args = [all|history|start] <file>
|
||||
help.save.args = [-all|-history|-start] <file>
|
||||
help.save =\
|
||||
Save the specified snippets and/or commands to the specified file.\n\
|
||||
\n\
|
||||
/save <file>\n\t\
|
||||
Save the source of current active snippets to the file.\n\n\
|
||||
/save all <file>\n\t\
|
||||
/save -all <file>\n\t\
|
||||
Save the source of all snippets to the file.\n\t\
|
||||
Includes source including overwritten, failed, and start-up code.\n\n\
|
||||
/save history <file>\n\t\
|
||||
/save -history <file>\n\t\
|
||||
Save the sequential history of all commands and snippets entered since jshell was launched.\n\n\
|
||||
/save start <file>\n\t\
|
||||
/save -start <file>\n\t\
|
||||
Save the default start-up definitions to the file.
|
||||
|
||||
help.open.summary = open a file as source input
|
||||
@ -211,19 +214,52 @@ Open a file and read its contents as snippets and commands.\n\
|
||||
Read the specified file as jshell input.
|
||||
|
||||
help.vars.summary = list the declared variables and their values
|
||||
help.vars.args =
|
||||
help.vars.args = [<name or id>|-all|-start]
|
||||
help.vars =\
|
||||
List the type, name, and value of the current active jshell variables.
|
||||
List the type, name, and value of jshell variables.\n\
|
||||
\n\
|
||||
/vars\n\t\
|
||||
List the type, name, and value of the current active jshell variables\n\n\
|
||||
/vars <name>\n\t\
|
||||
List jshell variables with the specified name (preference for active variables)\n\n\
|
||||
/vars <id>\n\t\
|
||||
List the jshell variable with the specified snippet id\n\n\
|
||||
/vars -start\n\t\
|
||||
List the automatically added start-up jshell variables\n\n\
|
||||
/vars -all\n\t\
|
||||
List all jshell variables including failed, overwritten, dropped, and start-up
|
||||
|
||||
help.methods.summary = list the declared methods and their signatures
|
||||
help.methods.args =
|
||||
help.methods.args = [<name or id>|-all|-start]
|
||||
help.methods =\
|
||||
List the name, parameter types, and return type of the current active jshell methods.
|
||||
List the name, parameter types, and return type of jshell methods.\n\
|
||||
\n\
|
||||
/methods\n\t\
|
||||
List the name, parameter types, and return type of the current active jshell methods\n\n\
|
||||
/methods <name>\n\t\
|
||||
List jshell methods with the specified name (preference for active methods)\n\n\
|
||||
/methods <id>\n\t\
|
||||
List the jshell method with the specified snippet id\n\n\
|
||||
/methods -start\n\t\
|
||||
List the automatically added start-up jshell methods\n\n\
|
||||
/methods -all\n\t\
|
||||
List all snippets including failed, overwritten, dropped, and start-up
|
||||
|
||||
help.classes.summary = list the declared classes
|
||||
help.classes.args =
|
||||
help.classes =\
|
||||
List the current active jshell classes, interfaces, and enums.
|
||||
help.types.summary = list the declared types
|
||||
help.types.args =[<name or id>|-all|-start]
|
||||
help.types =\
|
||||
List jshell classes, interfaces, and enums.\n\
|
||||
\n\
|
||||
/types\n\t\
|
||||
List the current active jshell classes, interfaces, and enums.\n\n\
|
||||
/types <name>\n\t\
|
||||
List jshell types with the specified name (preference for active types)\n\n\
|
||||
/types <id>\n\t\
|
||||
List the jshell type with the specified snippet id\n\n\
|
||||
/types -start\n\t\
|
||||
List the automatically added start-up jshell types\n\n\
|
||||
/types -all\n\t\
|
||||
List all jshell types including failed, overwritten, dropped, and start-up
|
||||
|
||||
help.imports.summary = list the imported items
|
||||
help.imports.args =
|
||||
@ -247,8 +283,8 @@ Reset the jshell tool code and execution state:\n\t\
|
||||
Tool settings are maintained, as set with: /set ...\n\
|
||||
Save any work before using this command
|
||||
|
||||
help.reload.summary = reset and replay relevant history -- current or previous (restore)
|
||||
help.reload.args = [restore] [quiet]
|
||||
help.reload.summary = reset and replay relevant history -- current or previous (-restore)
|
||||
help.reload.args = [-restore] [-quiet]
|
||||
help.reload =\
|
||||
Reset the jshell tool code and execution state then replay each\n\
|
||||
jshell valid command and valid snippet in the order they were entered.\n\
|
||||
@ -257,13 +293,13 @@ jshell valid command and valid snippet in the order they were entered.\n\
|
||||
Reset and replay the valid history since jshell was entered, or\n\t\
|
||||
a /reset, or /reload command was executed -- whichever is most\n\t\
|
||||
recent.\n\n\
|
||||
/reload restore\n\t\
|
||||
/reload -restore\n\t\
|
||||
Reset and replay the valid history between the previous and most\n\t\
|
||||
recent time that jshell was entered, or a /reset, or /reload\n\t\
|
||||
command was executed. This can thus be used to restore a previous\n\t\
|
||||
jshell tool sesson.\n\n\
|
||||
/reload [restore] quiet\n\t\
|
||||
With the 'quiet' argument the replay is not shown. Errors will display.
|
||||
/reload [-restore] -quiet\n\t\
|
||||
With the '-quiet' argument the replay is not shown. Errors will display.
|
||||
|
||||
help.classpath.summary = add a path to the classpath
|
||||
help.classpath.args = <path>
|
||||
@ -301,7 +337,7 @@ Display information about jshell.\n\
|
||||
Display information about the specified help subject. Example: /help intro
|
||||
|
||||
help.set.summary = set jshell configuration information
|
||||
help.set.args = editor|start|feedback|newmode|prompt|format ...
|
||||
help.set.args = editor|start|feedback|newmode|prompt|truncation|format ...
|
||||
help.set =\
|
||||
Set jshell configuration information, including:\n\
|
||||
the external editor to use, the start-up definitions to use, a new feedback mode,\n\
|
||||
@ -314,10 +350,12 @@ the command prompt, the feedback mode to use, or the format of output.\n\
|
||||
The contents of the specified <file> become the default start-up snippets and commands.\n\n\
|
||||
/set feedback <mode>\n\t\
|
||||
Set the feedback mode describing displayed feedback for entered snippets and commands.\n\n\
|
||||
/set newmode <new-mode> [command|quiet [<old-mode>]]\n\t\
|
||||
/set newmode <new-mode> [-command|-quiet [<old-mode>]]\n\t\
|
||||
Create a user-defined feedback mode, optionally copying from an existing mode.\n\n\
|
||||
/set prompt <mode> "<prompt>" "<continuation-prompt>"\n\t\
|
||||
Set the displayed prompts for a given feedback mode.\n\n\
|
||||
/set truncation <mode> <length> <selector>...\n\t\
|
||||
Set the maximum length of a displayed value\n\t\
|
||||
/set format <mode> <field> "<format>" <selector>...\n\t\
|
||||
Configure a feedback mode by setting the format of a field when the selector matchs.\n\n\
|
||||
To get more information about one of these forms, use /help with the form specified.\n\
|
||||
@ -462,6 +500,37 @@ Examples:\n\t\
|
||||
/set format myformat display '{pre}{action} variable {name}, reset to null{post}' replaced-vardecl,varinit-ok-update\n\n\
|
||||
Note that subsequent selectors for a field may overwrite some or all of previous used selectors -- last one wins\n
|
||||
|
||||
help.set.truncation = \
|
||||
Set the max length a displayed value.\n\
|
||||
\n\t\
|
||||
/set truncation <mode> <length> <selector>...\n\
|
||||
\n\
|
||||
Where <mode> is the name of a previously defined feedback mode -- see '/help /set newmode'.\n\
|
||||
Where <length> is an unsigned integer representing a maximum length.\n\
|
||||
Where <format> is a quoted string which will be the value of the field if one of\n\
|
||||
Where <selector> is only needed if you wish to fine-tune value truncation length\n\
|
||||
by context, <selector> is the context in which the truncation is applied.\n\
|
||||
The structure of selector is a hyphen separated list of selector kind lists.\n\
|
||||
A selector kind list is a comma separated list of values of one selector kind.\n\
|
||||
A selector matches if each selector kind list matches; A selector kind list\n\
|
||||
matches if one of the values matches.\n\n\
|
||||
Below are the relevant selector kinds for truncation.\n\n\
|
||||
The case selector kind describes the kind of snippet. The values are:\n\t\
|
||||
vardecl -- variable declaration without init\n\t\
|
||||
varinit -- variable declaration with init\n\t\
|
||||
expression -- expression -- note: {name}==scratch-variable-name\n\t\
|
||||
varvalue -- variable value expression\n\t\
|
||||
assignment -- assign variable\n\t\
|
||||
The action selector kind describes what happened to the snippet. The values are:\n\t\
|
||||
added -- snippet has been added\n\t\
|
||||
modified -- an existing snippet has been modified\n\t\
|
||||
replaced -- an existing snippet has been replaced with a new snippet\n\
|
||||
Examples:\n\t\
|
||||
/set trunc mymode 80\n\t\
|
||||
/set truncation mymode 45 expression\n\t\
|
||||
/set truncation mymode 0 vardecl-modified,replaced\n\n\
|
||||
Note that subsequent selectors for a field may overwrite some or all of previous used selectors -- last one wins\n
|
||||
|
||||
help.set.feedback = \
|
||||
Set the feedback mode describing displayed feedback for entered snippets and commands.\n\
|
||||
\n\t\
|
||||
@ -475,12 +544,12 @@ Currently defined feedback modes:\n
|
||||
help.set.newmode = \
|
||||
Create a user-defined feedback mode, optionally copying from an existing mode.\n\
|
||||
\n\t\
|
||||
/set newmode <new-mode> [command|quiet [<old-mode>]]\n\
|
||||
/set newmode <new-mode> [-command|-quiet [<old-mode>]]\n\
|
||||
\n\
|
||||
Where <new-mode> is the name of a mode you wish to create.\n\
|
||||
Where <old-mode> is the name of a previously defined feedback mode.\n\
|
||||
If <old-mode> is present, its settings are copied to the new mode.\n\
|
||||
'command' vs 'quiet' determines if informative/verifying command feedback is displayed.\n\
|
||||
'-command' vs '-quiet' determines if informative/verifying command feedback is displayed.\n\
|
||||
\n\
|
||||
Once the new mode is created, use '/set format' and '/set prompt' to configure it.\n\
|
||||
Use '/set feedback' to use the new mode.\n\
|
||||
@ -514,7 +583,7 @@ The contents of the specified <file> become the default start-up snippets and co
|
||||
which are run when the jshell tool is started or reset.
|
||||
|
||||
startup.feedback = \
|
||||
/set newmode verbose command \n\
|
||||
/set newmode verbose -command \n\
|
||||
\n\
|
||||
/set prompt verbose '\\njshell> ' ' ...> ' \n\
|
||||
\n\
|
||||
@ -578,11 +647,14 @@ startup.feedback = \
|
||||
/set format verbose display '{pre}attempted to use {typeKind} {name}{resolve}{post}' used-class,interface,enum,annotation \n\
|
||||
/set format verbose display '{pre}attempted to call method {name}({type}){resolve}{post}' used-method \n\
|
||||
\n\
|
||||
/set newmode normal command verbose \n\
|
||||
/set truncation verbose 80\n\
|
||||
/set truncation verbose 500 varvalue\n\
|
||||
\n\
|
||||
/set newmode normal -command verbose \n\
|
||||
/set format normal display '' added,modified,replaced,overwrote,dropped-update \n\
|
||||
/set format normal display '{pre}{action} variable {name}, reset to null{post}' replaced-vardecl,varinit-ok-update \n\
|
||||
/set format normal display '{result}' added,modified,replaced-expression,varvalue,assignment,varinit,vardecl-ok-primary \n\
|
||||
/set newmode concise quiet normal \n\
|
||||
/set newmode concise -quiet normal \n\
|
||||
\n\
|
||||
/set prompt concise 'jshell> ' ' ...> ' \n\
|
||||
\n\
|
||||
@ -590,7 +662,7 @@ startup.feedback = \
|
||||
\n\
|
||||
/set feedback normal \n\
|
||||
\n\
|
||||
/set newmode silent quiet \n\
|
||||
/set newmode silent -quiet \n\
|
||||
/set prompt silent '-> ' '>> ' \n\
|
||||
/set format silent pre '| ' \n\
|
||||
/set format silent post '%n' \n\
|
||||
|
||||
@ -66,7 +66,6 @@ jdk/jshell/ToolBasicTest.java
|
||||
# javac
|
||||
|
||||
tools/javac/Paths/AbsolutePathTest.java 8055768 generic-all ToolBox does not close opened files
|
||||
tools/javac/Paths/MineField.sh 8149599 generic-all needs rewrite
|
||||
tools/javac/annotations/typeAnnotations/failures/CantAnnotatePackages.java 8057679 generic-all clarify error messages trying to annotate scoping
|
||||
tools/javac/annotations/typeAnnotations/failures/CantAnnotateScoping.java 8057679 generic-all clarify error messages trying to annotate scoping
|
||||
tools/javac/annotations/typeAnnotations/failures/CantAnnotateStaticClass2.java 8057679 generic-all clarify error messages trying to annotate scoping
|
||||
@ -95,6 +94,3 @@ tools/sjavac/IncCompileWithChanges.java
|
||||
#
|
||||
# jdeps
|
||||
|
||||
tools/jdeps/modules/GenModuleInfo.java 8152502,8153481 generic-all fails to clean up files, java.util.HashMap$Node cannot be cast to java.util.HashMap$TreeNode
|
||||
tools/jdeps/modules/ModuleTest.java 8153481 generic-all java.util.HashMap$Node cannot be cast to java.util.HashMap$TreeNode
|
||||
|
||||
|
||||
@ -53,18 +53,18 @@ public class CommandCompletionTest extends ReplToolTesting {
|
||||
|
||||
public void testCommand() {
|
||||
assertCompletion("/deb|", false);
|
||||
assertCompletion("/c|", false, "/classes ", "/classpath ");
|
||||
assertCompletion("/re|", false, "/reload ", "/reset ");
|
||||
assertCompletion("/h|", false, "/help ", "/history ");
|
||||
}
|
||||
|
||||
public void testList() {
|
||||
test(false, new String[] {"-nostartup"},
|
||||
a -> assertCompletion(a, "/l|", false, "/list "),
|
||||
a -> assertCompletion(a, "/list |", false, "all ", "history ", "start "),
|
||||
a -> assertCompletion(a, "/list h|", false, "history "),
|
||||
a -> assertCompletion(a, "/list |", false, "-all ", "-history ", "-start "),
|
||||
a -> assertCompletion(a, "/list -h|", false, "-history "),
|
||||
a -> assertCompletion(a, "/list q|", false),
|
||||
a -> assertVariable(a, "int", "xray"),
|
||||
a -> assertCompletion(a, "/list |", false, "1", "all ", "history ", "start ", "xray"),
|
||||
a -> assertCompletion(a, "/list |", false, "-all ", "-history ", "-start ", "1", "xray"),
|
||||
a -> assertCompletion(a, "/list x|", false, "xray")
|
||||
);
|
||||
}
|
||||
@ -108,7 +108,7 @@ public class CommandCompletionTest extends ReplToolTesting {
|
||||
Compiler compiler = new Compiler();
|
||||
assertCompletion("/s|", false, "/save ", "/set ");
|
||||
List<String> p1 = listFiles(Paths.get(""));
|
||||
Collections.addAll(p1, "all ", "history ", "start ");
|
||||
Collections.addAll(p1, "-all ", "-history ", "-start ");
|
||||
FileSystems.getDefault().getRootDirectories().forEach(s -> p1.add(s.toString()));
|
||||
Collections.sort(p1);
|
||||
assertCompletion("/save |", false, p1.toArray(new String[p1.size()]));
|
||||
@ -116,7 +116,7 @@ public class CommandCompletionTest extends ReplToolTesting {
|
||||
List<String> p2 = listFiles(classDir);
|
||||
assertCompletion("/save " + classDir + "/|",
|
||||
false, p2.toArray(new String[p2.size()]));
|
||||
assertCompletion("/save all " + classDir + "/|",
|
||||
assertCompletion("/save -all " + classDir + "/|",
|
||||
false, p2.toArray(new String[p2.size()]));
|
||||
}
|
||||
|
||||
|
||||
@ -142,7 +142,7 @@ public abstract class EditorTestBase extends ReplToolTesting {
|
||||
exit();
|
||||
loadClass(true, "enum A {}", "enum", "A");
|
||||
}),
|
||||
a -> assertCommandCheckOutput(a, "/classes", assertClasses())
|
||||
a -> assertCommandCheckOutput(a, "/types", assertClasses())
|
||||
);
|
||||
}
|
||||
|
||||
@ -161,7 +161,7 @@ public abstract class EditorTestBase extends ReplToolTesting {
|
||||
exit();
|
||||
loadClass(true, "enum A {}", "enum", "A");
|
||||
}),
|
||||
a -> assertCommandCheckOutput(a, "/classes", assertClasses())
|
||||
a -> assertCommandCheckOutput(a, "/types", assertClasses())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, 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
|
||||
@ -24,152 +24,64 @@
|
||||
/*
|
||||
* @test
|
||||
* @summary Test Completion
|
||||
* @modules jdk.jshell/jdk.internal.jshell.tool
|
||||
* jdk.internal.le/jdk.internal.jline.console.history
|
||||
* @modules jdk.internal.le/jdk.internal.jline.extra
|
||||
* jdk.jshell/jdk.internal.jshell.tool
|
||||
* @build HistoryTest
|
||||
* @run testng HistoryTest
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.prefs.AbstractPreferences;
|
||||
import java.util.prefs.BackingStoreException;
|
||||
import jdk.internal.jline.console.history.MemoryHistory;
|
||||
|
||||
import jdk.jshell.JShell;
|
||||
import jdk.jshell.SourceCodeAnalysis;
|
||||
import jdk.jshell.SourceCodeAnalysis.CompletionInfo;
|
||||
import java.lang.reflect.Field;
|
||||
import jdk.internal.jline.extra.EditingHistory;
|
||||
import org.testng.annotations.Test;
|
||||
import jdk.internal.jshell.tool.EditingHistory;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
@Test
|
||||
public class HistoryTest {
|
||||
public class HistoryTest extends ReplToolTesting {
|
||||
|
||||
public void testHistory() {
|
||||
JShell eval = JShell.builder()
|
||||
.in(new ByteArrayInputStream(new byte[0]))
|
||||
.out(new PrintStream(new ByteArrayOutputStream()))
|
||||
.err(new PrintStream(new ByteArrayOutputStream()))
|
||||
.build();
|
||||
SourceCodeAnalysis analysis = eval.sourceCodeAnalysis();
|
||||
MemoryPreferences prefs = new MemoryPreferences(null, "");
|
||||
EditingHistory history = new EditingHistory(prefs) {
|
||||
@Override protected CompletionInfo analyzeCompletion(String input) {
|
||||
return analysis.analyzeCompletion(input);
|
||||
}
|
||||
};
|
||||
history.add("void test() {");
|
||||
history.add(" System.err.println(1);");
|
||||
history.add("}");
|
||||
history.add("/exit");
|
||||
|
||||
previousAndAssert(history, "/exit");
|
||||
|
||||
history.previous(); history.previous(); history.previous();
|
||||
|
||||
history.add("void test() { /*changed*/");
|
||||
|
||||
previousAndAssert(history, "}");
|
||||
previousAndAssert(history, " System.err.println(1);");
|
||||
previousAndAssert(history, "void test() {");
|
||||
|
||||
assertFalse(history.previous());
|
||||
|
||||
nextAndAssert(history, " System.err.println(1);");
|
||||
nextAndAssert(history, "}");
|
||||
nextAndAssert(history, "");
|
||||
|
||||
history.add(" System.err.println(2);");
|
||||
history.add("} /*changed*/");
|
||||
|
||||
assertEquals(history.size(), 7);
|
||||
|
||||
history.save();
|
||||
|
||||
history = new EditingHistory(prefs) {
|
||||
@Override protected CompletionInfo analyzeCompletion(String input) {
|
||||
return analysis.analyzeCompletion(input);
|
||||
}
|
||||
};
|
||||
|
||||
previousSnippetAndAssert(history, "void test() { /*changed*/");
|
||||
previousSnippetAndAssert(history, "/exit");
|
||||
previousSnippetAndAssert(history, "void test() {");
|
||||
|
||||
assertFalse(history.previousSnippet());
|
||||
|
||||
nextSnippetAndAssert(history, "/exit");
|
||||
nextSnippetAndAssert(history, "void test() { /*changed*/");
|
||||
nextSnippetAndAssert(history, "");
|
||||
|
||||
assertFalse(history.nextSnippet());
|
||||
|
||||
history.add("{");
|
||||
history.add("}");
|
||||
|
||||
history.save();
|
||||
|
||||
history = new EditingHistory(prefs) {
|
||||
@Override protected CompletionInfo analyzeCompletion(String input) {
|
||||
return analysis.analyzeCompletion(input);
|
||||
}
|
||||
};
|
||||
|
||||
previousSnippetAndAssert(history, "{");
|
||||
previousSnippetAndAssert(history, "void test() { /*changed*/");
|
||||
previousSnippetAndAssert(history, "/exit");
|
||||
previousSnippetAndAssert(history, "void test() {");
|
||||
|
||||
while (history.next());
|
||||
|
||||
history.add("/*current1*/");
|
||||
history.add("/*current2*/");
|
||||
history.add("/*current3*/");
|
||||
|
||||
assertEquals(history.currentSessionEntries(), Arrays.asList("/*current1*/", "/*current2*/", "/*current3*/"));
|
||||
|
||||
history.remove(0);
|
||||
|
||||
assertEquals(history.currentSessionEntries(), Arrays.asList("/*current1*/", "/*current2*/", "/*current3*/"));
|
||||
|
||||
while (history.size() > 2)
|
||||
history.remove(0);
|
||||
|
||||
assertEquals(history.currentSessionEntries(), Arrays.asList("/*current2*/", "/*current3*/"));
|
||||
|
||||
for (int i = 0; i < MemoryHistory.DEFAULT_MAX_SIZE * 2; i++) {
|
||||
history.add("/exit");
|
||||
}
|
||||
|
||||
history.add("void test() { /*after full*/");
|
||||
history.add(" System.err.println(1);");
|
||||
history.add("}");
|
||||
|
||||
previousSnippetAndAssert(history, "void test() { /*after full*/");
|
||||
test(
|
||||
a -> {if (!a) setCommandInput("void test() {\n");},
|
||||
a -> {if (!a) setCommandInput(" System.err.println(1);\n");},
|
||||
a -> {if (!a) setCommandInput(" System.err.println(1);\n");},
|
||||
a -> {assertCommand(a, "} //test", "| created method test()");},
|
||||
a -> {
|
||||
if (!a) {
|
||||
try {
|
||||
previousAndAssert(getHistory(), "} //test");
|
||||
previousSnippetAndAssert(getHistory(), "void test() {");
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
assertCommand(a, "int dummy;", "dummy ==> 0");
|
||||
});
|
||||
test(
|
||||
a -> {if (!a) setCommandInput("void test2() {\n");},
|
||||
a -> {assertCommand(a, "} //test2", "| created method test2()");},
|
||||
a -> {
|
||||
if (!a) {
|
||||
try {
|
||||
previousAndAssert(getHistory(), "} //test2");
|
||||
previousSnippetAndAssert(getHistory(), "void test2() {");
|
||||
previousSnippetAndAssert(getHistory(), "/debug 0"); //added by test framework
|
||||
previousSnippetAndAssert(getHistory(), "/exit");
|
||||
previousSnippetAndAssert(getHistory(), "int dummy;");
|
||||
previousSnippetAndAssert(getHistory(), "void test() {");
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
assertCommand(a, "int dummy;", "dummy ==> 0");
|
||||
});
|
||||
}
|
||||
|
||||
public void testSaveOneHistory() {
|
||||
JShell eval = JShell.builder()
|
||||
.in(new ByteArrayInputStream(new byte[0]))
|
||||
.out(new PrintStream(new ByteArrayOutputStream()))
|
||||
.err(new PrintStream(new ByteArrayOutputStream()))
|
||||
.build();
|
||||
SourceCodeAnalysis analysis = eval.sourceCodeAnalysis();
|
||||
MemoryPreferences prefs = new MemoryPreferences(null, "");
|
||||
EditingHistory history = new EditingHistory(prefs) {
|
||||
@Override protected CompletionInfo analyzeCompletion(String input) {
|
||||
return analysis.analyzeCompletion(input);
|
||||
}
|
||||
};
|
||||
|
||||
history.add("first");
|
||||
history.save();
|
||||
private EditingHistory getHistory() throws Exception {
|
||||
Field input = repl.getClass().getDeclaredField("input");
|
||||
input.setAccessible(true);
|
||||
Object console = input.get(repl);
|
||||
Field history = console.getClass().getDeclaredField("history");
|
||||
history.setAccessible(true);
|
||||
return (EditingHistory) history.get(console);
|
||||
}
|
||||
|
||||
private void previousAndAssert(EditingHistory history, String expected) {
|
||||
@ -177,71 +89,9 @@ public class HistoryTest {
|
||||
assertEquals(history.current().toString(), expected);
|
||||
}
|
||||
|
||||
private void nextAndAssert(EditingHistory history, String expected) {
|
||||
assertTrue(history.next());
|
||||
assertEquals(history.current().toString(), expected);
|
||||
}
|
||||
|
||||
private void previousSnippetAndAssert(EditingHistory history, String expected) {
|
||||
assertTrue(history.previousSnippet());
|
||||
assertEquals(history.current().toString(), expected);
|
||||
}
|
||||
|
||||
private void nextSnippetAndAssert(EditingHistory history, String expected) {
|
||||
assertTrue(history.nextSnippet());
|
||||
assertEquals(history.current().toString(), expected);
|
||||
}
|
||||
|
||||
private static final class MemoryPreferences extends AbstractPreferences {
|
||||
|
||||
private final Map<String, String> key2Value = new HashMap<>();
|
||||
private final Map<String, MemoryPreferences> key2SubNode = new HashMap<>();
|
||||
|
||||
public MemoryPreferences(AbstractPreferences parent, String name) {
|
||||
super(parent, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void putSpi(String key, String value) {
|
||||
key2Value.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSpi(String key) {
|
||||
return key2Value.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeSpi(String key) {
|
||||
key2Value.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeNodeSpi() throws BackingStoreException {
|
||||
((MemoryPreferences) parent()).key2SubNode.remove(name());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] keysSpi() throws BackingStoreException {
|
||||
return key2Value.keySet().toArray(new String[key2Value.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] childrenNamesSpi() throws BackingStoreException {
|
||||
return key2SubNode.keySet().toArray(new String[key2SubNode.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractPreferences childSpi(String name) {
|
||||
return key2SubNode.computeIfAbsent(name, n -> new MemoryPreferences(this, n));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void syncSpi() throws BackingStoreException {}
|
||||
|
||||
@Override
|
||||
protected void flushSpi() throws BackingStoreException {}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -70,6 +70,9 @@ public class ReplToolTesting {
|
||||
new MethodInfo("void printf(String format, Object... args) { System.out.printf(format, args); }",
|
||||
"(String,Object...)void", "printf"))
|
||||
.collect(toList());
|
||||
final static List<String> START_UP_CMD_METHOD = Stream.of(
|
||||
"| printf (String,Object...)void")
|
||||
.collect(toList());
|
||||
final static List<String> START_UP = Collections.unmodifiableList(
|
||||
Stream.concat(START_UP_IMPORTS.stream(), START_UP_METHODS.stream())
|
||||
.map(s -> s.getSource())
|
||||
|
||||
@ -118,9 +118,9 @@ public class ToolBasicTest extends ReplToolTesting {
|
||||
(a) -> assertCommand(a, "class A {" + s, ""),
|
||||
interrupt,
|
||||
(a) -> assertCommand(a, "class A {}\u0003", ""),
|
||||
(a) -> assertCommandCheckOutput(a, "/classes", assertClasses()),
|
||||
(a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
(a) -> assertClass(a, "interface A {}", "interface", "A"),
|
||||
(a) -> assertCommandCheckOutput(a, "/classes", assertClasses()),
|
||||
(a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
(a) -> assertCommand(a, "import java.util.stream." + s, ""),
|
||||
interrupt,
|
||||
(a) -> assertCommand(a, "import java.util.stream.\u0003", ""),
|
||||
@ -338,13 +338,13 @@ public class ToolBasicTest extends ReplToolTesting {
|
||||
(a) -> assertMethod(a, "void f() { }", "()void", "f"),
|
||||
(a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
|
||||
(a) -> assertClass(a, "class A { }", "class", "A"),
|
||||
(a) -> assertCommandCheckOutput(a, "/classes", assertClasses()),
|
||||
(a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
(a) -> assertImport(a, "import java.util.stream.*;", "", "java.util.stream.*"),
|
||||
(a) -> assertCommandCheckOutput(a, "/imports", assertImports()),
|
||||
(a) -> assertReset(a, "/reset"),
|
||||
(a) -> assertCommandCheckOutput(a, "/vars", assertVariables()),
|
||||
(a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
|
||||
(a) -> assertCommandCheckOutput(a, "/classes", assertClasses()),
|
||||
(a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
(a) -> assertCommandCheckOutput(a, "/imports", assertImports())
|
||||
);
|
||||
}
|
||||
@ -369,7 +369,7 @@ public class ToolBasicTest extends ReplToolTesting {
|
||||
loadClass(a, "class A { public String toString() { return \"A\"; } }",
|
||||
"class", "A");
|
||||
loadImport(a, "import java.util.stream.*;", "", "java.util.stream.*");
|
||||
assertCommandCheckOutput(a, "/classes", assertClasses());
|
||||
assertCommandCheckOutput(a, "/types", assertClasses());
|
||||
},
|
||||
(a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
|
||||
(a) -> assertCommandCheckOutput(a, "/vars", assertVariables()),
|
||||
@ -403,13 +403,13 @@ public class ToolBasicTest extends ReplToolTesting {
|
||||
(a) -> assertCommand(a, "int a;", null),
|
||||
(a) -> assertCommand(a, "()", null, null, null, "", ""),
|
||||
(a) -> assertClass(a, "class A { public String toString() { return \"A\"; } }", "class", "A"),
|
||||
(a) -> assertCommandCheckOutput(a, "/list all", (out) ->
|
||||
(a) -> assertCommandCheckOutput(a, "/list -all", (out) ->
|
||||
output.addAll(Stream.of(out.split("\n"))
|
||||
.filter(str -> !str.isEmpty())
|
||||
.map(str -> str.substring(str.indexOf(':') + 2))
|
||||
.filter(str -> !str.startsWith("/"))
|
||||
.collect(Collectors.toList()))),
|
||||
(a) -> assertCommand(a, "/save all " + path.toString(), "")
|
||||
(a) -> assertCommand(a, "/save -all " + path.toString(), "")
|
||||
);
|
||||
assertEquals(Files.readAllLines(path), output);
|
||||
}
|
||||
@ -451,7 +451,7 @@ public class ToolBasicTest extends ReplToolTesting {
|
||||
loadVariable(a, "double", "b", "10.0", "10.0");
|
||||
loadMethod(a, "void f() {}", "()void", "f");
|
||||
loadImport(a, "import java.util.stream.*;", "", "java.util.stream.*");
|
||||
assertCommandCheckOutput(a, "/classes", assertClasses());
|
||||
assertCommandCheckOutput(a, "/types", assertClasses());
|
||||
},
|
||||
(a) -> assertCommandCheckOutput(a, "/vars", assertVariables()),
|
||||
(a) -> assertCommandCheckOutput(a, "/methods", assertMethods()),
|
||||
@ -472,7 +472,7 @@ public class ToolBasicTest extends ReplToolTesting {
|
||||
public void testStartSave() throws IOException {
|
||||
Compiler compiler = new Compiler();
|
||||
Path startSave = compiler.getPath("startSave.txt");
|
||||
test(a -> assertCommand(a, "/save start " + startSave.toString(), null));
|
||||
test(a -> assertCommand(a, "/save -start " + startSave.toString(), null));
|
||||
List<String> lines = Files.lines(startSave)
|
||||
.filter(s -> !s.isEmpty())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8148316 8148317 8151755 8152246 8153551
|
||||
* @bug 8148316 8148317 8151755 8152246 8153551 8154812
|
||||
* @summary Tests for output customization
|
||||
* @library /tools/lib
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
@ -43,7 +43,7 @@ public class ToolFormatTest extends ReplToolTesting {
|
||||
public void testSetFormat() {
|
||||
try {
|
||||
test(
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode test command", "| Created new feedback mode: test"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode test -command", "| Created new feedback mode: test"),
|
||||
(a) -> assertCommand(a, "/set format test pre '$ '", ""),
|
||||
(a) -> assertCommand(a, "/set format test post ''", ""),
|
||||
(a) -> assertCommand(a, "/set format test act 'ADD' added", ""),
|
||||
@ -82,7 +82,7 @@ public class ToolFormatTest extends ReplToolTesting {
|
||||
|
||||
public void testSetFormatSelector() {
|
||||
List<ReplTest> tests = new ArrayList<>();
|
||||
tests.add((a) -> assertCommandOutputStartsWith(a, "/set newmode ate quiet",
|
||||
tests.add((a) -> assertCommandOutputStartsWith(a, "/set newmode ate -quiet",
|
||||
"| Created new feedback mode: ate"));
|
||||
tests.add((a) -> assertCommand(a, "/set feedback ate", ""));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@ -155,6 +155,29 @@ public class ToolFormatTest extends ReplToolTesting {
|
||||
}
|
||||
}
|
||||
|
||||
public void testSetTruncation() {
|
||||
try {
|
||||
test(
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set feedback normal", ""),
|
||||
(a) -> assertCommand(a, "String s = java.util.stream.IntStream.range(65, 74)"+
|
||||
".mapToObj(i -> \"\"+(char)i).reduce((a,b) -> a + b + a).get()",
|
||||
"s ==> \"ABACABADABACABAEABACABADABACABAFABACABADABACABAEABACABADABACABAGABACABADABA ..."),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode test -quiet", ""),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set feedback test", ""),
|
||||
(a) -> assertCommand(a, "/set format test display '{type}:{value}' primary", ""),
|
||||
(a) -> assertCommand(a, "/set truncation test 20", ""),
|
||||
(a) -> assertCommand(a, "/set trunc test 10 varvalue", ""),
|
||||
(a) -> assertCommand(a, "/set trunc test 3 assignment", ""),
|
||||
(a) -> assertCommand(a, "String r = s", "String:\"ABACABADABACABA ..."),
|
||||
(a) -> assertCommand(a, "r", "String:\"ABACA ..."),
|
||||
(a) -> assertCommand(a, "r=s", "String:\"AB")
|
||||
);
|
||||
} finally {
|
||||
assertCommandCheckOutput(false, "/set feedback normal", s -> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void testShowFeedbackModes() {
|
||||
test(
|
||||
(a) -> assertCommandOutputContains(a, "/set feedback", "normal")
|
||||
@ -164,11 +187,11 @@ public class ToolFormatTest extends ReplToolTesting {
|
||||
public void testSetNewModeQuiet() {
|
||||
try {
|
||||
test(
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode nmq quiet normal", "| Created new feedback mode: nmq"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode nmq -quiet normal", "| Created new feedback mode: nmq"),
|
||||
(a) -> assertCommand(a, "/set feedback nmq", ""),
|
||||
(a) -> assertCommand(a, "/se ne nmq2 q nor", ""),
|
||||
(a) -> assertCommand(a, "/se ne nmq2 -q nor", ""),
|
||||
(a) -> assertCommand(a, "/se fee nmq2", ""),
|
||||
(a) -> assertCommand(a, "/set newmode nmc command normal", ""),
|
||||
(a) -> assertCommand(a, "/set newmode nmc -command normal", ""),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set feedback nmc", "| Feedback mode: nmc"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode nm", "| Created new feedback mode: nm"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set feedback nm", "| Feedback mode: nm"),
|
||||
@ -183,15 +206,15 @@ public class ToolFormatTest extends ReplToolTesting {
|
||||
public void testSetError() {
|
||||
try {
|
||||
test(
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode tee command foo",
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode tee -command foo",
|
||||
"| Does not match any current feedback mode: foo"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode tee flurb",
|
||||
"| Specify either 'command' or 'quiet'"),
|
||||
"| Specify either '-command' or '-quiet'"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode te2",
|
||||
"| Created new feedback mode: te2"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode te2 command",
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode te2 -command",
|
||||
"| Expected a new feedback mode name. 'te2' is a known feedback mode"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode te command normal",
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode te -command normal",
|
||||
"| Created new feedback mode: te"),
|
||||
(a) -> assertCommand(a, "/set format te errorpre 'ERROR: '", ""),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set feedback te",
|
||||
@ -226,13 +249,19 @@ public class ToolFormatTest extends ReplToolTesting {
|
||||
"ERROR: Selector kind in multiple sections of"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set format te fld 'aaa' import,added",
|
||||
"ERROR: Different selector kinds in same sections of"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set trunc te 20x",
|
||||
"ERROR: Truncation length must be an integer: 20x"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set trunc te",
|
||||
"ERROR: Expected truncation length"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set truncation te 111 import,added",
|
||||
"ERROR: Different selector kinds in same sections of"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode",
|
||||
"ERROR: Expected new feedback mode"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode te",
|
||||
"ERROR: Expected a new feedback mode name"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode x xyz",
|
||||
"ERROR: Specify either 'command' or 'quiet'"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode x quiet y",
|
||||
"ERROR: Specify either '-command' or '-quiet'"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set newmode x -quiet y",
|
||||
"ERROR: Does not match any current feedback mode"),
|
||||
(a) -> assertCommandOutputStartsWith(a, "/set prompt",
|
||||
"ERROR: Expected a feedback mode"),
|
||||
|
||||
@ -76,7 +76,7 @@ public class ToolLocaleMessageTest extends ReplToolTesting {
|
||||
public void testSample() {
|
||||
try {
|
||||
testLocale(
|
||||
(a) -> assertCommandOK(a, "/set newmode test command normal", "test"),
|
||||
(a) -> assertCommandOK(a, "/set newmode test -command normal", "test"),
|
||||
(a) -> assertCommandOK(a, "/set format test errorpre 'ERROR: '"),
|
||||
(a) -> assertCommandOK(a, "/set feedback test", "test"),
|
||||
|
||||
@ -101,7 +101,7 @@ public class ToolLocaleMessageTest extends ReplToolTesting {
|
||||
public void testCommand() {
|
||||
try {
|
||||
testLocale(
|
||||
(a) -> assertCommandOK(a, "/set newmode test command normal", "test"),
|
||||
(a) -> assertCommandOK(a, "/set newmode test -command normal", "test"),
|
||||
(a) -> assertCommandOK(a, "/set format test errorpre 'ERROR: '"),
|
||||
(a) -> assertCommandOK(a, "/set feedback test", "test"),
|
||||
|
||||
@ -134,13 +134,13 @@ public class ToolLocaleMessageTest extends ReplToolTesting {
|
||||
|
||||
public void testHelp() {
|
||||
testLocale(
|
||||
(a) -> assertCommandOK(a, "/help", "/list", "/save", "/set", "[restore]"),
|
||||
(a) -> assertCommandOK(a, "/help /list", "start", "all"),
|
||||
(a) -> assertCommandOK(a, "/help", "/list", "/save", "/set", "[-restore]"),
|
||||
(a) -> assertCommandOK(a, "/help /list", "-start", "-all"),
|
||||
(a) -> assertCommandOK(a, "/help /edit", "/set editor"),
|
||||
(a) -> assertCommandOK(a, "/help /drop", "/drop"),
|
||||
(a) -> assertCommandOK(a, "/help /save", "all", "start"),
|
||||
(a) -> assertCommandOK(a, "/help /save", "-all", "-start"),
|
||||
(a) -> assertCommandOK(a, "/help /open", "/open"),
|
||||
(a) -> assertCommandOK(a, "/help /reload", "restore"),
|
||||
(a) -> assertCommandOK(a, "/help /reload", "-restore"),
|
||||
(a) -> assertCommandOK(a, "/help /help", "intro"),
|
||||
(a) -> assertCommandOK(a, "/help /set", "newmode"),
|
||||
(a) -> assertCommandOK(a, "/help /?", "intro"),
|
||||
@ -156,11 +156,11 @@ public class ToolLocaleMessageTest extends ReplToolTesting {
|
||||
public void testFeedbackError() {
|
||||
try {
|
||||
testLocale(
|
||||
(a) -> assertCommandOK(a, "/set newmode tee command foo", "foo"),
|
||||
(a) -> assertCommandOK(a, "/set newmode tee flurb", "command", "quiet"),
|
||||
(a) -> assertCommandOK(a, "/set newmode tee -command foo", "foo"),
|
||||
(a) -> assertCommandOK(a, "/set newmode tee flurb", "-command", "-quiet"),
|
||||
(a) -> assertCommandOK(a, "/set newmode te2", "te2"),
|
||||
(a) -> assertCommandOK(a, "/set newmode te2 command", "te2"),
|
||||
(a) -> assertCommandOK(a, "/set newmode te command normal", "te"),
|
||||
(a) -> assertCommandOK(a, "/set newmode te2 -command", "te2"),
|
||||
(a) -> assertCommandOK(a, "/set newmode te -command normal", "te"),
|
||||
(a) -> assertCommandOK(a, "/set format te errorpre 'ERROR: '"),
|
||||
(a) -> assertCommandOK(a, "/set feedback te"),
|
||||
|
||||
@ -182,7 +182,7 @@ public class ToolLocaleMessageTest extends ReplToolTesting {
|
||||
(a) -> assertCommandFail(a, "/set newmode"),
|
||||
(a) -> assertCommandFail(a, "/set newmode te"),
|
||||
(a) -> assertCommandFail(a, "/set newmode x xyz"),
|
||||
(a) -> assertCommandFail(a, "/set newmode x quiet y"),
|
||||
(a) -> assertCommandFail(a, "/set newmode x -quiet y"),
|
||||
(a) -> assertCommandFail(a, "/set prompt"),
|
||||
(a) -> assertCommandFail(a, "/set prompt te"),
|
||||
(a) -> assertCommandFail(a, "/set prompt te aaa xyz", "aaa"),
|
||||
|
||||
@ -107,7 +107,7 @@ public class ToolReloadTest extends ReplToolTesting {
|
||||
"-: /drop A\n"),
|
||||
a -> assertCommandCheckOutput(a, "/vars", assertVariables()),
|
||||
a -> assertCommandCheckOutput(a, "/methods", assertMethods()),
|
||||
a -> assertCommandCheckOutput(a, "/classes", assertClasses()),
|
||||
a -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
a -> assertCommandCheckOutput(a, "/imports", assertImports())
|
||||
);
|
||||
}
|
||||
@ -120,11 +120,11 @@ public class ToolReloadTest extends ReplToolTesting {
|
||||
a -> dropMethod(a, "/drop b", "b ()I", "| dropped method b()"),
|
||||
a -> assertClass(a, "class A {}", "class", "A"),
|
||||
a -> dropClass(a, "/dr A", "class A", "| dropped class A"),
|
||||
a -> assertCommand(a, "/reload quiet",
|
||||
a -> assertCommand(a, "/reload -quiet",
|
||||
"| Restarting and restoring state."),
|
||||
a -> assertCommandCheckOutput(a, "/vars", assertVariables()),
|
||||
a -> assertCommandCheckOutput(a, "/methods", assertMethods()),
|
||||
a -> assertCommandCheckOutput(a, "/classes", assertClasses()),
|
||||
a -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
a -> assertCommandCheckOutput(a, "/imports", assertImports())
|
||||
);
|
||||
}
|
||||
@ -169,7 +169,7 @@ public class ToolReloadTest extends ReplToolTesting {
|
||||
"(int)int", "m"),
|
||||
(a) -> evaluateExpression(a, "int", "m(x)", "25"),
|
||||
(a) -> assertCommand(a, "/reset", "| Resetting state."),
|
||||
(a) -> assertCommand(a, "/reload restore",
|
||||
(a) -> assertCommand(a, "/reload -restore",
|
||||
"| Restarting and restoring from previous state.\n" +
|
||||
"-: int x = 5;\n" +
|
||||
"-: int m(int z) { return z * z; }\n" +
|
||||
@ -188,8 +188,8 @@ public class ToolReloadTest extends ReplToolTesting {
|
||||
(a) -> evaluateExpression(a, "int", "m(x)", "25"),
|
||||
(a) -> assertCommand(a, "System.exit(1);",
|
||||
"| State engine terminated.\n" +
|
||||
"| Restore definitions with: /reload restore"),
|
||||
(a) -> assertCommand(a, "/reload restore",
|
||||
"| Restore definitions with: /reload -restore"),
|
||||
(a) -> assertCommand(a, "/reload -restore",
|
||||
"| Restarting and restoring from previous state.\n" +
|
||||
"-: int x = 5;\n" +
|
||||
"-: int m(int z) { return z * z; }\n" +
|
||||
@ -208,7 +208,7 @@ public class ToolReloadTest extends ReplToolTesting {
|
||||
(a) -> evaluateExpression(a, "int", "m(x)", "25")
|
||||
);
|
||||
test(false, new String[]{"-nostartup"},
|
||||
(a) -> assertCommand(a, "/reload restore",
|
||||
(a) -> assertCommand(a, "/reload -restore",
|
||||
"| Restarting and restoring from previous state.\n" +
|
||||
"-: int x = 5;\n" +
|
||||
"-: int m(int z) { return z * z; }\n" +
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8153716 8143955 8151754 8150382
|
||||
* @bug 8153716 8143955 8151754 8150382 8153920
|
||||
* @summary Simple jshell tool tests
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
@ -101,22 +101,22 @@ public class ToolSimpleTest extends ReplToolTesting {
|
||||
);
|
||||
}
|
||||
|
||||
public void defineClasses() {
|
||||
public void defineTypes() {
|
||||
test(
|
||||
(a) -> assertCommandCheckOutput(a, "/list", assertList()),
|
||||
(a) -> assertCommandCheckOutput(a, "/classes", assertClasses()),
|
||||
(a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
(a) -> assertClass(a, "class A { }", "class", "A"),
|
||||
(a) -> assertCommandCheckOutput(a, "/list", assertList()),
|
||||
(a) -> assertCommandCheckOutput(a, "/classes", assertClasses()),
|
||||
(a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
(a) -> assertClass(a, "interface A { }", "interface", "A"),
|
||||
(a) -> assertCommandCheckOutput(a, "/list", assertList()),
|
||||
(a) -> assertCommandCheckOutput(a, "/classes", assertClasses()),
|
||||
(a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
(a) -> assertClass(a, "enum A { }", "enum", "A"),
|
||||
(a) -> assertCommandCheckOutput(a, "/list", assertList()),
|
||||
(a) -> assertCommandCheckOutput(a, "/classes", assertClasses()),
|
||||
(a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
(a) -> assertClass(a, "@interface A { }", "@interface", "A"),
|
||||
(a) -> assertCommandCheckOutput(a, "/list", assertList()),
|
||||
(a) -> assertCommandCheckOutput(a, "/classes", assertClasses())
|
||||
(a) -> assertCommandCheckOutput(a, "/types", assertClasses())
|
||||
);
|
||||
}
|
||||
|
||||
@ -211,7 +211,7 @@ public class ToolSimpleTest extends ReplToolTesting {
|
||||
a -> dropImport(a, "/drop 4", "import java.util.stream.*", ""),
|
||||
a -> assertCommandCheckOutput(a, "/vars", assertVariables()),
|
||||
a -> assertCommandCheckOutput(a, "/methods", assertMethods()),
|
||||
a -> assertCommandCheckOutput(a, "/classes", assertClasses()),
|
||||
a -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
a -> assertCommandCheckOutput(a, "/imports", assertImports())
|
||||
);
|
||||
test(false, new String[]{"-nostartup"},
|
||||
@ -223,15 +223,15 @@ public class ToolSimpleTest extends ReplToolTesting {
|
||||
a -> dropClass(a, "/drop A", "class A", "| dropped class A"),
|
||||
a -> assertCommandCheckOutput(a, "/vars", assertVariables()),
|
||||
a -> assertCommandCheckOutput(a, "/methods", assertMethods()),
|
||||
a -> assertCommandCheckOutput(a, "/classes", assertClasses()),
|
||||
a -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
a -> assertCommandCheckOutput(a, "/imports", assertImports())
|
||||
);
|
||||
}
|
||||
|
||||
public void testDropNegative() {
|
||||
test(false, new String[]{"-nostartup"},
|
||||
a -> assertCommandOutputStartsWith(a, "/drop 0", "| No definition or id found named: 0"),
|
||||
a -> assertCommandOutputStartsWith(a, "/drop a", "| No definition or id found named: a"),
|
||||
a -> assertCommandOutputStartsWith(a, "/drop 0", "| No applicable definition or id found named: 0"),
|
||||
a -> assertCommandOutputStartsWith(a, "/drop a", "| No applicable definition or id found named: a"),
|
||||
a -> assertCommandCheckOutput(a, "/drop",
|
||||
assertStartsWith("| In the /drop argument, please specify an import, variable, method, or class to drop.")),
|
||||
a -> assertVariable(a, "int", "a"),
|
||||
@ -253,7 +253,7 @@ public class ToolSimpleTest extends ReplToolTesting {
|
||||
a -> assertCommandCheckOutput(a, "/drop a", check),
|
||||
a -> assertCommandCheckOutput(a, "/vars", assertVariables()),
|
||||
a -> assertCommandCheckOutput(a, "/methods", assertMethods()),
|
||||
a -> assertCommandCheckOutput(a, "/classes", assertClasses()),
|
||||
a -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
a -> assertCommandCheckOutput(a, "/imports", assertImports())
|
||||
);
|
||||
test(
|
||||
@ -284,7 +284,7 @@ public class ToolSimpleTest extends ReplToolTesting {
|
||||
(a) -> assertHelp(a, "/?", "/list", "/help", "/exit", "intro"),
|
||||
(a) -> assertHelp(a, "/help", "/list", "/help", "/exit", "intro"),
|
||||
(a) -> assertHelp(a, "/help short", "shortcuts", "<tab>"),
|
||||
(a) -> assertHelp(a, "/? /li", "/list all", "snippets"),
|
||||
(a) -> assertHelp(a, "/? /li", "/list -all", "snippets"),
|
||||
(a) -> assertHelp(a, "/help /help", "/help <command>")
|
||||
);
|
||||
}
|
||||
@ -299,7 +299,10 @@ public class ToolSimpleTest extends ReplToolTesting {
|
||||
|
||||
// Check that each line of output contains the corresponding string from the list
|
||||
private void checkLineToList(String in, List<String> match) {
|
||||
String[] res = in.trim().split("\n");
|
||||
String trimmed = in.trim();
|
||||
String[] res = trimmed.isEmpty()
|
||||
? new String[0]
|
||||
: trimmed.split("\n");
|
||||
assertEquals(res.length, match.size(), "Got: " + Arrays.asList(res));
|
||||
for (int i = 0; i < match.size(); ++i) {
|
||||
assertTrue(res[i].contains(match.get(i)));
|
||||
@ -311,23 +314,131 @@ public class ToolSimpleTest extends ReplToolTesting {
|
||||
List<String> startVarList = new ArrayList<>(START_UP);
|
||||
startVarList.add("int aardvark");
|
||||
test(
|
||||
a -> assertCommandCheckOutput(a, "/list all",
|
||||
a -> assertCommandCheckOutput(a, "/list -all",
|
||||
s -> checkLineToList(s, START_UP)),
|
||||
a -> assertCommandOutputStartsWith(a, "/list " + arg,
|
||||
"| No definition or id found named: " + arg),
|
||||
"| No applicable definition or id found named: " + arg),
|
||||
a -> assertVariable(a, "int", "aardvark"),
|
||||
a -> assertCommandOutputContains(a, "/list aardvark", "aardvark"),
|
||||
a -> assertCommandCheckOutput(a, "/list start",
|
||||
a -> assertCommandCheckOutput(a, "/list -start",
|
||||
s -> checkLineToList(s, START_UP)),
|
||||
a -> assertCommandCheckOutput(a, "/list all",
|
||||
a -> assertCommandCheckOutput(a, "/list -all",
|
||||
s -> checkLineToList(s, startVarList)),
|
||||
a -> assertCommandCheckOutput(a, "/list printf",
|
||||
s -> assertTrue(s.contains("void printf"))),
|
||||
a -> assertCommandOutputStartsWith(a, "/list " + arg,
|
||||
"| No definition or id found named: " + arg)
|
||||
"| No applicable definition or id found named: " + arg)
|
||||
);
|
||||
}
|
||||
|
||||
public void testVarsArgs() {
|
||||
String arg = "qqqq";
|
||||
List<String> startVarList = new ArrayList<>();
|
||||
test(
|
||||
a -> assertCommandCheckOutput(a, "/vars -all",
|
||||
s -> checkLineToList(s, startVarList)),
|
||||
a -> assertCommandOutputStartsWith(a, "/vars " + arg,
|
||||
"| No applicable definition or id found named: " + arg),
|
||||
a -> assertVariable(a, "int", "aardvark"),
|
||||
a -> assertMethod(a, "int f() { return 0; }", "()int", "f"),
|
||||
a -> assertVariable(a, "int", "a"),
|
||||
a -> assertVariable(a, "double", "a", "1", "1.0"),
|
||||
a -> assertCommandOutputStartsWith(a, "/vars aardvark", "| int aardvark = 0"),
|
||||
a -> assertCommandCheckOutput(a, "/vars -start",
|
||||
s -> checkLineToList(s, startVarList)),
|
||||
a -> assertCommandOutputStartsWith(a, "/vars -all",
|
||||
"| int aardvark = 0\n| int a = "),
|
||||
a -> assertCommandOutputStartsWith(a, "/vars printf",
|
||||
"| No applicable definition or id found named: printf"),
|
||||
a -> assertCommandOutputStartsWith(a, "/vars " + arg,
|
||||
"| No applicable definition or id found named: " + arg)
|
||||
);
|
||||
}
|
||||
|
||||
public void testMethodsArgs() {
|
||||
String arg = "qqqq";
|
||||
List<String> startMethodList = new ArrayList<>(START_UP_CMD_METHOD);
|
||||
test(
|
||||
a -> assertCommandCheckOutput(a, "/methods -all",
|
||||
s -> checkLineToList(s, startMethodList)),
|
||||
a -> assertCommandCheckOutput(a, "/methods -start",
|
||||
s -> checkLineToList(s, startMethodList)),
|
||||
a -> assertCommandCheckOutput(a, "/methods printf",
|
||||
s -> checkLineToList(s, startMethodList)),
|
||||
a -> assertCommandCheckOutput(a, "/methods",
|
||||
s -> checkLineToList(s, startMethodList)),
|
||||
a -> assertCommandOutputStartsWith(a, "/methods " + arg,
|
||||
"| No applicable definition or id found named: " + arg),
|
||||
a -> assertMethod(a, "int f() { return 0; }", "()int", "f"),
|
||||
a -> assertVariable(a, "int", "aardvark"),
|
||||
a -> assertMethod(a, "void f(int a) { g(); }", "(int)void", "f"),
|
||||
a -> assertMethod(a, "void g() {}", "()void", "g"),
|
||||
a -> assertCommandOutputStartsWith(a, "/methods " + arg,
|
||||
"| No applicable definition or id found named: " + arg),
|
||||
a -> assertCommandOutputStartsWith(a, "/methods aardvark",
|
||||
"| No applicable definition or id found named: aardvark"),
|
||||
a -> assertCommandCheckOutput(a, "/methods -start",
|
||||
s -> checkLineToList(s, startMethodList)),
|
||||
a -> assertCommandCheckOutput(a, "/methods printf",
|
||||
s -> checkLineToList(s, startMethodList)),
|
||||
a -> assertCommandOutputStartsWith(a, "/methods g",
|
||||
"| g ()void"),
|
||||
a -> assertCommandOutputStartsWith(a, "/methods f",
|
||||
"| f ()int\n" +
|
||||
"| f (int)void")
|
||||
);
|
||||
}
|
||||
|
||||
public void testTypesArgs() {
|
||||
String arg = "qqqq";
|
||||
List<String> startTypeList = new ArrayList<>();
|
||||
test(
|
||||
a -> assertCommandCheckOutput(a, "/types -all",
|
||||
s -> checkLineToList(s, startTypeList)),
|
||||
a -> assertCommandCheckOutput(a, "/types -start",
|
||||
s -> checkLineToList(s, startTypeList)),
|
||||
a -> assertCommandOutputStartsWith(a, "/types " + arg,
|
||||
"| No applicable definition or id found named: " + arg),
|
||||
a -> assertVariable(a, "int", "aardvark"),
|
||||
(a) -> assertClass(a, "class A { }", "class", "A"),
|
||||
(a) -> assertClass(a, "interface A { }", "interface", "A"),
|
||||
a -> assertCommandOutputStartsWith(a, "/types -all",
|
||||
"| class A\n" +
|
||||
"| interface A"),
|
||||
(a) -> assertClass(a, "enum E { }", "enum", "E"),
|
||||
(a) -> assertClass(a, "@interface B { }", "@interface", "B"),
|
||||
a -> assertCommandOutputStartsWith(a, "/types aardvark",
|
||||
"| No applicable definition or id found named: aardvark"),
|
||||
a -> assertCommandOutputStartsWith(a, "/types A",
|
||||
"| interface A"),
|
||||
a -> assertCommandOutputStartsWith(a, "/types E",
|
||||
"| enum E"),
|
||||
a -> assertCommandOutputStartsWith(a, "/types B",
|
||||
"| @interface B"),
|
||||
a -> assertCommandOutputStartsWith(a, "/types " + arg,
|
||||
"| No applicable definition or id found named: " + arg),
|
||||
a -> assertCommandCheckOutput(a, "/types -start",
|
||||
s -> checkLineToList(s, startTypeList))
|
||||
);
|
||||
}
|
||||
public void defineClasses() {
|
||||
test(
|
||||
(a) -> assertCommandCheckOutput(a, "/list", assertList()),
|
||||
(a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
(a) -> assertClass(a, "class A { }", "class", "A"),
|
||||
(a) -> assertCommandCheckOutput(a, "/list", assertList()),
|
||||
(a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
(a) -> assertClass(a, "interface A { }", "interface", "A"),
|
||||
(a) -> assertCommandCheckOutput(a, "/list", assertList()),
|
||||
(a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
(a) -> assertClass(a, "enum A { }", "enum", "A"),
|
||||
(a) -> assertCommandCheckOutput(a, "/list", assertList()),
|
||||
(a) -> assertCommandCheckOutput(a, "/types", assertClasses()),
|
||||
(a) -> assertClass(a, "@interface A { }", "@interface", "A"),
|
||||
(a) -> assertCommandCheckOutput(a, "/list", assertList()),
|
||||
(a) -> assertCommandCheckOutput(a, "/types", assertClasses())
|
||||
);
|
||||
}
|
||||
public void testCommandPrefix() {
|
||||
test(a -> assertCommandCheckOutput(a, "/s",
|
||||
assertStartsWith("| Command: '/s' is ambiguous: /save, /set")),
|
||||
|
||||
@ -50,6 +50,17 @@ import javax.tools.ToolProvider;
|
||||
|
||||
import com.sun.tools.javac.util.Assert;
|
||||
|
||||
/**
|
||||
* This is a test to verify specific coding standards for source code in the langtools repository.
|
||||
*
|
||||
* As such, it is not a standard unit, regression or functional test, and will
|
||||
* automatically skip if the langtools source code is not available.
|
||||
*
|
||||
* If the source code is available, it will find and compile the coding
|
||||
* style analyzers found in langtools/make/tools/crules/*.java, and run the resulting
|
||||
* code on all source files under langtools/src/share/classes. Any coding style
|
||||
* violations will cause the test to fail.
|
||||
*/
|
||||
public class RunCodingRules {
|
||||
public static void main(String... args) throws Exception {
|
||||
new RunCodingRules().run();
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* @test /nodynamiccopyright/
|
||||
* @bug 8156180
|
||||
* @summary javac accepts code that violates JLS chapter 16
|
||||
*
|
||||
* @compile/fail/ref=T8156180.out -XDrawDiagnostics T8156180.java
|
||||
*/
|
||||
|
||||
class T8156180 {
|
||||
public final int a1, b1, c1, d1;
|
||||
public int a2, b2, c2, d2;
|
||||
|
||||
T8156180(int value) {
|
||||
a2 = this.a1;
|
||||
b2 = (this).b1;
|
||||
c2 = ((this)).c1;
|
||||
d2 = (((this))).d1;
|
||||
a1 = value;
|
||||
b1 = value;
|
||||
c1 = value;
|
||||
d1 = value;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
T8156180.java:14:18: compiler.err.var.might.not.have.been.initialized: a1
|
||||
T8156180.java:15:20: compiler.err.var.might.not.have.been.initialized: b1
|
||||
T8156180.java:16:22: compiler.err.var.might.not.have.been.initialized: c1
|
||||
T8156180.java:17:24: compiler.err.var.might.not.have.been.initialized: d1
|
||||
4 errors
|
||||
@ -1,5 +1,5 @@
|
||||
T8012003b.java:30:12: compiler.err.prob.found.req: (compiler.misc.invalid.mref: kindname.method, (compiler.misc.cant.apply.symbol: kindname.method, g, java.lang.String, compiler.misc.no.args, kindname.class, T8012003b, (compiler.misc.arg.length.mismatch)))
|
||||
T8012003b.java:31:16: compiler.err.prob.found.req: (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.inconvertible.types: int, void))
|
||||
T8012003b.java:31:16: compiler.err.prob.found.req: (compiler.misc.stat.expr.expected)
|
||||
T8012003b.java:32:22: compiler.err.prob.found.req: (compiler.misc.incompatible.ret.type.in.lambda: (compiler.misc.conditional.target.cant.be.void))
|
||||
T8012003b.java:33:12: compiler.err.prob.found.req: (compiler.misc.invalid.mref: kindname.method, (compiler.misc.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Integer, java.lang.String)))
|
||||
T8012003b.java:34:12: compiler.err.prob.found.req: (compiler.misc.incompatible.ret.type.in.mref: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer))
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,7 +25,7 @@
|
||||
|
||||
#
|
||||
# @test
|
||||
# @bug 4758537 4809833
|
||||
# @bug 4758537 4809833 8149599
|
||||
# @summary Test that javac and java find files in similar ways
|
||||
# @author Martin Buchholz
|
||||
#
|
||||
@ -49,14 +49,33 @@
|
||||
# - Only for the user classpath is an empty string value equivalent to "."
|
||||
# - Specifying a bootclasspath on the command line obliterates any
|
||||
# previous -Xbootclasspath/p: or -Xbootclasspath/a: command line flags.
|
||||
#
|
||||
# JDK 9 update:
|
||||
# java: The java launcher does not support any of the following:
|
||||
# * -Xbootclasspath/p: -Xbootclasspath:
|
||||
# * -endorseddirs -Djava.endorsed.dirs
|
||||
# * -extdirs -Djava.ext.dirs
|
||||
# All test cases exercising these features have been removed.
|
||||
# javac: The following features are only supported when compiling
|
||||
# for older releases:
|
||||
# * -Xbootclasspath/p: -Xbootclasspath: -bootclasspath -Xbootclasspath/a:
|
||||
# * -endorseddirs -Djava.endorsed.dirs
|
||||
# * -extdirs -Djava.ext.dirs
|
||||
# All test cases exercising these features have been modified to
|
||||
# use -source 8 -target 8. In addition, javac test cases involving
|
||||
# use of the runtime properties java.endorsed.dirs and java.extdirs
|
||||
# (by means of -J-Dname=value) have been removed.
|
||||
# Although the primary purpose of the test cases in this file is to
|
||||
# compare javac and java behavior, some tests remain for javac for
|
||||
# which there is no java equivalent. However, the cases remain as useful
|
||||
# test cases for javac handling of the paths involved.
|
||||
|
||||
#----------------------------------------------------------------
|
||||
|
||||
. ${TESTSRC-.}/Util.sh
|
||||
|
||||
set -u
|
||||
|
||||
# BCP=`DefaultBootClassPath`
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Prepare the "Minefield"
|
||||
#----------------------------------------------------------------
|
||||
@ -64,6 +83,7 @@ Cleanup() {
|
||||
Sys rm -rf GooSrc GooJar GooZip GooClass
|
||||
Sys rm -rf BadSrc BadJar BadZip BadClass
|
||||
Sys rm -rf OneDir *.class Main.java MANIFEST.MF
|
||||
Sys rm -f java-lang.jar
|
||||
}
|
||||
|
||||
Cleanup
|
||||
@ -88,215 +108,95 @@ CheckFiles BadZip/Lib.zip BadJar/Lib.jar BadSrc/Lib.java
|
||||
|
||||
echo 'public class Main {public static void main(String[] a) {Lib.f();}}' > Main.java
|
||||
|
||||
# Create a jar file that is good enough to put on the javac boot class path (i.e. contains java.lang.**)
|
||||
Sys "$jimage" extract --dir modules ${TESTJAVA}/lib/modules
|
||||
Sys "$jar" cf java-lang.jar -C modules/java.base java/lang
|
||||
Sys rm -rf modules
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Verify that javac class search order is the same as java's
|
||||
#----------------------------------------------------------------
|
||||
# Failure "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -Xbootclasspath/p:"BadClass" \
|
||||
# -J-Djava.endorsed.dirs="GooJar" \
|
||||
# Main.java
|
||||
# Success "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -Xbootclasspath/p:"GooClass${PS}BadJar/Lib.jar" \
|
||||
# -J-Djava.endorsed.dirs="BadJar${PS}GooZip" \
|
||||
# Main.java
|
||||
# Failure "$java" ${TESTVMOPTS} \
|
||||
# -Xbootclasspath/p:"BadClass" \
|
||||
# -Djava.endorsed.dirs="GooJar" \
|
||||
# Main
|
||||
# Success "$java" ${TESTVMOPTS} \
|
||||
# -Xbootclasspath/p:"GooClass${PS}BadJar/Lib.jar" \
|
||||
# -Djava.endorsed.dirs="BadJar${PS}GooZip" \
|
||||
# Main
|
||||
Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-Xbootclasspath/p:"GooClass" \
|
||||
-bootclasspath "java-lang.jar${PS}BadZip/Lib.zip" \
|
||||
Main.java
|
||||
Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-Xbootclasspath/p:"BadClass${PS}GooClass" \
|
||||
-bootclasspath "java-lang.jar${PS}GooZip/Lib.zip${PS}BadClass" \
|
||||
Main.java
|
||||
Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-Xbootclasspath/p:"BadJar/Lib.jar" \
|
||||
-Xbootclasspath:"java-lang.jar${PS}GooClass" \
|
||||
Main.java
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Failure "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -Xbootclasspath/p:"GooClass" \
|
||||
# -bootclasspath "$BCP${PS}BadZip/Lib.zip" \
|
||||
# Main.java
|
||||
# Success "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -Xbootclasspath/p:"BadClass${PS}GooClass" \
|
||||
# -bootclasspath "$BCP${PS}GooZip/Lib.zip${PS}BadClass" \
|
||||
# Main.java
|
||||
# Success "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -Xbootclasspath/p:"BadJar/Lib.jar" \
|
||||
# -Xbootclasspath:"$BCP${PS}GooClass" \
|
||||
# Main.java
|
||||
# Failure "$java" ${TESTVMOPTS} \
|
||||
# -Xbootclasspath/p:"GooClass" \
|
||||
# -Xbootclasspath:"$BCP${PS}BadZip/Lib.zip" \
|
||||
# Main
|
||||
# Success "$java" ${TESTVMOPTS} \
|
||||
# -Xbootclasspath/p:"BadClass${PS}GooClass" \
|
||||
# -Xbootclasspath:"$BCP${PS}GooZip/Lib.zip${PS}BadClass" \
|
||||
# Main
|
||||
Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-bootclasspath "java-lang.jar${PS}GooZip/Lib.zip" \
|
||||
-Xbootclasspath/p:"BadClass" \
|
||||
Main.java
|
||||
Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-bootclasspath "java-lang.jar${PS}BadZip/Lib.zip" \
|
||||
-Xbootclasspath/p:"GooClass${PS}BadJar/Lib.jar" \
|
||||
Main.java
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Failure "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -bootclasspath "$BCP${PS}GooZip/Lib.zip" \
|
||||
# -Xbootclasspath/p:"BadClass" \
|
||||
# Main.java
|
||||
# Success "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -bootclasspath "$BCP${PS}BadZip/Lib.zip" \
|
||||
# -Xbootclasspath/p:"GooClass${PS}BadJar/Lib.jar" \
|
||||
# Main.java
|
||||
# Failure "$java" ${TESTVMOPTS} \
|
||||
# -Xbootclasspath:"$BCP${PS}GooClass" \
|
||||
# -Xbootclasspath/p:"BadClass" \
|
||||
# Main
|
||||
# Success "$java" ${TESTVMOPTS} \
|
||||
# -Xbootclasspath:"$BCP${PS}BadClass" \
|
||||
# -Xbootclasspath/p:"GooClass${PS}BadJar/Lib.jar" \
|
||||
# Main
|
||||
|
||||
#----------------------------------------------------------------
|
||||
Failure "$javac" ${TESTTOOLVMOPTS} \
|
||||
Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-Xbootclasspath/p:"BadClass" \
|
||||
-Xbootclasspath/a:"GooClass" \
|
||||
Main.java
|
||||
Success "$javac" ${TESTTOOLVMOPTS} \
|
||||
Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-Xbootclasspath/p:"GooClass${PS}BadClass" \
|
||||
-Xbootclasspath/a:"BadClass" \
|
||||
Main.java
|
||||
Failure "$java" ${TESTVMOPTS} \
|
||||
-Xbootclasspath/p:"BadClass" \
|
||||
-Xbootclasspath/a:"GooClass" \
|
||||
Main
|
||||
Success "$java" ${TESTVMOPTS} \
|
||||
-Xbootclasspath/p:"GooClass${PS}BadClass" \
|
||||
-Xbootclasspath/a:"BadClass" \
|
||||
-Xbootclasspath/a:"GooClass" \
|
||||
Main
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Failure "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -Xbootclasspath/p:"GooClass" \
|
||||
# -J-Djava.endorsed.dirs="BadZip" \
|
||||
# -bootclasspath "GooClass${PS}$BCP" \
|
||||
# Main.java
|
||||
# Success "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -Xbootclasspath/p:"BadClass" \
|
||||
# -J-Djava.endorsed.dirs="BadClass${PS}GooZip${PS}BadJar" \
|
||||
# -bootclasspath "BadClass${PS}$BCP" \
|
||||
# Main.java
|
||||
# Failure "$java" ${TESTVMOPTS} \
|
||||
# -Xbootclasspath/p:"GooClass" \
|
||||
# -Djava.endorsed.dirs="BadZip" \
|
||||
# -Xbootclasspath:"GooClass${PS}$BCP" \
|
||||
# Main
|
||||
# Success "$java" ${TESTVMOPTS} \
|
||||
# -Xbootclasspath/p:"BadClass" \
|
||||
# -Djava.endorsed.dirs="BadClass${PS}GooZip${PS}BadJar" \
|
||||
# -Xbootclasspath:"BadClass${PS}$BCP" \
|
||||
# Main
|
||||
Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-Xbootclasspath/p:"GooClass" \
|
||||
-Xbootclasspath:"BadClass${PS}java-lang.jar" \
|
||||
-Xbootclasspath/a:"GooClass" \
|
||||
Main.java
|
||||
Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-Xbootclasspath/p:"BadClass" \
|
||||
-Xbootclasspath:"GooClass${PS}BadClass${PS}java-lang.jar" \
|
||||
-Xbootclasspath/a:"BadClass" \
|
||||
Main.java
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Failure "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -Xbootclasspath/p:"GooClass" \
|
||||
# -Xbootclasspath:"BadClass${PS}$BCP" \
|
||||
# -Xbootclasspath/a:"GooClass" \
|
||||
# Main.java
|
||||
# Success "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -Xbootclasspath/p:"BadClass" \
|
||||
# -Xbootclasspath:"GooClass${PS}BadClass${PS}$BCP" \
|
||||
# -Xbootclasspath/a:"BadClass" \
|
||||
# Main.java
|
||||
# Failure "$java" ${TESTVMOPTS} \
|
||||
# -Xbootclasspath/p:"GooClass" \
|
||||
# -Xbootclasspath:"BadClass${PS}$BCP" \
|
||||
# -Xbootclasspath/a:"GooClass" \
|
||||
# Main
|
||||
# Success "$java" ${TESTVMOPTS} \
|
||||
# -Xbootclasspath/p:"BadClass" \
|
||||
# -Xbootclasspath:"GooClass${PS}BadClass${PS}$BCP" \
|
||||
# -Xbootclasspath/a:"BadClass" \
|
||||
# Main
|
||||
Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-endorseddirs "BadClass${PS}GooZip${PS}BadJar" \
|
||||
-Xbootclasspath:"BadClass${PS}java-lang.jar" \
|
||||
Main.java
|
||||
Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-Djava.endorsed.dirs="BadClass${PS}GooZip${PS}BadJar" \
|
||||
-Xbootclasspath:"BadClass${PS}java-lang.jar" \
|
||||
Main.java
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Failure "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -J-Djava.endorsed.dirs="BadZip" \
|
||||
# -Xbootclasspath:"GooClass${PS}$BCP" \
|
||||
# Main.java
|
||||
# Success "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -endorseddirs "BadClass${PS}GooZip${PS}BadJar" \
|
||||
# -Xbootclasspath:"BadClass${PS}$BCP" \
|
||||
# Main.java
|
||||
# Success "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -Djava.endorsed.dirs="BadClass${PS}GooZip${PS}BadJar" \
|
||||
# -Xbootclasspath:"BadClass${PS}$BCP" \
|
||||
# Main.java
|
||||
# Success "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -J-Djava.endorsed.dirs="BadClass${PS}GooZip${PS}BadJar" \
|
||||
# -Xbootclasspath:"BadClass${PS}$BCP" \
|
||||
# Main.java
|
||||
# Failure "$java" ${TESTVMOPTS} \
|
||||
# -Djava.endorsed.dirs="BadZip" \
|
||||
# -Xbootclasspath:"GooClass${PS}$BCP" \
|
||||
# Main
|
||||
# Success "$java" ${TESTVMOPTS} \
|
||||
# -Djava.endorsed.dirs="BadClass${PS}GooZip${PS}BadJar" \
|
||||
# -Xbootclasspath:"BadClass${PS}$BCP" \
|
||||
# Main
|
||||
|
||||
#----------------------------------------------------------------
|
||||
Failure "$javac" ${TESTTOOLVMOPTS} \
|
||||
Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-Xbootclasspath/a:"BadClass" \
|
||||
-extdirs "GooZip" \
|
||||
Main.java
|
||||
Success "$javac" ${TESTTOOLVMOPTS} \
|
||||
Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-Xbootclasspath/a:"GooClass${PS}BadClass" \
|
||||
-extdirs "BadZip" \
|
||||
Main.java
|
||||
# Failure "$java" ${TESTVMOPTS} \
|
||||
# -Xbootclasspath/a:"BadClass" \
|
||||
# -Djava.ext.dirs="GooZip" \
|
||||
# Main
|
||||
# Success "$java" ${TESTVMOPTS} \
|
||||
# -Xbootclasspath/a:"GooClass${PS}BadClass" \
|
||||
# -Djava.ext.dirs="BadZip" \
|
||||
# Main
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Failure "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -bootclasspath "$BCP${PS}BadJar/Lib.jar" \
|
||||
# -J-Djava.ext.dir="GooJar" \
|
||||
# Main.java
|
||||
# Success "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -bootclasspath "$BCP${PS}GooJar/Lib.jar${PS}BadClass" \
|
||||
# -J-Djava.ext.dir="BadJar" \
|
||||
# Main.java
|
||||
# Failure "$java" ${TESTVMOPTS} \
|
||||
# -Xbootclasspath:"$BCP${PS}BadJar/Lib.jar" \
|
||||
# -Djava.ext.dirs="GooJar" \
|
||||
# Main
|
||||
# Success "$java" ${TESTVMOPTS} \
|
||||
# -Xbootclasspath:"$BCP${PS}GooJar/Lib.jar${PS}BadClass" \
|
||||
# -Djava.ext.dirs="BadJar" \
|
||||
# Main
|
||||
|
||||
#----------------------------------------------------------------
|
||||
Failure "$javac" ${TESTTOOLVMOPTS} \
|
||||
Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-extdirs "GooClass${PS}BadZip" \
|
||||
-cp "GooZip/Lib.zip" \
|
||||
Main.java
|
||||
Success "$javac" ${TESTTOOLVMOPTS} \
|
||||
Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-extdirs "BadClass${PS}GooZip${PS}BadJar" \
|
||||
-cp "BadZip/Lib.zip" \
|
||||
Main.java
|
||||
Success "$javac" ${TESTTOOLVMOPTS} \
|
||||
Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} \
|
||||
-Djava.ext.dirs="GooZip${PS}BadJar" \
|
||||
-classpath "BadZip/Lib.zip" \
|
||||
Main.java
|
||||
# Success "$javac" ${TESTTOOLVMOPTS} \
|
||||
# -J-Djava.ext.dirs="GooZip${PS}BadJar" \
|
||||
# -classpath "BadZip/Lib.zip" \
|
||||
# Main.java
|
||||
# Failure "$java" ${TESTVMOPTS} \
|
||||
# -Djava.ext.dirs="GooClass${PS}BadZip" \
|
||||
# -cp "GooZip/Lib.zip${PS}." \
|
||||
# Main
|
||||
# Success "$java" ${TESTVMOPTS} \
|
||||
# -Djava.ext.dirs="GooZip${PS}BadJar" \
|
||||
# -cp "BadZip/Lib.zip${PS}." \
|
||||
# Main
|
||||
|
||||
#----------------------------------------------------------------
|
||||
Failure "$javac" ${TESTTOOLVMOPTS} -classpath "BadClass${PS}GooClass" Main.java
|
||||
@ -338,45 +238,32 @@ In GooClass Success "$javac" ${TESTTOOLVMOPTS} -cp "..${PS}${PS}/xyzzy" ../Main.
|
||||
In GooClass Success "$java" ${TESTVMOPTS} -cp "..${PS}${PS}/xyzzy" Main
|
||||
|
||||
# All other empty path elements are ignored.
|
||||
In GooJar Failure "$javac" ${TESTTOOLVMOPTS} -extdirs "" -cp ".." ../Main.java
|
||||
# In GooJar Failure "$java" ${TESTVMOPTS} -Djava.ext.dirs="" -cp ".." Main
|
||||
In GooJar Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -extdirs "" -cp ".." ../Main.java
|
||||
|
||||
In GooJar Failure "$javac" ${TESTTOOLVMOPTS} -extdirs "${PS}" -cp ".." ../Main.java
|
||||
In GooJar Failure "$javac" ${TESTTOOLVMOPTS} -Djava.ext.dirs="${PS}" -cp ".." ../Main.java
|
||||
# In GooJar Failure "$java" ${TESTVMOPTS} -Djava.ext.dirs="${PS}" -cp ".." Main
|
||||
In GooJar Failure "$javac" -source 8 -targt 8 ${TESTTOOLVMOPTS} -extdirs "${PS}" -cp ".." ../Main.java
|
||||
In GooJar Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Djava.ext.dirs="${PS}" -cp ".." ../Main.java
|
||||
|
||||
In GooJar Success "$javac" ${TESTTOOLVMOPTS} -extdirs "." -cp ".." ../Main.java
|
||||
In GooJar Success "$javac" ${TESTTOOLVMOPTS} -Djava.ext.dirs="." -cp ".." ../Main.java
|
||||
# In GooJar Success "$java" ${TESTVMOPTS} -Djava.ext.dirs="." -cp ".." Main
|
||||
In GooJar Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -extdirs "." -cp ".." ../Main.java
|
||||
In GooJar Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Djava.ext.dirs="." -cp ".." ../Main.java
|
||||
|
||||
# In GooJar Failure "$javac" ${TESTTOOLVMOPTS} -J-Djava.endorsed.dirs="" -cp ".." ../Main.java
|
||||
In GooJar Failure "$javac" ${TESTTOOLVMOPTS} -Djava.endorsed.dirs="" -cp ".." ../Main.java
|
||||
# In GooJar Failure "$java" ${TESTVMOPTS} -Djava.endorsed.dirs="" -cp ".." Main
|
||||
In GooJar Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Djava.endorsed.dirs="" -cp ".." ../Main.java
|
||||
|
||||
# In GooJar Failure "$javac" ${TESTTOOLVMOPTS} -J-Djava.endorsed.dirs="${PS}" -cp ".." ../Main.java
|
||||
In GooJar Failure "$javac" ${TESTTOOLVMOPTS} -endorseddirs "${PS}" -cp ".." ../Main.java
|
||||
# In GooJar Failure "$java" ${TESTVMOPTS} -Djava.endorsed.dirs="${PS}" -cp ".." Main
|
||||
In GooJar Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -endorseddirs "${PS}" -cp ".." ../Main.java
|
||||
|
||||
# In GooJar Success "$javac" ${TESTTOOLVMOPTS} -J-Djava.endorsed.dirs="." -cp ".." ../Main.java
|
||||
In GooJar Success "$javac" ${TESTTOOLVMOPTS} -Djava.endorsed.dirs="." -cp ".." ../Main.java
|
||||
# In GooJar Success "$java" ${TESTVMOPTS} -Djava.endorsed.dirs="." -cp ".." Main
|
||||
In GooJar Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Djava.endorsed.dirs="." -cp ".." ../Main.java
|
||||
|
||||
In GooClass Failure "$javac" ${TESTTOOLVMOPTS} -Xbootclasspath/p: -cp ".." ../Main.java
|
||||
In GooClass Failure "$java" ${TESTVMOPTS} -Xbootclasspath/p: -cp ".." Main
|
||||
In GooClass Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Xbootclasspath/p: -cp ".." ../Main.java
|
||||
|
||||
In GooClass Success "$javac" ${TESTTOOLVMOPTS} -Xbootclasspath/p:. -cp ".." ../Main.java
|
||||
In GooClass Success "$java" ${TESTVMOPTS} -Xbootclasspath/p:. -cp ".." Main
|
||||
In GooClass Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Xbootclasspath/p:. -cp ".." ../Main.java
|
||||
|
||||
# In GooClass Failure "$javac" ${TESTTOOLVMOPTS} -Xbootclasspath:"$BCP" -cp ".." ../Main.java
|
||||
# In GooClass Failure "$java" ${TESTVMOPTS} -Xbootclasspath:"$BCP" -cp ".." Main
|
||||
In GooClass Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Xbootclasspath:"../java-lang.jar" -cp ".." ../Main.java
|
||||
|
||||
# In GooClass Success "$javac" ${TESTTOOLVMOPTS} -Xbootclasspath:"$BCP${PS}." -cp ".." ../Main.java
|
||||
# In GooClass Success "$java" ${TESTVMOPTS} -Xbootclasspath:"$BCP${PS}." -cp ".." Main
|
||||
In GooClass Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Xbootclasspath:"../java-lang.jar${PS}." -cp ".." ../Main.java
|
||||
|
||||
In GooClass Failure "$javac" ${TESTTOOLVMOPTS} -Xbootclasspath/a: -cp ".." ../Main.java
|
||||
In GooClass Failure "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Xbootclasspath/a: -cp ".." ../Main.java
|
||||
In GooClass Failure "$java" ${TESTVMOPTS} -Xbootclasspath/a: -cp ".." Main
|
||||
|
||||
In GooClass Success "$javac" ${TESTTOOLVMOPTS} -Xbootclasspath/a:. -cp ".." ../Main.java
|
||||
In GooClass Success "$javac" -source 8 -target 8 ${TESTTOOLVMOPTS} -Xbootclasspath/a:. -cp ".." ../Main.java
|
||||
In GooClass Success "$java" ${TESTVMOPTS} -Xbootclasspath/a:. -cp ".." Main
|
||||
|
||||
Cleanup
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2003, 2016, 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
|
||||
@ -24,9 +24,10 @@
|
||||
# Utilities for shell tests
|
||||
|
||||
: ${TESTSRC=.} ${TESTCLASSES=.}
|
||||
java="${TESTJAVA+${TESTJAVA}/bin/}java"
|
||||
javac="${TESTJAVA+${TESTJAVA}/bin/}javac"
|
||||
jar="${TESTJAVA+${TESTJAVA}/bin/}jar"
|
||||
java="${TESTJAVA+${TESTJAVA}/bin/}java"
|
||||
javac="${TESTJAVA+${TESTJAVA}/bin/}javac"
|
||||
jar="${TESTJAVA+${TESTJAVA}/bin/}jar"
|
||||
jimage="${TESTJAVA+${TESTJAVA}/bin/}jimage"
|
||||
|
||||
case `uname -s` in
|
||||
Windows*|CYGWIN*)
|
||||
@ -105,21 +106,6 @@ BadJarFile() {
|
||||
for jarfilename in "$@"; do pwd > "$jarfilename"; done
|
||||
}
|
||||
|
||||
# #----------------------------------------------------------------
|
||||
# # Usage: BCP=`DefaultBootClassPath`
|
||||
# # Returns default bootclasspath, discarding non-existent entries
|
||||
# #----------------------------------------------------------------
|
||||
# DefaultBootClassPath() {
|
||||
# echo 'public class B {public static void main(String[] a) {
|
||||
# System.out.println(System.getProperty("sun.boot.class.path"));}}' > B.java
|
||||
# "$javac" ${TESTTOOLVMOPTS} B.java
|
||||
# _BCP_=""
|
||||
# for elt in `"$java" ${TESTVMOPTS} B | tr "${PS}" " "`; do
|
||||
# test -r "$elt" -a -n "$elt" && _BCP_="${_BCP_:+${_BCP_}${PS}}${elt}"
|
||||
# done
|
||||
# rm -f B.java B.class
|
||||
# printf "%s" "$_BCP_" # Don't use echo -- unsafe on Windows
|
||||
# }
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Foil message localization
|
||||
|
||||
@ -63,6 +63,8 @@ class Example implements Comparable<Example> {
|
||||
procFiles = new ArrayList<File>();
|
||||
supportFiles = new ArrayList<File>();
|
||||
srcPathFiles = new ArrayList<File>();
|
||||
moduleSourcePathFiles = new ArrayList<File>();
|
||||
additionalFiles = new ArrayList<File>();
|
||||
|
||||
findFiles(file, srcFiles);
|
||||
for (File f: srcFiles) {
|
||||
@ -81,13 +83,23 @@ class Example implements Comparable<Example> {
|
||||
else if (files == srcFiles && c.getName().equals("sourcepath")) {
|
||||
srcPathDir = c;
|
||||
findFiles(c, srcPathFiles);
|
||||
} else if (files == srcFiles && c.getName().equals("modulesourcepath")) {
|
||||
moduleSourcePathDir = c;
|
||||
findFiles(c, moduleSourcePathFiles);
|
||||
} else if (files == srcFiles && c.getName().equals("additional")) {
|
||||
additionalFilesDir = c;
|
||||
findFiles(c, additionalFiles);
|
||||
} else if (files == srcFiles && c.getName().equals("support"))
|
||||
findFiles(c, supportFiles);
|
||||
else
|
||||
findFiles(c, files);
|
||||
}
|
||||
} else if (f.isFile() && f.getName().endsWith(".java")) {
|
||||
files.add(f);
|
||||
} else if (f.isFile()) {
|
||||
if (f.getName().endsWith(".java")) {
|
||||
files.add(f);
|
||||
} else if (f.getName().equals("modulesourcepath")) {
|
||||
moduleSourcePathDir = f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,14 +240,27 @@ class Example implements Comparable<Example> {
|
||||
}
|
||||
}
|
||||
|
||||
List<File> files = srcFiles;
|
||||
|
||||
if (srcPathDir != null) {
|
||||
opts.add("-sourcepath");
|
||||
opts.add(srcPathDir.getPath());
|
||||
}
|
||||
|
||||
if (moduleSourcePathDir != null) {
|
||||
opts.add("-modulesourcepath");
|
||||
opts.add(moduleSourcePathDir.getPath());
|
||||
files = moduleSourcePathFiles;
|
||||
}
|
||||
|
||||
if (additionalFiles.size() > 0) {
|
||||
List<String> sOpts = Arrays.asList("-d", classesDir.getPath());
|
||||
new Jsr199Compiler(verbose).run(null, null, false, sOpts, additionalFiles);
|
||||
}
|
||||
|
||||
try {
|
||||
Compiler c = Compiler.getCompiler(runOpts, verbose);
|
||||
c.run(out, keys, raw, opts, srcFiles);
|
||||
c.run(out, keys, raw, opts, files);
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (out != null) {
|
||||
out.println("Invalid value for run tag: " + runOpts);
|
||||
@ -298,7 +323,11 @@ class Example implements Comparable<Example> {
|
||||
List<File> srcFiles;
|
||||
List<File> procFiles;
|
||||
File srcPathDir;
|
||||
File moduleSourcePathDir;
|
||||
File additionalFilesDir;
|
||||
List<File> srcPathFiles;
|
||||
List<File> moduleSourcePathFiles;
|
||||
List<File> additionalFiles;
|
||||
List<File> supportFiles;
|
||||
File infoFile;
|
||||
private List<String> runOpts;
|
||||
|
||||
@ -110,67 +110,26 @@ compiler.err.cant.inherit.from.anon # error for subclass of
|
||||
compiler.misc.bad.class.file # class file is malformed
|
||||
compiler.misc.bad.const.pool.entry # constant pool entry has wrong type
|
||||
|
||||
# The following are new module-related messages, that need new examples to be created
|
||||
compiler.err.addmods.all.module.path.invalid
|
||||
compiler.err.cant.find.module
|
||||
compiler.err.cyclic.requires
|
||||
compiler.err.duplicate.exports
|
||||
compiler.err.duplicate.module
|
||||
# The following module-related messages will have to stay on the not-yet list for various reasons:
|
||||
compiler.warn.locn.unknown.file.on.module.path # Never issued ATM (short circuited with an if (false))
|
||||
compiler.err.no.output.dir # -d is always specified by test infrastructure
|
||||
compiler.err.output.dir.must.be.specified.with.dash.m.option # -d is always specified by test infrastructure
|
||||
compiler.warn.outdir.is.in.exploded.module # No control over -d specified by test infrastructure
|
||||
compiler.err.invalid.module.specifier # Not possible (?)
|
||||
compiler.err.locn.cant.get.module.name.for.jar # bad binary ? Infer module name failure
|
||||
compiler.misc.bad.module-info.name # bad class file
|
||||
compiler.err.locn.bad.module-info # bad class file
|
||||
compiler.err.locn.cant.read.file # bad class file
|
||||
compiler.err.locn.cant.read.directory # file system issue
|
||||
compiler.misc.unnamed.module # fragment uninteresting in and of itself
|
||||
compiler.misc.kindname.module # fragment uninteresting in and of itself
|
||||
compiler.misc.locn.module_path # fragment uninteresting in and of itself
|
||||
compiler.misc.locn.module_source_path # fragment uninteresting in and of itself
|
||||
compiler.misc.locn.system_modules # fragment uninteresting in and of itself
|
||||
compiler.misc.locn.upgrade_module_path # fragment uninteresting in and of itself
|
||||
|
||||
# The following are new module-related messages, that need new examples to be created
|
||||
compiler.err.duplicate.module.on.path
|
||||
compiler.err.duplicate.provides
|
||||
compiler.err.duplicate.requires
|
||||
compiler.err.duplicate.uses
|
||||
compiler.err.expected.module
|
||||
compiler.err.illegal.argument.for.option
|
||||
compiler.err.invalid.module.specifier
|
||||
compiler.err.locn.bad.module-info
|
||||
compiler.err.locn.cant.get.module.name.for.jar
|
||||
compiler.err.locn.cant.read.directory
|
||||
compiler.err.locn.cant.read.file
|
||||
compiler.err.locn.invalid.arg.for.xpatch
|
||||
compiler.err.locn.module-info.not.allowed.on.patch.path
|
||||
compiler.err.module-info.with.xmodule.classpath
|
||||
compiler.err.module-info.with.xmodule.sourcepath
|
||||
compiler.err.module.decl.sb.in.module-info.java
|
||||
compiler.err.module.name.mismatch
|
||||
compiler.err.module.not.found
|
||||
compiler.err.module.not.found.in.module.source.path
|
||||
compiler.err.modules.not.supported.in.source
|
||||
compiler.err.modulesourcepath.must.be.specified.with.dash.m.option
|
||||
compiler.err.multi-module.outdir.cannot.be.exploded.module
|
||||
compiler.err.no.output.dir
|
||||
compiler.err.not.def.access.package.cant.access
|
||||
compiler.err.output.dir.must.be.specified.with.dash.m.option
|
||||
compiler.err.package.clash.from.requires
|
||||
compiler.err.package.empty.or.not.found
|
||||
compiler.err.package.in.other.module
|
||||
compiler.err.processorpath.no.processormodulepath
|
||||
compiler.err.service.definition.is.inner
|
||||
compiler.err.service.definition.is.enum
|
||||
compiler.err.service.implementation.doesnt.have.a.no.args.constructor
|
||||
compiler.err.service.implementation.is.abstract
|
||||
compiler.err.service.implementation.is.inner
|
||||
compiler.err.service.implementation.no.args.constructor.not.public
|
||||
compiler.err.service.implementation.not.in.right.module
|
||||
compiler.err.too.many.modules
|
||||
compiler.err.unexpected.after.module
|
||||
compiler.err.unnamed.pkg.not.allowed.named.modules
|
||||
compiler.err.xaddexports.malformed.entry
|
||||
compiler.err.xaddexports.too.many
|
||||
compiler.err.xaddreads.malformed.entry
|
||||
compiler.err.xaddreads.too.many
|
||||
compiler.err.xmodule.no.module.sourcepath
|
||||
compiler.misc.bad.module-info.name
|
||||
compiler.misc.cant.resolve.modules
|
||||
compiler.misc.file.does.not.contain.module
|
||||
compiler.misc.kindname.module
|
||||
compiler.misc.locn.module_path
|
||||
compiler.misc.locn.module_source_path
|
||||
compiler.misc.locn.system_modules
|
||||
compiler.misc.locn.upgrade_module_path
|
||||
compiler.misc.unnamed.module
|
||||
compiler.warn.dir.path.element.not.directory
|
||||
compiler.warn.locn.unknown.file.on.module.path
|
||||
compiler.warn.outdir.is.in.exploded.module
|
||||
compiler.warn.service.provided.but.not.exported.or.used
|
||||
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.addmods.all.module.path.invalid
|
||||
// options: -addmods ALL-MODULE-PATH
|
||||
module m {}
|
||||
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.cant.find.module
|
||||
// key: compiler.err.doesnt.exist
|
||||
|
||||
// options: -XaddExports:undef/undef=ALL-UNNAMED
|
||||
|
||||
import undef.Any;
|
||||
|
||||
class Test {}
|
||||
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.cyclic.requires
|
||||
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
module m1 {
|
||||
requires m2;
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
module m2 {
|
||||
requires m1;
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.warn.dir.path.element.not.directory
|
||||
// options: -Xlint:path
|
||||
// run: simple
|
||||
|
||||
class DirPathElementNotDirectory { }
|
||||
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// a file to be passed in where a directory is expected (-modulesourcepath option)
|
||||
// to trigger an error deliberately.
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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 exported;
|
||||
|
||||
public class Class {
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.duplicate.exports
|
||||
|
||||
module DuplicateExports {
|
||||
exports exported;
|
||||
exports exported;
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.duplicate.module
|
||||
// key: compiler.err.module.name.mismatch
|
||||
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
module blah {}
|
||||
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
module blah {}
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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 exported;
|
||||
|
||||
public interface Service {
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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 impl;
|
||||
|
||||
import exported.Service;
|
||||
|
||||
public class ServiceImplementation implements Service {
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.duplicate.provides
|
||||
|
||||
module DuplicateExports {
|
||||
exports exported;
|
||||
provides exported.Service with impl.ServiceImplementation;
|
||||
provides exported.Service with impl.ServiceImplementation;
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.duplicate.requires
|
||||
|
||||
module DuplicateRequires {
|
||||
requires java.se;
|
||||
requires java.se;
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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 exported;
|
||||
|
||||
public interface Service {
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.duplicate.uses
|
||||
|
||||
module DuplicateExports {
|
||||
exports exported;
|
||||
uses exported.Service;
|
||||
uses exported.Service;
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.illegal.argument.for.option
|
||||
// options: -modulepath doesNotExist
|
||||
// run: simple
|
||||
|
||||
class X {}
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.locn.invalid.arg.for.xpatch
|
||||
// options: -Xpatch:blah
|
||||
|
||||
class InvalidArgForXpatch {}
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
//key: compiler.err.module.decl.sb.in.module-info.java
|
||||
|
||||
module ModuleDeclSbInModuleInfoJava {
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.module-info.with.xmodule.sourcepath
|
||||
// options: -Xmodule:java.compiler
|
||||
|
||||
package javax.lang.model.element;
|
||||
|
||||
public interface Extra {}
|
||||
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
module java.compiler {}
|
||||
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.module-info.with.xmodule.classpath
|
||||
// options: -Xmodule:java.compiler
|
||||
|
||||
package javax.lang.model.element;
|
||||
|
||||
public interface ModuleInfoWithXModuleClasspath {}
|
||||
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
module mod {}
|
||||
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.expected.module
|
||||
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// No module description here
|
||||
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.module.name.mismatch
|
||||
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
module other {
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.module.not.found
|
||||
|
||||
module m {
|
||||
requires does.not.exist;
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.module.not.found.in.module.source.path
|
||||
// options: -m m1 -modulesourcepath src
|
||||
|
||||
class ModuleNotFoundInModuleSourcePath {}
|
||||
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
// key: compiler.err.modulesourcepath.must.be.specified.with.dash.m.option
|
||||
// options: -m m1 -d blah
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user