提交 385ae67f 编写于 作者: R rfield

8159111: JShell API: Add access to wrappers and dependencies

Reviewed-by: jlahoda
上级 9bc054b0
...@@ -55,8 +55,9 @@ public abstract class DeclarationSnippet extends PersistentSnippet { ...@@ -55,8 +55,9 @@ public abstract class DeclarationSnippet extends PersistentSnippet {
DeclarationSnippet(DeclarationKey key, String userSource, Wrap guts, DeclarationSnippet(DeclarationKey key, String userSource, Wrap guts,
String unitName, SubKind subkind, Wrap corralled, String unitName, SubKind subkind, Wrap corralled,
Collection<String> declareReferences, Collection<String> declareReferences,
Collection<String> bodyReferences) { Collection<String> bodyReferences,
super(key, userSource, guts, unitName, subkind); DiagList syntheticDiags) {
super(key, userSource, guts, unitName, subkind, syntheticDiags);
this.corralled = corralled; this.corralled = corralled;
this.declareReferences = declareReferences; this.declareReferences = declareReferences;
this.bodyReferences = bodyReferences; this.bodyReferences = bodyReferences;
......
...@@ -28,8 +28,7 @@ package jdk.jshell; ...@@ -28,8 +28,7 @@ package jdk.jshell;
import jdk.jshell.Key.ErroneousKey; import jdk.jshell.Key.ErroneousKey;
/** /**
* A snippet of code that is not valid Java programming language code, and for * A snippet of code that is not valid Java programming language code.
* which the kind of snippet could not be determined.
* The Kind is {@link jdk.jshell.Snippet.Kind#ERRONEOUS ERRONEOUS}. * The Kind is {@link jdk.jshell.Snippet.Kind#ERRONEOUS ERRONEOUS}.
* <p> * <p>
* <code>ErroneousSnippet</code> is immutable: an access to * <code>ErroneousSnippet</code> is immutable: an access to
...@@ -38,7 +37,21 @@ import jdk.jshell.Key.ErroneousKey; ...@@ -38,7 +37,21 @@ import jdk.jshell.Key.ErroneousKey;
*/ */
public class ErroneousSnippet extends Snippet { public class ErroneousSnippet extends Snippet {
ErroneousSnippet(ErroneousKey key, String userSource, Wrap guts, SubKind subkind) { private final Kind probableKind;
super(key, userSource, guts, null, subkind);
ErroneousSnippet(ErroneousKey key, String userSource, Wrap guts,
Kind probableKind, SubKind subkind) {
super(key, userSource, guts, null, subkind, null);
this.probableKind = probableKind;
}
/**
* Returns what appears to be the intended Kind in this erroneous snippet.
*
* @return the probable Kind; or {@link Kind#ERRONEOUS} if that cannot be
* determined.
*/
public Kind probableKind() {
return probableKind;
} }
} }
...@@ -52,6 +52,7 @@ import java.util.Set; ...@@ -52,6 +52,7 @@ import java.util.Set;
import jdk.jshell.Key.ErroneousKey; import jdk.jshell.Key.ErroneousKey;
import jdk.jshell.Key.MethodKey; import jdk.jshell.Key.MethodKey;
import jdk.jshell.Key.TypeDeclKey; import jdk.jshell.Key.TypeDeclKey;
import jdk.jshell.Snippet.Kind;
import jdk.jshell.Snippet.SubKind; import jdk.jshell.Snippet.SubKind;
import jdk.jshell.TaskFactory.AnalyzeTask; import jdk.jshell.TaskFactory.AnalyzeTask;
import jdk.jshell.TaskFactory.BaseTask; import jdk.jshell.TaskFactory.BaseTask;
...@@ -62,6 +63,7 @@ import jdk.jshell.Wrap.Range; ...@@ -62,6 +63,7 @@ import jdk.jshell.Wrap.Range;
import jdk.jshell.Snippet.Status; import jdk.jshell.Snippet.Status;
import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet; import static java.util.stream.Collectors.toSet;
import static java.util.Collections.singletonList;
import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN; import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
import static jdk.jshell.Util.DOIT_METHOD_NAME; import static jdk.jshell.Util.DOIT_METHOD_NAME;
import static jdk.jshell.Util.PREFIX_PATTERN; import static jdk.jshell.Util.PREFIX_PATTERN;
...@@ -89,24 +91,75 @@ class Eval { ...@@ -89,24 +91,75 @@ class Eval {
this.state = state; this.state = state;
} }
/**
* Evaluates a snippet of source.
*
* @param userSource the source of the snippet
* @return the list of primary and update events
* @throws IllegalStateException
*/
List<SnippetEvent> eval(String userSource) throws IllegalStateException { List<SnippetEvent> eval(String userSource) throws IllegalStateException {
List<SnippetEvent> allEvents = new ArrayList<>();
for (Snippet snip : sourceToSnippets(userSource)) {
if (snip.kind() == Kind.ERRONEOUS) {
state.maps.installSnippet(snip);
allEvents.add(new SnippetEvent(
snip, Status.NONEXISTENT, Status.REJECTED,
false, null, null, null));
} else {
allEvents.addAll(declare(snip, snip.syntheticDiags()));
}
}
return allEvents;
}
/**
* Converts the user source of a snippet into a Snippet list -- Snippet will
* have wrappers.
*
* @param userSource the source of the snippet
* @return usually a singleton list of Snippet, but may be empty or multiple
*/
List<Snippet> sourceToSnippetsWithWrappers(String userSource) {
List<Snippet> snippets = sourceToSnippets(userSource);
for (Snippet snip : snippets) {
if (snip.outerWrap() == null) {
snip.setOuterWrap(
(snip.kind() == Kind.IMPORT)
? state.outerMap.wrapImport(snip.guts(), snip)
: state.outerMap.wrapInTrialClass(snip.guts())
);
}
}
return snippets;
}
/**
* Converts the user source of a snippet into a Snippet object (or list of
* objects in the case of: int x, y, z;). Does not install the Snippets
* or execute them.
*
* @param userSource the source of the snippet
* @return usually a singleton list of Snippet, but may be empty or multiple
*/
private List<Snippet> sourceToSnippets(String userSource) {
String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, false).cleared()); String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, false).cleared());
if (compileSource.length() == 0) { if (compileSource.length() == 0) {
return Collections.emptyList(); return Collections.emptyList();
} }
// String folding messes up position information.
ParseTask pt = state.taskFactory.new ParseTask(compileSource); ParseTask pt = state.taskFactory.new ParseTask(compileSource);
if (pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
return compileFailResult(pt, userSource);
}
List<? extends Tree> units = pt.units(); List<? extends Tree> units = pt.units();
if (units.isEmpty()) { if (units.isEmpty()) {
return compileFailResult(pt, userSource); return compileFailResult(pt, userSource, Kind.ERRONEOUS);
} }
// Erase illegal modifiers
compileSource = new MaskCommentsAndModifiers(compileSource, true).cleared();
Tree unitTree = units.get(0); Tree unitTree = units.get(0);
if (pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
return compileFailResult(pt, userSource, kindOfTree(unitTree));
}
// Erase illegal/ignored modifiers
compileSource = new MaskCommentsAndModifiers(compileSource, true).cleared();
state.debug(DBG_GEN, "Kind: %s -- %s\n", unitTree.getKind(), unitTree); state.debug(DBG_GEN, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
switch (unitTree.getKind()) { switch (unitTree.getKind()) {
case IMPORT: case IMPORT:
...@@ -130,7 +183,7 @@ class Eval { ...@@ -130,7 +183,7 @@ class Eval {
} }
} }
private List<SnippetEvent> processImport(String userSource, String compileSource) { private List<Snippet> processImport(String userSource, String compileSource) {
Wrap guts = Wrap.simpleWrap(compileSource); Wrap guts = Wrap.simpleWrap(compileSource);
Matcher mat = IMPORT_PATTERN.matcher(compileSource); Matcher mat = IMPORT_PATTERN.matcher(compileSource);
String fullname; String fullname;
...@@ -155,7 +208,7 @@ class Eval { ...@@ -155,7 +208,7 @@ class Eval {
: (isStatic ? SINGLE_STATIC_IMPORT_SUBKIND : SINGLE_TYPE_IMPORT_SUBKIND); : (isStatic ? SINGLE_STATIC_IMPORT_SUBKIND : SINGLE_TYPE_IMPORT_SUBKIND);
Snippet snip = new ImportSnippet(state.keyMap.keyForImport(keyName, snippetKind), Snippet snip = new ImportSnippet(state.keyMap.keyForImport(keyName, snippetKind),
userSource, guts, fullname, name, snippetKind, fullkey, isStatic, isStar); userSource, guts, fullname, name, snippetKind, fullkey, isStatic, isStar);
return declare(snip); return singletonList(snip);
} }
private static class EvalPretty extends Pretty { private static class EvalPretty extends Pretty {
...@@ -187,8 +240,8 @@ class Eval { ...@@ -187,8 +240,8 @@ class Eval {
} }
} }
private List<SnippetEvent> processVariables(String userSource, List<? extends Tree> units, String compileSource, ParseTask pt) { private List<Snippet> processVariables(String userSource, List<? extends Tree> units, String compileSource, ParseTask pt) {
List<SnippetEvent> allEvents = new ArrayList<>(); List<Snippet> snippets = new ArrayList<>();
TreeDissector dis = TreeDissector.createByFirstClass(pt); TreeDissector dis = TreeDissector.createByFirstClass(pt);
for (Tree unitTree : units) { for (Tree unitTree : units) {
VariableTree vt = (VariableTree) unitTree; VariableTree vt = (VariableTree) unitTree;
...@@ -224,18 +277,16 @@ class Eval { ...@@ -224,18 +277,16 @@ class Eval {
int nameEnd = nameStart + name.length(); int nameEnd = nameStart + name.length();
Range rname = new Range(nameStart, nameEnd); Range rname = new Range(nameStart, nameEnd);
Wrap guts = Wrap.varWrap(compileSource, rtype, sbBrackets.toString(), rname, rinit); Wrap guts = Wrap.varWrap(compileSource, rtype, sbBrackets.toString(), rname, rinit);
DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true);
Snippet snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts, Snippet snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts,
name, subkind, typeName, name, subkind, typeName,
tds.declareReferences()); tds.declareReferences(), modDiag);
DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true); snippets.add(snip);
List<SnippetEvent> res1 = declare(snip, modDiag);
allEvents.addAll(res1);
} }
return snippets;
return allEvents;
} }
private List<SnippetEvent> processExpression(String userSource, String compileSource) { private List<Snippet> processExpression(String userSource, String compileSource) {
String name = null; String name = null;
ExpressionInfo ei = typeOfExpression(compileSource); ExpressionInfo ei = typeOfExpression(compileSource);
ExpressionTree assignVar; ExpressionTree assignVar;
...@@ -266,7 +317,7 @@ class Eval { ...@@ -266,7 +317,7 @@ class Eval {
guts = Wrap.tempVarWrap(compileSource, typeName, name); guts = Wrap.tempVarWrap(compileSource, typeName, name);
Collection<String> declareReferences = null; //TODO Collection<String> declareReferences = null; //TODO
snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts, snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts,
name, SubKind.TEMP_VAR_EXPRESSION_SUBKIND, typeName, declareReferences); name, SubKind.TEMP_VAR_EXPRESSION_SUBKIND, typeName, declareReferences, null);
} else { } else {
guts = Wrap.methodReturnWrap(compileSource); guts = Wrap.methodReturnWrap(compileSource);
snip = new ExpressionSnippet(state.keyMap.keyForExpression(name, typeName), userSource, guts, snip = new ExpressionSnippet(state.keyMap.keyForExpression(name, typeName), userSource, guts,
...@@ -282,15 +333,15 @@ class Eval { ...@@ -282,15 +333,15 @@ class Eval {
at = trialCompile(guts); at = trialCompile(guts);
} }
if (at.hasErrors()) { if (at.hasErrors()) {
return compileFailResult(at, userSource); return compileFailResult(at, userSource, Kind.EXPRESSION);
} }
} }
snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts); snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
} }
return declare(snip); return singletonList(snip);
} }
private List<SnippetEvent> processClass(String userSource, Tree unitTree, String compileSource, SubKind snippetKind, ParseTask pt) { private List<Snippet> processClass(String userSource, Tree unitTree, String compileSource, SubKind snippetKind, ParseTask pt) {
TreeDependencyScanner tds = new TreeDependencyScanner(); TreeDependencyScanner tds = new TreeDependencyScanner();
tds.scan(unitTree); tds.scan(unitTree);
...@@ -306,11 +357,11 @@ class Eval { ...@@ -306,11 +357,11 @@ class Eval {
Wrap guts = Wrap.classMemberWrap(compileSource); Wrap guts = Wrap.classMemberWrap(compileSource);
Snippet snip = new TypeDeclSnippet(key, userSource, guts, Snippet snip = new TypeDeclSnippet(key, userSource, guts,
name, snippetKind, name, snippetKind,
corralled, tds.declareReferences(), tds.bodyReferences()); corralled, tds.declareReferences(), tds.bodyReferences(), modDiag);
return declare(snip, modDiag); return singletonList(snip);
} }
private List<SnippetEvent> processStatement(String userSource, String compileSource) { private List<Snippet> processStatement(String userSource, String compileSource) {
Wrap guts = Wrap.methodWrap(compileSource); Wrap guts = Wrap.methodWrap(compileSource);
// Check for unreachable by trying // Check for unreachable by trying
AnalyzeTask at = trialCompile(guts); AnalyzeTask at = trialCompile(guts);
...@@ -325,15 +376,15 @@ class Eval { ...@@ -325,15 +376,15 @@ class Eval {
at = trialCompile(guts); at = trialCompile(guts);
} }
if (at.hasErrors()) { if (at.hasErrors()) {
return compileFailResult(at, userSource); return compileFailResult(at, userSource, Kind.STATEMENT);
} }
} }
} else { } else {
return compileFailResult(at, userSource); return compileFailResult(at, userSource, Kind.STATEMENT);
} }
} }
Snippet snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts); Snippet snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
return declare(snip); return singletonList(snip);
} }
private AnalyzeTask trialCompile(Wrap guts) { private AnalyzeTask trialCompile(Wrap guts) {
...@@ -341,7 +392,7 @@ class Eval { ...@@ -341,7 +392,7 @@ class Eval {
return state.taskFactory.new AnalyzeTask(outer); return state.taskFactory.new AnalyzeTask(outer);
} }
private List<SnippetEvent> processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) { private List<Snippet> processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) {
TreeDependencyScanner tds = new TreeDependencyScanner(); TreeDependencyScanner tds = new TreeDependencyScanner();
tds.scan(unitTree); tds.scan(unitTree);
TreeDissector dis = TreeDissector.createByFirstClass(pt); TreeDissector dis = TreeDissector.createByFirstClass(pt);
...@@ -360,7 +411,7 @@ class Eval { ...@@ -360,7 +411,7 @@ class Eval {
Wrap corralled = new Corraller(key.index(), pt.getContext()).corralMethod(mt); Wrap corralled = new Corraller(key.index(), pt.getContext()).corralMethod(mt);
if (modDiag.hasErrors()) { if (modDiag.hasErrors()) {
return compileFailResult(modDiag, userSource); return compileFailResult(modDiag, userSource, Kind.METHOD);
} }
Wrap guts = Wrap.classMemberWrap(compileSource); Wrap guts = Wrap.classMemberWrap(compileSource);
Range typeRange = dis.treeToRange(returnType); Range typeRange = dis.treeToRange(returnType);
...@@ -368,37 +419,76 @@ class Eval { ...@@ -368,37 +419,76 @@ class Eval {
Snippet snip = new MethodSnippet(key, userSource, guts, Snippet snip = new MethodSnippet(key, userSource, guts,
name, signature, name, signature,
corralled, tds.declareReferences(), tds.bodyReferences()); corralled, tds.declareReferences(), tds.bodyReferences(), modDiag);
return declare(snip, modDiag); return singletonList(snip);
}
private Kind kindOfTree(Tree tree) {
switch (tree.getKind()) {
case IMPORT:
return Kind.IMPORT;
case VARIABLE:
return Kind.VAR;
case EXPRESSION_STATEMENT:
return Kind.EXPRESSION;
case CLASS:
case ENUM:
case ANNOTATION_TYPE:
case INTERFACE:
return Kind.TYPE_DECL;
case METHOD:
return Kind.METHOD;
default:
return Kind.STATEMENT;
}
} }
/** /**
* The snippet has failed, return with the rejected event * The snippet has failed, return with the rejected snippet
* *
* @param xt the task from which to extract the failure diagnostics * @param xt the task from which to extract the failure diagnostics
* @param userSource the incoming bad user source * @param userSource the incoming bad user source
* @return a rejected snippet event * @return a rejected snippet
*/ */
private List<SnippetEvent> compileFailResult(BaseTask xt, String userSource) { private List<Snippet> compileFailResult(BaseTask xt, String userSource, Kind probableKind) {
return compileFailResult(xt.getDiagnostics(), userSource); return compileFailResult(xt.getDiagnostics(), userSource, probableKind);
} }
/** /**
* The snippet has failed, return with the rejected event * The snippet has failed, return with the rejected snippet
* *
* @param diags the failure diagnostics * @param diags the failure diagnostics
* @param userSource the incoming bad user source * @param userSource the incoming bad user source
* @return a rejected snippet event * @return a rejected snippet
*/ */
private List<SnippetEvent> compileFailResult(DiagList diags, String userSource) { private List<Snippet> compileFailResult(DiagList diags, String userSource, Kind probableKind) {
ErroneousKey key = state.keyMap.keyForErroneous(); ErroneousKey key = state.keyMap.keyForErroneous();
Snippet snip = new ErroneousSnippet(key, userSource, null, SubKind.UNKNOWN_SUBKIND); Snippet snip = new ErroneousSnippet(key, userSource, null,
probableKind, SubKind.UNKNOWN_SUBKIND);
snip.setFailed(diags); snip.setFailed(diags);
state.maps.installSnippet(snip);
return Collections.singletonList(new SnippetEvent( // Install wrapper for query by SourceCodeAnalysis.wrapper
snip, Status.NONEXISTENT, Status.REJECTED, String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, true).cleared());
false, null, null, null) OuterWrap outer;
); switch (probableKind) {
case IMPORT:
outer = state.outerMap.wrapImport(Wrap.simpleWrap(compileSource), snip);
break;
case EXPRESSION:
outer = state.outerMap.wrapInTrialClass(Wrap.methodReturnWrap(compileSource));
break;
case VAR:
case TYPE_DECL:
case METHOD:
outer = state.outerMap.wrapInTrialClass(Wrap.classMemberWrap(compileSource));
break;
default:
outer = state.outerMap.wrapInTrialClass(Wrap.methodWrap(compileSource));
break;
}
snip.setOuterWrap(outer);
return singletonList(snip);
} }
private ExpressionInfo typeOfExpression(String expression) { private ExpressionInfo typeOfExpression(String expression) {
...@@ -430,10 +520,6 @@ class Eval { ...@@ -430,10 +520,6 @@ class Eval {
return events(c, outs, null, null); return events(c, outs, null, null);
} }
private List<SnippetEvent> declare(Snippet si) {
return declare(si, new DiagList());
}
private List<SnippetEvent> declare(Snippet si, DiagList generatedDiagnostics) { private List<SnippetEvent> declare(Snippet si, DiagList generatedDiagnostics) {
Unit c = new Unit(state, si, null, generatedDiagnostics); Unit c = new Unit(state, si, null, generatedDiagnostics);
Set<Unit> ins = new LinkedHashSet<>(); Set<Unit> ins = new LinkedHashSet<>();
......
...@@ -39,7 +39,7 @@ import jdk.jshell.Key.ExpressionKey; ...@@ -39,7 +39,7 @@ import jdk.jshell.Key.ExpressionKey;
public class ExpressionSnippet extends Snippet { public class ExpressionSnippet extends Snippet {
ExpressionSnippet(ExpressionKey key, String userSource, Wrap guts, String name, SubKind subkind) { ExpressionSnippet(ExpressionKey key, String userSource, Wrap guts, String name, SubKind subkind) {
super(key, userSource, guts, name, subkind); super(key, userSource, guts, name, subkind, null);
} }
/** /**
......
...@@ -46,7 +46,7 @@ public class ImportSnippet extends PersistentSnippet { ...@@ -46,7 +46,7 @@ public class ImportSnippet extends PersistentSnippet {
ImportSnippet(ImportKey key, String userSource, Wrap guts, ImportSnippet(ImportKey key, String userSource, Wrap guts,
String fullname, String name, SubKind subkind, String fullkey, String fullname, String name, SubKind subkind, String fullkey,
boolean isStatic, boolean isStar) { boolean isStatic, boolean isStar) {
super(key, userSource, guts, name, subkind); super(key, userSource, guts, name, subkind, null);
this.fullname = fullname; this.fullname = fullname;
this.fullkey = fullkey; this.fullkey = fullkey;
this.isStatic = isStatic; this.isStatic = isStatic;
......
...@@ -377,7 +377,9 @@ public class JShell implements AutoCloseable { ...@@ -377,7 +377,9 @@ public class JShell implements AutoCloseable {
* Evaluate the input String, including definition and/or execution, if * Evaluate the input String, including definition and/or execution, if
* applicable. The input is checked for errors, unless the errors can be * applicable. The input is checked for errors, unless the errors can be
* deferred (as is the case with some unresolvedDependencies references), * deferred (as is the case with some unresolvedDependencies references),
* errors will abort evaluation. The input should be * errors will abort evaluation.
* <p>
* The input should be
* exactly one complete snippet of source code, that is, one expression, * exactly one complete snippet of source code, that is, one expression,
* statement, variable declaration, method declaration, class declaration, * statement, variable declaration, method declaration, class declaration,
* or import. * or import.
......
...@@ -44,8 +44,10 @@ public class MethodSnippet extends DeclarationSnippet { ...@@ -44,8 +44,10 @@ public class MethodSnippet extends DeclarationSnippet {
MethodSnippet(MethodKey key, String userSource, Wrap guts, MethodSnippet(MethodKey key, String userSource, Wrap guts,
String name, String signature, Wrap corralled, String name, String signature, Wrap corralled,
Collection<String> declareReferences, Collection<String> bodyReferences) { Collection<String> declareReferences, Collection<String> bodyReferences,
super(key, userSource, guts, name, SubKind.METHOD_SUBKIND, corralled, declareReferences, bodyReferences); DiagList syntheticDiags) {
super(key, userSource, guts, name, SubKind.METHOD_SUBKIND, corralled,
declareReferences, bodyReferences, syntheticDiags);
this.signature = signature; this.signature = signature;
} }
......
...@@ -25,8 +25,6 @@ ...@@ -25,8 +25,6 @@
package jdk.jshell; package jdk.jshell;
import java.util.IdentityHashMap;
import java.util.List;
import javax.tools.Diagnostic; import javax.tools.Diagnostic;
import javax.tools.JavaFileObject; import javax.tools.JavaFileObject;
......
...@@ -37,8 +37,9 @@ package jdk.jshell; ...@@ -37,8 +37,9 @@ package jdk.jshell;
*/ */
public abstract class PersistentSnippet extends Snippet { public abstract class PersistentSnippet extends Snippet {
PersistentSnippet(Key key, String userSource, Wrap guts, String unitName, SubKind subkind) { PersistentSnippet(Key key, String userSource, Wrap guts, String unitName,
super(key, userSource, guts, unitName, subkind); SubKind subkind, DiagList syntheticDiags) {
super(key, userSource, guts, unitName, subkind, syntheticDiags);
} }
/** /**
......
...@@ -563,13 +563,18 @@ public abstract class Snippet { ...@@ -563,13 +563,18 @@ public abstract class Snippet {
private Status status; private Status status;
private List<String> unresolved; private List<String> unresolved;
private DiagList diagnostics; private DiagList diagnostics;
private final DiagList syntheticDiags;
Snippet(Key key, String userSource, Wrap guts, String unitName, SubKind subkind) { Snippet(Key key, String userSource, Wrap guts, String unitName,
SubKind subkind, DiagList syntheticDiags) {
this.key = key; this.key = key;
this.source = userSource; this.source = userSource;
this.guts = guts; this.guts = guts;
this.unitName = unitName; this.unitName = unitName;
this.subkind = subkind; this.subkind = subkind;
this.syntheticDiags = syntheticDiags==null
? new DiagList()
: syntheticDiags;
this.status = Status.NONEXISTENT; this.status = Status.NONEXISTENT;
setSequenceNumber(0); setSequenceNumber(0);
} }
...@@ -644,6 +649,10 @@ public abstract class Snippet { ...@@ -644,6 +649,10 @@ public abstract class Snippet {
return diagnostics; return diagnostics;
} }
DiagList syntheticDiags() {
return syntheticDiags;
}
/** /**
* @return the corralled guts * @return the corralled guts
*/ */
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
package jdk.jshell; package jdk.jshell;
import java.util.Collection;
import java.util.List; import java.util.List;
/** /**
...@@ -91,6 +92,51 @@ public abstract class SourceCodeAnalysis { ...@@ -91,6 +92,51 @@ public abstract class SourceCodeAnalysis {
*/ */
public abstract QualifiedNames listQualifiedNames(String code, int cursor); public abstract QualifiedNames listQualifiedNames(String code, int cursor);
/**
* Returns the wrapper information for the {@code Snippet}. The wrapper changes as
* the environment changes, so calls to this method at different times may
* yield different results.
*
* @param snippet the {@code Snippet} from which to retrieve the wrapper
* @return information on the wrapper
*/
public abstract SnippetWrapper wrapper(Snippet snippet);
/**
* Returns the wrapper information for the snippet within the
* input source string.
* <p>
* Wrapper information for malformed and incomplete
* snippets also generate wrappers. The list is in snippet encounter
* order. The wrapper changes as the environment changes, so calls to this
* method at different times may yield different results.
* <p>
* The input should be
* exactly one complete snippet of source code, that is, one expression,
* statement, variable declaration, method declaration, class declaration,
* or import.
* To break arbitrary input into individual complete snippets, use
* {@link SourceCodeAnalysis#analyzeCompletion(String)}.
* <p>
* The wrapper may not match that returned by
* {@link SourceCodeAnalysis#wrapper(Snippet) wrapper(Snippet)},
* were the source converted to a {@code Snippet}.
*
* @param input the source input from which to generate wrappers
* @return a list of wrapper information
*/
public abstract List<SnippetWrapper> wrappers(String input);
/**
* Returns a collection of {@code Snippet}s which might need updating if the
* given {@code Snippet} is updated. The returned collection is designed to
* be inclusive and may include many false positives.
*
* @param snippet the {@code Snippet} whose dependents are requested
* @return the collection of dependents
*/
public abstract Collection<Snippet> dependents(Snippet snippet);
/** /**
* Internal only constructor * Internal only constructor
*/ */
...@@ -302,7 +348,7 @@ public abstract class SourceCodeAnalysis { ...@@ -302,7 +348,7 @@ public abstract class SourceCodeAnalysis {
} }
/** /**
* Indicates whether the result is based on up to date data. The * Indicates whether the result is based on up-to-date data. The
* {@link SourceCodeAnalysis#listQualifiedNames(java.lang.String, int) listQualifiedNames} * {@link SourceCodeAnalysis#listQualifiedNames(java.lang.String, int) listQualifiedNames}
* method may return before the classpath is fully inspected, in which case this method will * method may return before the classpath is fully inspected, in which case this method will
* return {@code false}. If the result is based on a fully inspected classpath, this method * return {@code false}. If the result is based on a fully inspected classpath, this method
...@@ -327,4 +373,83 @@ public abstract class SourceCodeAnalysis { ...@@ -327,4 +373,83 @@ public abstract class SourceCodeAnalysis {
} }
} }
/**
* The wrapping of a snippet of Java source into valid top-level Java
* source. The wrapping will always either be an import or include a
* synthetic class at the top-level. If a synthetic class is generated, it
* will be proceeded by the package and import declarations, and may contain
* synthetic class members.
* <p>
* This interface, in addition to the mapped form, provides the context and
* position mapping information.
*/
public interface SnippetWrapper {
/**
* Returns the input that is wrapped. For
* {@link SourceCodeAnalysis#wrappers(java.lang.String) wrappers(String)},
* this is the source of the snippet within the input. A variable
* declaration of {@code N} variables will map to {@code N} wrappers
* with the source separated.
* <p>
* For {@link SourceCodeAnalysis#wrapper(Snippet) wrapper(Snippet)},
* this is {@link Snippet#source() }.
*
* @return the input source corresponding to the wrapper.
*/
String source();
/**
* Returns a Java class definition that wraps the
* {@link SnippetWrapper#source()} or, if an import, the import source.
* <p>
* If the input is not a valid Snippet, this will not be a valid
* class/import definition.
* <p>
* The source may be divided and mapped to different locations within
* the wrapped source.
*
* @return the source wrapped into top-level Java code
*/
String wrapped();
/**
* Returns the fully qualified class name of the
* {@link SnippetWrapper#wrapped() } class.
* For erroneous input, a best guess is returned.
*
* @return the name of the synthetic wrapped class; if an import, the
* name is not defined
*/
String fullClassName();
/**
* Returns the {@link Snippet.Kind} of the
* {@link SnippetWrapper#source()}.
*
* @return an enum representing the general kind of snippet.
*/
Snippet.Kind kind();
/**
* Maps character position within the source to character position
* within the wrapped.
*
* @param pos the position in {@link SnippetWrapper#source()}
* @return the corresponding position in
* {@link SnippetWrapper#wrapped() }
*/
int sourceToWrappedPosition(int pos);
/**
* Maps character position within the wrapped to character position
* within the source.
*
* @param pos the position in {@link SnippetWrapper#wrapped()}
* @return the corresponding position in
* {@link SnippetWrapper#source() }
*/
int wrappedToSourcePosition(int pos);
}
} }
...@@ -413,6 +413,55 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis { ...@@ -413,6 +413,55 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
return result; return result;
} }
@Override
public SnippetWrapper wrapper(Snippet snippet) {
return new SnippetWrapper() {
@Override
public String source() {
return snippet.source();
}
@Override
public String wrapped() {
return snippet.outerWrap().wrapped();
}
@Override
public String fullClassName() {
return snippet.classFullName();
}
@Override
public Snippet.Kind kind() {
return snippet.kind() == Snippet.Kind.ERRONEOUS
? ((ErroneousSnippet) snippet).probableKind()
: snippet.kind();
}
@Override
public int sourceToWrappedPosition(int pos) {
return snippet.outerWrap().snippetIndexToWrapIndex(pos);
}
@Override
public int wrappedToSourcePosition(int pos) {
return snippet.outerWrap().wrapIndexToSnippetIndex(pos);
}
};
}
@Override
public List<SnippetWrapper> wrappers(String input) {
return proc.eval.sourceToSnippetsWithWrappers(input).stream()
.map(sn -> wrapper(sn))
.collect(toList());
}
@Override
public Collection<Snippet> dependents(Snippet snippet) {
return proc.maps.getDependents(snippet);
}
private boolean isStaticContext(AnalyzeTask at, TreePath path) { private boolean isStaticContext(AnalyzeTask at, TreePath path) {
switch (path.getLeaf().getKind()) { switch (path.getLeaf().getKind()) {
case ARRAY_TYPE: case ARRAY_TYPE:
......
...@@ -39,6 +39,6 @@ import jdk.jshell.Key.StatementKey; ...@@ -39,6 +39,6 @@ import jdk.jshell.Key.StatementKey;
public class StatementSnippet extends Snippet { public class StatementSnippet extends Snippet {
StatementSnippet(StatementKey key, String userSource, Wrap guts) { StatementSnippet(StatementKey key, String userSource, Wrap guts) {
super(key, userSource, guts, null, SubKind.STATEMENT_SUBKIND); super(key, userSource, guts, null, SubKind.STATEMENT_SUBKIND, null);
} }
} }
...@@ -42,8 +42,10 @@ public class TypeDeclSnippet extends DeclarationSnippet { ...@@ -42,8 +42,10 @@ public class TypeDeclSnippet extends DeclarationSnippet {
TypeDeclSnippet(TypeDeclKey key, String userSource, Wrap guts, TypeDeclSnippet(TypeDeclKey key, String userSource, Wrap guts,
String unitName, SubKind subkind, Wrap corralled, String unitName, SubKind subkind, Wrap corralled,
Collection<String> declareReferences, Collection<String> declareReferences,
Collection<String> bodyReferences) { Collection<String> bodyReferences,
super(key, userSource, guts, unitName, subkind, corralled, declareReferences, bodyReferences); DiagList syntheticDiags) {
super(key, userSource, guts, unitName, subkind, corralled,
declareReferences, bodyReferences, syntheticDiags);
} }
/**** internal access ****/ /**** internal access ****/
......
...@@ -43,8 +43,10 @@ public class VarSnippet extends DeclarationSnippet { ...@@ -43,8 +43,10 @@ public class VarSnippet extends DeclarationSnippet {
VarSnippet(VarKey key, String userSource, Wrap guts, VarSnippet(VarKey key, String userSource, Wrap guts,
String name, SubKind subkind, String typeName, String name, SubKind subkind, String typeName,
Collection<String> declareReferences) { Collection<String> declareReferences,
super(key, userSource, guts, name, subkind, null, declareReferences, null); DiagList syntheticDiags) {
super(key, userSource, guts, name, subkind, null, declareReferences,
null, syntheticDiags);
this.typeName = typeName; this.typeName = typeName;
} }
......
/*
* 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., start1 Franklin St, Fifth Floor, Boston, MA 02110-1length01 USA.
*
* Please contact Oracle, start00 Oracle Parkway, Redwood Shores, CA 9406start USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8159111
* @summary test wrappers and dependencies
* @modules jdk.jshell/jdk.jshell
* @build KullaTesting
* @run testng WrapperTest
*/
import java.util.Collection;
import java.util.List;
import org.testng.annotations.Test;
import jdk.jshell.ErroneousSnippet;
import jdk.jshell.Snippet;
import jdk.jshell.Snippet.Kind;
import jdk.jshell.SourceCodeAnalysis.SnippetWrapper;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static jdk.jshell.Snippet.Status.RECOVERABLE_DEFINED;
import static jdk.jshell.Snippet.Status.VALID;
@Test
public class WrapperTest extends KullaTesting {
public void testMethod() {
String src = "void glib() { System.out.println(\"hello\"); }";
List<SnippetWrapper> swl = getState().sourceCodeAnalysis().wrappers(src);
assertEquals(swl.size(), 1, "unexpected list length");
assertWrapperHas(swl.get(0), src, Kind.METHOD, "void", "glib", "println");
assertPosition(swl.get(0), src, 0, 4);
assertPosition(swl.get(0), src, 5, 4);
assertPosition(swl.get(0), src, 15, 6);
Snippet g = methodKey(assertEval(src, added(VALID)));
SnippetWrapper swg = getState().sourceCodeAnalysis().wrapper(g);
assertWrapperHas(swg, src, Kind.METHOD, "void", "glib", "println");
assertPosition(swg, src, 0, 4);
assertPosition(swg, src, 5, 4);
assertPosition(swg, src, 15, 6);
}
@Test(enabled = false) // TODO 8159740
public void testMethodCorralled() {
String src = "void glib() { f(); }";
Snippet g = methodKey(assertEval(src, added(RECOVERABLE_DEFINED)));
SnippetWrapper swg = getState().sourceCodeAnalysis().wrapper(g);
assertWrapperHas(swg, src, Kind.METHOD, "void", "glib");
assertPosition(swg, src, 5, 4);
}
public void testMethodBad() {
String src = "void flob() { ?????; }";
List<SnippetWrapper> swl = getState().sourceCodeAnalysis().wrappers(src);
assertEquals(swl.size(), 1, "unexpected list length");
assertWrapperHas(swl.get(0), src, Kind.METHOD, "void", "flob", "?????");
assertPosition(swl.get(0), src, 9, 2);
Snippet f = key(assertEvalFail(src));
assertEquals(f.kind(), Kind.ERRONEOUS);
assertEquals(((ErroneousSnippet)f).probableKind(), Kind.METHOD);
SnippetWrapper sw = getState().sourceCodeAnalysis().wrapper(f);
assertWrapperHas(sw, src, Kind.METHOD, "void", "flob", "?????");
assertPosition(swl.get(0), src, 14, 5);
}
public void testVar() {
String src = "int gx = 1234;";
List<SnippetWrapper> swl = getState().sourceCodeAnalysis().wrappers(src);
assertEquals(swl.size(), 1, "unexpected list length");
assertWrapperHas(swl.get(0), src, Kind.VAR, "int", "gx", "1234");
assertPosition(swl.get(0), src, 4, 2);
Snippet g = varKey(assertEval(src, added(VALID)));
SnippetWrapper swg = getState().sourceCodeAnalysis().wrapper(g);
assertWrapperHas(swg, src, Kind.VAR, "int", "gx", "1234");
assertPosition(swg, src, 0, 3);
}
public void testVarBad() {
String src = "double dd = ?????;";
List<SnippetWrapper> swl = getState().sourceCodeAnalysis().wrappers(src);
assertEquals(swl.size(), 1, "unexpected list length");
assertWrapperHas(swl.get(0), src, Kind.VAR, "double", "dd", "?????");
assertPosition(swl.get(0), src, 9, 2);
Snippet f = key(assertEvalFail(src));
assertEquals(f.kind(), Kind.ERRONEOUS);
assertEquals(((ErroneousSnippet)f).probableKind(), Kind.VAR);
SnippetWrapper sw = getState().sourceCodeAnalysis().wrapper(f);
assertWrapperHas(sw, src, Kind.VAR, "double", "dd", "?????");
assertPosition(swl.get(0), src, 12, 5);
}
public void testImport() {
String src = "import java.lang.*;";
List<SnippetWrapper> swl = getState().sourceCodeAnalysis().wrappers(src);
assertEquals(swl.size(), 1, "unexpected list length");
assertWrapperHas(swl.get(0), src, Kind.IMPORT, "import", "java.lang");
assertPosition(swl.get(0), src, 7, 4);
Snippet g = key(assertEval(src, added(VALID)));
SnippetWrapper swg = getState().sourceCodeAnalysis().wrapper(g);
assertWrapperHas(swg, src, Kind.IMPORT, "import", "java.lang");
assertPosition(swg, src, 0, 6);
}
public void testImportBad() {
String src = "import java.?????;";
List<SnippetWrapper> swl = getState().sourceCodeAnalysis().wrappers(src);
assertEquals(swl.size(), 1, "unexpected list length");
assertWrapperHas(swl.get(0), src, Kind.IMPORT, "import", "?????");
assertPosition(swl.get(0), src, 7, 4);
Snippet f = key(assertEvalFail(src));
assertEquals(f.kind(), Kind.ERRONEOUS);
assertEquals(((ErroneousSnippet)f).probableKind(), Kind.IMPORT);
SnippetWrapper sw = getState().sourceCodeAnalysis().wrapper(f);
assertWrapperHas(sw, src, Kind.IMPORT, "import", "?????");
assertPosition(swl.get(0), src, 0, 6);
}
public void testErroneous() {
String src = "@@@@@@@@@@";
List<SnippetWrapper> swl = getState().sourceCodeAnalysis().wrappers(src);
assertEquals(swl.size(), 1, "unexpected list length");
assertWrapperHas(swl.get(0), src, Kind.ERRONEOUS, "@@@@@@@@@@");
assertPosition(swl.get(0), src, 0, 10);
Snippet f = key(assertEvalFail(src));
assertEquals(f.kind(), Kind.ERRONEOUS);
assertEquals(((ErroneousSnippet)f).probableKind(), Kind.ERRONEOUS);
SnippetWrapper sw = getState().sourceCodeAnalysis().wrapper(f);
assertWrapperHas(sw, src, Kind.ERRONEOUS, "@@@@@@@@@@");
assertPosition(swl.get(0), src, 0, 10);
}
public void testEmpty() {
String src = "";
List<SnippetWrapper> swl = getState().sourceCodeAnalysis().wrappers(src);
assertEquals(swl.size(), 0, "expected empty list");
}
public void testDependencies() {
Snippet a = key(assertEval("int aaa = 6;", added(VALID)));
Snippet b = key(assertEval("class B { B(int x) { aaa = x; } }", added(VALID)));
Snippet c = key(assertEval("B ccc() { return new B(aaa); }", added(VALID)));
Collection<Snippet> dep;
dep = getState().sourceCodeAnalysis().dependents(c);
assertEquals(dep.size(), 0);
dep = getState().sourceCodeAnalysis().dependents(b);
assertEquals(dep.size(), 1);
assertTrue(dep.contains(c));
dep = getState().sourceCodeAnalysis().dependents(a);
assertEquals(dep.size(), 2);
assertTrue(dep.contains(c));
assertTrue(dep.contains(b));
}
private void assertWrapperHas(SnippetWrapper sw, String source, Kind kind, String... has) {
assertEquals(sw.source(), source);
assertEquals(sw.kind(), kind);
if (kind == Kind.IMPORT) {
assertTrue(sw.wrapped().contains("import"));
} else {
String cn = sw.fullClassName();
int idx = cn.lastIndexOf(".");
assertTrue(sw.wrapped().contains(cn.substring(idx+1)));
assertTrue(sw.wrapped().contains("class"));
}
for (String s : has) {
assertTrue(sw.wrapped().contains(s));
}
}
private void assertPosition(SnippetWrapper sw, String source, int start, int length) {
int wpg = sw.sourceToWrappedPosition(start);
assertEquals(sw.wrapped().substring(wpg, wpg+length),
source.substring(start, start+length),
"position " + wpg + " in " + sw.wrapped());
assertEquals(sw.wrappedToSourcePosition(wpg), start);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册