This commit is contained in:
Lana Steuck 2016-05-19 19:47:04 +00:00
commit aa53efc689
199 changed files with 8940 additions and 3734 deletions

View File

@ -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

View File

@ -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/"

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View File

@ -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>

View 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>

View File

@ -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>

View File

@ -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 {

View File

@ -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) {

View File

@ -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);
}
/**

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}
/**

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
/**

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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();

View File

@ -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);
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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> {

View File

@ -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();
}
}
}

View File

@ -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()) {

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
});
}
}
}

View File

@ -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;
});
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}
}
}

View File

@ -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);
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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))

View File

@ -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\

View File

@ -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

View File

@ -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)

View File

@ -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 {}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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\

View File

@ -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

View File

@ -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()]));
}

View File

@ -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())
);
}

View File

@ -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 {}
}
}

View File

@ -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())

View File

@ -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());

View File

@ -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"),

View File

@ -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"),

View File

@ -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" +

View File

@ -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")),

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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

View File

@ -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))

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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 {}

View File

@ -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 {}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 { }

View File

@ -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.

View File

@ -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 {
}

View File

@ -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;
}

View File

@ -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

View File

@ -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 {}

View File

@ -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 {}

View File

@ -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 {
}

View File

@ -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 {
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 {
}

View File

@ -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;
}

View File

@ -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 {}

View File

@ -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 {}

View File

@ -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 {
}

View File

@ -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 {}

View File

@ -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 {}

View File

@ -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 {}

View File

@ -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 {}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {
}

View File

@ -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;
}

View File

@ -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 {}

View File

@ -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