8191842: JShell: Inferred type information is lost when assigning types to a \"var\"
For vars, upgrading all anonymous classes to member classes; stripping intersection types from fields before writing. Reviewed-by: rfield
This commit is contained in:
parent
1de9d061ec
commit
e332323760
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -36,6 +36,7 @@ import javax.lang.model.element.Modifier;
|
|||||||
import com.sun.source.tree.ArrayTypeTree;
|
import com.sun.source.tree.ArrayTypeTree;
|
||||||
import com.sun.source.tree.AssignmentTree;
|
import com.sun.source.tree.AssignmentTree;
|
||||||
import com.sun.source.tree.ClassTree;
|
import com.sun.source.tree.ClassTree;
|
||||||
|
import com.sun.source.tree.ExpressionStatementTree;
|
||||||
import com.sun.source.tree.ExpressionTree;
|
import com.sun.source.tree.ExpressionTree;
|
||||||
import com.sun.source.tree.IdentifierTree;
|
import com.sun.source.tree.IdentifierTree;
|
||||||
import com.sun.source.tree.MethodTree;
|
import com.sun.source.tree.MethodTree;
|
||||||
@ -43,6 +44,7 @@ import com.sun.source.tree.ModifiersTree;
|
|||||||
import com.sun.source.tree.NewClassTree;
|
import com.sun.source.tree.NewClassTree;
|
||||||
import com.sun.source.tree.Tree;
|
import com.sun.source.tree.Tree;
|
||||||
import com.sun.source.tree.VariableTree;
|
import com.sun.source.tree.VariableTree;
|
||||||
|
import com.sun.source.util.TreeScanner;
|
||||||
import com.sun.tools.javac.tree.JCTree;
|
import com.sun.tools.javac.tree.JCTree;
|
||||||
import com.sun.tools.javac.tree.Pretty;
|
import com.sun.tools.javac.tree.Pretty;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -52,6 +54,8 @@ import java.util.Arrays;
|
|||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo;
|
import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo;
|
||||||
|
import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo.AnonymousDescription;
|
||||||
|
import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo.AnonymousDescription.VariableDesc;
|
||||||
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;
|
||||||
@ -60,6 +64,7 @@ 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;
|
||||||
import jdk.jshell.TaskFactory.ParseTask;
|
import jdk.jshell.TaskFactory.ParseTask;
|
||||||
|
import jdk.jshell.Util.Pair;
|
||||||
import jdk.jshell.Wrap.CompoundWrap;
|
import jdk.jshell.Wrap.CompoundWrap;
|
||||||
import jdk.jshell.Wrap.Range;
|
import jdk.jshell.Wrap.Range;
|
||||||
import jdk.jshell.Snippet.Status;
|
import jdk.jshell.Snippet.Status;
|
||||||
@ -74,6 +79,7 @@ import jdk.jshell.spi.ExecutionControl.UserException;
|
|||||||
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 java.util.Collections.singletonList;
|
||||||
|
import com.sun.tools.javac.code.Symbol.TypeSymbol;
|
||||||
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;
|
||||||
@ -98,6 +104,11 @@ class Eval {
|
|||||||
|
|
||||||
private int varNumber = 0;
|
private int varNumber = 0;
|
||||||
|
|
||||||
|
/* The number of anonymous innerclasses seen so far. Used to generate unique
|
||||||
|
* names of these classes.
|
||||||
|
*/
|
||||||
|
private int anonCount = 0;
|
||||||
|
|
||||||
private final JShell state;
|
private final JShell state;
|
||||||
|
|
||||||
// The set of names of methods on Object
|
// The set of names of methods on Object
|
||||||
@ -203,7 +214,7 @@ class Eval {
|
|||||||
case VARIABLE:
|
case VARIABLE:
|
||||||
return processVariables(userSource, units, compileSourceInt, pt);
|
return processVariables(userSource, units, compileSourceInt, pt);
|
||||||
case EXPRESSION_STATEMENT:
|
case EXPRESSION_STATEMENT:
|
||||||
return processExpression(userSource, compileSourceInt);
|
return processExpression(userSource, unitTree, compileSourceInt, pt);
|
||||||
case CLASS:
|
case CLASS:
|
||||||
return processClass(userSource, unitTree, compileSourceInt, SubKind.CLASS_SUBKIND, pt);
|
return processClass(userSource, unitTree, compileSourceInt, SubKind.CLASS_SUBKIND, pt);
|
||||||
case ENUM:
|
case ENUM:
|
||||||
@ -285,15 +296,19 @@ class Eval {
|
|||||||
String name = vt.getName().toString();
|
String name = vt.getName().toString();
|
||||||
String typeName;
|
String typeName;
|
||||||
String fullTypeName;
|
String fullTypeName;
|
||||||
|
String displayType;
|
||||||
|
boolean hasEnhancedType = false;
|
||||||
TreeDependencyScanner tds = new TreeDependencyScanner();
|
TreeDependencyScanner tds = new TreeDependencyScanner();
|
||||||
Wrap typeWrap;
|
Wrap typeWrap;
|
||||||
Wrap anonDeclareWrap = null;
|
Wrap anonDeclareWrap = null;
|
||||||
Wrap winit = null;
|
Wrap winit = null;
|
||||||
|
boolean enhancedDesugaring = false;
|
||||||
|
Set<String> anonymousClasses = Collections.emptySet();
|
||||||
StringBuilder sbBrackets = new StringBuilder();
|
StringBuilder sbBrackets = new StringBuilder();
|
||||||
Tree baseType = vt.getType();
|
Tree baseType = vt.getType();
|
||||||
if (baseType != null) {
|
if (baseType != null) {
|
||||||
tds.scan(baseType); // Not dependent on initializer
|
tds.scan(baseType); // Not dependent on initializer
|
||||||
fullTypeName = typeName = EvalPretty.prettyExpr((JCTree) vt.getType(), false);
|
fullTypeName = displayType = typeName = EvalPretty.prettyExpr((JCTree) vt.getType(), false);
|
||||||
while (baseType instanceof ArrayTypeTree) {
|
while (baseType instanceof ArrayTypeTree) {
|
||||||
//TODO handle annotations too
|
//TODO handle annotations too
|
||||||
baseType = ((ArrayTypeTree) baseType).getType();
|
baseType = ((ArrayTypeTree) baseType).getType();
|
||||||
@ -311,83 +326,27 @@ class Eval {
|
|||||||
Range rinit = dis.treeToRange(init);
|
Range rinit = dis.treeToRange(init);
|
||||||
String initCode = rinit.part(compileSource);
|
String initCode = rinit.part(compileSource);
|
||||||
ExpressionInfo ei =
|
ExpressionInfo ei =
|
||||||
ExpressionToTypeInfo.localVariableTypeForInitializer(initCode, state);
|
ExpressionToTypeInfo.localVariableTypeForInitializer(initCode, state, false);
|
||||||
typeName = ei == null ? "java.lang.Object" : ei.typeName;
|
if (ei != null) {
|
||||||
fullTypeName = ei == null ? "java.lang.Object" : ei.fullTypeName;
|
typeName = ei.declareTypeName;
|
||||||
if (ei != null && init.getKind() == Tree.Kind.NEW_CLASS &&
|
fullTypeName = ei.fullTypeName;
|
||||||
((NewClassTree) init).getClassBody() != null) {
|
displayType = ei.displayTypeName;
|
||||||
NewClassTree nct = (NewClassTree) init;
|
|
||||||
StringBuilder constructor = new StringBuilder();
|
|
||||||
constructor.append(fullTypeName).append("(");
|
|
||||||
String sep = "";
|
|
||||||
if (ei.enclosingInstanceType != null) {
|
|
||||||
constructor.append(ei.enclosingInstanceType);
|
|
||||||
constructor.append(" encl");
|
|
||||||
sep = ", ";
|
|
||||||
}
|
|
||||||
int idx = 0;
|
|
||||||
for (String type : ei.parameterTypes) {
|
|
||||||
constructor.append(sep);
|
|
||||||
constructor.append(type);
|
|
||||||
constructor.append(" ");
|
|
||||||
constructor.append("arg" + idx++);
|
|
||||||
sep = ", ";
|
|
||||||
}
|
|
||||||
if (ei.enclosingInstanceType != null) {
|
|
||||||
constructor.append(") { encl.super (");
|
|
||||||
} else {
|
|
||||||
constructor.append(") { super (");
|
|
||||||
}
|
|
||||||
sep = "";
|
|
||||||
for (int i = 0; i < idx; i++) {
|
|
||||||
constructor.append(sep);
|
|
||||||
constructor.append("arg" + i++);
|
|
||||||
sep = ", ";
|
|
||||||
}
|
|
||||||
constructor.append("); }");
|
|
||||||
List<? extends Tree> members = nct.getClassBody().getMembers();
|
|
||||||
Range bodyRange = dis.treeListToRange(members);
|
|
||||||
Wrap bodyWrap;
|
|
||||||
|
|
||||||
if (bodyRange != null) {
|
hasEnhancedType = !typeName.equals(fullTypeName);
|
||||||
bodyWrap = Wrap.rangeWrap(compileSource, bodyRange);
|
|
||||||
} else {
|
|
||||||
bodyWrap = Wrap.simpleWrap(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
Range argRange = dis.treeListToRange(nct.getArguments());
|
enhancedDesugaring = !ei.isPrimitiveType;
|
||||||
Wrap argWrap;
|
|
||||||
|
|
||||||
if (argRange != null) {
|
Pair<Wrap, Wrap> anonymous2Member =
|
||||||
argWrap = Wrap.rangeWrap(compileSource, argRange);
|
anonymous2Member(ei, compileSource, rinit, dis, init);
|
||||||
} else {
|
anonDeclareWrap = anonymous2Member.first;
|
||||||
argWrap = Wrap.simpleWrap(" ");
|
winit = anonymous2Member.second;
|
||||||
}
|
anonymousClasses = ei.anonymousClasses.stream().map(ad -> ad.declareTypeName).collect(Collectors.toSet());
|
||||||
|
} else {
|
||||||
if (ei.enclosingInstanceType != null) {
|
displayType = fullTypeName = typeName = "java.lang.Object";
|
||||||
Range enclosingRanges =
|
|
||||||
dis.treeToRange(nct.getEnclosingExpression());
|
|
||||||
Wrap enclosingWrap = Wrap.rangeWrap(compileSource, enclosingRanges);
|
|
||||||
argWrap = argRange != null ? new CompoundWrap(enclosingWrap,
|
|
||||||
Wrap.simpleWrap(","),
|
|
||||||
argWrap)
|
|
||||||
: enclosingWrap;
|
|
||||||
}
|
|
||||||
Wrap hwrap = Wrap.simpleWrap("public static class " + fullTypeName +
|
|
||||||
(ei.isClass ? " extends " : " implements ") +
|
|
||||||
typeName + " { " + constructor);
|
|
||||||
anonDeclareWrap = new CompoundWrap(hwrap, bodyWrap, Wrap.simpleWrap("}"));
|
|
||||||
winit = new CompoundWrap("new " + fullTypeName + "(", argWrap, ")");
|
|
||||||
|
|
||||||
String superType = typeName;
|
|
||||||
|
|
||||||
typeName = fullTypeName;
|
|
||||||
fullTypeName = ei.isClass ? "<anonymous class extending " + superType + ">"
|
|
||||||
: "<anonymous class implementing " + superType + ">";
|
|
||||||
}
|
}
|
||||||
tds.scan(init);
|
tds.scan(init);
|
||||||
} else {
|
} else {
|
||||||
fullTypeName = typeName = "java.lang.Object";
|
displayType = fullTypeName = typeName = "java.lang.Object";
|
||||||
}
|
}
|
||||||
typeWrap = Wrap.identityWrap(typeName);
|
typeWrap = Wrap.identityWrap(typeName);
|
||||||
}
|
}
|
||||||
@ -411,17 +370,193 @@ 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, typeWrap, sbBrackets.toString(), rname,
|
Wrap guts = Wrap.varWrap(compileSource, typeWrap, sbBrackets.toString(), rname,
|
||||||
winit, anonDeclareWrap);
|
winit, enhancedDesugaring, anonDeclareWrap);
|
||||||
DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true);
|
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, fullTypeName,
|
name, subkind, displayType, hasEnhancedType ? fullTypeName : null, anonymousClasses,
|
||||||
tds.declareReferences(), modDiag);
|
tds.declareReferences(), modDiag);
|
||||||
snippets.add(snip);
|
snippets.add(snip);
|
||||||
}
|
}
|
||||||
return snippets;
|
return snippets;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Snippet> processExpression(String userSource, String compileSource) {
|
/**Convert anonymous classes in "init" to member classes, based
|
||||||
|
* on the additional information from ExpressionInfo.anonymousClasses.
|
||||||
|
*
|
||||||
|
* This means:
|
||||||
|
* -if the code in the anonymous class captures any variables from the
|
||||||
|
* enclosing context, create fields for them
|
||||||
|
* -creating an explicit constructor that:
|
||||||
|
* --if the new class expression has a base/enclosing expression, make it an
|
||||||
|
* explicit constructor parameter "encl" and use "encl.super" when invoking
|
||||||
|
* the supertype constructor
|
||||||
|
* --if the (used) supertype constructor has any parameters, declare them
|
||||||
|
* as explicit parameters of the constructor, and pass them to the super
|
||||||
|
* constructor
|
||||||
|
* --if the code in the anonymous class captures any variables from the
|
||||||
|
* enclosing context, make them an explicit paramters of the constructor
|
||||||
|
* and assign to respective fields.
|
||||||
|
* --if there are any explicit fields with initializers in the anonymous class,
|
||||||
|
* move the initializers at the end of the constructor (after the captured fields
|
||||||
|
* are assigned, so that the initializers of these fields can use them).
|
||||||
|
* -from the captured variables fields, constructor, and existing members
|
||||||
|
* (with cleared field initializers), create an explicit class that extends or
|
||||||
|
* implements the supertype of the anonymous class.
|
||||||
|
*
|
||||||
|
* This method returns two wraps: the first contains the class declarations for the
|
||||||
|
* converted classes, the first one should be used instead of "init" in the variable
|
||||||
|
* declaration.
|
||||||
|
*/
|
||||||
|
private Pair<Wrap, Wrap> anonymous2Member(ExpressionInfo ei,
|
||||||
|
String compileSource,
|
||||||
|
Range rinit,
|
||||||
|
TreeDissector dis,
|
||||||
|
Tree init) {
|
||||||
|
List<Wrap> anonymousDeclarations = new ArrayList<>();
|
||||||
|
List<Wrap> partitionedInit = new ArrayList<>();
|
||||||
|
int lastPos = rinit.begin;
|
||||||
|
com.sun.tools.javac.util.List<NewClassTree> toConvert =
|
||||||
|
ExpressionToTypeInfo.listAnonymousClassesToConvert(init);
|
||||||
|
com.sun.tools.javac.util.List<AnonymousDescription> descriptions =
|
||||||
|
ei.anonymousClasses;
|
||||||
|
while (toConvert.nonEmpty() && descriptions.nonEmpty()) {
|
||||||
|
NewClassTree node = toConvert.head;
|
||||||
|
AnonymousDescription ad = descriptions.head;
|
||||||
|
|
||||||
|
toConvert = toConvert.tail;
|
||||||
|
descriptions = descriptions.tail;
|
||||||
|
|
||||||
|
List<Object> classBodyParts = new ArrayList<>();
|
||||||
|
//declarations of the captured variables:
|
||||||
|
for (VariableDesc vd : ad.capturedVariables) {
|
||||||
|
classBodyParts.add(vd.type + " " + vd.name + ";\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Object> constructorParts = new ArrayList<>();
|
||||||
|
constructorParts.add(ad.declareTypeName + "(");
|
||||||
|
String sep = "";
|
||||||
|
//add the parameter for the base/enclosing expression, if any:
|
||||||
|
if (ad.enclosingInstanceType != null) {
|
||||||
|
constructorParts.add(ad.enclosingInstanceType + " encl");
|
||||||
|
sep = ", ";
|
||||||
|
}
|
||||||
|
int idx = 0;
|
||||||
|
//add parameters of the super constructor, if any:
|
||||||
|
for (String type : ad.parameterTypes) {
|
||||||
|
constructorParts.add(sep);
|
||||||
|
constructorParts.add(type + " " + "arg" + idx++);
|
||||||
|
sep = ", ";
|
||||||
|
}
|
||||||
|
//add parameters for the captured variables:
|
||||||
|
for (VariableDesc vd : ad.capturedVariables) {
|
||||||
|
constructorParts.add(sep);
|
||||||
|
constructorParts.add(vd.type + " " + "cap$" + vd.name);
|
||||||
|
sep = ", ";
|
||||||
|
}
|
||||||
|
//construct super constructor call:
|
||||||
|
if (ad.enclosingInstanceType != null) {
|
||||||
|
//if there's an enclosing instance, call super on it:
|
||||||
|
constructorParts.add(") { encl.super (");
|
||||||
|
} else {
|
||||||
|
constructorParts.add(") { super (");
|
||||||
|
}
|
||||||
|
sep = "";
|
||||||
|
for (int i = 0; i < idx; i++) {
|
||||||
|
constructorParts.add(sep);
|
||||||
|
constructorParts.add("arg" + i);
|
||||||
|
sep = ", ";
|
||||||
|
}
|
||||||
|
constructorParts.add(");");
|
||||||
|
//initialize the captured variables:
|
||||||
|
for (VariableDesc vd : ad.capturedVariables) {
|
||||||
|
constructorParts.add("this." + vd.name + " = " + "cap$" + vd.name + ";\n");
|
||||||
|
}
|
||||||
|
List<? extends Tree> members =
|
||||||
|
node.getClassBody().getMembers();
|
||||||
|
for (Tree member : members) {
|
||||||
|
if (member.getKind() == Tree.Kind.VARIABLE) {
|
||||||
|
VariableTree vt = (VariableTree) member;
|
||||||
|
|
||||||
|
if (vt.getInitializer() != null) {
|
||||||
|
//for variables with initializer, explicitly move the initializer
|
||||||
|
//to the constructor after the captured variables as assigned
|
||||||
|
//(the initializers would otherwise run too early):
|
||||||
|
Range wholeVar = dis.treeToRange(vt);
|
||||||
|
int name = ((JCTree) vt).pos;
|
||||||
|
classBodyParts.add(new CompoundWrap(Wrap.rangeWrap(compileSource,
|
||||||
|
new Range(wholeVar.begin, name)),
|
||||||
|
vt.getName().toString(),
|
||||||
|
";\n"));
|
||||||
|
constructorParts.add(Wrap.rangeWrap(compileSource,
|
||||||
|
new Range(name, wholeVar.end)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
classBodyParts.add(Wrap.rangeWrap(compileSource,
|
||||||
|
dis.treeToRange(member)));
|
||||||
|
}
|
||||||
|
|
||||||
|
constructorParts.add("}");
|
||||||
|
|
||||||
|
//construct the member class:
|
||||||
|
classBodyParts.add(new CompoundWrap(constructorParts.toArray()));
|
||||||
|
|
||||||
|
Wrap classBodyWrap = new CompoundWrap(classBodyParts.toArray());
|
||||||
|
|
||||||
|
anonymousDeclarations.add(new CompoundWrap("public static class ", ad.declareTypeName,
|
||||||
|
(ad.isClass ? " extends " : " implements "),
|
||||||
|
ad.superTypeName, " { ", classBodyWrap, "}"));
|
||||||
|
|
||||||
|
//change the new class expression to use the newly created member type:
|
||||||
|
Range argRange = dis.treeListToRange(node.getArguments());
|
||||||
|
Wrap argWrap;
|
||||||
|
|
||||||
|
if (argRange != null) {
|
||||||
|
argWrap = Wrap.rangeWrap(compileSource, argRange);
|
||||||
|
} else {
|
||||||
|
argWrap = Wrap.simpleWrap(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ad.enclosingInstanceType != null) {
|
||||||
|
//if there's an enclosing expression, set it as the first parameter:
|
||||||
|
Range enclosingRanges =
|
||||||
|
dis.treeToRange(node.getEnclosingExpression());
|
||||||
|
Wrap enclosingWrap = Wrap.rangeWrap(compileSource, enclosingRanges);
|
||||||
|
argWrap = argRange != null ? new CompoundWrap(enclosingWrap,
|
||||||
|
Wrap.simpleWrap(","),
|
||||||
|
argWrap)
|
||||||
|
: enclosingWrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
Range current = dis.treeToRange(node);
|
||||||
|
String capturedArgs;
|
||||||
|
if (!ad.capturedVariables.isEmpty()) {
|
||||||
|
capturedArgs = (ad.parameterTypes.isEmpty() ? "" : ", ") +
|
||||||
|
ad.capturedVariables.stream()
|
||||||
|
.map(vd -> vd.name)
|
||||||
|
.collect(Collectors.joining(","));
|
||||||
|
} else {
|
||||||
|
capturedArgs = "";
|
||||||
|
}
|
||||||
|
if (lastPos < current.begin)
|
||||||
|
partitionedInit.add(Wrap.rangeWrap(compileSource,
|
||||||
|
new Range(lastPos, current.begin)));
|
||||||
|
partitionedInit.add(new CompoundWrap("new " + ad.declareTypeName + "(",
|
||||||
|
argWrap,
|
||||||
|
capturedArgs,
|
||||||
|
")"));
|
||||||
|
lastPos = current.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastPos < rinit.end)
|
||||||
|
partitionedInit.add(Wrap.rangeWrap(compileSource, new Range(lastPos, rinit.end)));
|
||||||
|
|
||||||
|
return new Pair<>(new CompoundWrap(anonymousDeclarations.toArray()),
|
||||||
|
new CompoundWrap(partitionedInit.toArray()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Snippet> processExpression(String userSource, Tree tree, String compileSource, ParseTask pt) {
|
||||||
|
ExpressionStatementTree expr = (ExpressionStatementTree) tree;
|
||||||
String name = null;
|
String name = null;
|
||||||
ExpressionInfo ei = ExpressionToTypeInfo.expressionInfo(compileSource, state);
|
ExpressionInfo ei = ExpressionToTypeInfo.expressionInfo(compileSource, state);
|
||||||
ExpressionTree assignVar;
|
ExpressionTree assignVar;
|
||||||
@ -453,10 +588,31 @@ class Eval {
|
|||||||
name = "$" + ++varNumber;
|
name = "$" + ++varNumber;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
guts = Wrap.tempVarWrap(compileSource, ei.accessibleTypeName, name);
|
TreeDissector dis = TreeDissector.createByFirstClass(pt);
|
||||||
|
ExpressionInfo varEI =
|
||||||
|
ExpressionToTypeInfo.localVariableTypeForInitializer(compileSource, state, true);
|
||||||
|
String declareTypeName;
|
||||||
|
String fullTypeName;
|
||||||
|
String displayTypeName;
|
||||||
|
Set<String> anonymousClasses;
|
||||||
|
if (varEI != null) {
|
||||||
|
declareTypeName = varEI.declareTypeName;
|
||||||
|
fullTypeName = varEI.fullTypeName;
|
||||||
|
displayTypeName = varEI.displayTypeName;
|
||||||
|
|
||||||
|
Pair<Wrap, Wrap> anonymous2Member =
|
||||||
|
anonymous2Member(varEI, compileSource, new Range(0, compileSource.length()), dis, expr.getExpression());
|
||||||
|
guts = Wrap.tempVarWrap(anonymous2Member.second.wrapped(), declareTypeName, name, anonymous2Member.first);
|
||||||
|
anonymousClasses = varEI.anonymousClasses.stream().map(ad -> ad.declareTypeName).collect(Collectors.toSet());
|
||||||
|
} else {
|
||||||
|
declareTypeName = ei.accessibleTypeName;
|
||||||
|
displayTypeName = fullTypeName = typeName;
|
||||||
|
guts = Wrap.tempVarWrap(compileSource, declareTypeName, name, null);
|
||||||
|
anonymousClasses = Collections.emptySet();
|
||||||
|
}
|
||||||
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, null);
|
name, SubKind.TEMP_VAR_EXPRESSION_SUBKIND, displayTypeName, fullTypeName, anonymousClasses, 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,
|
||||||
@ -1059,4 +1215,7 @@ class Eval {
|
|||||||
: new DiagList(new ModifierDiagnostic(list, fatal));
|
: new DiagList(new ModifierDiagnostic(list, fatal));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String computeDeclareName(TypeSymbol ts) {
|
||||||
|
return Util.JSHELL_ANONYMOUS + "$" + Long.toUnsignedString(anonCount++);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,12 +25,21 @@
|
|||||||
|
|
||||||
package jdk.jshell;
|
package jdk.jshell;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import javax.lang.model.element.Element;
|
||||||
|
import javax.lang.model.element.ElementKind;
|
||||||
|
import javax.lang.model.element.VariableElement;
|
||||||
import com.sun.source.tree.ReturnTree;
|
import com.sun.source.tree.ReturnTree;
|
||||||
import com.sun.source.tree.ClassTree;
|
import com.sun.source.tree.ClassTree;
|
||||||
import com.sun.source.tree.CompilationUnitTree;
|
import com.sun.source.tree.CompilationUnitTree;
|
||||||
import com.sun.source.tree.ConditionalExpressionTree;
|
import com.sun.source.tree.ConditionalExpressionTree;
|
||||||
import com.sun.source.tree.ExpressionStatementTree;
|
|
||||||
import com.sun.source.tree.ExpressionTree;
|
import com.sun.source.tree.ExpressionTree;
|
||||||
|
import com.sun.source.tree.IdentifierTree;
|
||||||
import com.sun.source.tree.MethodInvocationTree;
|
import com.sun.source.tree.MethodInvocationTree;
|
||||||
import com.sun.source.tree.MethodTree;
|
import com.sun.source.tree.MethodTree;
|
||||||
import com.sun.source.tree.NewClassTree;
|
import com.sun.source.tree.NewClassTree;
|
||||||
@ -39,13 +48,21 @@ import com.sun.source.tree.Tree.Kind;
|
|||||||
import com.sun.source.tree.VariableTree;
|
import com.sun.source.tree.VariableTree;
|
||||||
import com.sun.source.util.TreePath;
|
import com.sun.source.util.TreePath;
|
||||||
import com.sun.source.util.TreePathScanner;
|
import com.sun.source.util.TreePathScanner;
|
||||||
|
import com.sun.source.util.TreeScanner;
|
||||||
import com.sun.tools.javac.code.Flags;
|
import com.sun.tools.javac.code.Flags;
|
||||||
import com.sun.tools.javac.code.Symbol;
|
import com.sun.tools.javac.code.Symbol;
|
||||||
|
import com.sun.tools.javac.code.Symbol.TypeSymbol;
|
||||||
import com.sun.tools.javac.code.Symtab;
|
import com.sun.tools.javac.code.Symtab;
|
||||||
import com.sun.tools.javac.code.Type;
|
import com.sun.tools.javac.code.Type;
|
||||||
import com.sun.tools.javac.code.Types;
|
import com.sun.tools.javac.code.Types;
|
||||||
|
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
|
||||||
|
import com.sun.tools.javac.tree.TreeInfo;
|
||||||
import com.sun.tools.javac.util.List;
|
import com.sun.tools.javac.util.List;
|
||||||
|
import com.sun.tools.javac.util.ListBuffer;
|
||||||
|
import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo.AnonymousDescription;
|
||||||
|
import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo.AnonymousDescription.VariableDesc;
|
||||||
import jdk.jshell.TaskFactory.AnalyzeTask;
|
import jdk.jshell.TaskFactory.AnalyzeTask;
|
||||||
|
import jdk.jshell.TypePrinter.AnonymousTypeKind;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute information about an expression string, particularly its type name.
|
* Compute information about an expression string, particularly its type name.
|
||||||
@ -57,26 +74,75 @@ class ExpressionToTypeInfo {
|
|||||||
final AnalyzeTask at;
|
final AnalyzeTask at;
|
||||||
final CompilationUnitTree cu;
|
final CompilationUnitTree cu;
|
||||||
final JShell state;
|
final JShell state;
|
||||||
|
final boolean computeEnhancedInfo;
|
||||||
|
final boolean enhancedTypesAccessible;
|
||||||
final Symtab syms;
|
final Symtab syms;
|
||||||
final Types types;
|
final Types types;
|
||||||
|
final Map<TypeSymbol, String> anon2Name = new HashMap<>();
|
||||||
|
|
||||||
private ExpressionToTypeInfo(AnalyzeTask at, CompilationUnitTree cu, JShell state) {
|
private ExpressionToTypeInfo(AnalyzeTask at, CompilationUnitTree cu, JShell state,
|
||||||
|
boolean computeEnhancedInfo, boolean enhancedTypesAccessible) {
|
||||||
this.at = at;
|
this.at = at;
|
||||||
this.cu = cu;
|
this.cu = cu;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
this.computeEnhancedInfo = computeEnhancedInfo;
|
||||||
|
this.enhancedTypesAccessible = enhancedTypesAccessible;
|
||||||
this.syms = Symtab.instance(at.context);
|
this.syms = Symtab.instance(at.context);
|
||||||
this.types = Types.instance(at.context);
|
this.types = Types.instance(at.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ExpressionInfo {
|
public static class ExpressionInfo {
|
||||||
ExpressionTree tree;
|
ExpressionTree tree;
|
||||||
|
boolean isPrimitiveType;
|
||||||
String typeName;
|
String typeName;
|
||||||
String accessibleTypeName;
|
String accessibleTypeName;
|
||||||
|
/* In result of localVariableTypeForInitializer, the type that should be used
|
||||||
|
* as a declaration type of the field. This does not include intersection types,
|
||||||
|
* but does contain references to anonymous types converted to member types.
|
||||||
|
*/
|
||||||
|
String declareTypeName;
|
||||||
|
/* In result of localVariableTypeForInitializer, the apparent/infered type of
|
||||||
|
* the variable. This includes intersection types, and references to anonymous
|
||||||
|
* types converted to member types.
|
||||||
|
*/
|
||||||
String fullTypeName;
|
String fullTypeName;
|
||||||
List<String> parameterTypes;
|
/* In result of localVariableTypeForInitializer, the human readable type of
|
||||||
String enclosingInstanceType;
|
* the variable. This includes intersection types, and human readable descriptions
|
||||||
boolean isClass;
|
* of anonymous types.
|
||||||
|
*/
|
||||||
|
String displayTypeName;
|
||||||
boolean isNonVoid;
|
boolean isNonVoid;
|
||||||
|
/* In result of localVariableTypeForInitializer, description of important anonymous
|
||||||
|
* classes.
|
||||||
|
*/
|
||||||
|
List<AnonymousDescription> anonymousClasses = List.nil();
|
||||||
|
|
||||||
|
/* A description of an anonymous class. */
|
||||||
|
static class AnonymousDescription {
|
||||||
|
/* Parameter types of the invoked super constructor.*/
|
||||||
|
List<String> parameterTypes;
|
||||||
|
/* Type of the base/enclosing expression, if any.*/
|
||||||
|
String enclosingInstanceType;
|
||||||
|
/* The denotable name of the supertype.*/
|
||||||
|
String superTypeName;
|
||||||
|
/* The human-readable name of this class.*/
|
||||||
|
String declareTypeName;
|
||||||
|
/* If the supertype of this anonymous is a class. */
|
||||||
|
boolean isClass;
|
||||||
|
/* Variables captured by this anonymous class*/
|
||||||
|
List<VariableDesc> capturedVariables;
|
||||||
|
|
||||||
|
static class VariableDesc {
|
||||||
|
String type;
|
||||||
|
String name;
|
||||||
|
|
||||||
|
public VariableDesc(String type, String name) {
|
||||||
|
this.type = type;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// return mechanism and other general structure from TreePath.getPath()
|
// return mechanism and other general structure from TreePath.getPath()
|
||||||
@ -173,7 +239,7 @@ class ExpressionToTypeInfo {
|
|||||||
if (at.hasErrors() || cu == null) {
|
if (at.hasErrors() || cu == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new ExpressionToTypeInfo(at, cu, state).typeOfExpression();
|
return new ExpressionToTypeInfo(at, cu, state, false, false).typeOfExpression();
|
||||||
});
|
});
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
return null;
|
return null;
|
||||||
@ -187,7 +253,7 @@ class ExpressionToTypeInfo {
|
|||||||
* @param state a JShell instance
|
* @param state a JShell instance
|
||||||
* @return type information
|
* @return type information
|
||||||
*/
|
*/
|
||||||
public static ExpressionInfo localVariableTypeForInitializer(String code, JShell state) {
|
public static ExpressionInfo localVariableTypeForInitializer(String code, JShell state, boolean onlyAccessible) {
|
||||||
if (code == null || code.isEmpty()) {
|
if (code == null || code.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -198,13 +264,37 @@ class ExpressionToTypeInfo {
|
|||||||
if (at.hasErrors() || cu == null) {
|
if (at.hasErrors() || cu == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new ExpressionToTypeInfo(at, cu, state).typeOfExpression();
|
return new ExpressionToTypeInfo(at, cu, state, true, onlyAccessible)
|
||||||
|
.typeOfExpression();
|
||||||
});
|
});
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**List (in a stable order) all NewClassTree instances under {@code from} that should be
|
||||||
|
* converted to member classes
|
||||||
|
*
|
||||||
|
* @param from tree to inspect
|
||||||
|
* @return NewClassTree instances that should be converted to member classes
|
||||||
|
*/
|
||||||
|
public static List<NewClassTree> listAnonymousClassesToConvert(Tree from) {
|
||||||
|
ListBuffer<NewClassTree> classes = new ListBuffer<>();
|
||||||
|
|
||||||
|
new TreeScanner<Void, Void>() {
|
||||||
|
@Override
|
||||||
|
public Void visitNewClass(NewClassTree node, Void p) {
|
||||||
|
if (node.getClassBody() != null) {
|
||||||
|
classes.append(node);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return super.visitNewClass(node, p);
|
||||||
|
}
|
||||||
|
}.scan(from, null);
|
||||||
|
|
||||||
|
return classes.toList();
|
||||||
|
}
|
||||||
|
|
||||||
private ExpressionInfo typeOfExpression() {
|
private ExpressionInfo typeOfExpression() {
|
||||||
return treeToInfo(findExpressionPath());
|
return treeToInfo(findExpressionPath());
|
||||||
}
|
}
|
||||||
@ -256,23 +346,31 @@ class ExpressionToTypeInfo {
|
|||||||
* @return the type, if it is accessible, otherwise a superclass or
|
* @return the type, if it is accessible, otherwise a superclass or
|
||||||
* interface which is
|
* interface which is
|
||||||
*/
|
*/
|
||||||
private Type findAccessibleSupertype(Type type) {
|
private List<Type> findAccessibleSupertypes(Type type) {
|
||||||
|
List<Type> accessible = List.nil();
|
||||||
|
Type accessibleSuper = syms.objectType;
|
||||||
// Iterate up the superclasses, see if any are accessible
|
// Iterate up the superclasses, see if any are accessible
|
||||||
for (Type sup = type; !types.isSameType(sup, syms.objectType); sup = supertype(sup)) {
|
for (Type sup = type; !types.isSameType(sup, syms.objectType); sup = supertype(sup)) {
|
||||||
if (isAccessible(sup)) {
|
if (isAccessible(sup)) {
|
||||||
return sup;
|
accessible = accessible.prepend(sup);
|
||||||
|
accessibleSuper = sup;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Failing superclasses, look through superclasses for accessible interfaces
|
// then look through superclasses for accessible interfaces
|
||||||
for (Type sup = type; !types.isSameType(sup, syms.objectType); sup = supertype(sup)) {
|
for (Type sup = type; !types.isSameType(sup, accessibleSuper); sup = supertype(sup)) {
|
||||||
for (Type itf : types.interfaces(sup)) {
|
for (Type itf : types.interfaces(sup)) {
|
||||||
if (isAccessible(itf)) {
|
if (isAccessible(itf)) {
|
||||||
return itf;
|
accessible = accessible.prepend(itf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Punt, return Object which is the supertype of everything
|
if (accessible.isEmpty()) {
|
||||||
return syms.objectType;
|
// Punt, use Object which is the supertype of everything
|
||||||
|
accessible = accessible.prepend(syms.objectType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return accessible.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExpressionInfo treeToInfo(TreePath tp) {
|
private ExpressionInfo treeToInfo(TreePath tp) {
|
||||||
@ -298,48 +396,125 @@ class ExpressionToTypeInfo {
|
|||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
ei.isNonVoid = true;
|
ei.isNonVoid = true;
|
||||||
ei.typeName = varTypeName(type, false);
|
ei.isPrimitiveType = type.isPrimitive();
|
||||||
ei.accessibleTypeName = varTypeName(findAccessibleSupertype(type), false);
|
ei.typeName = varTypeName(type, false, AnonymousTypeKind.SUPER);
|
||||||
ei.fullTypeName = varTypeName(type, true);
|
List<Type> accessibleTypes = findAccessibleSupertypes(type);
|
||||||
|
ei.accessibleTypeName =
|
||||||
|
varTypeName(accessibleTypes.head, false, AnonymousTypeKind.SUPER);
|
||||||
|
if (computeEnhancedInfo) {
|
||||||
|
Type accessibleType = accessibleTypes.size() == 1 ? accessibleTypes.head
|
||||||
|
: types.makeIntersectionType(accessibleTypes);
|
||||||
|
ei.declareTypeName =
|
||||||
|
varTypeName(accessibleType, false, AnonymousTypeKind.DECLARE);
|
||||||
|
ei.fullTypeName =
|
||||||
|
varTypeName(enhancedTypesAccessible ? accessibleType : type,
|
||||||
|
true, AnonymousTypeKind.DECLARE);
|
||||||
|
ei.displayTypeName =
|
||||||
|
varTypeName(type, true, AnonymousTypeKind.DISPLAY);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tree.getKind() == Tree.Kind.VARIABLE) {
|
if (tree.getKind() == Tree.Kind.VARIABLE && computeEnhancedInfo) {
|
||||||
Tree init = ((VariableTree) tree).getInitializer();
|
Tree init = ((VariableTree) tree).getInitializer();
|
||||||
if (init.getKind() == Tree.Kind.NEW_CLASS &&
|
for (NewClassTree node : listAnonymousClassesToConvert(init)) {
|
||||||
((NewClassTree) init).getClassBody() != null) {
|
Set<VariableElement> captured = capturedVariables(at,
|
||||||
NewClassTree nct = (NewClassTree) init;
|
tp.getCompilationUnit(),
|
||||||
ClassTree clazz = nct.getClassBody();
|
node);
|
||||||
MethodTree constructor = (MethodTree) clazz.getMembers().get(0);
|
JCClassDecl clazz = (JCClassDecl) node.getClassBody();
|
||||||
ExpressionStatementTree superCallStatement =
|
|
||||||
(ExpressionStatementTree) constructor.getBody().getStatements().get(0);
|
|
||||||
MethodInvocationTree superCall =
|
MethodInvocationTree superCall =
|
||||||
(MethodInvocationTree) superCallStatement.getExpression();
|
clazz.getMembers()
|
||||||
TreePath superCallPath =
|
.stream()
|
||||||
at.trees().getPath(tp.getCompilationUnit(), superCall.getMethodSelect());
|
.map(TreeInfo::firstConstructorCall)
|
||||||
|
.findAny()
|
||||||
|
.get();
|
||||||
|
TreePath superCallPath
|
||||||
|
= at.trees().
|
||||||
|
getPath(tp.getCompilationUnit(), superCall.
|
||||||
|
getMethodSelect());
|
||||||
Type constrType = pathToType(superCallPath);
|
Type constrType = pathToType(superCallPath);
|
||||||
ei.parameterTypes = constrType.getParameterTypes()
|
AnonymousDescription desc = new AnonymousDescription();
|
||||||
.stream()
|
desc.parameterTypes = constrType.getParameterTypes().
|
||||||
.map(t -> varTypeName(t, false))
|
stream().
|
||||||
.collect(List.collector());
|
map(t -> varTypeName(t, false, AnonymousTypeKind.DECLARE)).
|
||||||
if (nct.getEnclosingExpression() != null) {
|
collect(List.collector());
|
||||||
TreePath enclPath = new TreePath(tp, nct.getEnclosingExpression());
|
if (node.getEnclosingExpression() != null) {
|
||||||
ei.enclosingInstanceType = varTypeName(pathToType(enclPath), false);
|
TreePath enclPath = new TreePath(tp,
|
||||||
|
node.getEnclosingExpression());
|
||||||
|
desc.enclosingInstanceType = varTypeName(pathToType(enclPath),
|
||||||
|
false,
|
||||||
|
AnonymousTypeKind.DECLARE);
|
||||||
}
|
}
|
||||||
ei.isClass = at.task.getTypes().directSupertypes(type).size() == 1;
|
TreePath currentPath = at.trees()
|
||||||
|
.getPath(tp.getCompilationUnit(),
|
||||||
|
node);
|
||||||
|
Type nodeType = pathToType(currentPath, node);
|
||||||
|
desc.superTypeName = varTypeName(nodeType,
|
||||||
|
false,
|
||||||
|
AnonymousTypeKind.SUPER);
|
||||||
|
desc.declareTypeName = varTypeName(nodeType,
|
||||||
|
true, AnonymousTypeKind.DECLARE);
|
||||||
|
desc.capturedVariables =
|
||||||
|
captured.stream()
|
||||||
|
.map(ve -> new VariableDesc(varTypeName((Type) ve.asType(),
|
||||||
|
false,
|
||||||
|
AnonymousTypeKind.DECLARE),
|
||||||
|
ve.getSimpleName().toString()))
|
||||||
|
.collect(List.collector());
|
||||||
|
|
||||||
|
desc.isClass = at.task.getTypes().directSupertypes(nodeType).size() == 1;
|
||||||
|
ei.anonymousClasses = ei.anonymousClasses.prepend(desc);
|
||||||
}
|
}
|
||||||
|
ei.anonymousClasses = ei.anonymousClasses.reverse();
|
||||||
}
|
}
|
||||||
return ei;
|
return ei;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
//where:
|
||||||
|
private static Set<VariableElement> capturedVariables(AnalyzeTask at,
|
||||||
|
CompilationUnitTree topLevel,
|
||||||
|
Tree tree) {
|
||||||
|
Set<VariableElement> capturedVars = new HashSet<>();
|
||||||
|
new TreeScanner<Void, Void>() {
|
||||||
|
Set<VariableElement> declaredLocalVars = new HashSet<>();
|
||||||
|
@Override
|
||||||
|
public Void visitVariable(VariableTree node, Void p) {
|
||||||
|
TreePath currentPath = at.trees()
|
||||||
|
.getPath(topLevel, node);
|
||||||
|
declaredLocalVars.add((VariableElement) at.trees().getElement(currentPath));
|
||||||
|
return super.visitVariable(node, p);
|
||||||
|
}
|
||||||
|
|
||||||
private String varTypeName(Type type, boolean printIntersectionTypes) {
|
@Override
|
||||||
|
public Void visitIdentifier(IdentifierTree node, Void p) {
|
||||||
|
TreePath currentPath = at.trees()
|
||||||
|
.getPath(topLevel, node);
|
||||||
|
Element el = at.trees().getElement(currentPath);
|
||||||
|
if (el != null &&
|
||||||
|
LOCAL_VARIABLES.contains(el.getKind()) &&
|
||||||
|
!declaredLocalVars.contains(el)) {
|
||||||
|
capturedVars.add((VariableElement) el);
|
||||||
|
}
|
||||||
|
return super.visitIdentifier(node, p);
|
||||||
|
}
|
||||||
|
}.scan(tree, null);
|
||||||
|
|
||||||
|
return capturedVars;
|
||||||
|
}
|
||||||
|
private static final Set<ElementKind> LOCAL_VARIABLES =
|
||||||
|
EnumSet.of(ElementKind.EXCEPTION_PARAMETER, ElementKind.LOCAL_VARIABLE,
|
||||||
|
ElementKind.PARAMETER, ElementKind.RESOURCE_VARIABLE);
|
||||||
|
|
||||||
|
private String varTypeName(Type type, boolean printIntersectionTypes, AnonymousTypeKind anonymousTypesKind) {
|
||||||
try {
|
try {
|
||||||
|
Function<TypeSymbol, String> anonymousClass2DeclareName =
|
||||||
|
cs -> anon2Name.computeIfAbsent(cs, state.eval::computeDeclareName);
|
||||||
TypePrinter tp = new TypePrinter(at.messages(),
|
TypePrinter tp = new TypePrinter(at.messages(),
|
||||||
state.maps::fullClassNameAndPackageToClass, printIntersectionTypes);
|
state.maps::fullClassNameAndPackageToClass, anonymousClass2DeclareName,
|
||||||
|
printIntersectionTypes, anonymousTypesKind);
|
||||||
List<Type> captures = types.captures(type);
|
List<Type> captures = types.captures(type);
|
||||||
String res = tp.toString(types.upward(type, captures));
|
String res = tp.toString(types.upward(type, captures));
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -61,24 +61,33 @@ import jdk.jshell.MemoryFileManager.SourceMemoryJavaFileObject;
|
|||||||
import java.lang.Runtime.Version;
|
import java.lang.Runtime.Version;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
import com.sun.source.tree.ClassTree;
|
||||||
import com.sun.source.tree.Tree.Kind;
|
import com.sun.source.tree.Tree.Kind;
|
||||||
import com.sun.source.util.TaskEvent;
|
import com.sun.source.util.TaskEvent;
|
||||||
import com.sun.source.util.TaskListener;
|
import com.sun.source.util.TaskListener;
|
||||||
import com.sun.tools.javac.api.JavacTaskPool;
|
import com.sun.tools.javac.api.JavacTaskPool;
|
||||||
import com.sun.tools.javac.code.ClassFinder;
|
import com.sun.tools.javac.code.ClassFinder;
|
||||||
import com.sun.tools.javac.code.Kinds;
|
import com.sun.tools.javac.code.Kinds;
|
||||||
|
import com.sun.tools.javac.code.Symbol;
|
||||||
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
||||||
import com.sun.tools.javac.code.Symbol.PackageSymbol;
|
import com.sun.tools.javac.code.Symbol.PackageSymbol;
|
||||||
|
import com.sun.tools.javac.code.Symbol.TypeSymbol;
|
||||||
import com.sun.tools.javac.code.Symbol.VarSymbol;
|
import com.sun.tools.javac.code.Symbol.VarSymbol;
|
||||||
import com.sun.tools.javac.code.Symtab;
|
import com.sun.tools.javac.code.Symtab;
|
||||||
|
import com.sun.tools.javac.code.Type;
|
||||||
import com.sun.tools.javac.comp.Attr;
|
import com.sun.tools.javac.comp.Attr;
|
||||||
|
import com.sun.tools.javac.comp.AttrContext;
|
||||||
|
import com.sun.tools.javac.comp.Enter;
|
||||||
|
import com.sun.tools.javac.comp.Env;
|
||||||
|
import com.sun.tools.javac.comp.Resolve;
|
||||||
import com.sun.tools.javac.parser.Parser;
|
import com.sun.tools.javac.parser.Parser;
|
||||||
import com.sun.tools.javac.parser.ParserFactory;
|
import com.sun.tools.javac.parser.ParserFactory;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
|
|
||||||
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
|
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCExpression;
|
import com.sun.tools.javac.tree.JCTree.JCExpression;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCTypeCast;
|
import com.sun.tools.javac.tree.JCTree.JCTypeCast;
|
||||||
|
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
|
||||||
import com.sun.tools.javac.tree.JCTree.Tag;
|
import com.sun.tools.javac.tree.JCTree.Tag;
|
||||||
|
import com.sun.tools.javac.util.Context.Factory;
|
||||||
import com.sun.tools.javac.util.Log;
|
import com.sun.tools.javac.util.Log;
|
||||||
import com.sun.tools.javac.util.Log.DiscardDiagnosticHandler;
|
import com.sun.tools.javac.util.Log.DiscardDiagnosticHandler;
|
||||||
import com.sun.tools.javac.util.Names;
|
import com.sun.tools.javac.util.Names;
|
||||||
@ -197,6 +206,7 @@ class TaskFactory {
|
|||||||
compilationUnits, task -> {
|
compilationUnits, task -> {
|
||||||
JavacTaskImpl jti = (JavacTaskImpl) task;
|
JavacTaskImpl jti = (JavacTaskImpl) task;
|
||||||
Context context = jti.getContext();
|
Context context = jti.getContext();
|
||||||
|
DisableAccessibilityResolve.preRegister(context);
|
||||||
jti.addTaskListener(new TaskListenerImpl(context, state));
|
jti.addTaskListener(new TaskListenerImpl(context, state));
|
||||||
try {
|
try {
|
||||||
return worker.withTask(creator.apply(jti, diagnostics));
|
return worker.withTask(creator.apply(jti, diagnostics));
|
||||||
@ -578,54 +588,111 @@ class TaskFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**The variable types inferred for "var"s may be non-denotable.
|
||||||
|
* jshell desugars these variables into fields, and fields must have
|
||||||
|
* a denotable type. So these fields are declared with some simpler denotable
|
||||||
|
* type, and the listener here enhances the types of the fields to be the full
|
||||||
|
* inferred types. This is mainly when the inferred type contains:
|
||||||
|
* -intersection types (e.g. <Z extends Runnable&CharSequence> Z get() {...} var z = get();)
|
||||||
|
* -types that are inaccessible at the given place
|
||||||
|
*
|
||||||
|
* This type enhancement does not need to do anything about anonymous classes, as these
|
||||||
|
* are desugared into member classes.
|
||||||
|
*/
|
||||||
private static final class TaskListenerImpl implements TaskListener {
|
private static final class TaskListenerImpl implements TaskListener {
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final JShell state;
|
private final JShell state;
|
||||||
|
/* Keep the original (declaration) types of the fields that were enhanced.
|
||||||
|
* The declaration types need to be put back before writing the fields
|
||||||
|
* into classfiles.*/
|
||||||
|
private final Map<VarSymbol, Type> var2OriginalType = new HashMap<>();
|
||||||
|
|
||||||
public TaskListenerImpl(Context context, JShell state) {
|
public TaskListenerImpl(Context context, JShell state) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void started(TaskEvent e) {
|
||||||
|
if (e.getKind() != TaskEvent.Kind.GENERATE)
|
||||||
|
return ;
|
||||||
|
//clear enhanced types in fields we are about to write to the classfiles:
|
||||||
|
for (Tree clazz : e.getCompilationUnit().getTypeDecls()) {
|
||||||
|
ClassTree ct = (ClassTree) clazz;
|
||||||
|
|
||||||
|
for (Tree member : ct.getMembers()) {
|
||||||
|
if (member.getKind() != Tree.Kind.VARIABLE)
|
||||||
|
continue;
|
||||||
|
VarSymbol vsym = ((JCVariableDecl) member).sym;
|
||||||
|
Type original = var2OriginalType.remove(vsym);
|
||||||
|
if (original != null) {
|
||||||
|
vsym.type = original;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean variablesSet = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void finished(TaskEvent e) {
|
public void finished(TaskEvent e) {
|
||||||
if (e.getKind() != TaskEvent.Kind.ENTER)
|
if (e.getKind() != TaskEvent.Kind.ENTER || variablesSet)
|
||||||
return ;
|
return ;
|
||||||
state.maps
|
state.maps
|
||||||
.snippetList()
|
.snippetList()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(s -> s.status() == Status.VALID)
|
.filter(s -> s.status() == Status.VALID)
|
||||||
.filter(s -> s.kind() == Snippet.Kind.VAR)
|
.filter(s -> s.kind() == Snippet.Kind.VAR)
|
||||||
.filter(s -> s.subKind() == Snippet.SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND)
|
.filter(s -> s.subKind() == Snippet.SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND ||
|
||||||
.forEach(s -> setVariableType((JCCompilationUnit) e.getCompilationUnit(), (VarSnippet) s));
|
s.subKind() == Snippet.SubKind.TEMP_VAR_EXPRESSION_SUBKIND)
|
||||||
|
.forEach(s -> setVariableType((VarSnippet) s));
|
||||||
|
variablesSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setVariableType(JCCompilationUnit root, VarSnippet s) {
|
/* If the snippet contain enhanced types, enhance the type of
|
||||||
|
* the variable from snippet s to be the enhanced type.
|
||||||
|
*/
|
||||||
|
private void setVariableType(VarSnippet s) {
|
||||||
|
String typeName = s.fullTypeName;
|
||||||
|
|
||||||
|
if (typeName == null)
|
||||||
|
return ;
|
||||||
|
|
||||||
Symtab syms = Symtab.instance(context);
|
Symtab syms = Symtab.instance(context);
|
||||||
Names names = Names.instance(context);
|
Names names = Names.instance(context);
|
||||||
Log log = Log.instance(context);
|
Log log = Log.instance(context);
|
||||||
ParserFactory parserFactory = ParserFactory.instance(context);
|
ParserFactory parserFactory = ParserFactory.instance(context);
|
||||||
Attr attr = Attr.instance(context);
|
Attr attr = Attr.instance(context);
|
||||||
|
Enter enter = Enter.instance(context);
|
||||||
|
DisableAccessibilityResolve rs = (DisableAccessibilityResolve) Resolve.instance(context);
|
||||||
|
|
||||||
|
//find the variable:
|
||||||
ClassSymbol clazz = syms.getClass(syms.unnamedModule, names.fromString(s.classFullName()));
|
ClassSymbol clazz = syms.getClass(syms.unnamedModule, names.fromString(s.classFullName()));
|
||||||
if (clazz == null || !clazz.isCompleted())
|
if (clazz == null || !clazz.isCompleted())
|
||||||
return;
|
return;
|
||||||
VarSymbol field = (VarSymbol) clazz.members().findFirst(names.fromString(s.name()), sym -> sym.kind == Kinds.Kind.VAR);
|
VarSymbol field = (VarSymbol) clazz.members().findFirst(names.fromString(s.name()), sym -> sym.kind == Kinds.Kind.VAR);
|
||||||
if (field != null) {
|
|
||||||
|
if (field != null && !var2OriginalType.containsKey(field)) {
|
||||||
|
//if it was not enhanced yet:
|
||||||
|
//ignore any errors:
|
||||||
JavaFileObject prev = log.useSource(null);
|
JavaFileObject prev = log.useSource(null);
|
||||||
DiscardDiagnosticHandler h = new DiscardDiagnosticHandler(log);
|
DiscardDiagnosticHandler h = new DiscardDiagnosticHandler(log);
|
||||||
try {
|
try {
|
||||||
String typeName = s.typeName();
|
//parse the type as a cast, i.e. "(<typeName>) x". This is to support
|
||||||
|
//intersection types:
|
||||||
CharBuffer buf = CharBuffer.wrap(("(" + typeName +")x\u0000").toCharArray(), 0, typeName.length() + 3);
|
CharBuffer buf = CharBuffer.wrap(("(" + typeName +")x\u0000").toCharArray(), 0, typeName.length() + 3);
|
||||||
Parser parser = parserFactory.newParser(buf, false, false, false);
|
Parser parser = parserFactory.newParser(buf, false, false, false);
|
||||||
JCExpression expr = parser.parseExpression();
|
JCExpression expr = parser.parseExpression();
|
||||||
if (expr.hasTag(Tag.TYPECAST)) {
|
if (expr.hasTag(Tag.TYPECAST)) {
|
||||||
|
//if parsed OK, attribute and set the type:
|
||||||
|
var2OriginalType.put(field, field.type);
|
||||||
|
|
||||||
JCTypeCast tree = (JCTypeCast) expr;
|
JCTypeCast tree = (JCTypeCast) expr;
|
||||||
if (tree.clazz.hasTag(Tag.TYPEINTERSECTION)) {
|
rs.runWithoutAccessChecks(() -> {
|
||||||
field.type = attr.attribType(tree.clazz,
|
field.type = attr.attribType(tree.clazz,
|
||||||
((JCClassDecl) root.getTypeDecls().head).sym);
|
enter.getEnvs().iterator().next().enclClass.sym);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
log.popDiagnosticHandler(h);
|
log.popDiagnosticHandler(h);
|
||||||
@ -635,4 +702,48 @@ class TaskFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class DisableAccessibilityResolve extends Resolve {
|
||||||
|
|
||||||
|
public static void preRegister(Context context) {
|
||||||
|
if (context.get(Marker.class) == null) {
|
||||||
|
context.put(resolveKey, ((Factory<Resolve>) c -> new DisableAccessibilityResolve(c)));
|
||||||
|
context.put(Marker.class, new Marker());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean noAccessChecks;
|
||||||
|
|
||||||
|
public DisableAccessibilityResolve(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Run the given Runnable with all access checks disabled.
|
||||||
|
*
|
||||||
|
* @param r Runnnable to run
|
||||||
|
*/
|
||||||
|
public void runWithoutAccessChecks(Runnable r) {
|
||||||
|
boolean prevNoAccessCheckes = noAccessChecks;
|
||||||
|
try {
|
||||||
|
noAccessChecks = true;
|
||||||
|
r.run();
|
||||||
|
} finally {
|
||||||
|
noAccessChecks = prevNoAccessCheckes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccessible(Env<AttrContext> env, TypeSymbol c, boolean checkInner) {
|
||||||
|
if (noAccessChecks) return true;
|
||||||
|
return super.isAccessible(env, c, checkInner);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAccessible(Env<AttrContext> env, Type site, Symbol sym, boolean checkInner) {
|
||||||
|
if (noAccessChecks) return true;
|
||||||
|
return super.isAccessible(env, site, sym, checkInner);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Marker {}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -48,6 +48,7 @@ import java.util.List;
|
|||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import javax.lang.model.type.TypeMirror;
|
import javax.lang.model.type.TypeMirror;
|
||||||
|
import jdk.jshell.TypePrinter.AnonymousTypeKind;
|
||||||
import jdk.jshell.Util.Pair;
|
import jdk.jshell.Util.Pair;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -227,7 +228,7 @@ class TreeDissector {
|
|||||||
Type typeImpl = (Type) type;
|
Type typeImpl = (Type) type;
|
||||||
try {
|
try {
|
||||||
TypePrinter tp = new TypePrinter(at.messages(),
|
TypePrinter tp = new TypePrinter(at.messages(),
|
||||||
state.maps::fullClassNameAndPackageToClass, true);
|
state.maps::fullClassNameAndPackageToClass, true, AnonymousTypeKind.DISPLAY);
|
||||||
return tp.toString(typeImpl);
|
return tp.toString(typeImpl);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -30,14 +30,17 @@ import com.sun.tools.javac.code.Printer;
|
|||||||
import com.sun.tools.javac.code.Symbol;
|
import com.sun.tools.javac.code.Symbol;
|
||||||
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
||||||
import com.sun.tools.javac.code.Symbol.PackageSymbol;
|
import com.sun.tools.javac.code.Symbol.PackageSymbol;
|
||||||
|
import com.sun.tools.javac.code.Symbol.TypeSymbol;
|
||||||
import com.sun.tools.javac.code.Type;
|
import com.sun.tools.javac.code.Type;
|
||||||
import com.sun.tools.javac.code.Type.ClassType;
|
import com.sun.tools.javac.code.Type.ClassType;
|
||||||
import com.sun.tools.javac.code.Type.IntersectionClassType;
|
import com.sun.tools.javac.code.Type.IntersectionClassType;
|
||||||
import com.sun.tools.javac.util.JavacMessages;
|
import com.sun.tools.javac.util.JavacMessages;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.function.BinaryOperator;
|
import java.util.function.BinaryOperator;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Print types in source form.
|
* Print types in source form.
|
||||||
*/
|
*/
|
||||||
@ -47,14 +50,44 @@ class TypePrinter extends Printer {
|
|||||||
|
|
||||||
private final JavacMessages messages;
|
private final JavacMessages messages;
|
||||||
private final BinaryOperator<String> fullClassNameAndPackageToClass;
|
private final BinaryOperator<String> fullClassNameAndPackageToClass;
|
||||||
private final boolean printEnhancedTypes;
|
private final Function<TypeSymbol, String> anonymousToName;
|
||||||
|
private final boolean printIntersectionTypes;
|
||||||
|
private final AnonymousTypeKind anonymousTypesKind;
|
||||||
|
|
||||||
|
/**Create a TypePrinter.
|
||||||
|
*
|
||||||
|
* @param messages javac's messages
|
||||||
|
* @param fullClassNameAndPackageToClass convertor to convert full class names to
|
||||||
|
* simple class names.
|
||||||
|
* @param printIntersectionTypes whether intersection types should be printed
|
||||||
|
* @param anonymousTypesKind how the anonymous types should be printed
|
||||||
|
*/
|
||||||
TypePrinter(JavacMessages messages,
|
TypePrinter(JavacMessages messages,
|
||||||
BinaryOperator<String> fullClassNameAndPackageToClass,
|
BinaryOperator<String> fullClassNameAndPackageToClass,
|
||||||
boolean printEnhancedTypes) {
|
boolean printIntersectionTypes, AnonymousTypeKind anonymousTypesKind) {
|
||||||
|
this(messages, fullClassNameAndPackageToClass, cs -> cs.flatName().toString(),
|
||||||
|
printIntersectionTypes, anonymousTypesKind);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Create a TypePrinter.
|
||||||
|
*
|
||||||
|
* @param messages javac's messages
|
||||||
|
* @param fullClassNameAndPackageToClass convertor to convert full class names to
|
||||||
|
* simple class names.
|
||||||
|
* @param anonymousToName convertor from anonymous classes to name that should be printed
|
||||||
|
* if anonymousTypesKind == AnonymousTypeKind.DECLARE
|
||||||
|
* @param printIntersectionTypes whether intersection types should be printed
|
||||||
|
* @param anonymousTypesKind how the anonymous types should be printed
|
||||||
|
*/
|
||||||
|
TypePrinter(JavacMessages messages,
|
||||||
|
BinaryOperator<String> fullClassNameAndPackageToClass,
|
||||||
|
Function<TypeSymbol, String> anonymousToName,
|
||||||
|
boolean printIntersectionTypes, AnonymousTypeKind anonymousTypesKind) {
|
||||||
this.messages = messages;
|
this.messages = messages;
|
||||||
this.fullClassNameAndPackageToClass = fullClassNameAndPackageToClass;
|
this.fullClassNameAndPackageToClass = fullClassNameAndPackageToClass;
|
||||||
this.printEnhancedTypes = printEnhancedTypes;
|
this.anonymousToName = anonymousToName;
|
||||||
|
this.printIntersectionTypes = printIntersectionTypes;
|
||||||
|
this.anonymousTypesKind = anonymousTypesKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
String toString(Type t) {
|
String toString(Type t) {
|
||||||
@ -96,9 +129,9 @@ class TypePrinter extends Printer {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected String className(ClassType t, boolean longform, Locale locale) {
|
protected String className(ClassType t, boolean longform, Locale locale) {
|
||||||
Symbol sym = t.tsym;
|
TypeSymbol sym = t.tsym;
|
||||||
if (sym.name.length() == 0 && (sym.flags() & COMPOUND) != 0) {
|
if (sym.name.length() == 0 && (sym.flags() & COMPOUND) != 0) {
|
||||||
if (printEnhancedTypes) {
|
if (printIntersectionTypes) {
|
||||||
return ((IntersectionClassType) t).getExplicitComponents()
|
return ((IntersectionClassType) t).getExplicitComponents()
|
||||||
.stream()
|
.stream()
|
||||||
.map(i -> visit(i, locale))
|
.map(i -> visit(i, locale))
|
||||||
@ -107,18 +140,26 @@ class TypePrinter extends Printer {
|
|||||||
return OBJECT;
|
return OBJECT;
|
||||||
}
|
}
|
||||||
} else if (sym.name.length() == 0) {
|
} else if (sym.name.length() == 0) {
|
||||||
if (printEnhancedTypes) {
|
if (anonymousTypesKind == AnonymousTypeKind.DECLARE) {
|
||||||
return t.tsym.flatName().toString().substring(t.tsym.outermostClass().flatName().length());
|
return anonymousToName.apply(sym);
|
||||||
}
|
}
|
||||||
// Anonymous
|
// Anonymous
|
||||||
String s;
|
String s;
|
||||||
|
boolean isClass;
|
||||||
ClassType norm = (ClassType) t.tsym.type;
|
ClassType norm = (ClassType) t.tsym.type;
|
||||||
if (norm == null) {
|
if (norm == null) {
|
||||||
s = OBJECT;
|
s = OBJECT;
|
||||||
|
isClass = true;
|
||||||
} else if (norm.interfaces_field != null && norm.interfaces_field.nonEmpty()) {
|
} else if (norm.interfaces_field != null && norm.interfaces_field.nonEmpty()) {
|
||||||
s = visit(norm.interfaces_field.head, locale);
|
s = visit(norm.interfaces_field.head, locale);
|
||||||
|
isClass = false;
|
||||||
} else {
|
} else {
|
||||||
s = visit(norm.supertype_field, locale);
|
s = visit(norm.supertype_field, locale);
|
||||||
|
isClass = true;
|
||||||
|
}
|
||||||
|
if (anonymousTypesKind == AnonymousTypeKind.DISPLAY) {
|
||||||
|
s = isClass ? "<anonymous class extending " + s + ">"
|
||||||
|
: "<anonymous class implementing " + s + ">";
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
} else if (longform) {
|
} else if (longform) {
|
||||||
@ -152,4 +193,14 @@ class TypePrinter extends Printer {
|
|||||||
: s.fullname.toString();
|
: s.fullname.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Specifies how the anonymous classes should be handled. */
|
||||||
|
public enum AnonymousTypeKind {
|
||||||
|
/* The anonymous class is printed as the name of its supertype. */
|
||||||
|
SUPER,
|
||||||
|
/* The anonymous class is printed as converted by the anonymousToName
|
||||||
|
* convertor. */
|
||||||
|
DECLARE,
|
||||||
|
/* The anonymous class is printed in a human readable form. */
|
||||||
|
DISPLAY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -55,6 +55,11 @@ class Util {
|
|||||||
*/
|
*/
|
||||||
static final String DOIT_METHOD_NAME = "do_it$";
|
static final String DOIT_METHOD_NAME = "do_it$";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The prefix for all anonymous classes upgraded to member classes.
|
||||||
|
*/
|
||||||
|
static final String JSHELL_ANONYMOUS = "$JShell$anonymous$";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A pattern matching the full or simple class name of a wrapper class.
|
* A pattern matching the full or simple class name of a wrapper class.
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -26,6 +26,8 @@
|
|||||||
package jdk.jshell;
|
package jdk.jshell;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import jdk.jshell.Key.VarKey;
|
import jdk.jshell.Key.VarKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,15 +43,30 @@ import jdk.jshell.Key.VarKey;
|
|||||||
*/
|
*/
|
||||||
public class VarSnippet extends DeclarationSnippet {
|
public class VarSnippet extends DeclarationSnippet {
|
||||||
|
|
||||||
|
/**A human readable type of the variable. May include intersection types
|
||||||
|
* and human readable description of anonymous classes.
|
||||||
|
*/
|
||||||
final String typeName;
|
final String typeName;
|
||||||
|
|
||||||
|
/**The full type inferred for "var" variables. May include intersection types
|
||||||
|
* and inaccessible types. {@literal null} if enhancing the type is not necessary.
|
||||||
|
*/
|
||||||
|
final String fullTypeName;
|
||||||
|
|
||||||
|
/**The anonymous class declared in the initializer of the "var" variable.
|
||||||
|
* These are automatically statically imported when the field is imported.
|
||||||
|
*/
|
||||||
|
final Set<String> anonymousClasses;
|
||||||
|
|
||||||
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, String fullTypeName,
|
||||||
Collection<String> declareReferences,
|
Set<String> anonymousClasses, Collection<String> declareReferences,
|
||||||
DiagList syntheticDiags) {
|
DiagList syntheticDiags) {
|
||||||
super(key, userSource, guts, name, subkind, null, declareReferences,
|
super(key, userSource, guts, name, subkind, null, declareReferences,
|
||||||
null, syntheticDiags);
|
null, syntheticDiags);
|
||||||
this.typeName = typeName;
|
this.typeName = typeName;
|
||||||
|
this.fullTypeName = fullTypeName;
|
||||||
|
this.anonymousClasses = anonymousClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,4 +76,13 @@ public class VarSnippet extends DeclarationSnippet {
|
|||||||
public String typeName() {
|
public String typeName() {
|
||||||
return typeName;
|
return typeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String importLine(JShell state) {
|
||||||
|
return "import static " + classFullName() + "." + name() + ";\n" +
|
||||||
|
anonymousClasses.stream()
|
||||||
|
.map(c -> "import static " + classFullName() + "." + c + ";\n")
|
||||||
|
.collect(Collectors.joining());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,7 +25,9 @@
|
|||||||
|
|
||||||
package jdk.jshell;
|
package jdk.jshell;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import static java.util.stream.Collectors.joining;
|
import static java.util.stream.Collectors.joining;
|
||||||
import static jdk.jshell.Util.DOIT_METHOD_NAME;
|
import static jdk.jshell.Util.DOIT_METHOD_NAME;
|
||||||
|
|
||||||
@ -66,37 +68,76 @@ abstract class Wrap implements GeneralWrap {
|
|||||||
return "\n" + indent(n);
|
return "\n" + indent(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**Create a stub of a compilable representation of a variable snippet.
|
||||||
|
* The variable is always represented by a field. If the variable
|
||||||
|
* in the snippet has an initializer, the field is initialized by
|
||||||
|
* calling the DOIT_METHOD_NAME method.
|
||||||
*
|
*
|
||||||
* @param in
|
* In some cases, the real inferred type of the variable may be non-denotable
|
||||||
* @param rname
|
* (e.g. intersection types). The declared type of the field must always
|
||||||
* @param rinit Initializer or null
|
* be denotable (i.e. such that it can be written into the classfile), but
|
||||||
* @param rdecl Type name and name
|
* if the real type is potentially non-denotable, the {@code enhanced} parameter
|
||||||
* @return
|
* must be true.
|
||||||
|
*
|
||||||
|
* @param source the snippet's masked source code
|
||||||
|
* @param wtype variable's denotable type suitable for field declaration
|
||||||
|
* @param brackets any [] that should be appended to the type
|
||||||
|
* @param rname range in source that denotes the name of the
|
||||||
|
* @param winit Initializer or null
|
||||||
|
* @param enhanced if the real inferred type of the variable is potentially
|
||||||
|
* non-denotable, this must be true
|
||||||
|
* @return a Wrap that declares the given variable, potentially with
|
||||||
|
* an initialization method
|
||||||
*/
|
*/
|
||||||
public static Wrap varWrap(String source, Wrap wtype, String brackets,
|
public static Wrap varWrap(String source, Wrap wtype, String brackets,
|
||||||
Range rname, Wrap winit, Wrap anonDeclareWrap) {
|
Range rname, Wrap winit, boolean enhanced,
|
||||||
|
Wrap anonDeclareWrap) {
|
||||||
RangeWrap wname = new RangeWrap(source, rname);
|
RangeWrap wname = new RangeWrap(source, rname);
|
||||||
Wrap wVarDecl = new VarDeclareWrap(wtype, brackets, wname);
|
List<Object> components = new ArrayList<>();
|
||||||
|
components.add(new VarDeclareWrap(wtype, brackets, wname));
|
||||||
Wrap wmeth;
|
Wrap wmeth;
|
||||||
|
|
||||||
if (winit == null) {
|
if (winit == null) {
|
||||||
wmeth = new CompoundWrap(new NoWrap(" "), " return null;\n");
|
wmeth = new CompoundWrap(new NoWrap(" "), " return null;\n");
|
||||||
} else {
|
} else {
|
||||||
// int x = y
|
// int x = y
|
||||||
// int x_ = y; return x = x_;
|
if (enhanced) {
|
||||||
// decl + "_ = " + init ; + "return " + name + "=" + name + "_ ;"
|
// private static <Z> Z do_itAux() {
|
||||||
wmeth = new CompoundWrap(
|
// wtype x_ = y;
|
||||||
wtype, brackets + " ", wname, "_ =\n ", winit, semi(winit),
|
// @SuppressWarnings("unchecked")
|
||||||
" return ", wname, " = ", wname, "_;\n"
|
// Z x__ = (Z) x_;
|
||||||
);
|
// return x__;
|
||||||
|
// }
|
||||||
|
// in do_it method:
|
||||||
|
//return do_itAux();
|
||||||
|
Wrap waux = new CompoundWrap(
|
||||||
|
" private static <Z> Z ", DOIT_METHOD_NAME + "Aux", "() throws Throwable {\n",
|
||||||
|
wtype, brackets + " ", wname, "_ =\n ", winit, semi(winit),
|
||||||
|
" @SuppressWarnings(\"unchecked\") Z ", wname, "__ = (Z)", wname, "_;\n",
|
||||||
|
" return ", wname, "__;\n",
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
components.add(waux);
|
||||||
|
wmeth = new CompoundWrap(
|
||||||
|
" return ", wname, " = ", DOIT_METHOD_NAME + "Aux", "();\n"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// int x_ = y; return x = x_;
|
||||||
|
// decl + "_ = " + init ; + "return " + name + "= " + name + "_ ;"
|
||||||
|
wmeth = new CompoundWrap(
|
||||||
|
wtype, brackets + " ", wname, "_ =\n ", winit, semi(winit),
|
||||||
|
" return ", wname, " = ", wname, "_;\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Wrap wInitMeth = new DoitMethodWrap(wmeth);
|
components.add(new DoitMethodWrap(wmeth));
|
||||||
return anonDeclareWrap != null ? new CompoundWrap(wVarDecl, wInitMeth, anonDeclareWrap)
|
if (anonDeclareWrap != null) {
|
||||||
: new CompoundWrap(wVarDecl, wInitMeth);
|
components.add(anonDeclareWrap);
|
||||||
|
}
|
||||||
|
return new CompoundWrap(components.toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Wrap tempVarWrap(String source, String typename, String name) {
|
public static Wrap tempVarWrap(String source, String typename, String name, Wrap anonDeclareWrap) {
|
||||||
RangeWrap winit = new NoWrap(source);
|
RangeWrap winit = new NoWrap(source);
|
||||||
// y
|
// y
|
||||||
// return $1 = y;
|
// return $1 = y;
|
||||||
@ -105,7 +146,8 @@ abstract class Wrap implements GeneralWrap {
|
|||||||
Wrap wInitMeth = new DoitMethodWrap(wmeth);
|
Wrap wInitMeth = new DoitMethodWrap(wmeth);
|
||||||
|
|
||||||
String varDecl = " public static\n " + typename + " " + name + ";\n";
|
String varDecl = " public static\n " + typename + " " + name + ";\n";
|
||||||
return new CompoundWrap(varDecl, wInitMeth);
|
return anonDeclareWrap != null ? new CompoundWrap(varDecl, wInitMeth, anonDeclareWrap)
|
||||||
|
: new CompoundWrap(varDecl, wInitMeth);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Wrap simpleWrap(String source) {
|
public static Wrap simpleWrap(String source) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -22,7 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @test 8190939
|
* @test 8190939 8191842
|
||||||
* @summary test expressions whose type is inaccessible
|
* @summary test expressions whose type is inaccessible
|
||||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||||
* jdk.compiler/com.sun.tools.javac.main
|
* jdk.compiler/com.sun.tools.javac.main
|
||||||
@ -86,6 +86,8 @@ public class InaccessibleExpressionTest extends KullaTesting {
|
|||||||
assertEval(list.name() + ".size()", "0");
|
assertEval(list.name() + ".size()", "0");
|
||||||
VarSnippet one = varKey(assertEval("priv()", "One"));
|
VarSnippet one = varKey(assertEval("priv()", "One"));
|
||||||
assertEquals(one.typeName(), "priv.GetPriv.Count");
|
assertEquals(one.typeName(), "priv.GetPriv.Count");
|
||||||
|
assertEval("var v = down();", "Packp");
|
||||||
|
assertDeclareFail("v.toString()", "compiler.err.not.def.access.class.intf.cant.access");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testInternal() {
|
public void testInternal() {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8153716 8143955 8151754 8150382 8153920 8156910 8131024 8160089 8153897 8167128 8154513 8170015 8170368 8172102 8172103 8165405 8173073 8173848 8174041 8173916 8174028 8174262 8174797 8177079 8180508 8177466 8172154 8192979
|
* @bug 8153716 8143955 8151754 8150382 8153920 8156910 8131024 8160089 8153897 8167128 8154513 8170015 8170368 8172102 8172103 8165405 8173073 8173848 8174041 8173916 8174028 8174262 8174797 8177079 8180508 8177466 8172154 8192979 8191842
|
||||||
* @summary Simple jshell tool tests
|
* @summary Simple jshell tool tests
|
||||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||||
* jdk.compiler/com.sun.tools.javac.main
|
* jdk.compiler/com.sun.tools.javac.main
|
||||||
@ -786,7 +786,11 @@ public class ToolSimpleTest extends ReplToolTesting {
|
|||||||
(a) -> assertCommandOutputContains(a, "var r1 = new Object() {}", "r1"),
|
(a) -> assertCommandOutputContains(a, "var r1 = new Object() {}", "r1"),
|
||||||
(a) -> assertCommandOutputContains(a, "/vars r1", "| <anonymous class extending Object> r1 = "),
|
(a) -> assertCommandOutputContains(a, "/vars r1", "| <anonymous class extending Object> r1 = "),
|
||||||
(a) -> assertCommandOutputContains(a, "var r2 = new Runnable() { public void run() { } }", "r2"),
|
(a) -> assertCommandOutputContains(a, "var r2 = new Runnable() { public void run() { } }", "r2"),
|
||||||
(a) -> assertCommandOutputContains(a, "/vars r2", "| <anonymous class implementing Runnable> r2 = ")
|
(a) -> assertCommandOutputContains(a, "/vars r2", "| <anonymous class implementing Runnable> r2 = "),
|
||||||
|
(a) -> assertCommandOutputContains(a, "import java.util.stream.*;", ""),
|
||||||
|
(a) -> assertCommandOutputContains(a, "var list = Stream.of(1, 2, 3).map(j -> new Object() { int i = j; }).collect(Collectors.toList());",
|
||||||
|
"list"),
|
||||||
|
(a) -> assertCommandOutputContains(a, "/vars list", "| List<<anonymous class extending Object>> list = ")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8144903 8171981 8191802
|
* @bug 8144903 8171981 8191802 8191842
|
||||||
* @summary Tests for determining the type from the expression
|
* @summary Tests for determining the type from the expression
|
||||||
* @build KullaTesting TestingInputStream
|
* @build KullaTesting TestingInputStream
|
||||||
* @run testng TypeNameTest
|
* @run testng TypeNameTest
|
||||||
@ -38,8 +38,12 @@ public class TypeNameTest extends KullaTesting {
|
|||||||
|
|
||||||
|
|
||||||
private void assertType(String expr, String type) {
|
private void assertType(String expr, String type) {
|
||||||
|
assertType(expr, type, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertType(String expr, String type, String inferType) {
|
||||||
assertEquals(varKey(assertEval(expr)).typeName(), type);
|
assertEquals(varKey(assertEval(expr)).typeName(), type);
|
||||||
assertInferredType(expr, type);
|
assertInferredType(expr, inferType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testTypeInference() {
|
public void testTypeInference() {
|
||||||
@ -58,7 +62,7 @@ public class TypeNameTest extends KullaTesting {
|
|||||||
assertType("d.getS()", "D<?>");
|
assertType("d.getS()", "D<?>");
|
||||||
assertType("null", "Object");
|
assertType("null", "Object");
|
||||||
assertType("Class.forName( \"java.util.ArrayList\" )", "Class<?>");
|
assertType("Class.forName( \"java.util.ArrayList\" )", "Class<?>");
|
||||||
assertType("new ArrayList<Boolean>() {}", "ArrayList<Boolean>");
|
assertType("new ArrayList<Boolean>() {}", "<anonymous class extending ArrayList<Boolean>>", "ArrayList<Boolean>");
|
||||||
assertType("new ArrayList<String>().stream()", "java.util.stream.Stream<String>");
|
assertType("new ArrayList<String>().stream()", "java.util.stream.Stream<String>");
|
||||||
assertType("Arrays.asList( 1, 2, 3)", "List<Integer>");
|
assertType("Arrays.asList( 1, 2, 3)", "List<Integer>");
|
||||||
assertType("new ArrayList().getClass().getClass()", "Class<? extends Class>");
|
assertType("new ArrayList().getClass().getClass()", "Class<? extends Class>");
|
||||||
@ -188,7 +192,7 @@ public class TypeNameTest extends KullaTesting {
|
|||||||
assertType("arrayOf(99)[0]", "Integer");
|
assertType("arrayOf(99)[0]", "Integer");
|
||||||
|
|
||||||
assertEval("<Z> Z choose(Z z1, Z z2) { return z1; }");
|
assertEval("<Z> Z choose(Z z1, Z z2) { return z1; }");
|
||||||
assertType("choose(1, 1L);", "Object");
|
assertType("choose(1, 1L);", "Number&Comparable<? extends Number&Comparable<?>>", "Object");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testVariableTypeName() {
|
public void testVariableTypeName() {
|
||||||
@ -215,7 +219,7 @@ public class TypeNameTest extends KullaTesting {
|
|||||||
public void testAnonymousClassName() {
|
public void testAnonymousClassName() {
|
||||||
assertEval("class C {}");
|
assertEval("class C {}");
|
||||||
assertType("new C();", "C");
|
assertType("new C();", "C");
|
||||||
assertType("new C() { int x; };", "C");
|
assertType("new C() { int x; };", "<anonymous class extending C>", "C");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCapturedTypeName() {
|
public void testCapturedTypeName() {
|
||||||
@ -243,7 +247,7 @@ public class TypeNameTest extends KullaTesting {
|
|||||||
assertType("test1.get()", "CharSequence");
|
assertType("test1.get()", "CharSequence");
|
||||||
assertEval("class Test2<X extends Number & CharSequence> { public X get() { return null; } }");
|
assertEval("class Test2<X extends Number & CharSequence> { public X get() { return null; } }");
|
||||||
assertEval("Test2<?> test2 = new Test2<>();");
|
assertEval("Test2<?> test2 = new Test2<>();");
|
||||||
assertType("test2.get()", "Object");
|
assertType("test2.get()", "Number&CharSequence", "Object");
|
||||||
assertEval("class Test3<T> { T[][] get() { return null; } }");
|
assertEval("class Test3<T> { T[][] get() { return null; } }");
|
||||||
assertEval("Test3<? extends String> test3 = new Test3<>();");
|
assertEval("Test3<? extends String> test3 = new Test3<>();");
|
||||||
assertType("test3.get()", "String[][]");
|
assertType("test3.get()", "String[][]");
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,12 +23,15 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8144903 8177466
|
* @bug 8144903 8177466 8191842
|
||||||
* @summary Tests for EvaluationState.variables
|
* @summary Tests for EvaluationState.variables
|
||||||
* @build KullaTesting TestingInputStream ExpectedDiagnostic
|
* @library /tools/lib
|
||||||
|
* @build Compiler KullaTesting TestingInputStream ExpectedDiagnostic
|
||||||
* @run testng VariablesTest
|
* @run testng VariablesTest
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.tools.Diagnostic;
|
import javax.tools.Diagnostic;
|
||||||
|
|
||||||
@ -37,6 +40,7 @@ import jdk.jshell.TypeDeclSnippet;
|
|||||||
import jdk.jshell.VarSnippet;
|
import jdk.jshell.VarSnippet;
|
||||||
import jdk.jshell.Snippet.SubKind;
|
import jdk.jshell.Snippet.SubKind;
|
||||||
import jdk.jshell.SnippetEvent;
|
import jdk.jshell.SnippetEvent;
|
||||||
|
import org.testng.annotations.BeforeMethod;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
@ -362,5 +366,157 @@ public class VariablesTest extends KullaTesting {
|
|||||||
assertEval("class O2 { public class Inner { public Inner(int i) { } public String test() { return \"good\"; } } }");
|
assertEval("class O2 { public class Inner { public Inner(int i) { } public String test() { return \"good\"; } } }");
|
||||||
assertEval("var r5 = new O2().new Inner(1) { public String get() { return \"good\"; } };");
|
assertEval("var r5 = new O2().new Inner(1) { public String get() { return \"good\"; } };");
|
||||||
assertEval("r5.get()", "\"good\"");
|
assertEval("r5.get()", "\"good\"");
|
||||||
|
assertEval("<Z> Z identity(Z z) { return z; }");
|
||||||
|
assertEval("var r6 = identity(new Object() { String s = \"good\"; });");
|
||||||
|
assertEval("r6.s", "\"good\"");
|
||||||
|
assertEval("interface I<B, C> { C get(B b); }");
|
||||||
|
assertEval("<A, B, C> C cascade(A a, I<A, B> c1, I<B, C> c2) { return c2.get(c1.get(a)); }");
|
||||||
|
assertEval("var r7 = cascade(\"good\", a -> new Object() { String s = a; }, b -> new java.util.ArrayList<String>(5) { String s = b.s; });");
|
||||||
|
assertEval("r7.s", "\"good\"");
|
||||||
|
assertEval("var r8 = cascade(\"good\", a -> new Object() { String s = a; public String getS() { return s; } }, b -> new java.util.ArrayList<String>(5) { String s = b.getS(); public String getS() { return s; } });");
|
||||||
|
assertEval("r8.getS()", "\"good\"");
|
||||||
|
assertEval("var r9 = new Object() { class T { class Inner { public String g() { return outer(); } } public String outer() { return \"good\"; } public String test() { return new Inner() {}.g(); } } public String test() { return new T().test(); } };");
|
||||||
|
assertEval("r9.test()", "\"good\"");
|
||||||
|
assertEval("var nested1 = new Object() { class N { public String get() { return \"good\"; } } };");
|
||||||
|
assertEval("nested1.new N().get()", "\"good\"");
|
||||||
|
assertEval("var nested2 = cascade(\"good\", a -> new Object() { abstract class G { abstract String g(); } G g = new G() { String g() { return a; } }; }, b -> new java.util.ArrayList<String>(5) { String s = b.g.g(); });");
|
||||||
|
assertEval("nested2.s", "\"good\"");
|
||||||
|
assertEval("<A, B> B convert(A a, I<A, B> c) { return c.get(a); }");
|
||||||
|
assertEval("var r10 = convert(\"good\", a -> new api.C(12) { public String val = \"\" + i + s + l + a; } );");
|
||||||
|
assertEval("r10.val", "\"12empty[empty]good\"");
|
||||||
|
assertEval("var r11 = convert(\"good\", a -> new api.C(\"a\") { public String val = \"\" + i + s + l + a; } );");
|
||||||
|
assertEval("r11.val", "\"3a[empty]good\"");
|
||||||
|
assertEval("import api.C;");
|
||||||
|
assertEval("var r12 = convert(\"good\", a -> new C(java.util.List.of(\"a\")) { public String val = \"\" + i + s + l + a; } );");
|
||||||
|
assertEval("r12.val", "\"4empty[a]good\"");
|
||||||
|
assertEval("var r13 = convert(\"good\", a -> new api.G<String>(java.util.List.of(\"b\")) { public String val = \"\" + l + a; } );");
|
||||||
|
assertEval("r13.val", "\"[b]good\"");
|
||||||
|
assertEval("var r14 = convert(\"good\", a -> new api.J<String>() { public java.util.List<String> get() { return java.util.List.of(a, \"c\"); } } );");
|
||||||
|
assertEval("r14.get()", "[good, c]");
|
||||||
|
assertEval("var r15a = new java.util.ArrayList<String>();");
|
||||||
|
assertEval("r15a.add(\"a\");");
|
||||||
|
assertEval("var r15b = r15a.get(0);");
|
||||||
|
assertEval("r15b", "\"a\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void test8191842() {
|
||||||
|
assertEval("import java.util.stream.*;");
|
||||||
|
assertEval("var list = Stream.of(1, 2, 3).map(j -> new Object() { int i = j; }).collect(Collectors.toList());");
|
||||||
|
assertEval("list.stream().map(a -> String.valueOf(a.i)).collect(Collectors.joining(\", \"));", "\"1, 2, 3\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void lvtiRecompileDependentsWithIntersectionTypes() {
|
||||||
|
assertEval("<Z extends Runnable & CharSequence> Z get1() { return null; }", added(VALID));
|
||||||
|
VarSnippet var = varKey(assertEval("var i1 = get1();", added(VALID)));
|
||||||
|
assertEval("import java.util.stream.*;", added(VALID),
|
||||||
|
ste(var, VALID, VALID, true, MAIN_SNIPPET));
|
||||||
|
assertEval("void t1() { i1.run(); i1.length(); }", added(VALID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void arrayInit() {
|
||||||
|
assertEval("int[] d = {1, 2, 3};");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAnonymousVar() {
|
||||||
|
assertEval("new Object() { public String get() { return \"a\"; } }");
|
||||||
|
assertEval("$1.get()", "\"a\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIntersectionVar() {
|
||||||
|
assertEval("<Z extends Runnable & CharSequence> Z get() { return null; }", added(VALID));
|
||||||
|
assertEval("get();", added(VALID));
|
||||||
|
assertEval("void t1() { $1.run(); $1.length(); }", added(VALID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void multipleCaptures() {
|
||||||
|
assertEval("class D { D(int foo, String bar) { this.foo = foo; this.bar = bar; } int foo; String bar; } ");
|
||||||
|
assertEval("var d = new D(34, \"hi\") { String z = foo + bar; };");
|
||||||
|
assertEval("d.z", "\"34hi\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void multipleAnonymous() {
|
||||||
|
VarSnippet v1 = varKey(assertEval("new Object() { public int i = 42; public int i1 = i; public int m1() { return i1; } };"));
|
||||||
|
VarSnippet v2 = varKey(assertEval("new Object() { public int i = 42; public int i2 = i; public int m2() { return i2; } };"));
|
||||||
|
assertEval(v1.name() + ".i", "42");
|
||||||
|
assertEval(v1.name() + ".i1", "42");
|
||||||
|
assertEval(v1.name() + ".m1()", "42");
|
||||||
|
assertDeclareFail(v1.name() + ".i2",
|
||||||
|
new ExpectedDiagnostic("compiler.err.cant.resolve.location", 0, 5, 2,
|
||||||
|
-1, -1, Diagnostic.Kind.ERROR));
|
||||||
|
assertEval(v2.name() + ".i", "42");
|
||||||
|
assertEval(v2.name() + ".i2", "42");
|
||||||
|
assertEval(v2.name() + ".m2()", "42");
|
||||||
|
assertDeclareFail(v2.name() + ".i1",
|
||||||
|
new ExpectedDiagnostic("compiler.err.cant.resolve.location", 0, 5, 2,
|
||||||
|
-1, -1, Diagnostic.Kind.ERROR));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void displayName() {
|
||||||
|
assertVarDisplayName("var v1 = 234;", "int");
|
||||||
|
assertVarDisplayName("var v2 = new int[] {234};", "int[]");
|
||||||
|
assertEval("<Z extends Runnable & CharSequence> Z get() { return null; }", added(VALID));
|
||||||
|
assertVarDisplayName("var v3 = get();", "CharSequence&Runnable");
|
||||||
|
assertVarDisplayName("var v4a = new java.util.ArrayList<String>();", "java.util.ArrayList<String>");
|
||||||
|
assertEval("v4a.add(\"a\");");
|
||||||
|
assertVarDisplayName("var v4b = v4a.get(0);", "String");
|
||||||
|
assertVarDisplayName("var v5 = new Object() { };", "<anonymous class extending Object>");
|
||||||
|
assertVarDisplayName("var v6 = new Runnable() { public void run() { } };", "<anonymous class implementing Runnable>");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertVarDisplayName(String var, String typeName) {
|
||||||
|
assertEquals(varKey(assertEval(var)).typeName(), typeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeMethod
|
||||||
|
@Override
|
||||||
|
public void setUp() {
|
||||||
|
Path path = Paths.get("cp");
|
||||||
|
Compiler compiler = new Compiler();
|
||||||
|
compiler.compile(path,
|
||||||
|
"package api;\n" +
|
||||||
|
"\n" +
|
||||||
|
"import java.util.List;\n" +
|
||||||
|
"\n" +
|
||||||
|
"public class C {\n" +
|
||||||
|
" public int i;\n" +
|
||||||
|
" public String s;\n" +
|
||||||
|
" public List<String> l;\n" +
|
||||||
|
" public C(int i) {\n" +
|
||||||
|
" this.i = i;\n" +
|
||||||
|
" this.s = \"empty\";\n" +
|
||||||
|
" this.l = List.of(\"empty\");\n" +
|
||||||
|
" }\n" +
|
||||||
|
" public C(String s) {\n" +
|
||||||
|
" this.i = 3;\n" +
|
||||||
|
" this.s = s;\n" +
|
||||||
|
" this.l = List.of(\"empty\");\n" +
|
||||||
|
" }\n" +
|
||||||
|
" public C(List<String> l) {\n" +
|
||||||
|
" this.i = 4;\n" +
|
||||||
|
" this.s = \"empty\";\n" +
|
||||||
|
" this.l = l;\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}\n",
|
||||||
|
"package api;\n" +
|
||||||
|
"\n" +
|
||||||
|
"import java.util.List;\n" +
|
||||||
|
"\n" +
|
||||||
|
"public class G<T> {\n" +
|
||||||
|
" public List<T> l;\n" +
|
||||||
|
" public G(List<T> l) {\n" +
|
||||||
|
" this.l = l;\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}\n",
|
||||||
|
"package api;\n" +
|
||||||
|
"\n" +
|
||||||
|
"import java.util.List;\n" +
|
||||||
|
"\n" +
|
||||||
|
"public interface J<T> {\n" +
|
||||||
|
" public List<T> get();\n" +
|
||||||
|
"}\n");
|
||||||
|
String tpath = compiler.getPath(path).toString();
|
||||||
|
setUp(b -> b
|
||||||
|
.remoteVMOptions("--class-path", tpath)
|
||||||
|
.compilerOptions("--class-path", tpath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user