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.
|
||||
*
|
||||
* 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.AssignmentTree;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.ExpressionStatementTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.IdentifierTree;
|
||||
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.Tree;
|
||||
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.Pretty;
|
||||
import java.io.IOException;
|
||||
@ -52,6 +54,8 @@ import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
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.MethodKey;
|
||||
import jdk.jshell.Key.TypeDeclKey;
|
||||
@ -60,6 +64,7 @@ import jdk.jshell.Snippet.SubKind;
|
||||
import jdk.jshell.TaskFactory.AnalyzeTask;
|
||||
import jdk.jshell.TaskFactory.BaseTask;
|
||||
import jdk.jshell.TaskFactory.ParseTask;
|
||||
import jdk.jshell.Util.Pair;
|
||||
import jdk.jshell.Wrap.CompoundWrap;
|
||||
import jdk.jshell.Wrap.Range;
|
||||
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.toSet;
|
||||
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.jshell.Util.DOIT_METHOD_NAME;
|
||||
import static jdk.jshell.Util.PREFIX_PATTERN;
|
||||
@ -98,6 +104,11 @@ class Eval {
|
||||
|
||||
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;
|
||||
|
||||
// The set of names of methods on Object
|
||||
@ -203,7 +214,7 @@ class Eval {
|
||||
case VARIABLE:
|
||||
return processVariables(userSource, units, compileSourceInt, pt);
|
||||
case EXPRESSION_STATEMENT:
|
||||
return processExpression(userSource, compileSourceInt);
|
||||
return processExpression(userSource, unitTree, compileSourceInt, pt);
|
||||
case CLASS:
|
||||
return processClass(userSource, unitTree, compileSourceInt, SubKind.CLASS_SUBKIND, pt);
|
||||
case ENUM:
|
||||
@ -285,15 +296,19 @@ class Eval {
|
||||
String name = vt.getName().toString();
|
||||
String typeName;
|
||||
String fullTypeName;
|
||||
String displayType;
|
||||
boolean hasEnhancedType = false;
|
||||
TreeDependencyScanner tds = new TreeDependencyScanner();
|
||||
Wrap typeWrap;
|
||||
Wrap anonDeclareWrap = null;
|
||||
Wrap winit = null;
|
||||
boolean enhancedDesugaring = false;
|
||||
Set<String> anonymousClasses = Collections.emptySet();
|
||||
StringBuilder sbBrackets = new StringBuilder();
|
||||
Tree baseType = vt.getType();
|
||||
if (baseType != null) {
|
||||
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) {
|
||||
//TODO handle annotations too
|
||||
baseType = ((ArrayTypeTree) baseType).getType();
|
||||
@ -311,83 +326,27 @@ class Eval {
|
||||
Range rinit = dis.treeToRange(init);
|
||||
String initCode = rinit.part(compileSource);
|
||||
ExpressionInfo ei =
|
||||
ExpressionToTypeInfo.localVariableTypeForInitializer(initCode, state);
|
||||
typeName = ei == null ? "java.lang.Object" : ei.typeName;
|
||||
fullTypeName = ei == null ? "java.lang.Object" : ei.fullTypeName;
|
||||
if (ei != null && init.getKind() == Tree.Kind.NEW_CLASS &&
|
||||
((NewClassTree) init).getClassBody() != null) {
|
||||
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;
|
||||
ExpressionToTypeInfo.localVariableTypeForInitializer(initCode, state, false);
|
||||
if (ei != null) {
|
||||
typeName = ei.declareTypeName;
|
||||
fullTypeName = ei.fullTypeName;
|
||||
displayType = ei.displayTypeName;
|
||||
|
||||
if (bodyRange != null) {
|
||||
bodyWrap = Wrap.rangeWrap(compileSource, bodyRange);
|
||||
} else {
|
||||
bodyWrap = Wrap.simpleWrap(" ");
|
||||
}
|
||||
hasEnhancedType = !typeName.equals(fullTypeName);
|
||||
|
||||
Range argRange = dis.treeListToRange(nct.getArguments());
|
||||
Wrap argWrap;
|
||||
enhancedDesugaring = !ei.isPrimitiveType;
|
||||
|
||||
if (argRange != null) {
|
||||
argWrap = Wrap.rangeWrap(compileSource, argRange);
|
||||
} else {
|
||||
argWrap = Wrap.simpleWrap(" ");
|
||||
}
|
||||
|
||||
if (ei.enclosingInstanceType != null) {
|
||||
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 + ">";
|
||||
Pair<Wrap, Wrap> anonymous2Member =
|
||||
anonymous2Member(ei, compileSource, rinit, dis, init);
|
||||
anonDeclareWrap = anonymous2Member.first;
|
||||
winit = anonymous2Member.second;
|
||||
anonymousClasses = ei.anonymousClasses.stream().map(ad -> ad.declareTypeName).collect(Collectors.toSet());
|
||||
} else {
|
||||
displayType = fullTypeName = typeName = "java.lang.Object";
|
||||
}
|
||||
tds.scan(init);
|
||||
} else {
|
||||
fullTypeName = typeName = "java.lang.Object";
|
||||
displayType = fullTypeName = typeName = "java.lang.Object";
|
||||
}
|
||||
typeWrap = Wrap.identityWrap(typeName);
|
||||
}
|
||||
@ -411,17 +370,193 @@ class Eval {
|
||||
int nameEnd = nameStart + name.length();
|
||||
Range rname = new Range(nameStart, nameEnd);
|
||||
Wrap guts = Wrap.varWrap(compileSource, typeWrap, sbBrackets.toString(), rname,
|
||||
winit, anonDeclareWrap);
|
||||
DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true);
|
||||
winit, enhancedDesugaring, anonDeclareWrap);
|
||||
DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true);
|
||||
Snippet snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts,
|
||||
name, subkind, fullTypeName,
|
||||
name, subkind, displayType, hasEnhancedType ? fullTypeName : null, anonymousClasses,
|
||||
tds.declareReferences(), modDiag);
|
||||
snippets.add(snip);
|
||||
}
|
||||
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;
|
||||
ExpressionInfo ei = ExpressionToTypeInfo.expressionInfo(compileSource, state);
|
||||
ExpressionTree assignVar;
|
||||
@ -453,10 +588,31 @@ class Eval {
|
||||
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
|
||||
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 {
|
||||
guts = Wrap.methodReturnWrap(compileSource);
|
||||
snip = new ExpressionSnippet(state.keyMap.keyForExpression(name, typeName), userSource, guts,
|
||||
@ -1059,4 +1215,7 @@ class Eval {
|
||||
: 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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,12 +25,21 @@
|
||||
|
||||
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.ClassTree;
|
||||
import com.sun.source.tree.CompilationUnitTree;
|
||||
import com.sun.source.tree.ConditionalExpressionTree;
|
||||
import com.sun.source.tree.ExpressionStatementTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.IdentifierTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
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.util.TreePath;
|
||||
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.Symbol;
|
||||
import com.sun.tools.javac.code.Symbol.TypeSymbol;
|
||||
import com.sun.tools.javac.code.Symtab;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
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.ListBuffer;
|
||||
import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo.AnonymousDescription;
|
||||
import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo.AnonymousDescription.VariableDesc;
|
||||
import jdk.jshell.TaskFactory.AnalyzeTask;
|
||||
import jdk.jshell.TypePrinter.AnonymousTypeKind;
|
||||
|
||||
/**
|
||||
* Compute information about an expression string, particularly its type name.
|
||||
@ -57,26 +74,75 @@ class ExpressionToTypeInfo {
|
||||
final AnalyzeTask at;
|
||||
final CompilationUnitTree cu;
|
||||
final JShell state;
|
||||
final boolean computeEnhancedInfo;
|
||||
final boolean enhancedTypesAccessible;
|
||||
final Symtab syms;
|
||||
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.cu = cu;
|
||||
this.state = state;
|
||||
this.computeEnhancedInfo = computeEnhancedInfo;
|
||||
this.enhancedTypesAccessible = enhancedTypesAccessible;
|
||||
this.syms = Symtab.instance(at.context);
|
||||
this.types = Types.instance(at.context);
|
||||
}
|
||||
|
||||
public static class ExpressionInfo {
|
||||
ExpressionTree tree;
|
||||
boolean isPrimitiveType;
|
||||
String typeName;
|
||||
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;
|
||||
List<String> parameterTypes;
|
||||
String enclosingInstanceType;
|
||||
boolean isClass;
|
||||
/* In result of localVariableTypeForInitializer, the human readable type of
|
||||
* the variable. This includes intersection types, and human readable descriptions
|
||||
* of anonymous types.
|
||||
*/
|
||||
String displayTypeName;
|
||||
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()
|
||||
@ -173,7 +239,7 @@ class ExpressionToTypeInfo {
|
||||
if (at.hasErrors() || cu == null) {
|
||||
return null;
|
||||
}
|
||||
return new ExpressionToTypeInfo(at, cu, state).typeOfExpression();
|
||||
return new ExpressionToTypeInfo(at, cu, state, false, false).typeOfExpression();
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
@ -187,7 +253,7 @@ class ExpressionToTypeInfo {
|
||||
* @param state a JShell instance
|
||||
* @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()) {
|
||||
return null;
|
||||
}
|
||||
@ -198,13 +264,37 @@ class ExpressionToTypeInfo {
|
||||
if (at.hasErrors() || cu == null) {
|
||||
return null;
|
||||
}
|
||||
return new ExpressionToTypeInfo(at, cu, state).typeOfExpression();
|
||||
return new ExpressionToTypeInfo(at, cu, state, true, onlyAccessible)
|
||||
.typeOfExpression();
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
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() {
|
||||
return treeToInfo(findExpressionPath());
|
||||
}
|
||||
@ -256,23 +346,31 @@ class ExpressionToTypeInfo {
|
||||
* @return the type, if it is accessible, otherwise a superclass or
|
||||
* 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
|
||||
for (Type sup = type; !types.isSameType(sup, syms.objectType); sup = supertype(sup)) {
|
||||
if (isAccessible(sup)) {
|
||||
return sup;
|
||||
accessible = accessible.prepend(sup);
|
||||
accessibleSuper = sup;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Failing superclasses, look through superclasses for accessible interfaces
|
||||
for (Type sup = type; !types.isSameType(sup, syms.objectType); sup = supertype(sup)) {
|
||||
// then look through superclasses for accessible interfaces
|
||||
for (Type sup = type; !types.isSameType(sup, accessibleSuper); sup = supertype(sup)) {
|
||||
for (Type itf : types.interfaces(sup)) {
|
||||
if (isAccessible(itf)) {
|
||||
return itf;
|
||||
accessible = accessible.prepend(itf);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Punt, return Object which is the supertype of everything
|
||||
return syms.objectType;
|
||||
if (accessible.isEmpty()) {
|
||||
// Punt, use Object which is the supertype of everything
|
||||
accessible = accessible.prepend(syms.objectType);
|
||||
}
|
||||
|
||||
return accessible.reverse();
|
||||
}
|
||||
|
||||
private ExpressionInfo treeToInfo(TreePath tp) {
|
||||
@ -298,48 +396,125 @@ class ExpressionToTypeInfo {
|
||||
break;
|
||||
default: {
|
||||
ei.isNonVoid = true;
|
||||
ei.typeName = varTypeName(type, false);
|
||||
ei.accessibleTypeName = varTypeName(findAccessibleSupertype(type), false);
|
||||
ei.fullTypeName = varTypeName(type, true);
|
||||
ei.isPrimitiveType = type.isPrimitive();
|
||||
ei.typeName = varTypeName(type, false, AnonymousTypeKind.SUPER);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tree.getKind() == Tree.Kind.VARIABLE) {
|
||||
if (tree.getKind() == Tree.Kind.VARIABLE && computeEnhancedInfo) {
|
||||
Tree init = ((VariableTree) tree).getInitializer();
|
||||
if (init.getKind() == Tree.Kind.NEW_CLASS &&
|
||||
((NewClassTree) init).getClassBody() != null) {
|
||||
NewClassTree nct = (NewClassTree) init;
|
||||
ClassTree clazz = nct.getClassBody();
|
||||
MethodTree constructor = (MethodTree) clazz.getMembers().get(0);
|
||||
ExpressionStatementTree superCallStatement =
|
||||
(ExpressionStatementTree) constructor.getBody().getStatements().get(0);
|
||||
for (NewClassTree node : listAnonymousClassesToConvert(init)) {
|
||||
Set<VariableElement> captured = capturedVariables(at,
|
||||
tp.getCompilationUnit(),
|
||||
node);
|
||||
JCClassDecl clazz = (JCClassDecl) node.getClassBody();
|
||||
MethodInvocationTree superCall =
|
||||
(MethodInvocationTree) superCallStatement.getExpression();
|
||||
TreePath superCallPath =
|
||||
at.trees().getPath(tp.getCompilationUnit(), superCall.getMethodSelect());
|
||||
clazz.getMembers()
|
||||
.stream()
|
||||
.map(TreeInfo::firstConstructorCall)
|
||||
.findAny()
|
||||
.get();
|
||||
TreePath superCallPath
|
||||
= at.trees().
|
||||
getPath(tp.getCompilationUnit(), superCall.
|
||||
getMethodSelect());
|
||||
Type constrType = pathToType(superCallPath);
|
||||
ei.parameterTypes = constrType.getParameterTypes()
|
||||
.stream()
|
||||
.map(t -> varTypeName(t, false))
|
||||
.collect(List.collector());
|
||||
if (nct.getEnclosingExpression() != null) {
|
||||
TreePath enclPath = new TreePath(tp, nct.getEnclosingExpression());
|
||||
ei.enclosingInstanceType = varTypeName(pathToType(enclPath), false);
|
||||
AnonymousDescription desc = new AnonymousDescription();
|
||||
desc.parameterTypes = constrType.getParameterTypes().
|
||||
stream().
|
||||
map(t -> varTypeName(t, false, AnonymousTypeKind.DECLARE)).
|
||||
collect(List.collector());
|
||||
if (node.getEnclosingExpression() != null) {
|
||||
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 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 {
|
||||
Function<TypeSymbol, String> anonymousClass2DeclareName =
|
||||
cs -> anon2Name.computeIfAbsent(cs, state.eval::computeDeclareName);
|
||||
TypePrinter tp = new TypePrinter(at.messages(),
|
||||
state.maps::fullClassNameAndPackageToClass, printIntersectionTypes);
|
||||
state.maps::fullClassNameAndPackageToClass, anonymousClass2DeclareName,
|
||||
printIntersectionTypes, anonymousTypesKind);
|
||||
List<Type> captures = types.captures(type);
|
||||
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.
|
||||
*
|
||||
* 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.nio.CharBuffer;
|
||||
import java.util.function.BiFunction;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.Tree.Kind;
|
||||
import com.sun.source.util.TaskEvent;
|
||||
import com.sun.source.util.TaskListener;
|
||||
import com.sun.tools.javac.api.JavacTaskPool;
|
||||
import com.sun.tools.javac.code.ClassFinder;
|
||||
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.PackageSymbol;
|
||||
import com.sun.tools.javac.code.Symbol.TypeSymbol;
|
||||
import com.sun.tools.javac.code.Symbol.VarSymbol;
|
||||
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.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.ParserFactory;
|
||||
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
|
||||
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
|
||||
import com.sun.tools.javac.tree.JCTree.JCExpression;
|
||||
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.util.Context.Factory;
|
||||
import com.sun.tools.javac.util.Log;
|
||||
import com.sun.tools.javac.util.Log.DiscardDiagnosticHandler;
|
||||
import com.sun.tools.javac.util.Names;
|
||||
@ -197,6 +206,7 @@ class TaskFactory {
|
||||
compilationUnits, task -> {
|
||||
JavacTaskImpl jti = (JavacTaskImpl) task;
|
||||
Context context = jti.getContext();
|
||||
DisableAccessibilityResolve.preRegister(context);
|
||||
jti.addTaskListener(new TaskListenerImpl(context, state));
|
||||
try {
|
||||
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 final Context context;
|
||||
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) {
|
||||
this.context = context;
|
||||
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
|
||||
public void finished(TaskEvent e) {
|
||||
if (e.getKind() != TaskEvent.Kind.ENTER)
|
||||
if (e.getKind() != TaskEvent.Kind.ENTER || variablesSet)
|
||||
return ;
|
||||
state.maps
|
||||
.snippetList()
|
||||
.stream()
|
||||
.filter(s -> s.status() == Status.VALID)
|
||||
.filter(s -> s.kind() == Snippet.Kind.VAR)
|
||||
.filter(s -> s.subKind() == Snippet.SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND)
|
||||
.forEach(s -> setVariableType((JCCompilationUnit) e.getCompilationUnit(), (VarSnippet) s));
|
||||
.filter(s -> s.subKind() == Snippet.SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND ||
|
||||
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);
|
||||
Names names = Names.instance(context);
|
||||
Log log = Log.instance(context);
|
||||
ParserFactory parserFactory = ParserFactory.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()));
|
||||
if (clazz == null || !clazz.isCompleted())
|
||||
return;
|
||||
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);
|
||||
DiscardDiagnosticHandler h = new DiscardDiagnosticHandler(log);
|
||||
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);
|
||||
Parser parser = parserFactory.newParser(buf, false, false, false);
|
||||
JCExpression expr = parser.parseExpression();
|
||||
if (expr.hasTag(Tag.TYPECAST)) {
|
||||
//if parsed OK, attribute and set the type:
|
||||
var2OriginalType.put(field, field.type);
|
||||
|
||||
JCTypeCast tree = (JCTypeCast) expr;
|
||||
if (tree.clazz.hasTag(Tag.TYPEINTERSECTION)) {
|
||||
rs.runWithoutAccessChecks(() -> {
|
||||
field.type = attr.attribType(tree.clazz,
|
||||
((JCClassDecl) root.getTypeDecls().head).sym);
|
||||
}
|
||||
enter.getEnvs().iterator().next().enclClass.sym);
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
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.
|
||||
*
|
||||
* 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.stream.Stream;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import jdk.jshell.TypePrinter.AnonymousTypeKind;
|
||||
import jdk.jshell.Util.Pair;
|
||||
|
||||
/**
|
||||
@ -227,7 +228,7 @@ class TreeDissector {
|
||||
Type typeImpl = (Type) type;
|
||||
try {
|
||||
TypePrinter tp = new TypePrinter(at.messages(),
|
||||
state.maps::fullClassNameAndPackageToClass, true);
|
||||
state.maps::fullClassNameAndPackageToClass, true, AnonymousTypeKind.DISPLAY);
|
||||
return tp.toString(typeImpl);
|
||||
} catch (Exception ex) {
|
||||
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.
|
||||
*
|
||||
* 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.ClassSymbol;
|
||||
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.ClassType;
|
||||
import com.sun.tools.javac.code.Type.IntersectionClassType;
|
||||
import com.sun.tools.javac.util.JavacMessages;
|
||||
import java.util.Locale;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* Print types in source form.
|
||||
*/
|
||||
@ -47,14 +50,44 @@ class TypePrinter extends Printer {
|
||||
|
||||
private final JavacMessages messages;
|
||||
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,
|
||||
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.fullClassNameAndPackageToClass = fullClassNameAndPackageToClass;
|
||||
this.printEnhancedTypes = printEnhancedTypes;
|
||||
this.anonymousToName = anonymousToName;
|
||||
this.printIntersectionTypes = printIntersectionTypes;
|
||||
this.anonymousTypesKind = anonymousTypesKind;
|
||||
}
|
||||
|
||||
String toString(Type t) {
|
||||
@ -96,9 +129,9 @@ class TypePrinter extends Printer {
|
||||
*/
|
||||
@Override
|
||||
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 (printEnhancedTypes) {
|
||||
if (printIntersectionTypes) {
|
||||
return ((IntersectionClassType) t).getExplicitComponents()
|
||||
.stream()
|
||||
.map(i -> visit(i, locale))
|
||||
@ -107,18 +140,26 @@ class TypePrinter extends Printer {
|
||||
return OBJECT;
|
||||
}
|
||||
} else if (sym.name.length() == 0) {
|
||||
if (printEnhancedTypes) {
|
||||
return t.tsym.flatName().toString().substring(t.tsym.outermostClass().flatName().length());
|
||||
if (anonymousTypesKind == AnonymousTypeKind.DECLARE) {
|
||||
return anonymousToName.apply(sym);
|
||||
}
|
||||
// Anonymous
|
||||
String s;
|
||||
boolean isClass;
|
||||
ClassType norm = (ClassType) t.tsym.type;
|
||||
if (norm == null) {
|
||||
s = OBJECT;
|
||||
isClass = true;
|
||||
} else if (norm.interfaces_field != null && norm.interfaces_field.nonEmpty()) {
|
||||
s = visit(norm.interfaces_field.head, locale);
|
||||
isClass = false;
|
||||
} else {
|
||||
s = visit(norm.supertype_field, locale);
|
||||
isClass = true;
|
||||
}
|
||||
if (anonymousTypesKind == AnonymousTypeKind.DISPLAY) {
|
||||
s = isClass ? "<anonymous class extending " + s + ">"
|
||||
: "<anonymous class implementing " + s + ">";
|
||||
}
|
||||
return s;
|
||||
} else if (longform) {
|
||||
@ -152,4 +193,14 @@ class TypePrinter extends Printer {
|
||||
: 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.
|
||||
*
|
||||
* 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$";
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@ -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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,6 +26,8 @@
|
||||
package jdk.jshell;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import jdk.jshell.Key.VarKey;
|
||||
|
||||
/**
|
||||
@ -41,15 +43,30 @@ import jdk.jshell.Key.VarKey;
|
||||
*/
|
||||
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;
|
||||
|
||||
/**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,
|
||||
String name, SubKind subkind, String typeName,
|
||||
Collection<String> declareReferences,
|
||||
String name, SubKind subkind, String typeName, String fullTypeName,
|
||||
Set<String> anonymousClasses, Collection<String> declareReferences,
|
||||
DiagList syntheticDiags) {
|
||||
super(key, userSource, guts, name, subkind, null, declareReferences,
|
||||
null, syntheticDiags);
|
||||
this.typeName = typeName;
|
||||
this.fullTypeName = fullTypeName;
|
||||
this.anonymousClasses = anonymousClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,4 +76,13 @@ public class VarSnippet extends DeclarationSnippet {
|
||||
public String 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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,7 +25,9 @@
|
||||
|
||||
package jdk.jshell;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static jdk.jshell.Util.DOIT_METHOD_NAME;
|
||||
|
||||
@ -66,37 +68,76 @@ abstract class Wrap implements GeneralWrap {
|
||||
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
|
||||
* @param rname
|
||||
* @param rinit Initializer or null
|
||||
* @param rdecl Type name and name
|
||||
* @return
|
||||
* In some cases, the real inferred type of the variable may be non-denotable
|
||||
* (e.g. intersection types). The declared type of the field must always
|
||||
* be denotable (i.e. such that it can be written into the classfile), but
|
||||
* if the real type is potentially non-denotable, the {@code enhanced} parameter
|
||||
* 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,
|
||||
Range rname, Wrap winit, Wrap anonDeclareWrap) {
|
||||
Range rname, Wrap winit, boolean enhanced,
|
||||
Wrap anonDeclareWrap) {
|
||||
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;
|
||||
|
||||
if (winit == null) {
|
||||
wmeth = new CompoundWrap(new NoWrap(" "), " return null;\n");
|
||||
} else {
|
||||
// int x = y
|
||||
// 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"
|
||||
);
|
||||
// int x = y
|
||||
if (enhanced) {
|
||||
// private static <Z> Z do_itAux() {
|
||||
// wtype x_ = y;
|
||||
// @SuppressWarnings("unchecked")
|
||||
// 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);
|
||||
return anonDeclareWrap != null ? new CompoundWrap(wVarDecl, wInitMeth, anonDeclareWrap)
|
||||
: new CompoundWrap(wVarDecl, wInitMeth);
|
||||
components.add(new DoitMethodWrap(wmeth));
|
||||
if (anonDeclareWrap != null) {
|
||||
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);
|
||||
// y
|
||||
// return $1 = y;
|
||||
@ -105,7 +146,8 @@ abstract class Wrap implements GeneralWrap {
|
||||
Wrap wInitMeth = new DoitMethodWrap(wmeth);
|
||||
|
||||
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) {
|
||||
|
@ -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.
|
||||
*
|
||||
* 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
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
@ -86,6 +86,8 @@ public class InaccessibleExpressionTest extends KullaTesting {
|
||||
assertEval(list.name() + ".size()", "0");
|
||||
VarSnippet one = varKey(assertEval("priv()", "One"));
|
||||
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() {
|
||||
|
@ -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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @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
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
* 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, "/vars r1", "| <anonymous class extending Object> r1 = "),
|
||||
(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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8144903 8171981 8191802
|
||||
* @bug 8144903 8171981 8191802 8191842
|
||||
* @summary Tests for determining the type from the expression
|
||||
* @build KullaTesting TestingInputStream
|
||||
* @run testng TypeNameTest
|
||||
@ -38,8 +38,12 @@ public class TypeNameTest extends KullaTesting {
|
||||
|
||||
|
||||
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);
|
||||
assertInferredType(expr, type);
|
||||
assertInferredType(expr, inferType);
|
||||
}
|
||||
|
||||
public void testTypeInference() {
|
||||
@ -58,7 +62,7 @@ public class TypeNameTest extends KullaTesting {
|
||||
assertType("d.getS()", "D<?>");
|
||||
assertType("null", "Object");
|
||||
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("Arrays.asList( 1, 2, 3)", "List<Integer>");
|
||||
assertType("new ArrayList().getClass().getClass()", "Class<? extends Class>");
|
||||
@ -188,7 +192,7 @@ public class TypeNameTest extends KullaTesting {
|
||||
assertType("arrayOf(99)[0]", "Integer");
|
||||
|
||||
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() {
|
||||
@ -215,7 +219,7 @@ public class TypeNameTest extends KullaTesting {
|
||||
public void testAnonymousClassName() {
|
||||
assertEval("class C {}");
|
||||
assertType("new C();", "C");
|
||||
assertType("new C() { int x; };", "C");
|
||||
assertType("new C() { int x; };", "<anonymous class extending C>", "C");
|
||||
}
|
||||
|
||||
public void testCapturedTypeName() {
|
||||
@ -243,7 +247,7 @@ public class TypeNameTest extends KullaTesting {
|
||||
assertType("test1.get()", "CharSequence");
|
||||
assertEval("class Test2<X extends Number & CharSequence> { public X get() { return null; } }");
|
||||
assertEval("Test2<?> test2 = new Test2<>();");
|
||||
assertType("test2.get()", "Object");
|
||||
assertType("test2.get()", "Number&CharSequence", "Object");
|
||||
assertEval("class Test3<T> { T[][] get() { return null; } }");
|
||||
assertEval("Test3<? extends String> test3 = new Test3<>();");
|
||||
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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,12 +23,15 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8144903 8177466
|
||||
* @bug 8144903 8177466 8191842
|
||||
* @summary Tests for EvaluationState.variables
|
||||
* @build KullaTesting TestingInputStream ExpectedDiagnostic
|
||||
* @library /tools/lib
|
||||
* @build Compiler KullaTesting TestingInputStream ExpectedDiagnostic
|
||||
* @run testng VariablesTest
|
||||
*/
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import javax.tools.Diagnostic;
|
||||
|
||||
@ -37,6 +40,7 @@ import jdk.jshell.TypeDeclSnippet;
|
||||
import jdk.jshell.VarSnippet;
|
||||
import jdk.jshell.Snippet.SubKind;
|
||||
import jdk.jshell.SnippetEvent;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
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("var r5 = new O2().new Inner(1) { public String get() { return \"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