| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.netbeans.modules.java.hints.spiimpl; |
| |
| import com.sun.source.tree.AnnotationTree; |
| import com.sun.source.tree.AssignmentTree; |
| import com.sun.source.tree.BlockTree; |
| import com.sun.source.tree.ClassTree; |
| import com.sun.source.tree.CompilationUnitTree; |
| import com.sun.source.tree.ExpressionStatementTree; |
| import com.sun.source.tree.ExpressionTree; |
| import com.sun.source.tree.IdentifierTree; |
| import com.sun.source.tree.ImportTree; |
| import com.sun.source.tree.LiteralTree; |
| import com.sun.source.tree.MemberSelectTree; |
| import com.sun.source.tree.MethodInvocationTree; |
| import com.sun.source.tree.MethodTree; |
| import com.sun.source.tree.ModifiersTree; |
| import com.sun.source.tree.NewArrayTree; |
| import com.sun.source.tree.NewClassTree; |
| import com.sun.source.tree.Scope; |
| import com.sun.source.tree.StatementTree; |
| import com.sun.source.tree.SwitchTree; |
| import com.sun.source.tree.Tree; |
| import com.sun.source.tree.Tree.Kind; |
| import com.sun.source.tree.TypeParameterTree; |
| import com.sun.source.tree.VariableTree; |
| import com.sun.source.util.SourcePositions; |
| import com.sun.source.util.TreePath; |
| import org.netbeans.api.java.source.support.ErrorAwareTreePathScanner; |
| import org.netbeans.api.java.source.support.ErrorAwareTreeScanner; |
| import com.sun.source.util.Trees; |
| import com.sun.tools.javac.api.JavacScope; |
| import com.sun.tools.javac.api.JavacTaskImpl; |
| import com.sun.tools.javac.api.JavacTrees; |
| import com.sun.tools.javac.code.Flags; |
| import com.sun.tools.javac.code.Symbol.ClassSymbol; |
| import com.sun.tools.javac.code.Symtab; |
| import com.sun.tools.javac.code.Type; |
| import com.sun.tools.javac.comp.Annotate; |
| import com.sun.tools.javac.comp.Attr; |
| import com.sun.tools.javac.comp.AttrContext; |
| import com.sun.tools.javac.comp.Env; |
| import com.sun.tools.javac.comp.Modules; |
| import com.sun.tools.javac.comp.Todo; |
| import com.sun.tools.javac.main.JavaCompiler; |
| import com.sun.tools.javac.parser.JavacParser; |
| import com.sun.tools.javac.parser.Lexer; |
| import com.sun.tools.javac.parser.Parser; |
| import com.sun.tools.javac.parser.ParserFactory; |
| import com.sun.tools.javac.parser.Scanner; |
| import com.sun.tools.javac.parser.ScannerFactory; |
| import com.sun.tools.javac.parser.Tokens.Token; |
| import com.sun.tools.javac.parser.Tokens.TokenKind; |
| import com.sun.tools.javac.tree.EndPosTable; |
| import com.sun.tools.javac.tree.JCTree; |
| import com.sun.tools.javac.tree.JCTree.JCCase; |
| import com.sun.tools.javac.tree.JCTree.JCCatch; |
| import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; |
| import com.sun.tools.javac.tree.JCTree.JCConstantCaseLabel; |
| import com.sun.tools.javac.tree.JCTree.JCExpression; |
| import com.sun.tools.javac.tree.JCTree.JCFieldAccess; |
| import com.sun.tools.javac.tree.JCTree.JCIdent; |
| import com.sun.tools.javac.tree.JCTree.JCMethodDecl; |
| import com.sun.tools.javac.tree.JCTree.JCModifiers; |
| import com.sun.tools.javac.tree.JCTree.JCStatement; |
| import com.sun.tools.javac.tree.JCTree.JCVariableDecl; |
| import com.sun.tools.javac.util.Context; |
| import com.sun.tools.javac.util.JCDiagnostic; |
| import com.sun.tools.javac.util.ListBuffer; |
| import com.sun.tools.javac.util.Log; |
| import com.sun.tools.javac.util.Names; |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.invoke.MethodHandles; |
| import java.lang.ref.Reference; |
| import java.lang.ref.WeakReference; |
| import java.net.URI; |
| import java.nio.CharBuffer; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.EnumSet; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import javax.lang.model.element.AnnotationMirror; |
| import javax.lang.model.element.AnnotationValue; |
| import javax.lang.model.element.AnnotationValueVisitor; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.ElementKind; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.Modifier; |
| import javax.lang.model.element.Name; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.element.VariableElement; |
| import javax.lang.model.type.TypeKind; |
| import javax.lang.model.type.TypeMirror; |
| import javax.lang.model.util.Elements; |
| import javax.tools.Diagnostic; |
| import javax.tools.JavaCompiler.CompilationTask; |
| import javax.tools.JavaFileObject; |
| import javax.tools.SimpleJavaFileObject; |
| import net.bytebuddy.dynamic.DynamicType.Loaded; |
| import net.bytebuddy.dynamic.DynamicType.Unloaded; |
| import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; |
| import org.netbeans.api.annotations.common.CheckForNull; |
| import org.netbeans.api.annotations.common.NonNull; |
| import org.netbeans.api.java.classpath.ClassPath; |
| import org.netbeans.api.java.platform.JavaPlatform; |
| import org.netbeans.api.java.platform.JavaPlatformManager; |
| import org.netbeans.api.java.queries.SourceForBinaryQuery; |
| import org.netbeans.api.java.queries.SourceForBinaryQuery.Result2; |
| import org.netbeans.api.java.source.ClasspathInfo; |
| import org.netbeans.api.java.source.CompilationInfo; |
| import org.netbeans.api.java.source.CompilationInfo.CacheClearPolicy; |
| import org.netbeans.api.java.source.SourceUtils; |
| import org.netbeans.api.java.source.TreeMaker; |
| import org.netbeans.modules.java.hints.providers.spi.ClassPathBasedHintProvider; |
| import org.netbeans.modules.java.hints.providers.spi.HintDescription; |
| import org.netbeans.modules.java.hints.providers.spi.Trigger.PatternDescription; |
| import org.netbeans.modules.java.hints.spiimpl.JackpotTrees.CatchWildcard; |
| import org.netbeans.modules.java.source.JavaSourceAccessor; |
| import org.netbeans.modules.java.source.builder.TreeFactory; |
| import org.netbeans.lib.nbjavac.services.CancelService; |
| import org.netbeans.lib.nbjavac.services.NBParserFactory.NBJavacParser; |
| import org.netbeans.lib.nbjavac.services.NBParserFactory; |
| import org.netbeans.lib.nbjavac.services.NBResolve; |
| import org.netbeans.modules.java.hints.spiimpl.JackpotTrees.AnnotationWildcard; |
| import org.netbeans.modules.java.hints.spiimpl.JackpotTrees.FakeBlock; |
| import org.netbeans.modules.java.source.parsing.FileObjects; |
| import org.netbeans.modules.java.source.pretty.ImportAnalysis2; |
| import org.netbeans.modules.java.source.transform.ImmutableTreeTranslator; |
| import org.netbeans.spi.editor.hints.Severity; |
| import org.netbeans.spi.java.classpath.support.ClassPathSupport; |
| import org.openide.filesystems.FileObject; |
| import org.openide.filesystems.FileUtil; |
| import org.openide.util.Lookup; |
| import org.openide.util.NbCollections; |
| import org.openide.util.WeakListeners; |
| import org.openide.util.lookup.ServiceProvider; |
| |
| import static com.sun.source.tree.CaseTree.CaseKind.STATEMENT; |
| |
| /** |
| * |
| * @author Jan Lahoda |
| */ |
| public class Utilities { |
| |
| private Utilities() {} |
| |
| public static Set<Severity> disableErrors(FileObject file) { |
| if (file.getAttribute(DISABLE_ERRORS) != null) { |
| return EnumSet.allOf(Severity.class); |
| } |
| if (!file.canWrite() && FileUtil.getArchiveFile(file) != null) { |
| return EnumSet.allOf(Severity.class); |
| } |
| |
| return EnumSet.noneOf(Severity.class); |
| } |
| |
| private static final String DISABLE_ERRORS = "disable-java-errors"; |
| private static final String SWITCH_EXPRESSION = "SWITCH_EXPRESSION"; |
| |
| |
| public static <E> Iterable<E> checkedIterableByFilter(final Iterable<?> raw, final Class<E> type, final boolean strict) { |
| return () -> NbCollections.checkedIteratorByFilter(raw.iterator(), type, strict); |
| } |
| |
| // public static AnnotationTree constructConstraint(WorkingCopy wc, String name, TypeMirror tm) { |
| // TreeMaker make = wc.getTreeMaker(); |
| // ExpressionTree variable = prepareAssignment(make, "variable", make.Literal(name)); |
| // ExpressionTree type = prepareAssignment(make, "type", make.MemberSelect((ExpressionTree) make.Type(wc.getTypes().erasure(tm)), "class")); |
| // TypeElement constraint = wc.getElements().getTypeElement(Annotations.CONSTRAINT.toFQN()); |
| // |
| // return make.Annotation(make.QualIdent(constraint), Arrays.asList(variable, type)); |
| // } |
| |
| public static ExpressionTree prepareAssignment(TreeMaker make, String name, ExpressionTree value) { |
| return make.Assignment(make.Identifier(name), value); |
| } |
| |
| public static ExpressionTree findValue(AnnotationTree m, String name) { |
| for (ExpressionTree et : m.getArguments()) { |
| if (et.getKind() == Kind.ASSIGNMENT) { |
| AssignmentTree at = (AssignmentTree) et; |
| String varName = ((IdentifierTree) at.getVariable()).getName().toString(); |
| |
| if (varName.equals(name)) { |
| return at.getExpression(); |
| } |
| } |
| |
| if (et instanceof LiteralTree/*XXX*/ && "value".equals(name)) { |
| return et; |
| } |
| } |
| |
| return null; |
| } |
| |
| public static List<AnnotationTree> findArrayValue(AnnotationTree at, String name) { |
| ExpressionTree fixesArray = findValue(at, name); |
| List<AnnotationTree> fixes = new LinkedList<>(); |
| |
| if (fixesArray != null && fixesArray.getKind() == Kind.NEW_ARRAY) { |
| NewArrayTree trees = (NewArrayTree) fixesArray; |
| |
| for (ExpressionTree fix : trees.getInitializers()) { |
| if (fix.getKind() == Kind.ANNOTATION) { |
| fixes.add((AnnotationTree) fix); |
| } |
| } |
| } |
| |
| if (fixesArray != null && fixesArray.getKind() == Kind.ANNOTATION) { |
| fixes.add((AnnotationTree) fixesArray); |
| } |
| |
| return fixes; |
| } |
| |
| public static boolean isPureMemberSelect(Tree mst, boolean allowVariables) { |
| switch (mst.getKind()) { |
| case IDENTIFIER: return allowVariables || ((IdentifierTree) mst).getName().charAt(0) != '$'; |
| case MEMBER_SELECT: return isPureMemberSelect(((MemberSelectTree) mst).getExpression(), allowVariables); |
| default: return false; |
| } |
| } |
| |
| public static Map<String, Collection<HintDescription>> sortOutHints(Iterable<? extends HintDescription> hints, Map<String, Collection<HintDescription>> output) { |
| for (HintDescription d : hints) { |
| Collection<HintDescription> h = output.get(d.getMetadata().displayName); |
| |
| if (h == null) { |
| output.put(d.getMetadata().displayName, h = new LinkedList<>()); |
| } |
| |
| h.add(d); |
| } |
| |
| return output; |
| } |
| |
| public static List<HintDescription> listAllHints(Set<ClassPath> cps) { |
| List<HintDescription> result = new LinkedList<>(); |
| |
| for (Collection<? extends HintDescription> hints : RulesManager.getInstance().readHints(null, cps, new AtomicBoolean()).values()) { |
| for (HintDescription hd : hints) { |
| if (!(hd.getTrigger() instanceof PatternDescription)) continue; //TODO: only pattern based hints are currently supported |
| result.add(hd); |
| } |
| } |
| |
| result.addAll(listClassPathHints(Collections.<ClassPath>emptySet(), cps)); |
| |
| return result; |
| } |
| |
| public static List<HintDescription> listClassPathHints(Set<ClassPath> sourceCPs, Set<ClassPath> binaryCPs) { |
| List<HintDescription> result = new LinkedList<>(); |
| Set<FileObject> roots = new HashSet<>(); |
| |
| for (ClassPath cp : binaryCPs) { |
| for (FileObject r : cp.getRoots()) { |
| Result2 src = SourceForBinaryQuery.findSourceRoots2(r.toURL()); |
| |
| if (src != null && src.preferSources()) { |
| roots.addAll(Arrays.asList(src.getRoots())); |
| } else { |
| roots.add(r); |
| } |
| } |
| } |
| |
| Set<ClassPath> cps = new HashSet<>(sourceCPs); |
| |
| cps.add(ClassPathSupport.createClassPath(roots.toArray(new FileObject[0]))); |
| |
| ClassPath cp = ClassPathSupport.createProxyClassPath(cps.toArray(new ClassPath[0])); |
| |
| for (ClassPathBasedHintProvider p : Lookup.getDefault().lookupAll(ClassPathBasedHintProvider.class)) { |
| result.addAll(p.computeHints(cp, new AtomicBoolean())); |
| } |
| |
| return result; |
| } |
| |
| public static Tree parseAndAttribute(CompilationInfo info, String pattern, Scope scope) { |
| return parseAndAttribute(info, pattern, scope, null); |
| } |
| |
| public static Tree parseAndAttribute(CompilationInfo info, String pattern, Scope scope, Collection<Diagnostic<? extends JavaFileObject>> errors) { |
| return parseAndAttribute(info, JavaSourceAccessor.getINSTANCE().getJavacTask(info), pattern, scope, errors); |
| } |
| |
| public static Tree parseAndAttribute(CompilationInfo info, String pattern, Scope scope, SourcePositions[] sourcePositions, Collection<Diagnostic<? extends JavaFileObject>> errors) { |
| return parseAndAttribute(info, JavaSourceAccessor.getINSTANCE().getJavacTask(info), pattern, scope, sourcePositions, errors); |
| } |
| |
| public static Tree parseAndAttribute(JavacTaskImpl jti, String pattern) { |
| return parseAndAttribute(jti, pattern, null); |
| } |
| |
| public static Tree parseAndAttribute(JavacTaskImpl jti, String pattern, Collection<Diagnostic<? extends JavaFileObject>> errors) { |
| return parseAndAttribute(null, jti, pattern, null, errors); |
| } |
| |
| public static Tree parseAndAttribute(JavacTaskImpl jti, String pattern, SourcePositions[] sourcePositions, Collection<Diagnostic<? extends JavaFileObject>> errors) { |
| return parseAndAttribute(null, jti, pattern, null, sourcePositions, errors); |
| } |
| |
| private static Tree parseAndAttribute(CompilationInfo info, JavacTaskImpl jti, String pattern, Scope scope, Collection<Diagnostic<? extends JavaFileObject>> errors) { |
| return parseAndAttribute(info, jti, pattern, scope, new SourcePositions[1], errors); |
| } |
| |
| private static Tree parseAndAttribute(CompilationInfo info, JavacTaskImpl jti, String pattern, Scope scope, SourcePositions[] sourcePositions, Collection<Diagnostic<? extends JavaFileObject>> errors) { |
| Context c = jti.getContext(); |
| JavaCompiler.instance(c); //force reasonable initialization order |
| TreeFactory make = TreeFactory.instance(c); |
| List<Diagnostic<? extends JavaFileObject>> patternTreeErrors = new LinkedList<>(); |
| Tree toAttribute; |
| Tree patternTree = toAttribute = !isStatement(pattern) ? parseExpression(c, pattern, true, sourcePositions, patternTreeErrors) : null; |
| int offset = 0; |
| boolean expression = true; |
| boolean classMember = false; |
| |
| if (pattern.startsWith("case ")) {//XXX: should be a lexer token |
| List<Diagnostic<? extends JavaFileObject>> currentPatternTreeErrors = new LinkedList<>(); |
| Tree switchTree = parseStatement(c, "switch ($$foo) {" + pattern + "}", sourcePositions, currentPatternTreeErrors); |
| |
| offset = "switch ($$foo) {".length(); |
| patternTreeErrors = currentPatternTreeErrors; |
| toAttribute = switchTree; |
| patternTree = ((SwitchTree) switchTree).getCases().get(0); |
| } |
| |
| if (patternTree == null || isErrorTree(patternTree) || SWITCH_EXPRESSION.equals(patternTree.getKind().name())) { |
| SourcePositions[] currentPatternTreePositions = new SourcePositions[1]; |
| List<Diagnostic<? extends JavaFileObject>> currentPatternTreeErrors = new LinkedList<>(); |
| Tree currentPatternTree = parseStatement(c, "{" + pattern + "}", currentPatternTreePositions, currentPatternTreeErrors); |
| |
| assert currentPatternTree.getKind() == Kind.BLOCK : currentPatternTree.getKind(); |
| |
| List<? extends StatementTree> statements = ((BlockTree) currentPatternTree).getStatements(); |
| |
| if (statements.size() == 1) { |
| currentPatternTree = statements.get(0); |
| } else { |
| com.sun.tools.javac.util.List<JCStatement> newStatements = com.sun.tools.javac.util.List.<JCStatement>nil(); |
| |
| if (!statements.isEmpty() && !Utilities.isMultistatementWildcardTree(statements.get(0))) |
| newStatements = newStatements.append((JCStatement) make.ExpressionStatement(make.Identifier("$$1$"))); |
| for (StatementTree st : statements) { |
| newStatements = newStatements.append((JCStatement) st); |
| } |
| if (!statements.isEmpty() && !Utilities.isMultistatementWildcardTree(statements.get(statements.size() - 1))) |
| newStatements = newStatements.append((JCStatement) make.ExpressionStatement(make.Identifier("$$2$"))); |
| |
| currentPatternTree = new FakeBlock(0L, newStatements); |
| } |
| |
| if (!currentPatternTreeErrors.isEmpty() || containsError(currentPatternTree)) { |
| //maybe a class member? |
| SourcePositions[] classPatternTreePositions = new SourcePositions[1]; |
| List<Diagnostic<? extends JavaFileObject>> classPatternTreeErrors = new LinkedList<>(); |
| Tree classPatternTree = parseExpression(c, "new Object() {" + pattern + "}", false, classPatternTreePositions, classPatternTreeErrors); |
| |
| if (!containsError(classPatternTree)) { |
| sourcePositions[0] = classPatternTreePositions[0]; |
| offset = "new Object() {".length(); |
| patternTreeErrors = classPatternTreeErrors; |
| patternTree = toAttribute = classPatternTree; |
| classMember = true; |
| } else { |
| offset = 1; |
| sourcePositions[0] = currentPatternTreePositions[0]; |
| VariableTree var; |
| Names names = Names.instance(jti.getContext()); |
| if (currentPatternTree.getKind() == Kind.VARIABLE && (var = ((VariableTree) currentPatternTree)).getType().getKind() == Kind.ERRONEOUS && var.getName() == names.error && var.getInitializer() == null && var.getModifiers().getAnnotations().size() == 1 && !containsError(var.getModifiers().getAnnotations().get(0))) { |
| patternTreeErrors = currentPatternTreeErrors; //TODO: the errors are incorrect |
| toAttribute = currentPatternTree; |
| patternTree = var.getModifiers().getAnnotations().get(0); |
| } else { |
| patternTreeErrors = currentPatternTreeErrors; |
| patternTree = toAttribute = currentPatternTree; |
| } |
| } |
| } else { |
| sourcePositions[0] = currentPatternTreePositions[0]; |
| offset = 1; |
| patternTreeErrors = currentPatternTreeErrors; |
| patternTree = toAttribute = currentPatternTree; |
| } |
| |
| expression = false; |
| } |
| |
| if (scope != null) { |
| TypeMirror type = attributeTree(jti, toAttribute, scope, patternTreeErrors); |
| |
| if (isError(type) && expression) { |
| //maybe type? |
| if (Utilities.isPureMemberSelect(patternTree, false)) { |
| SourcePositions[] varPositions = new SourcePositions[1]; |
| List<Diagnostic<? extends JavaFileObject>> varErrors = new LinkedList<>(); |
| Tree var = parseExpression(c, pattern + ".Class.class;", false, varPositions, varErrors); |
| |
| attributeTree(jti, var, scope, varErrors); |
| |
| ExpressionTree typeTree = ((MemberSelectTree) ((MemberSelectTree) var).getExpression()).getExpression(); |
| final Symtab symtab = Symtab.instance(c); |
| final Elements el = jti.getElements(); |
| final Trees trees = JavacTrees.instance(c); |
| CompilationUnitTree cut = ((JavacScope) scope).getEnv().toplevel; |
| final boolean[] found = new boolean[1]; |
| |
| new ErrorAwareTreePathScanner<Void, Void>() { |
| @Override public Void visitMemberSelect(MemberSelectTree node, Void p) { |
| Element currentElement = trees.getElement(getCurrentPath()); |
| |
| if (!isError(currentElement)) { |
| if (currentElement.getKind() == ElementKind.PACKAGE && el.getPackageElement(node.toString()) == null) { |
| ((JCFieldAccess) node).sym = symtab.errSymbol; |
| ((JCFieldAccess) node).type = symtab.errType; |
| } else { |
| found[0] = true; |
| return null; |
| } |
| } |
| |
| return super.visitMemberSelect(node, p); |
| } |
| @Override public Void visitIdentifier(IdentifierTree node, Void p) { |
| Element currentElement = trees.getElement(getCurrentPath()); |
| |
| if (!isError(currentElement)) { |
| if (currentElement.getKind() == ElementKind.PACKAGE && el.getPackageElement(node.toString()) == null) { |
| ((JCIdent) node).sym = symtab.errSymbol; |
| ((JCIdent) node).type = symtab.errType; |
| } else { |
| found[0] = true; |
| return null; |
| } |
| } |
| return super.visitIdentifier(node, p); |
| } |
| |
| }.scan(new TreePath(new TreePath(cut), typeTree), null); |
| |
| if (found[0]) { |
| sourcePositions[0] = varPositions[0]; |
| offset = 0; |
| patternTreeErrors = varErrors; |
| patternTree = typeTree; |
| } |
| } |
| } |
| } |
| |
| if (classMember) { |
| List<? extends Tree> members = ((NewClassTree) patternTree).getClassBody().getMembers(); |
| |
| int syntheticOffset = !members.isEmpty() && members.get(0).getKind() == Kind.METHOD && (((JCMethodDecl) members.get(0)).mods.flags & Flags.GENERATEDCONSTR) != 0 ? 1 : 0; |
| |
| if (members.size() > 1 + syntheticOffset) { |
| ModifiersTree mt = make.Modifiers(EnumSet.noneOf(Modifier.class)); |
| List<Tree> newMembers = new LinkedList<>(); |
| |
| newMembers.add(make.ExpressionStatement(make.Identifier("$$1$"))); |
| newMembers.addAll(members.subList(syntheticOffset, members.size())); |
| |
| patternTree = make.Class(mt, "$", Collections.<TypeParameterTree>emptyList(), null, Collections.<Tree>emptyList(), newMembers); |
| } else { |
| patternTree = members.get(0 + syntheticOffset); |
| } |
| } |
| |
| if (errors != null) { |
| for (Diagnostic<? extends JavaFileObject> d : patternTreeErrors) { |
| if (d.getCode().equals("compiler.err.cant.resolve")) { // NOI18N |
| String msg = d.getMessage(Locale.ENGLISH); |
| if (msg != null) { |
| int symIdx = msg.indexOf("symbol: "); // NOI18N |
| if (symIdx > 0) { |
| symIdx += 8; |
| // ignore errors for $ placeholders; may be identified as classnames as well |
| if (msg.charAt(symIdx) == '$' || msg.substring(symIdx, symIdx + 7).equals("class $")) { // NOI18N |
| continue; |
| } |
| } |
| } |
| } |
| if (d.getStartPosition() == -1 || d.getEndPosition() == -1) { |
| continue; |
| } |
| errors.add(new OffsetDiagnostic<JavaFileObject>(d, sourcePositions[0], -offset)); |
| } |
| } |
| |
| sourcePositions[0] = new OffsetSourcePositions(sourcePositions[0], -offset); |
| |
| return patternTree; |
| } |
| |
| static boolean isError(Element el) { |
| return (el == null || (el.getKind() == ElementKind.CLASS) && isError(((TypeElement) el).asType())); |
| } |
| |
| private static boolean isError(TypeMirror type) { |
| return type == null || type.getKind() == TypeKind.ERROR; |
| } |
| |
| private static boolean isStatement(String pattern) { |
| return pattern.trim().endsWith(";"); |
| } |
| |
| private static boolean isErrorTree(Tree t) { |
| return t.getKind() == Kind.ERRONEOUS || (t.getKind() == Kind.IDENTIFIER && ((IdentifierTree) t).getName().contentEquals("<error>")); //TODO: <error>... |
| } |
| |
| @SuppressWarnings({"BoxedValueEquality"}) |
| private static boolean containsError(Tree t) { |
| return new ErrorAwareTreeScanner<Boolean, Void>() { |
| @Override |
| public Boolean scan(Tree node, Void p) { |
| if (node != null && isErrorTree(node)) { |
| return true; |
| } |
| return super.scan(node, p) == Boolean.TRUE; |
| } |
| @Override |
| public Boolean reduce(Boolean r1, Boolean r2) { |
| return r1 == Boolean.TRUE || r2 == Boolean.TRUE; |
| } |
| }.scan(t, null); |
| } |
| |
| private static JCStatement parseStatement(Context context, CharSequence stmt, SourcePositions[] pos, final List<Diagnostic<? extends JavaFileObject>> errors) { |
| if (stmt == null || (pos != null && pos.length != 1)) |
| throw new IllegalArgumentException(); |
| JavaCompiler compiler = JavaCompiler.instance(context); |
| JavaFileObject prev = compiler.log.useSource(new DummyJFO()); |
| Log.DiagnosticHandler discardHandler = new Log.DiscardDiagnosticHandler(compiler.log) { |
| @Override |
| public void report(JCDiagnostic diag) { |
| errors.add(diag); |
| } |
| }; |
| try { |
| CharBuffer buf = CharBuffer.wrap((stmt+"\u0000").toCharArray(), 0, stmt.length()); |
| ParserFactory factory = ParserFactory.instance(context); |
| ScannerFactory scannerFactory = ScannerFactory.instance(context); |
| Names names = Names.instance(context); |
| Parser parser = new JackpotJavacParser(context, (NBParserFactory) factory, scannerFactory.newScanner(buf, false), false, false, CancelService.instance(context), names); |
| if (parser instanceof JavacParser) { |
| if (pos != null) |
| pos[0] = new ParserSourcePositions((JavacParser)parser); |
| return parser.parseStatement(); |
| } |
| return null; |
| } finally { |
| compiler.log.useSource(prev); |
| compiler.log.popDiagnosticHandler(discardHandler); |
| } |
| } |
| |
| private static JCExpression parseExpression(Context context, CharSequence expr, boolean onlyFullInput, SourcePositions[] pos, final List<Diagnostic<? extends JavaFileObject>> errors) { |
| if (expr == null || (pos != null && pos.length != 1)) |
| throw new IllegalArgumentException(); |
| JavaCompiler compiler = JavaCompiler.instance(context); |
| JavaFileObject prev = compiler.log.useSource(new DummyJFO()); |
| Log.DiagnosticHandler discardHandler = new Log.DiscardDiagnosticHandler(compiler.log) { |
| @Override |
| public void report(JCDiagnostic diag) { |
| errors.add(diag); |
| } |
| }; |
| try { |
| CharBuffer buf = CharBuffer.wrap((expr+"\u0000").toCharArray(), 0, expr.length()); |
| ParserFactory factory = ParserFactory.instance(context); |
| ScannerFactory scannerFactory = ScannerFactory.instance(context); |
| Names names = Names.instance(context); |
| Scanner scanner = scannerFactory.newScanner(buf, false); |
| Parser parser = new JackpotJavacParser(context, (NBParserFactory) factory, scanner, false, false, CancelService.instance(context), names); |
| if (parser instanceof JavacParser) { |
| if (pos != null) |
| pos[0] = new ParserSourcePositions((JavacParser)parser); |
| JCExpression result = parser.parseExpression(); |
| |
| if (!onlyFullInput || scanner.token().kind == TokenKind.EOF) { |
| return result; |
| } |
| } |
| return null; |
| } finally { |
| compiler.log.useSource(prev); |
| compiler.log.popDiagnosticHandler(discardHandler); |
| } |
| } |
| |
| private static TypeMirror attributeTree(JavacTaskImpl jti, Tree tree, Scope scope, final List<Diagnostic<? extends JavaFileObject>> errors) { |
| Log log = Log.instance(jti.getContext()); |
| JavaFileObject prev = log.useSource(new DummyJFO()); |
| Log.DiagnosticHandler discardHandler = new Log.DiscardDiagnosticHandler(log) { |
| @Override |
| public void report(JCDiagnostic diag) { |
| errors.add(diag); |
| } |
| }; |
| NBResolve resolve = NBResolve.instance(jti.getContext()); |
| resolve.disableAccessibilityChecks(); |
| // Enter enter = Enter.instance(jti.getContext()); |
| // enter.shadowTypeEnvs(true); |
| // ArgumentAttr argumentAttr = ArgumentAttr.instance(jti.getContext()); |
| // ArgumentAttr.LocalCacheContext cacheContext = argumentAttr.withLocalCacheContext(); |
| try { |
| Attr attr = Attr.instance(jti.getContext()); |
| Env<AttrContext> env = ((JavacScope) scope).getEnv(); |
| if (tree instanceof JCExpression) |
| return attr.attribExpr((JCTree) tree,env, Type.noType); |
| return attr.attribStat((JCTree) tree,env); |
| } finally { |
| // cacheContext.leave(); |
| log.useSource(prev); |
| log.popDiagnosticHandler(discardHandler); |
| resolve.restoreAccessbilityChecks(); |
| // enter.shadowTypeEnvs(false); |
| } |
| } |
| |
| public static @CheckForNull CharSequence getWildcardTreeName(@NonNull Tree t) { |
| if (t.getKind() == Kind.EXPRESSION_STATEMENT && ((ExpressionStatementTree) t).getExpression().getKind() == Kind.IDENTIFIER) { |
| IdentifierTree identTree = (IdentifierTree) ((ExpressionStatementTree) t).getExpression(); |
| |
| return identTree.getName().toString(); |
| } |
| |
| if (t.getKind() == Kind.IDENTIFIER) { |
| IdentifierTree identTree = (IdentifierTree) t; |
| String name = identTree.getName().toString(); |
| |
| if (name.startsWith("$")) { |
| return name; |
| } |
| } |
| |
| if (t.getKind() == Kind.TYPE_PARAMETER) { |
| String name = ((TypeParameterTree) t).getName().toString(); |
| |
| if (name.startsWith("$")) { |
| return name; |
| } |
| } |
| |
| return null; |
| } |
| |
| public static boolean isMultistatementWildcard(@NonNull CharSequence name) { |
| return name.charAt(name.length() - 1) == '$'; |
| } |
| |
| public static boolean isMultistatementWildcardTree(Tree tree) { |
| CharSequence name = Utilities.getWildcardTreeName(tree); |
| |
| return name != null && Utilities.isMultistatementWildcard(name); |
| } |
| |
| private static long inc; |
| |
| public static Scope constructScope(CompilationInfo info, Map<String, TypeMirror> constraints) { |
| return constructScope(info, constraints, Collections.<String>emptyList()); |
| } |
| |
| public static Scope constructScope(CompilationInfo info, Map<String, TypeMirror> constraints, Iterable<? extends String> auxiliaryImports) { |
| ScopeDescription desc = new ScopeDescription(constraints, auxiliaryImports); |
| Scope result = (Scope) info.getCachedValue(desc); |
| |
| if (result != null) return result; |
| |
| StringBuilder clazz = new StringBuilder(); |
| |
| clazz.append("package $$;"); |
| |
| for (String i : auxiliaryImports) { |
| clazz.append(i); |
| } |
| |
| long count = inc++; |
| |
| String classname = "$$scopeclass$constraints$" + count; |
| |
| clazz.append("public class " + classname + "{"); |
| |
| for (Entry<String, TypeMirror> e : constraints.entrySet()) { |
| if (e.getValue() != null) { |
| clazz.append("private "); |
| clazz.append(e.getValue().toString()); //XXX |
| clazz.append(" "); |
| clazz.append(e.getKey()); |
| clazz.append(";\n"); |
| } |
| } |
| |
| clazz.append("private void test() {\n"); |
| clazz.append("}\n"); |
| clazz.append("}\n"); |
| |
| JavacTaskImpl jti = JavaSourceAccessor.getINSTANCE().getJavacTask(info); |
| Context context = jti.getContext(); |
| JavaCompiler compiler = JavaCompiler.instance(context); |
| Modules modules = Modules.instance(context); |
| Log log = Log.instance(context); |
| NBResolve resolve = NBResolve.instance(context); |
| Annotate annotate = Annotate.instance(context); |
| Names names = Names.instance(context); |
| Symtab syms = Symtab.instance(context); |
| Log.DiagnosticHandler discardHandler = new Log.DiscardDiagnosticHandler(compiler.log); |
| |
| JavaFileObject jfo = FileObjects.memoryFileObject("$", "$", new File("/tmp/$$scopeclass$constraints$" + count + ".java").toURI(), System.currentTimeMillis(), clazz.toString()); |
| |
| try { |
| resolve.disableAccessibilityChecks(); |
| if (compiler.isEnterDone()) { |
| annotate.blockAnnotations(); |
| // try { |
| // Field f = compiler.getClass().getDeclaredField("enterDone"); |
| // f.setAccessible(true); |
| // f.set(compiler, false); |
| // } catch (Throwable t) { |
| // Logger.getLogger(Utilities.class.getName()).log(Level.FINE, null, t); |
| // } |
| //was: |
| // compiler.resetEnterDone(); |
| } |
| |
| JCCompilationUnit cut = compiler.parse(jfo); |
| ClassSymbol enteredClass = syms.enterClass(modules.getDefaultModule(), names.fromString("$$." + classname)); |
| modules.enter(com.sun.tools.javac.util.List.of(cut), enteredClass); |
| compiler.enterTrees(com.sun.tools.javac.util.List.of(cut)); |
| |
| Todo todo = compiler.todo; |
| ListBuffer<Env<AttrContext>> defer = new ListBuffer<>(); |
| |
| while (todo.peek() != null) { |
| Env<AttrContext> env = todo.remove(); |
| |
| if (env.toplevel == cut) |
| compiler.attribute(env); |
| else |
| defer = defer.append(env); |
| } |
| |
| todo.addAll(defer); |
| |
| Scope res = new ScannerImpl().scan(cut, info); |
| |
| info.putCachedValue(desc, res, CacheClearPolicy.ON_SIGNATURE_CHANGE); |
| |
| return res; |
| } finally { |
| resolve.restoreAccessbilityChecks(); |
| log.popDiagnosticHandler(discardHandler); |
| } |
| } |
| |
| private static final class ScannerImpl extends ErrorAwareTreePathScanner<Scope, CompilationInfo> { |
| |
| @Override |
| public Scope visitBlock(BlockTree node, CompilationInfo p) { |
| return p.getTrees().getScope(getCurrentPath()); |
| } |
| |
| @Override |
| public Scope visitMethod(MethodTree node, CompilationInfo p) { |
| if (node.getReturnType() == null) { |
| return null; |
| } |
| return super.visitMethod(node, p); |
| } |
| |
| @Override |
| public Scope reduce(Scope r1, Scope r2) { |
| return r1 != null ? r1 : r2; |
| } |
| |
| } |
| |
| private static final class ScopeDescription { |
| private final Map<String, TypeMirror> constraints; |
| private final Iterable<? extends String> auxiliaryImports; |
| |
| public ScopeDescription(Map<String, TypeMirror> constraints, Iterable<? extends String> auxiliaryImports) { |
| this.constraints = constraints; |
| this.auxiliaryImports = auxiliaryImports; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj == null) { |
| return false; |
| } |
| if (getClass() != obj.getClass()) { |
| return false; |
| } |
| final ScopeDescription other = (ScopeDescription) obj; |
| if (this.constraints != other.constraints && (this.constraints == null || !this.constraints.equals(other.constraints))) { |
| return false; |
| } |
| if (this.auxiliaryImports != other.auxiliaryImports && (this.auxiliaryImports == null || !this.auxiliaryImports.equals(other.auxiliaryImports))) { |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| int hash = 7; |
| hash = 47 * hash + (this.constraints != null ? this.constraints.hashCode() : 0); |
| hash = 47 * hash + (this.auxiliaryImports != null ? this.auxiliaryImports.hashCode() : 0); |
| return hash; |
| } |
| |
| } |
| |
| // private static Scope constructScope2(CompilationInfo info, Map<String, TypeMirror> constraints) { |
| // JavacScope s = (JavacScope) info.getTrees().getScope(new TreePath(info.getCompilationUnit())); |
| // Env<AttrContext> env = s.getEnv(); |
| // |
| // env = env.dup(env.tree); |
| // |
| // env.info. |
| // } |
| |
| public static String toHumanReadableTime(double d) { |
| StringBuilder result = new StringBuilder(); |
| long inSeconds = (long) (d / 1000); |
| int seconds = (int) (inSeconds % 60); |
| long inMinutes = inSeconds / 60; |
| int minutes = (int) (inMinutes % 60); |
| long inHours = inMinutes / 60; |
| |
| if (inHours > 0) { |
| result.append(inHours); |
| result.append("h"); |
| } |
| |
| if (minutes > 0) { |
| result.append(minutes); |
| result.append("m"); |
| } |
| |
| result.append(seconds); |
| result.append("s"); |
| |
| return result.toString(); |
| } |
| |
| public static ClasspathInfo createUniversalCPInfo() { |
| return Lookup.getDefault().lookup(SPI.class).createUniversalCPInfo(); |
| } |
| |
| @SuppressWarnings("deprecation") |
| public static void waitScanFinished() throws InterruptedException { |
| SourceUtils.waitScanFinished(); |
| } |
| |
| public static Set<? extends String> findSuppressedWarnings(CompilationInfo info, TreePath path) { |
| //TODO: cache? |
| Set<String> keys = new HashSet<>(); |
| |
| while (path != null) { |
| Tree leaf = path.getLeaf(); |
| |
| switch (leaf.getKind()) { |
| case METHOD: |
| handleSuppressWarnings(info, path, ((MethodTree) leaf).getModifiers(), keys); |
| break; |
| case CLASS: |
| handleSuppressWarnings(info, path, ((ClassTree) leaf).getModifiers(), keys); |
| break; |
| case VARIABLE: |
| handleSuppressWarnings(info, path, ((VariableTree) leaf).getModifiers(), keys); |
| break; |
| } |
| |
| path = path.getParentPath(); |
| } |
| |
| return Collections.unmodifiableSet(keys); |
| } |
| |
| private static void handleSuppressWarnings(CompilationInfo info, TreePath path, ModifiersTree modifiers, final Set<String> keys) { |
| Element el = info.getTrees().getElement(path); |
| |
| if (el == null) { |
| return ; |
| } |
| |
| for (AnnotationMirror am : el.getAnnotationMirrors()) { |
| Name fqn = ((TypeElement) am.getAnnotationType().asElement()).getQualifiedName(); |
| |
| if (!fqn.contentEquals("java.lang.SuppressWarnings")) { |
| continue; |
| } |
| |
| for (Entry<? extends ExecutableElement, ? extends AnnotationValue> e : am.getElementValues().entrySet()) { |
| if (!e.getKey().getSimpleName().contentEquals("value")) |
| continue; |
| |
| e.getValue().accept(new AnnotationValueVisitor<Void, Void>() { |
| @Override |
| public Void visit(AnnotationValue av, Void p) { |
| av.accept(this, p); |
| return null; |
| } |
| @Override |
| public Void visit(AnnotationValue av) { |
| av.accept(this, null); |
| return null; |
| } |
| @Override |
| public Void visitBoolean(boolean b, Void p) { |
| return null; |
| } |
| @Override |
| public Void visitByte(byte b, Void p) { |
| return null; |
| } |
| @Override |
| public Void visitChar(char c, Void p) { |
| return null; |
| } |
| @Override |
| public Void visitDouble(double d, Void p) { |
| return null; |
| } |
| @Override |
| public Void visitFloat(float f, Void p) { |
| return null; |
| } |
| @Override |
| public Void visitInt(int i, Void p) { |
| return null; |
| } |
| @Override |
| public Void visitLong(long i, Void p) { |
| return null; |
| } |
| @Override |
| public Void visitShort(short s, Void p) { |
| return null; |
| } |
| @Override |
| public Void visitString(String s, Void p) { |
| keys.add(s); |
| return null; |
| } |
| @Override |
| public Void visitType(TypeMirror t, Void p) { |
| return null; |
| } |
| @Override |
| public Void visitEnumConstant(VariableElement c, Void p) { |
| return null; |
| } |
| @Override |
| public Void visitAnnotation(AnnotationMirror a, Void p) { |
| return null; |
| } |
| @Override |
| public Void visitArray(List<? extends AnnotationValue> vals, Void p) { |
| for (AnnotationValue av : vals) { |
| av.accept(this, p); |
| } |
| return null; |
| } |
| @Override |
| public Void visitUnknown(AnnotationValue av, Void p) { |
| return null; |
| } |
| }, null); |
| } |
| } |
| } |
| |
| public static Tree generalizePattern(CompilationInfo info, TreePath original) { |
| return generalizePattern(JavaSourceAccessor.getINSTANCE().getJavacTask(info), original); |
| } |
| |
| public static Tree generalizePattern(CompilationTask task, TreePath original) { |
| JavacTaskImpl jti = (JavacTaskImpl) task; |
| com.sun.tools.javac.util.Context c = jti.getContext(); |
| TreeFactory make = TreeFactory.instance(c); |
| Trees javacTrees = Trees.instance(task); |
| GeneralizePattern gp = new GeneralizePattern(javacTrees, make); |
| |
| gp.scan(original, null); |
| |
| GeneralizePatternITT itt = new GeneralizePatternITT(gp.tree2Variable); |
| |
| itt.attach(c, new NoImports(c), null); |
| |
| return itt.translate(original.getLeaf()); |
| } |
| |
| public static Tree generalizePattern(CompilationInfo info, TreePath original, int firstStatement, int lastStatement) { |
| JavacTaskImpl jti = JavaSourceAccessor.getINSTANCE().getJavacTask(info); |
| com.sun.tools.javac.util.Context c = jti.getContext(); |
| TreeFactory make = TreeFactory.instance(c); |
| Tree translated = Utilities.generalizePattern(jti, original); |
| |
| assert translated.getKind() == Kind.BLOCK; |
| |
| List<StatementTree> newStatements = new LinkedList<>(); |
| BlockTree block = (BlockTree) translated; |
| |
| if (firstStatement != lastStatement) { |
| newStatements.add(make.ExpressionStatement(make.Identifier("$s0$"))); |
| newStatements.addAll(block.getStatements().subList(firstStatement, lastStatement + 1)); |
| newStatements.add(make.ExpressionStatement(make.Identifier("$s1$"))); |
| |
| translated = make.Block(newStatements, block.isStatic()); |
| } else { |
| translated = block.getStatements().get(firstStatement); |
| } |
| |
| return translated; |
| } |
| |
| public interface SPI { |
| public ClasspathInfo createUniversalCPInfo(); |
| } |
| |
| @ServiceProvider(service=SPI.class) |
| public static final class NbSPIImpl implements SPI, PropertyChangeListener { |
| |
| /** |
| * Cached reference to the ClasspathInfo created from the platform. |
| */ |
| private volatile Reference<ClasspathInfo> cached = new WeakReference<>(null); |
| |
| // @GuardedBy(this) |
| private PropertyChangeListener weakL; |
| |
| @Override |
| public synchronized ClasspathInfo createUniversalCPInfo() { |
| Reference<ClasspathInfo> r = cached; |
| if (r != null) { |
| ClasspathInfo c = r.get(); |
| if (c != null) { |
| return c; |
| } |
| } |
| JavaPlatform select = JavaPlatform.getDefault(); |
| final JavaPlatformManager man = JavaPlatformManager.getDefault(); |
| if (select.getSpecification().getVersion() != null) { |
| for (JavaPlatform p : JavaPlatformManager.getDefault().getInstalledPlatforms()) { |
| if (!p.isValid() || !"j2se".equals(p.getSpecification().getName()) || p.getSpecification().getVersion() == null) continue; |
| if (p.getSpecification().getVersion().compareTo(select.getSpecification().getVersion()) > 0) { |
| select = p; |
| } |
| } |
| } |
| final ClasspathInfo result = new ClasspathInfo.Builder(select.getBootstrapLibraries()) |
| .setModuleBootPath(select.getBootstrapLibraries()) |
| .build(); |
| if (cached != null) { |
| this.cached = new WeakReference<>(result); |
| } |
| if (weakL == null) { |
| man.addPropertyChangeListener(weakL = WeakListeners.propertyChange(this, man)); |
| } |
| return result; |
| } |
| |
| @Override |
| public void propertyChange(PropertyChangeEvent evt) { |
| cached = null; |
| } |
| } |
| |
| private static final class GeneralizePattern extends ErrorAwareTreePathScanner<Void, Void> { |
| |
| public final Map<Tree, Tree> tree2Variable = new HashMap<>(); |
| private final Map<Element, String> element2Variable = new HashMap<>(); |
| private final Trees javacTrees; |
| private final TreeFactory make; |
| |
| private int currentVariableIndex = 0; |
| |
| public GeneralizePattern(Trees javacTrees, TreeFactory make) { |
| this.javacTrees = javacTrees; |
| this.make = make; |
| } |
| |
| private @NonNull String getVariable(@NonNull Element el) { |
| String var = element2Variable.get(el); |
| |
| if (var == null) { |
| element2Variable.put(el, var = "$" + currentVariableIndex++); |
| } |
| |
| return var; |
| } |
| |
| private boolean shouldBeGeneralized(@NonNull Element el) { |
| if (el.getModifiers().contains(Modifier.PRIVATE)) { |
| return true; |
| } |
| |
| switch (el.getKind()) { |
| case LOCAL_VARIABLE: |
| case EXCEPTION_PARAMETER: |
| case PARAMETER: |
| return true; |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public Void visitIdentifier(IdentifierTree node, Void p) { |
| Element e = javacTrees.getElement(getCurrentPath()); |
| |
| if (e != null && shouldBeGeneralized(e)) { |
| tree2Variable.put(node, make.Identifier(getVariable(e))); |
| } |
| |
| return super.visitIdentifier(node, p); |
| } |
| |
| @Override |
| public Void visitVariable(VariableTree node, Void p) { |
| Element e = javacTrees.getElement(getCurrentPath()); |
| |
| if (e != null && shouldBeGeneralized(e)) { |
| VariableTree nue = make.Variable(node.getModifiers(), getVariable(e), node.getType(), node.getInitializer()); |
| |
| tree2Variable.put(node, nue); |
| } |
| |
| return super.visitVariable(node, p); |
| } |
| |
| @Override |
| public Void visitNewClass(NewClassTree node, Void p) { |
| //XXX: |
| if (node.getEnclosingExpression() != null) { |
| tree2Variable.put(node, make.Identifier("$" + currentVariableIndex++)); |
| return null; |
| } |
| |
| NewClassTree nue = make.NewClass(node.getEnclosingExpression(), Collections.<ExpressionTree>singletonList(make.Identifier("$" + currentVariableIndex++ + "$")), make.Identifier("$" + currentVariableIndex++), Collections.<ExpressionTree>singletonList(make.Identifier("$" + currentVariableIndex++ + "$")), null); |
| |
| tree2Variable.put(node, nue); |
| |
| return null; |
| } |
| |
| } |
| |
| private static final class GeneralizePatternITT extends ImmutableTreeTranslator { |
| |
| private final Map<Tree, Tree> tree2Variable; |
| |
| public GeneralizePatternITT(Map<Tree, Tree> tree2Variable) { |
| super(null); |
| this.tree2Variable = tree2Variable; |
| } |
| |
| @Override |
| public Tree translate(Tree tree) { |
| Tree var = tree2Variable.remove(tree); |
| |
| if (var != null) { |
| return super.translate(var); |
| } |
| |
| return super.translate(tree); |
| } |
| |
| } |
| |
| private static final class NoImports extends ImportAnalysis2 { |
| |
| public NoImports(Context env) { |
| super(env); |
| } |
| |
| @Override |
| public void classEntered(ClassTree clazz) {} |
| |
| @Override |
| public void enterVisibleThroughClasses(ClassTree clazz) {} |
| |
| @Override |
| public void classLeft() {} |
| |
| @Override |
| public ExpressionTree resolveImport(MemberSelectTree orig, Element element) { |
| return orig; |
| } |
| |
| @Override |
| public void setCompilationUnit(CompilationUnitTree cut) {} |
| |
| @Override |
| public void setImports(List<? extends ImportTree> importsToAdd) {} |
| |
| @Override |
| public Set<? extends Element> getImports() { |
| return Collections.emptySet(); |
| } |
| |
| @Override |
| public void setPackage(ExpressionTree packageNameTree) {} |
| |
| } |
| |
| public static long patternValue(Tree pattern) { |
| class VisitorImpl extends ErrorAwareTreeScanner<Void, Void> { |
| private int value; |
| @Override |
| public Void scan(Tree node, Void p) { |
| if (node != null) value++; |
| return super.scan(node, p); |
| } |
| @Override |
| public Void visitIdentifier(IdentifierTree node, Void p) { |
| if (node.getName().toString().startsWith("$")) value--; |
| |
| return super.visitIdentifier(node, p); |
| } |
| @Override |
| public Void visitNewClass(NewClassTree node, Void p) { |
| return null; |
| } |
| } |
| |
| VisitorImpl vi = new VisitorImpl(); |
| |
| vi.scan(pattern, null); |
| |
| return vi.value; |
| } |
| |
| public static boolean containsMultistatementTrees(List<? extends Tree> statements) { |
| for (Tree t : statements) { |
| if (Utilities.isMultistatementWildcardTree(t)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| public static boolean isJavadocSupported(CompilationInfo info) { //TODO: unnecessary? |
| return true; |
| } |
| |
| static <T> Loaded<T> load(Unloaded<T> unloaded) { |
| ClassLoadingStrategy<ClassLoader> strategy; |
| |
| try { |
| strategy = ClassLoadingStrategy.UsingLookup.of(MethodHandles.lookup()); |
| } catch (IllegalStateException ex) { |
| strategy = new ClassLoadingStrategy.ForUnsafeInjection(); |
| } |
| |
| return unloaded.load(JackpotTrees.class.getClassLoader(), strategy); |
| } |
| |
| private static class JackpotJavacParser extends NBJavacParser { |
| |
| private final Context ctx; |
| private final com.sun.tools.javac.tree.TreeMaker make; |
| private final com.sun.tools.javac.util.Name dollar; |
| public JackpotJavacParser(Context ctx, NBParserFactory fac, |
| Lexer S, |
| boolean keepDocComments, |
| boolean keepLineMap, |
| CancelService cancelService, |
| Names names) { |
| super(fac, S, keepDocComments, keepLineMap, true, false, cancelService); |
| this.ctx = ctx; |
| this.make = com.sun.tools.javac.tree.TreeMaker.instance(ctx); |
| this.dollar = names.fromString("$"); |
| } |
| |
| @Override |
| protected JCModifiers modifiersOpt(JCModifiers partial) { |
| if (token.kind == TokenKind.IDENTIFIER) { |
| String ident = token.name().toString(); |
| |
| if (Utilities.isMultistatementWildcard(ident)) { |
| com.sun.tools.javac.util.Name name = token.name(); |
| |
| nextToken(); |
| |
| JCModifiers result = super.modifiersOpt(partial); |
| |
| result.annotations = result.annotations.prepend(new AnnotationWildcard(name, F.Ident(name))); |
| |
| return result; |
| } |
| } |
| |
| return super.modifiersOpt(partial); |
| } |
| |
| |
| @Override |
| public JCVariableDecl formalParameter(boolean lambdaParam, boolean recordComponents) { |
| if (token.kind == TokenKind.IDENTIFIER) { |
| if (token.name().startsWith(dollar)) { |
| com.sun.tools.javac.util.Name name = token.name(); |
| |
| Token peeked = S.token(1); |
| |
| if (peeked.kind == TokenKind.COMMA || peeked.kind == TokenKind.RPAREN) { |
| nextToken(); |
| return JackpotTrees.createVariableWildcard(ctx, name); |
| } |
| } |
| } |
| return super.formalParameter(lambdaParam, recordComponents); |
| } |
| |
| @Override |
| protected JCVariableDecl implicitParameter() { |
| if (token.kind == TokenKind.IDENTIFIER) { |
| if (token.name().startsWith(dollar)) { |
| com.sun.tools.javac.util.Name name = token.name(); |
| |
| Token peeked = S.token(1); |
| |
| if (peeked.kind == TokenKind.COMMA || peeked.kind == TokenKind.RPAREN) { |
| nextToken(); |
| return JackpotTrees.createVariableWildcard(ctx, name); |
| } |
| } |
| } |
| |
| return super.implicitParameter(); |
| } |
| |
| @Override |
| protected JCCatch catchClause() { |
| if (token.kind == TokenKind.CATCH) { |
| Token peeked = S.token(1); |
| |
| if ( peeked.kind == TokenKind.IDENTIFIER |
| && Utilities.isMultistatementWildcard(peeked.name().toString())) { |
| accept(TokenKind.CATCH); |
| |
| com.sun.tools.javac.util.Name name = token.name(); |
| |
| accept(TokenKind.IDENTIFIER); |
| |
| return new CatchWildcard(ctx, name, F.Ident(name)); |
| } else { |
| nextToken(); |
| } |
| } |
| return super.catchClause(); |
| } |
| |
| @Override |
| public com.sun.tools.javac.util.List<JCTree> classOrInterfaceOrRecordBodyDeclaration(com.sun.tools.javac.util.Name className, boolean isInterface, boolean isRecord) { |
| |
| if (token.kind == TokenKind.IDENTIFIER) { |
| if (token.name().startsWith(dollar)) { |
| com.sun.tools.javac.util.Name name = token.name(); |
| |
| Token peeked = S.token(1); |
| |
| if (peeked.kind == TokenKind.SEMI) { |
| nextToken(); |
| nextToken(); |
| |
| return com.sun.tools.javac.util.List.<JCTree>of(F.Ident(name)); |
| } |
| } |
| } |
| |
| return super.classOrInterfaceOrRecordBodyDeclaration(className, isInterface, isRecord); |
| } |
| |
| @Override |
| protected JCExpression checkExprStat(JCExpression t) { |
| if (t.getTag() == JCTree.Tag.IDENT) { |
| if (((IdentifierTree) t).getName().toString().startsWith("$")) { |
| return t; |
| } |
| } |
| return super.checkExprStat(t); |
| } |
| |
| @Override |
| protected com.sun.tools.javac.util.List<JCCase> switchBlockStatementGroup() { |
| if (token.kind == TokenKind.CASE) { |
| Token peeked = S.token(1); |
| |
| if (peeked.kind == TokenKind.IDENTIFIER) { |
| String ident = peeked.name().toString(); |
| |
| if (ident.startsWith("$") && ident.endsWith("$")) { |
| nextToken(); |
| |
| int pos = token.pos; |
| com.sun.tools.javac.util.Name name = token.name(); |
| |
| nextToken(); |
| |
| if (token.kind == TokenKind.SEMI) { |
| nextToken(); |
| } |
| |
| JCIdent identTree = F.at(pos).Ident(name); |
| JCConstantCaseLabel labelTree = F.at(pos).ConstantCaseLabel(identTree); |
| return com.sun.tools.javac.util.List.of( |
| new JackpotTrees.CaseWildcard(name, identTree, STATEMENT, com.sun.tools.javac.util.List.of(labelTree), com.sun.tools.javac.util.List.nil(), null) |
| ); |
| } |
| } |
| } |
| return super.switchBlockStatementGroup(); |
| } |
| |
| @Override |
| protected JCTree resource() { |
| if (token.kind == TokenKind.IDENTIFIER && token.name().startsWith(dollar)) { |
| Token peeked = S.token(1); |
| |
| if (peeked.kind == TokenKind.SEMI || peeked.kind == TokenKind.RPAREN) { |
| int pos = token.pos; |
| com.sun.tools.javac.util.Name name = token.name(); |
| |
| nextToken(); |
| |
| return F.at(pos).Ident(name); |
| } |
| } |
| return super.resource(); |
| } |
| |
| } |
| |
| private static final class DummyJFO extends SimpleJavaFileObject { |
| private DummyJFO() { |
| super(URI.create("dummy.java"), JavaFileObject.Kind.SOURCE); |
| } |
| @Override |
| public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { |
| return ""; |
| } |
| }; |
| |
| /** |
| * Only for members (i.e. generated constructor): |
| */ |
| public static List<? extends Tree> filterHidden(TreePath basePath, Iterable<? extends Tree> members) { |
| List<Tree> result = new LinkedList<>(); |
| |
| for (Tree t : members) { |
| if (!isSynthetic(basePath != null ? basePath.getCompilationUnit() : null, t)) { |
| result.add(t); |
| } |
| } |
| |
| return result; |
| } |
| |
| private static boolean isSynthetic(CompilationUnitTree cut, Tree leaf) throws NullPointerException { |
| JCTree tree = (JCTree) leaf; |
| |
| if (tree.pos == (-1)) |
| return true; |
| |
| if (leaf.getKind() == Kind.METHOD) { |
| //check for synthetic constructor: |
| return (((JCMethodDecl)leaf).mods.flags & Flags.GENERATEDCONSTR) != 0L; |
| } |
| |
| //check for synthetic superconstructor call: |
| if (cut != null && leaf.getKind() == Kind.EXPRESSION_STATEMENT) { |
| ExpressionStatementTree est = (ExpressionStatementTree) leaf; |
| |
| if (est.getExpression().getKind() == Kind.METHOD_INVOCATION) { |
| MethodInvocationTree mit = (MethodInvocationTree) est.getExpression(); |
| |
| if (mit.getMethodSelect().getKind() == Kind.IDENTIFIER) { |
| IdentifierTree it = (IdentifierTree) mit.getMethodSelect(); |
| |
| if ("super".equals(it.getName().toString())) { |
| return ((JCCompilationUnit) cut).endPositions.getEndPos(tree) == (-1); |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| public static boolean isFakeBlock(Tree t) { |
| return t instanceof FakeBlock; |
| } |
| |
| public static boolean isFakeClass(Tree t) { |
| if (!(t instanceof ClassTree)) { |
| return false; |
| } |
| |
| ClassTree ct = (ClassTree) t; |
| |
| if (ct.getMembers().isEmpty()) { |
| return false; |
| } |
| |
| CharSequence wildcardTreeName = Utilities.getWildcardTreeName(ct.getMembers().get(0)); |
| |
| if (wildcardTreeName == null) { |
| return false; |
| } |
| |
| return wildcardTreeName.toString().startsWith("$$"); |
| } |
| |
| private static final class OffsetSourcePositions implements SourcePositions { |
| |
| private final SourcePositions delegate; |
| private final long offset; |
| |
| public OffsetSourcePositions(SourcePositions delegate, long offset) { |
| this.delegate = delegate; |
| this.offset = offset; |
| } |
| |
| @Override |
| public long getStartPosition(CompilationUnitTree cut, Tree tree) { |
| return delegate.getStartPosition(cut, tree) + offset; |
| } |
| |
| @Override |
| public long getEndPosition(CompilationUnitTree cut, Tree tree) { |
| return delegate.getEndPosition(cut, tree) + offset; |
| } |
| |
| } |
| |
| private static final class OffsetDiagnostic<S> implements Diagnostic<S> { |
| private final Diagnostic<? extends S> delegate; |
| private final SourcePositions sp; |
| private final long offset; |
| |
| public OffsetDiagnostic(Diagnostic<? extends S> delegate, SourcePositions sp, long offset) { |
| this.delegate = delegate; |
| this.sp = sp; |
| this.offset = offset; |
| } |
| |
| @Override |
| public Diagnostic.Kind getKind() { |
| return delegate.getKind(); |
| } |
| |
| @Override |
| public S getSource() { |
| return delegate.getSource(); |
| } |
| |
| @Override |
| public long getPosition() { |
| return delegate.getPosition() + offset; |
| } |
| |
| @Override |
| public long getStartPosition() { |
| return delegate.getStartPosition() + offset; |
| } |
| |
| @Override |
| public long getEndPosition() { |
| if (delegate instanceof JCDiagnostic) { |
| JCDiagnostic dImpl = (JCDiagnostic) delegate; |
| |
| return dImpl.getDiagnosticPosition().getEndPosition(new EndPosTable() { |
| @Override public int getEndPos(JCTree tree) { |
| return (int) sp.getEndPosition(null, tree); |
| } |
| @Override public void storeEnd(JCTree tree, int endpos) { |
| throw new UnsupportedOperationException("Not supported yet."); |
| } |
| @Override public int replaceTree(JCTree oldtree, JCTree newtree) { |
| throw new UnsupportedOperationException("Not supported yet."); |
| } |
| }) + offset; |
| } |
| return delegate.getEndPosition() + offset; |
| } |
| |
| @Override |
| public long getLineNumber() { |
| throw new UnsupportedOperationException("Not supported yet."); |
| } |
| |
| @Override |
| public long getColumnNumber() { |
| throw new UnsupportedOperationException("Not supported yet."); |
| } |
| |
| @Override |
| public String getCode() { |
| return delegate.getCode(); |
| } |
| |
| @Override |
| public String getMessage(Locale locale) { |
| return delegate.getMessage(locale); |
| } |
| |
| } |
| |
| private static class ParserSourcePositions implements SourcePositions { |
| |
| private final JavacParser parser; |
| |
| private ParserSourcePositions(JavacParser parser) { |
| this.parser = parser; |
| } |
| |
| @Override |
| public long getStartPosition(CompilationUnitTree file, Tree tree) { |
| return parser.getStartPos((JCTree)tree); |
| } |
| |
| @Override |
| public long getEndPosition(CompilationUnitTree file, Tree tree) { |
| return parser.getEndPos((JCTree)tree); |
| } |
| } |
| } |