8159111: JShell API: Add access to wrappers and dependencies
Reviewed-by: jlahoda
This commit is contained in:
parent
c8f9b12b5c
commit
850b281ff0
@ -55,8 +55,9 @@ public abstract class DeclarationSnippet extends PersistentSnippet {
|
||||
DeclarationSnippet(DeclarationKey key, String userSource, Wrap guts,
|
||||
String unitName, SubKind subkind, Wrap corralled,
|
||||
Collection<String> declareReferences,
|
||||
Collection<String> bodyReferences) {
|
||||
super(key, userSource, guts, unitName, subkind);
|
||||
Collection<String> bodyReferences,
|
||||
DiagList syntheticDiags) {
|
||||
super(key, userSource, guts, unitName, subkind, syntheticDiags);
|
||||
this.corralled = corralled;
|
||||
this.declareReferences = declareReferences;
|
||||
this.bodyReferences = bodyReferences;
|
||||
|
@ -28,8 +28,7 @@ package jdk.jshell;
|
||||
import jdk.jshell.Key.ErroneousKey;
|
||||
|
||||
/**
|
||||
* A snippet of code that is not valid Java programming language code, and for
|
||||
* which the kind of snippet could not be determined.
|
||||
* A snippet of code that is not valid Java programming language code.
|
||||
* The Kind is {@link jdk.jshell.Snippet.Kind#ERRONEOUS ERRONEOUS}.
|
||||
* <p>
|
||||
* <code>ErroneousSnippet</code> is immutable: an access to
|
||||
@ -38,7 +37,21 @@ import jdk.jshell.Key.ErroneousKey;
|
||||
*/
|
||||
public class ErroneousSnippet extends Snippet {
|
||||
|
||||
ErroneousSnippet(ErroneousKey key, String userSource, Wrap guts, SubKind subkind) {
|
||||
super(key, userSource, guts, null, subkind);
|
||||
private final Kind probableKind;
|
||||
|
||||
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;
|
||||
import jdk.jshell.Key.ErroneousKey;
|
||||
import jdk.jshell.Key.MethodKey;
|
||||
import jdk.jshell.Key.TypeDeclKey;
|
||||
import jdk.jshell.Snippet.Kind;
|
||||
import jdk.jshell.Snippet.SubKind;
|
||||
import jdk.jshell.TaskFactory.AnalyzeTask;
|
||||
import jdk.jshell.TaskFactory.BaseTask;
|
||||
@ -62,6 +63,7 @@ import jdk.jshell.Wrap.Range;
|
||||
import jdk.jshell.Snippet.Status;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
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.jshell.Util.DOIT_METHOD_NAME;
|
||||
import static jdk.jshell.Util.PREFIX_PATTERN;
|
||||
@ -89,24 +91,75 @@ class Eval {
|
||||
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> 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());
|
||||
if (compileSource.length() == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// String folding messes up position information.
|
||||
ParseTask pt = state.taskFactory.new ParseTask(compileSource);
|
||||
if (pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
|
||||
return compileFailResult(pt, userSource);
|
||||
}
|
||||
|
||||
List<? extends Tree> units = pt.units();
|
||||
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);
|
||||
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);
|
||||
switch (unitTree.getKind()) {
|
||||
case IMPORT:
|
||||
@ -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);
|
||||
Matcher mat = IMPORT_PATTERN.matcher(compileSource);
|
||||
String fullname;
|
||||
@ -155,7 +208,7 @@ class Eval {
|
||||
: (isStatic ? SINGLE_STATIC_IMPORT_SUBKIND : SINGLE_TYPE_IMPORT_SUBKIND);
|
||||
Snippet snip = new ImportSnippet(state.keyMap.keyForImport(keyName, snippetKind),
|
||||
userSource, guts, fullname, name, snippetKind, fullkey, isStatic, isStar);
|
||||
return declare(snip);
|
||||
return singletonList(snip);
|
||||
}
|
||||
|
||||
private static class EvalPretty extends Pretty {
|
||||
@ -187,8 +240,8 @@ class Eval {
|
||||
}
|
||||
}
|
||||
|
||||
private List<SnippetEvent> processVariables(String userSource, List<? extends Tree> units, String compileSource, ParseTask pt) {
|
||||
List<SnippetEvent> allEvents = new ArrayList<>();
|
||||
private List<Snippet> processVariables(String userSource, List<? extends Tree> units, String compileSource, ParseTask pt) {
|
||||
List<Snippet> snippets = new ArrayList<>();
|
||||
TreeDissector dis = TreeDissector.createByFirstClass(pt);
|
||||
for (Tree unitTree : units) {
|
||||
VariableTree vt = (VariableTree) unitTree;
|
||||
@ -224,18 +277,16 @@ class Eval {
|
||||
int nameEnd = nameStart + name.length();
|
||||
Range rname = new Range(nameStart, nameEnd);
|
||||
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,
|
||||
name, subkind, typeName,
|
||||
tds.declareReferences());
|
||||
DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true);
|
||||
List<SnippetEvent> res1 = declare(snip, modDiag);
|
||||
allEvents.addAll(res1);
|
||||
tds.declareReferences(), modDiag);
|
||||
snippets.add(snip);
|
||||
}
|
||||
|
||||
return allEvents;
|
||||
return snippets;
|
||||
}
|
||||
|
||||
private List<SnippetEvent> processExpression(String userSource, String compileSource) {
|
||||
private List<Snippet> processExpression(String userSource, String compileSource) {
|
||||
String name = null;
|
||||
ExpressionInfo ei = typeOfExpression(compileSource);
|
||||
ExpressionTree assignVar;
|
||||
@ -266,7 +317,7 @@ class Eval {
|
||||
guts = Wrap.tempVarWrap(compileSource, typeName, name);
|
||||
Collection<String> declareReferences = null; //TODO
|
||||
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 {
|
||||
guts = Wrap.methodReturnWrap(compileSource);
|
||||
snip = new ExpressionSnippet(state.keyMap.keyForExpression(name, typeName), userSource, guts,
|
||||
@ -282,15 +333,15 @@ class Eval {
|
||||
at = trialCompile(guts);
|
||||
}
|
||||
if (at.hasErrors()) {
|
||||
return compileFailResult(at, userSource);
|
||||
return compileFailResult(at, userSource, Kind.EXPRESSION);
|
||||
}
|
||||
}
|
||||
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();
|
||||
tds.scan(unitTree);
|
||||
|
||||
@ -306,11 +357,11 @@ class Eval {
|
||||
Wrap guts = Wrap.classMemberWrap(compileSource);
|
||||
Snippet snip = new TypeDeclSnippet(key, userSource, guts,
|
||||
name, snippetKind,
|
||||
corralled, tds.declareReferences(), tds.bodyReferences());
|
||||
return declare(snip, modDiag);
|
||||
corralled, tds.declareReferences(), tds.bodyReferences(), 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);
|
||||
// Check for unreachable by trying
|
||||
AnalyzeTask at = trialCompile(guts);
|
||||
@ -325,15 +376,15 @@ class Eval {
|
||||
at = trialCompile(guts);
|
||||
}
|
||||
if (at.hasErrors()) {
|
||||
return compileFailResult(at, userSource);
|
||||
return compileFailResult(at, userSource, Kind.STATEMENT);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return compileFailResult(at, userSource);
|
||||
return compileFailResult(at, userSource, Kind.STATEMENT);
|
||||
}
|
||||
}
|
||||
Snippet snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts);
|
||||
return declare(snip);
|
||||
return singletonList(snip);
|
||||
}
|
||||
|
||||
private AnalyzeTask trialCompile(Wrap guts) {
|
||||
@ -341,7 +392,7 @@ class Eval {
|
||||
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();
|
||||
tds.scan(unitTree);
|
||||
TreeDissector dis = TreeDissector.createByFirstClass(pt);
|
||||
@ -360,7 +411,7 @@ class Eval {
|
||||
Wrap corralled = new Corraller(key.index(), pt.getContext()).corralMethod(mt);
|
||||
|
||||
if (modDiag.hasErrors()) {
|
||||
return compileFailResult(modDiag, userSource);
|
||||
return compileFailResult(modDiag, userSource, Kind.METHOD);
|
||||
}
|
||||
Wrap guts = Wrap.classMemberWrap(compileSource);
|
||||
Range typeRange = dis.treeToRange(returnType);
|
||||
@ -368,37 +419,76 @@ class Eval {
|
||||
|
||||
Snippet snip = new MethodSnippet(key, userSource, guts,
|
||||
name, signature,
|
||||
corralled, tds.declareReferences(), tds.bodyReferences());
|
||||
return declare(snip, modDiag);
|
||||
corralled, tds.declareReferences(), tds.bodyReferences(), 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 userSource the incoming bad user source
|
||||
* @return a rejected snippet event
|
||||
* @return a rejected snippet
|
||||
*/
|
||||
private List<SnippetEvent> compileFailResult(BaseTask xt, String userSource) {
|
||||
return compileFailResult(xt.getDiagnostics(), userSource);
|
||||
private List<Snippet> compileFailResult(BaseTask xt, String userSource, Kind probableKind) {
|
||||
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 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();
|
||||
Snippet snip = new ErroneousSnippet(key, userSource, null, SubKind.UNKNOWN_SUBKIND);
|
||||
Snippet snip = new ErroneousSnippet(key, userSource, null,
|
||||
probableKind, SubKind.UNKNOWN_SUBKIND);
|
||||
snip.setFailed(diags);
|
||||
state.maps.installSnippet(snip);
|
||||
return Collections.singletonList(new SnippetEvent(
|
||||
snip, Status.NONEXISTENT, Status.REJECTED,
|
||||
false, null, null, null)
|
||||
);
|
||||
|
||||
// Install wrapper for query by SourceCodeAnalysis.wrapper
|
||||
String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, true).cleared());
|
||||
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) {
|
||||
@ -430,10 +520,6 @@ class Eval {
|
||||
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) {
|
||||
Unit c = new Unit(state, si, null, generatedDiagnostics);
|
||||
Set<Unit> ins = new LinkedHashSet<>();
|
||||
|
@ -39,7 +39,7 @@ import jdk.jshell.Key.ExpressionKey;
|
||||
public class ExpressionSnippet extends Snippet {
|
||||
|
||||
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 {
|
||||
ImportSnippet(ImportKey key, String userSource, Wrap guts,
|
||||
String fullname, String name, SubKind subkind, String fullkey,
|
||||
boolean isStatic, boolean isStar) {
|
||||
super(key, userSource, guts, name, subkind);
|
||||
super(key, userSource, guts, name, subkind, null);
|
||||
this.fullname = fullname;
|
||||
this.fullkey = fullkey;
|
||||
this.isStatic = isStatic;
|
||||
|
@ -377,7 +377,9 @@ public class JShell implements AutoCloseable {
|
||||
* Evaluate the input String, including definition and/or execution, if
|
||||
* applicable. The input is checked for errors, unless the errors can be
|
||||
* 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,
|
||||
* statement, variable declaration, method declaration, class declaration,
|
||||
* or import.
|
||||
|
@ -44,8 +44,10 @@ public class MethodSnippet extends DeclarationSnippet {
|
||||
|
||||
MethodSnippet(MethodKey key, String userSource, Wrap guts,
|
||||
String name, String signature, Wrap corralled,
|
||||
Collection<String> declareReferences, Collection<String> bodyReferences) {
|
||||
super(key, userSource, guts, name, SubKind.METHOD_SUBKIND, corralled, declareReferences, bodyReferences);
|
||||
Collection<String> declareReferences, Collection<String> bodyReferences,
|
||||
DiagList syntheticDiags) {
|
||||
super(key, userSource, guts, name, SubKind.METHOD_SUBKIND, corralled,
|
||||
declareReferences, bodyReferences, syntheticDiags);
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,6 @@
|
||||
|
||||
package jdk.jshell;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
|
@ -37,8 +37,9 @@ package jdk.jshell;
|
||||
*/
|
||||
public abstract class PersistentSnippet extends Snippet {
|
||||
|
||||
PersistentSnippet(Key key, String userSource, Wrap guts, String unitName, SubKind subkind) {
|
||||
super(key, userSource, guts, unitName, subkind);
|
||||
PersistentSnippet(Key key, String userSource, Wrap guts, String unitName,
|
||||
SubKind subkind, DiagList syntheticDiags) {
|
||||
super(key, userSource, guts, unitName, subkind, syntheticDiags);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -563,13 +563,18 @@ public abstract class Snippet {
|
||||
private Status status;
|
||||
private List<String> unresolved;
|
||||
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.source = userSource;
|
||||
this.guts = guts;
|
||||
this.unitName = unitName;
|
||||
this.subkind = subkind;
|
||||
this.syntheticDiags = syntheticDiags==null
|
||||
? new DiagList()
|
||||
: syntheticDiags;
|
||||
this.status = Status.NONEXISTENT;
|
||||
setSequenceNumber(0);
|
||||
}
|
||||
@ -644,6 +649,10 @@ public abstract class Snippet {
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
DiagList syntheticDiags() {
|
||||
return syntheticDiags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the corralled guts
|
||||
*/
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package jdk.jshell;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -91,6 +92,51 @@ public abstract class SourceCodeAnalysis {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@ -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}
|
||||
* 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
|
||||
@ -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 {
|
||||
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) {
|
||||
switch (path.getLeaf().getKind()) {
|
||||
case ARRAY_TYPE:
|
||||
|
@ -39,6 +39,6 @@ import jdk.jshell.Key.StatementKey;
|
||||
public class StatementSnippet extends Snippet {
|
||||
|
||||
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 {
|
||||
TypeDeclSnippet(TypeDeclKey key, String userSource, Wrap guts,
|
||||
String unitName, SubKind subkind, Wrap corralled,
|
||||
Collection<String> declareReferences,
|
||||
Collection<String> bodyReferences) {
|
||||
super(key, userSource, guts, unitName, subkind, corralled, declareReferences, bodyReferences);
|
||||
Collection<String> bodyReferences,
|
||||
DiagList syntheticDiags) {
|
||||
super(key, userSource, guts, unitName, subkind, corralled,
|
||||
declareReferences, bodyReferences, syntheticDiags);
|
||||
}
|
||||
|
||||
/**** internal access ****/
|
||||
|
@ -43,8 +43,10 @@ public class VarSnippet extends DeclarationSnippet {
|
||||
|
||||
VarSnippet(VarKey key, String userSource, Wrap guts,
|
||||
String name, SubKind subkind, String typeName,
|
||||
Collection<String> declareReferences) {
|
||||
super(key, userSource, guts, name, subkind, null, declareReferences, null);
|
||||
Collection<String> declareReferences,
|
||||
DiagList syntheticDiags) {
|
||||
super(key, userSource, guts, name, subkind, null, declareReferences,
|
||||
null, syntheticDiags);
|
||||
this.typeName = typeName;
|
||||
}
|
||||
|
||||
|
205
langtools/test/jdk/jshell/WrapperTest.java
Normal file
205
langtools/test/jdk/jshell/WrapperTest.java
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user