Merge
This commit is contained in:
commit
b4338fe75c
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2007, 2014, 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
|
||||
@ -168,6 +168,8 @@ sjavac.includes = \
|
||||
sjavac.tests = \
|
||||
tools/sjavac
|
||||
|
||||
crules.tests = ../make/test/crules
|
||||
|
||||
#
|
||||
|
||||
# The following files require the latest JDK to be available.
|
||||
|
@ -760,7 +760,8 @@
|
||||
**** crules targets.
|
||||
-->
|
||||
|
||||
<target name="build-crules" depends="-def-compilecrules,-def-build-jar-with-services">
|
||||
<target name="build-crules"
|
||||
depends="-def-compilecrules,-def-build-jar-with-services,build-bootstrap-javac,-create-import-jdk-stubs">
|
||||
<compilecrules/>
|
||||
<build-jar-with-services
|
||||
name="crules"
|
||||
@ -770,13 +771,19 @@
|
||||
jarmainclass=""
|
||||
jarclasspath="crules.jar"
|
||||
service.type="com.sun.source.util.Plugin"
|
||||
service.provider="crules.MutableFieldsAnalyzer"/>
|
||||
service.provider="crules.CodingRulesAnalyzerPlugin"/>
|
||||
<build-tool name="crules"/>
|
||||
</target>
|
||||
|
||||
<target name="jtreg-crules" depends="build-javac,build-crules,-def-jtreg">
|
||||
<jtreg-tool name="crules"
|
||||
tests="${crules.tests}"
|
||||
extra.jvmargs="-Xbootclasspath/a:${build.toolclasses.dir}/crules.jar" />
|
||||
</target>
|
||||
|
||||
<target name="check-coding-rules" depends="build-bootstrap-javac,-create-import-jdk-stubs,build-crules">
|
||||
<build-classes includes="${javac.includes}"
|
||||
plugin.options="-J-Xbootclasspath/a:${build.toolclasses.dir}/crules.jar -Xplugin:mutable_fields_analyzer" />
|
||||
plugin.options="-J-Xbootclasspath/a:${build.toolclasses.dir}/crules.jar -Xplugin:coding_rules" />
|
||||
</target>
|
||||
|
||||
<!--
|
||||
@ -1150,6 +1157,7 @@
|
||||
<attribute name="options" default="${other.jtreg.options}"/>
|
||||
<attribute name="keywords" default="-keywords:!ignore"/>
|
||||
<attribute name="jpda.jvmargs" default=""/>
|
||||
<attribute name="extra.jvmargs" default=""/>
|
||||
<sequential>
|
||||
<property name="coverage.options" value=""/> <!-- default -->
|
||||
<property name="coverage.classpath" value=""/> <!-- default -->
|
||||
@ -1163,7 +1171,7 @@
|
||||
samevm="@{samevm}" verbose="@{verbose}"
|
||||
failonerror="false" resultproperty="jtreg.@{name}.result"
|
||||
javacoptions="-g"
|
||||
vmoptions="${coverage.options} -Xbootclasspath/p:${coverage.classpath}${path.separator}${build.classes.dir} @{jpda.jvmargs}">
|
||||
vmoptions="${coverage.options} -Xbootclasspath/p:${coverage.classpath}${path.separator}${build.classes.dir} @{jpda.jvmargs} @{extra.jvmargs}">
|
||||
<arg line="@{keywords}"/>
|
||||
<arg line="@{options}"/>
|
||||
<arg line="@{tests}"/>
|
||||
|
@ -0,0 +1,11 @@
|
||||
/**@test /nodynamiccopyright/
|
||||
* @compile/fail/ref=Test.out -Xplugin:coding_rules -XDrawDiagnostics Test.java
|
||||
*/
|
||||
|
||||
import com.sun.tools.javac.util.Assert;
|
||||
|
||||
public class Test {
|
||||
public void check(String value) {
|
||||
Assert.check(value.trim().length() > 0, "value=" + value);
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
Test.java:9:21: compiler.err.proc.messager: compiler.misc.crules.should.not.use.string.concatenation
|
||||
1 error
|
@ -0,0 +1,9 @@
|
||||
/**@test /nodynamiccopyright/
|
||||
* @compile/fail/ref=Test.out -Xplugin:coding_rules -XDrawDiagnostics Test.java
|
||||
*/
|
||||
|
||||
package com.sun.tools.javac;
|
||||
|
||||
public class Test {
|
||||
public static String mutable;
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
Test.java:8:26: compiler.err.proc.messager: compiler.misc.crules.err.var.must.be.final: public static String mutable
|
||||
1 error
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2014, 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
|
||||
@ -27,64 +27,43 @@ import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.util.JavacTask;
|
||||
import com.sun.source.util.Plugin;
|
||||
import com.sun.source.util.TaskEvent;
|
||||
import com.sun.source.util.TaskListener;
|
||||
import com.sun.source.util.Trees;
|
||||
import com.sun.tools.javac.api.BasicJavacTask;
|
||||
import com.sun.tools.javac.code.Symtab;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.tree.TreeScanner;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import com.sun.tools.javac.util.JCDiagnostic;
|
||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;
|
||||
import com.sun.tools.javac.util.Log;
|
||||
import com.sun.tools.javac.util.Options;
|
||||
import com.sun.tools.javac.util.RawDiagnosticFormatter;
|
||||
|
||||
import static com.sun.source.util.TaskEvent.Kind;
|
||||
|
||||
public abstract class AbstractCodingRulesAnalyzer implements Plugin {
|
||||
public abstract class AbstractCodingRulesAnalyzer {
|
||||
|
||||
protected Log log;
|
||||
protected Trees trees;
|
||||
private final Log log;
|
||||
private final boolean rawDiagnostics;
|
||||
private final JCDiagnostic.Factory diags;
|
||||
private final Options options;
|
||||
protected final Messages messages;
|
||||
protected final Symtab syms;
|
||||
protected TreeScanner treeVisitor;
|
||||
protected Kind eventKind;
|
||||
protected Messages messages;
|
||||
|
||||
public void init(JavacTask task, String... args) {
|
||||
public AbstractCodingRulesAnalyzer(JavacTask task) {
|
||||
BasicJavacTask impl = (BasicJavacTask)task;
|
||||
Context context = impl.getContext();
|
||||
log = Log.instance(context);
|
||||
trees = Trees.instance(task);
|
||||
options = Options.instance(context);
|
||||
rawDiagnostics = options.isSet("rawDiagnostics");
|
||||
diags = JCDiagnostic.Factory.instance(context);
|
||||
messages = new Messages();
|
||||
task.addTaskListener(new PostAnalyzeTaskListener());
|
||||
syms = Symtab.instance(context);
|
||||
}
|
||||
|
||||
public class PostAnalyzeTaskListener implements TaskListener {
|
||||
|
||||
@Override
|
||||
public void started(TaskEvent taskEvent) {}
|
||||
|
||||
@Override
|
||||
public void finished(TaskEvent taskEvent) {
|
||||
if (taskEvent.getKind().equals(eventKind)) {
|
||||
TypeElement typeElem = taskEvent.getTypeElement();
|
||||
Tree tree = trees.getTree(typeElem);
|
||||
if (tree != null) {
|
||||
JavaFileObject prevSource = log.currentSourceFile();
|
||||
try {
|
||||
log.useSource(taskEvent.getCompilationUnit().getSourceFile());
|
||||
treeVisitor.scan((JCTree)tree);
|
||||
} finally {
|
||||
log.useSource(prevSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Messages {
|
||||
protected class Messages {
|
||||
ResourceBundle bundle;
|
||||
|
||||
Messages() {
|
||||
@ -93,7 +72,14 @@ public abstract class AbstractCodingRulesAnalyzer implements Plugin {
|
||||
}
|
||||
|
||||
public void error(JCTree tree, String code, Object... args) {
|
||||
String msg = (code == null) ? (String) args[0] : localize(code, args);
|
||||
String msg;
|
||||
if (rawDiagnostics) {
|
||||
RawDiagnosticFormatter f = new RawDiagnosticFormatter(options);
|
||||
msg = f.formatMessage(diags.create(DiagnosticType.FRAGMENT, log.currentSource(),
|
||||
tree.pos(), code, args), null);
|
||||
} else {
|
||||
msg = (code == null) ? (String) args[0] : localize(code, args);
|
||||
}
|
||||
log.error(tree, "proc.messager", msg.toString());
|
||||
}
|
||||
|
||||
|
64
langtools/make/tools/crules/AssertCheckAnalyzer.java
Normal file
64
langtools/make/tools/crules/AssertCheckAnalyzer.java
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package crules;
|
||||
|
||||
import com.sun.source.util.JavacTask;
|
||||
import com.sun.source.util.TaskEvent.Kind;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import com.sun.tools.javac.tree.JCTree.JCExpression;
|
||||
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
|
||||
import com.sun.tools.javac.tree.JCTree.Tag;
|
||||
import com.sun.tools.javac.tree.TreeInfo;
|
||||
import com.sun.tools.javac.tree.TreeScanner;
|
||||
import com.sun.tools.javac.util.Assert;
|
||||
|
||||
public class AssertCheckAnalyzer extends AbstractCodingRulesAnalyzer {
|
||||
|
||||
public AssertCheckAnalyzer(JavacTask task) {
|
||||
super(task);
|
||||
treeVisitor = new AssertCheckVisitor();
|
||||
eventKind = Kind.ANALYZE;
|
||||
}
|
||||
|
||||
class AssertCheckVisitor extends TreeScanner {
|
||||
|
||||
@Override
|
||||
public void visitApply(JCMethodInvocation tree) {
|
||||
Symbol method = TreeInfo.symbolFor(tree);
|
||||
if (method != null &&
|
||||
method.owner.getQualifiedName().contentEquals(Assert.class.getName()) &&
|
||||
!method.name.contentEquals("error")) {
|
||||
JCExpression lastParam = tree.args.last();
|
||||
if (lastParam != null &&
|
||||
lastParam.type.tsym == syms.stringType.tsym &&
|
||||
lastParam.hasTag(Tag.PLUS)) {
|
||||
messages.error(tree, "crules.should.not.use.string.concatenation");
|
||||
}
|
||||
}
|
||||
|
||||
super.visitApply(tree);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
107
langtools/make/tools/crules/CodingRulesAnalyzerPlugin.java
Normal file
107
langtools/make/tools/crules/CodingRulesAnalyzerPlugin.java
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package crules;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.util.JavacTask;
|
||||
import com.sun.source.util.Plugin;
|
||||
import com.sun.source.util.TaskEvent;
|
||||
import com.sun.source.util.TaskEvent.Kind;
|
||||
import com.sun.source.util.TaskListener;
|
||||
import com.sun.source.util.Trees;
|
||||
import com.sun.tools.javac.api.BasicJavacTask;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import com.sun.tools.javac.util.Log;
|
||||
|
||||
public class CodingRulesAnalyzerPlugin implements Plugin {
|
||||
|
||||
protected Log log;
|
||||
protected Trees trees;
|
||||
|
||||
public void init(JavacTask task, String... args) {
|
||||
BasicJavacTask impl = (BasicJavacTask)task;
|
||||
Context context = impl.getContext();
|
||||
log = Log.instance(context);
|
||||
trees = Trees.instance(task);
|
||||
task.addTaskListener(new PostAnalyzeTaskListener(
|
||||
new MutableFieldsAnalyzer(task),
|
||||
new AssertCheckAnalyzer(task)
|
||||
));
|
||||
}
|
||||
|
||||
public class PostAnalyzeTaskListener implements TaskListener {
|
||||
private final Map<Kind, List<AbstractCodingRulesAnalyzer>> analyzers = new HashMap<>();
|
||||
|
||||
public PostAnalyzeTaskListener(AbstractCodingRulesAnalyzer... analyzers) {
|
||||
for (AbstractCodingRulesAnalyzer analyzer : analyzers) {
|
||||
List<AbstractCodingRulesAnalyzer> currentAnalyzers = this.analyzers.get(analyzer.eventKind);
|
||||
|
||||
if (currentAnalyzers == null) {
|
||||
this.analyzers.put(analyzer.eventKind, currentAnalyzers = new ArrayList<>());
|
||||
}
|
||||
|
||||
currentAnalyzers.add(analyzer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void started(TaskEvent taskEvent) {}
|
||||
|
||||
@Override
|
||||
public void finished(TaskEvent taskEvent) {
|
||||
List<AbstractCodingRulesAnalyzer> currentAnalyzers = this.analyzers.get(taskEvent.getKind());
|
||||
|
||||
if (currentAnalyzers != null) {
|
||||
TypeElement typeElem = taskEvent.getTypeElement();
|
||||
Tree tree = trees.getTree(typeElem);
|
||||
if (tree != null) {
|
||||
JavaFileObject prevSource = log.currentSourceFile();
|
||||
try {
|
||||
log.useSource(taskEvent.getCompilationUnit().getSourceFile());
|
||||
for (AbstractCodingRulesAnalyzer analyzer : currentAnalyzers) {
|
||||
analyzer.treeVisitor.scan((JCTree)tree);
|
||||
}
|
||||
} finally {
|
||||
log.useSource(prevSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "coding_rules";
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2014, 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
|
||||
@ -28,24 +28,25 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sun.source.util.JavacTask;
|
||||
import com.sun.source.util.TaskEvent.Kind;
|
||||
import com.sun.tools.javac.code.Kinds;
|
||||
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
|
||||
import com.sun.tools.javac.tree.TreeScanner;
|
||||
|
||||
import static com.sun.source.util.TaskEvent.Kind;
|
||||
import static com.sun.tools.javac.code.Flags.*;
|
||||
import static com.sun.tools.javac.tree.JCTree.JCVariableDecl;
|
||||
import static com.sun.tools.javac.code.Flags.ENUM;
|
||||
import static com.sun.tools.javac.code.Flags.FINAL;
|
||||
import static com.sun.tools.javac.code.Flags.STATIC;
|
||||
import static com.sun.tools.javac.code.Flags.SYNTHETIC;
|
||||
|
||||
public class MutableFieldsAnalyzer extends AbstractCodingRulesAnalyzer {
|
||||
|
||||
public MutableFieldsAnalyzer() {
|
||||
public MutableFieldsAnalyzer(JavacTask task) {
|
||||
super(task);
|
||||
treeVisitor = new MutableFieldsVisitor();
|
||||
eventKind = Kind.ANALYZE;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "mutable_fields_analyzer";
|
||||
}
|
||||
|
||||
private boolean ignoreField(String className, String field) {
|
||||
List<String> currentFieldsToIgnore =
|
||||
classFieldsToIgnoreMap.get(className);
|
||||
@ -89,7 +90,7 @@ public class MutableFieldsAnalyzer extends AbstractCodingRulesAnalyzer {
|
||||
private static final String packageToCheck = "com.sun.tools.javac";
|
||||
|
||||
private static final Map<String, List<String>> classFieldsToIgnoreMap =
|
||||
new HashMap<String, List<String>>();
|
||||
new HashMap<>();
|
||||
|
||||
static {
|
||||
classFieldsToIgnoreMap.
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2013, 2014, 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,3 +26,5 @@
|
||||
# 0: symbol
|
||||
crules.err.var.must.be.final=\
|
||||
Static variable {0} must be final
|
||||
crules.should.not.use.string.concatenation=\
|
||||
Should not use string concatenation.
|
||||
|
@ -103,6 +103,10 @@ public class ClassFinder {
|
||||
*/
|
||||
private final JavaFileManager fileManager;
|
||||
|
||||
/** Dependency tracker
|
||||
*/
|
||||
private final Dependencies dependencies;
|
||||
|
||||
/** Factory for diagnostics
|
||||
*/
|
||||
JCDiagnostic.Factory diagFactory;
|
||||
@ -150,6 +154,7 @@ public class ClassFinder {
|
||||
names = Names.instance(context);
|
||||
syms = Symtab.instance(context);
|
||||
fileManager = context.get(JavaFileManager.class);
|
||||
dependencies = Dependencies.instance(context);
|
||||
if (fileManager == null)
|
||||
throw new AssertionError("FileManager initialization error");
|
||||
diagFactory = JCDiagnostic.Factory.instance(context);
|
||||
@ -179,18 +184,23 @@ public class ClassFinder {
|
||||
*/
|
||||
private void complete(Symbol sym) throws CompletionFailure {
|
||||
if (sym.kind == TYP) {
|
||||
ClassSymbol c = (ClassSymbol)sym;
|
||||
c.members_field = new Scope.ErrorScope(c); // make sure it's always defined
|
||||
annotate.enterStart();
|
||||
try {
|
||||
completeOwners(c.owner);
|
||||
completeEnclosing(c);
|
||||
ClassSymbol c = (ClassSymbol) sym;
|
||||
dependencies.push(c);
|
||||
c.members_field = new Scope.ErrorScope(c); // make sure it's always defined
|
||||
annotate.enterStart();
|
||||
try {
|
||||
completeOwners(c.owner);
|
||||
completeEnclosing(c);
|
||||
} finally {
|
||||
// The flush needs to happen only after annotations
|
||||
// are filled in.
|
||||
annotate.enterDoneWithoutFlush();
|
||||
}
|
||||
fillIn(c);
|
||||
} finally {
|
||||
// The flush needs to happen only after annotations
|
||||
// are filled in.
|
||||
annotate.enterDoneWithoutFlush();
|
||||
dependencies.pop();
|
||||
}
|
||||
fillIn(c);
|
||||
} else if (sym.kind == PCK) {
|
||||
PackageSymbol p = (PackageSymbol)sym;
|
||||
try {
|
||||
@ -257,7 +267,6 @@ public class ClassFinder {
|
||||
+ classfile.toUri());
|
||||
}
|
||||
}
|
||||
return;
|
||||
} finally {
|
||||
currentClassFile = previousClassFile;
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public class Flags {
|
||||
flags &= ~flag.value;
|
||||
}
|
||||
}
|
||||
Assert.check(flags == 0, "Flags parameter contains unknown flags " + flags);
|
||||
Assert.check(flags == 0);
|
||||
return flagSet;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2014, 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
|
||||
@ -67,22 +67,19 @@ public class TypeAnnotationPosition {
|
||||
private TypePathEntry(TypePathEntryKind tag) {
|
||||
Assert.check(tag == TypePathEntryKind.ARRAY ||
|
||||
tag == TypePathEntryKind.INNER_TYPE ||
|
||||
tag == TypePathEntryKind.WILDCARD,
|
||||
"Invalid TypePathEntryKind: " + tag);
|
||||
tag == TypePathEntryKind.WILDCARD);
|
||||
this.tag = tag;
|
||||
this.arg = 0;
|
||||
}
|
||||
|
||||
public TypePathEntry(TypePathEntryKind tag, int arg) {
|
||||
Assert.check(tag == TypePathEntryKind.TYPE_ARGUMENT,
|
||||
"Invalid TypePathEntryKind: " + tag);
|
||||
Assert.check(tag == TypePathEntryKind.TYPE_ARGUMENT);
|
||||
this.tag = tag;
|
||||
this.arg = arg;
|
||||
}
|
||||
|
||||
public static TypePathEntry fromBinary(int tag, int arg) {
|
||||
Assert.check(arg == 0 || tag == TypePathEntryKind.TYPE_ARGUMENT.tag,
|
||||
"Invalid TypePathEntry tag/arg: " + tag + "/" + arg);
|
||||
Assert.check(arg == 0 || tag == TypePathEntryKind.TYPE_ARGUMENT.tag);
|
||||
switch (tag) {
|
||||
case 0:
|
||||
return ARRAY;
|
||||
@ -351,7 +348,7 @@ public class TypeAnnotationPosition {
|
||||
Iterator<Integer> iter = list.iterator();
|
||||
while (iter.hasNext()) {
|
||||
Integer fst = iter.next();
|
||||
Assert.check(iter.hasNext(), "Could not decode type path: " + list);
|
||||
Assert.check(iter.hasNext());
|
||||
Integer snd = iter.next();
|
||||
loc = loc.append(TypePathEntry.fromBinary(fst, snd));
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ import com.sun.tools.javac.tree.*;
|
||||
import com.sun.tools.javac.tree.JCTree.*;
|
||||
import com.sun.tools.javac.tree.JCTree.JCPolyExpression.*;
|
||||
import com.sun.tools.javac.util.*;
|
||||
import com.sun.tools.javac.util.Dependencies.AttributionKind;
|
||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
|
||||
import com.sun.tools.javac.util.List;
|
||||
import static com.sun.tools.javac.code.Flags.*;
|
||||
@ -94,6 +95,7 @@ public class Attr extends JCTree.Visitor {
|
||||
final Annotate annotate;
|
||||
final DeferredLintHandler deferredLintHandler;
|
||||
final TypeEnvs typeEnvs;
|
||||
final Dependencies dependencies;
|
||||
|
||||
public static Attr instance(Context context) {
|
||||
Attr instance = context.get(attrKey);
|
||||
@ -123,6 +125,7 @@ public class Attr extends JCTree.Visitor {
|
||||
annotate = Annotate.instance(context);
|
||||
deferredLintHandler = DeferredLintHandler.instance(context);
|
||||
typeEnvs = TypeEnvs.instance(context);
|
||||
dependencies = Dependencies.instance(context);
|
||||
|
||||
Options options = Options.instance(context);
|
||||
|
||||
@ -254,7 +257,7 @@ public class Attr extends JCTree.Visitor {
|
||||
* @param env The current environment.
|
||||
*/
|
||||
boolean isAssignableAsBlankFinal(VarSymbol v, Env<AttrContext> env) {
|
||||
Symbol owner = owner(env);
|
||||
Symbol owner = env.info.scope.owner;
|
||||
// owner refers to the innermost variable, method or
|
||||
// initializer block declaration at this point.
|
||||
return
|
||||
@ -269,41 +272,6 @@ public class Attr extends JCTree.Visitor {
|
||||
((v.flags() & STATIC) != 0) == Resolve.isStatic(env));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the innermost enclosing owner symbol in a given attribution context
|
||||
*/
|
||||
Symbol owner(Env<AttrContext> env) {
|
||||
while (true) {
|
||||
switch (env.tree.getTag()) {
|
||||
case VARDEF:
|
||||
//a field can be owner
|
||||
VarSymbol vsym = ((JCVariableDecl)env.tree).sym;
|
||||
if (vsym.owner.kind == TYP) {
|
||||
return vsym;
|
||||
}
|
||||
break;
|
||||
case METHODDEF:
|
||||
//method def is always an owner
|
||||
return ((JCMethodDecl)env.tree).sym;
|
||||
case CLASSDEF:
|
||||
//class def is always an owner
|
||||
return ((JCClassDecl)env.tree).sym;
|
||||
case BLOCK:
|
||||
//static/instance init blocks are owner
|
||||
Symbol blockSym = env.info.scope.owner;
|
||||
if ((blockSym.flags() & BLOCK) != 0) {
|
||||
return blockSym;
|
||||
}
|
||||
break;
|
||||
case TOPLEVEL:
|
||||
//toplevel is always an owner (for pkge decls)
|
||||
return env.info.scope.owner;
|
||||
}
|
||||
Assert.checkNonNull(env.next);
|
||||
env = env.next;
|
||||
}
|
||||
}
|
||||
|
||||
/** Check that variable can be assigned to.
|
||||
* @param pos The current source code position.
|
||||
* @param v The assigned varaible
|
||||
@ -695,6 +663,7 @@ public class Attr extends JCTree.Visitor {
|
||||
*/
|
||||
void attribTypeVariables(List<JCTypeParameter> typarams, Env<AttrContext> env) {
|
||||
for (JCTypeParameter tvar : typarams) {
|
||||
dependencies.push(AttributionKind.TVAR, tvar);
|
||||
TypeVar a = (TypeVar)tvar.type;
|
||||
a.tsym.flags_field |= UNATTRIBUTED;
|
||||
a.bound = Type.noType;
|
||||
@ -710,6 +679,7 @@ public class Attr extends JCTree.Visitor {
|
||||
types.setBounds(a, List.of(syms.objectType));
|
||||
}
|
||||
a.tsym.flags_field &= ~UNATTRIBUTED;
|
||||
dependencies.pop();
|
||||
}
|
||||
for (JCTypeParameter tvar : typarams) {
|
||||
chk.checkNonCyclic(tvar.pos(), (TypeVar)tvar.type);
|
||||
@ -3815,7 +3785,7 @@ public class Attr extends JCTree.Visitor {
|
||||
// and are subject to definite assignment checking.
|
||||
if ((env.info.enclVar == v || v.pos > tree.pos) &&
|
||||
v.owner.kind == TYP &&
|
||||
canOwnInitializer(owner(env)) &&
|
||||
enclosingInitEnv(env) != null &&
|
||||
v.owner == env.info.scope.owner.enclClass() &&
|
||||
((v.flags() & STATIC) != 0) == Resolve.isStatic(env) &&
|
||||
(!env.tree.hasTag(ASSIGN) ||
|
||||
@ -3834,6 +3804,36 @@ public class Attr extends JCTree.Visitor {
|
||||
checkEnumInitializer(tree, env, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the enclosing init environment associated with this env (if any). An init env
|
||||
* can be either a field declaration env or a static/instance initializer env.
|
||||
*/
|
||||
Env<AttrContext> enclosingInitEnv(Env<AttrContext> env) {
|
||||
while (true) {
|
||||
switch (env.tree.getTag()) {
|
||||
case VARDEF:
|
||||
JCVariableDecl vdecl = (JCVariableDecl)env.tree;
|
||||
if (vdecl.sym.owner.kind == TYP) {
|
||||
//field
|
||||
return env;
|
||||
}
|
||||
break;
|
||||
case BLOCK:
|
||||
if (env.next.tree.hasTag(CLASSDEF)) {
|
||||
//instance/static initializer
|
||||
return env;
|
||||
}
|
||||
break;
|
||||
case METHODDEF:
|
||||
case CLASSDEF:
|
||||
case TOPLEVEL:
|
||||
return null;
|
||||
}
|
||||
Assert.checkNonNull(env.next);
|
||||
env = env.next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for illegal references to static members of enum. In
|
||||
* an enum type, constructors and initializers may not
|
||||
@ -3887,17 +3887,6 @@ public class Attr extends JCTree.Visitor {
|
||||
v.name != names._class;
|
||||
}
|
||||
|
||||
/** Can the given symbol be the owner of code which forms part
|
||||
* if class initialization? This is the case if the symbol is
|
||||
* a type or field, or if the symbol is the synthetic method.
|
||||
* owning a block.
|
||||
*/
|
||||
private boolean canOwnInitializer(Symbol sym) {
|
||||
return
|
||||
(sym.kind & (VAR | TYP)) != 0 ||
|
||||
(sym.kind == MTH && (sym.flags() & BLOCK) != 0);
|
||||
}
|
||||
|
||||
Warner noteWarner = new Warner();
|
||||
|
||||
/**
|
||||
|
@ -2765,7 +2765,7 @@ public class Check {
|
||||
}
|
||||
|
||||
public void validateTypeAnnotation(JCAnnotation a, boolean isTypeParameter) {
|
||||
Assert.checkNonNull(a.type, "annotation tree hasn't been attributed yet: " + a);
|
||||
Assert.checkNonNull(a.type);
|
||||
validateAnnotationTree(a);
|
||||
|
||||
if (a.hasTag(TYPE_ANNOTATION) &&
|
||||
|
@ -29,6 +29,7 @@ import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.tree.JCTree.JCTypeCast;
|
||||
import com.sun.tools.javac.tree.TreeInfo;
|
||||
import com.sun.tools.javac.util.*;
|
||||
import com.sun.tools.javac.util.GraphUtils.DottableNode;
|
||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
|
||||
import com.sun.tools.javac.util.List;
|
||||
import com.sun.tools.javac.code.*;
|
||||
@ -40,9 +41,9 @@ import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph;
|
||||
import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph.Node;
|
||||
import com.sun.tools.javac.comp.Resolve.InapplicableMethodException;
|
||||
import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode;
|
||||
import com.sun.tools.javac.util.GraphUtils.TarjanNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
@ -50,6 +51,7 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.sun.tools.javac.code.TypeTag.*;
|
||||
@ -1607,11 +1609,6 @@ public class Infer {
|
||||
private DependencyKind(String dotSyle) {
|
||||
this.dotSyle = dotSyle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDotStyle() {
|
||||
return dotSyle;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1684,7 +1681,7 @@ public class Infer {
|
||||
* updates on the structure of the graph this node belongs to (used to
|
||||
* keep dependencies in sync).
|
||||
*/
|
||||
class Node extends GraphUtils.TarjanNode<ListBuffer<Type>> {
|
||||
class Node extends GraphUtils.TarjanNode<ListBuffer<Type>, Node> implements DottableNode<ListBuffer<Type>, Node> {
|
||||
|
||||
/** map listing all dependencies (grouped by kind) */
|
||||
EnumMap<DependencyKind, Set<Node>> deps;
|
||||
@ -1699,33 +1696,12 @@ public class Infer {
|
||||
return DependencyKind.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDependencyName(GraphUtils.Node<ListBuffer<Type>> to, GraphUtils.DependencyKind dk) {
|
||||
if (dk == DependencyKind.STUCK) return "";
|
||||
else {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
String sep = "";
|
||||
for (Type from : data) {
|
||||
UndetVar uv = (UndetVar)inferenceContext.asUndetVar(from);
|
||||
for (Type bound : uv.getBounds(InferenceBound.values())) {
|
||||
if (bound.containsAny(List.from(to.data))) {
|
||||
buf.append(sep);
|
||||
buf.append(bound);
|
||||
sep = ",";
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<? extends Node> getAllDependencies() {
|
||||
return getDependencies(DependencyKind.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<? extends TarjanNode<ListBuffer<Type>>> getDependenciesByKind(GraphUtils.DependencyKind dk) {
|
||||
public Collection<? extends Node> getDependenciesByKind(GraphUtils.DependencyKind dk) {
|
||||
return getDependencies((DependencyKind)dk);
|
||||
}
|
||||
|
||||
@ -1855,6 +1831,36 @@ public class Infer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties nodeAttributes() {
|
||||
Properties p = new Properties();
|
||||
p.put("label", toString());
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties dependencyAttributes(Node sink, GraphUtils.DependencyKind dk) {
|
||||
Properties p = new Properties();
|
||||
p.put("style", ((DependencyKind)dk).dotSyle);
|
||||
if (dk == DependencyKind.STUCK) return p;
|
||||
else {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
String sep = "";
|
||||
for (Type from : data) {
|
||||
UndetVar uv = (UndetVar)inferenceContext.asUndetVar(from);
|
||||
for (Type bound : uv.getBounds(InferenceBound.values())) {
|
||||
if (bound.containsAny(List.from(sink.data))) {
|
||||
buf.append(sep);
|
||||
buf.append(bound);
|
||||
sep = ",";
|
||||
}
|
||||
}
|
||||
}
|
||||
p.put("label", buf.toString());
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
/** the nodes in the inference graph */
|
||||
|
@ -55,6 +55,7 @@ import static com.sun.tools.javac.code.TypeTag.ERROR;
|
||||
import static com.sun.tools.javac.code.TypeTag.TYPEVAR;
|
||||
import static com.sun.tools.javac.tree.JCTree.Tag.*;
|
||||
|
||||
import com.sun.tools.javac.util.Dependencies.AttributionKind;
|
||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
|
||||
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
|
||||
|
||||
@ -90,6 +91,7 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
|
||||
private final DeferredLintHandler deferredLintHandler;
|
||||
private final Lint lint;
|
||||
private final TypeEnvs typeEnvs;
|
||||
private final Dependencies dependencies;
|
||||
|
||||
public static MemberEnter instance(Context context) {
|
||||
MemberEnter instance = context.get(memberEnterKey);
|
||||
@ -116,6 +118,7 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
|
||||
deferredLintHandler = DeferredLintHandler.instance(context);
|
||||
lint = Lint.instance(context);
|
||||
typeEnvs = TypeEnvs.instance(context);
|
||||
dependencies = Dependencies.instance(context);
|
||||
allowTypeAnnos = source.allowTypeAnnotations();
|
||||
}
|
||||
|
||||
@ -555,6 +558,7 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
|
||||
|
||||
// process the non-static imports and the static imports of types.
|
||||
public void visitImport(JCImport tree) {
|
||||
dependencies.push(AttributionKind.IMPORT, tree);
|
||||
JCFieldAccess imp = (JCFieldAccess)tree.qualid;
|
||||
Name name = TreeInfo.name(imp);
|
||||
|
||||
@ -581,6 +585,7 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
|
||||
importNamed(tree.pos(), c, env);
|
||||
}
|
||||
}
|
||||
dependencies.pop();
|
||||
}
|
||||
|
||||
public void visitMethodDef(JCMethodDecl tree) {
|
||||
@ -952,6 +957,8 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
|
||||
JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
|
||||
DiagnosticPosition prevLintPos = deferredLintHandler.setPos(tree.pos());
|
||||
try {
|
||||
dependencies.push(c);
|
||||
|
||||
// Save class environment for later member enter (2) processing.
|
||||
halfcompleted.append(env);
|
||||
|
||||
@ -990,16 +997,21 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
|
||||
Type supertype;
|
||||
|
||||
if (tree.extending != null) {
|
||||
supertype = attr.attribBase(tree.extending, baseEnv,
|
||||
true, false, true);
|
||||
if (sym.isAnonymous()) {
|
||||
annotate.annotateAnonClassDefLater(tree.extending,
|
||||
tree.mods.annotations,
|
||||
baseEnv, sym, tree.pos(),
|
||||
annotate.extendsCreator);
|
||||
} else {
|
||||
annotate.annotateTypeLater(tree.extending, baseEnv, sym,
|
||||
tree.pos(), annotate.extendsCreator);
|
||||
dependencies.push(AttributionKind.EXTENDS, tree.extending);
|
||||
try {
|
||||
supertype = attr.attribBase(tree.extending, baseEnv,
|
||||
true, false, true);
|
||||
if (sym.isAnonymous()) {
|
||||
annotate.annotateAnonClassDefLater(tree.extending,
|
||||
tree.mods.annotations,
|
||||
baseEnv, sym, tree.pos(),
|
||||
annotate.extendsCreator);
|
||||
} else {
|
||||
annotate.annotateTypeLater(tree.extending, baseEnv, sym,
|
||||
tree.pos(), annotate.extendsCreator);
|
||||
}
|
||||
} finally {
|
||||
dependencies.pop();
|
||||
}
|
||||
} else {
|
||||
supertype = ((tree.mods.flags & Flags.ENUM) != 0)
|
||||
@ -1018,29 +1030,34 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
|
||||
List<JCExpression> interfaceTrees = tree.implementing;
|
||||
int i = 0;
|
||||
for (JCExpression iface : interfaceTrees) {
|
||||
Type it = attr.attribBase(iface, baseEnv, false, true, true);
|
||||
if (it.hasTag(CLASS)) {
|
||||
interfaces.append(it);
|
||||
if (all_interfaces != null) all_interfaces.append(it);
|
||||
chk.checkNotRepeated(iface.pos(), types.erasure(it), interfaceSet);
|
||||
} else {
|
||||
if (all_interfaces == null)
|
||||
all_interfaces = new ListBuffer<Type>().appendList(interfaces);
|
||||
all_interfaces.append(modelMissingTypes(it, iface, true));
|
||||
dependencies.push(AttributionKind.IMPLEMENTS, iface);
|
||||
try {
|
||||
Type it = attr.attribBase(iface, baseEnv, false, true, true);
|
||||
if (it.hasTag(CLASS)) {
|
||||
interfaces.append(it);
|
||||
if (all_interfaces != null) all_interfaces.append(it);
|
||||
chk.checkNotRepeated(iface.pos(), types.erasure(it), interfaceSet);
|
||||
} else {
|
||||
if (all_interfaces == null)
|
||||
all_interfaces = new ListBuffer<Type>().appendList(interfaces);
|
||||
all_interfaces.append(modelMissingTypes(it, iface, true));
|
||||
|
||||
}
|
||||
if (sym.isAnonymous()) {
|
||||
// Note: if an anonymous class ever has more than
|
||||
// one supertype for some reason, this will
|
||||
// incorrectly attach tree.mods.annotations to ALL
|
||||
// supertypes, not just the first.
|
||||
annotate.annotateAnonClassDefLater(iface, tree.mods.annotations,
|
||||
baseEnv, sym, tree.pos(),
|
||||
annotate.implementsCreator(i++));
|
||||
} else {
|
||||
annotate.annotateTypeLater(iface, baseEnv, sym, tree.pos(),
|
||||
annotate.implementsCreator(i++));
|
||||
}
|
||||
} finally {
|
||||
dependencies.pop();
|
||||
}
|
||||
if (sym.isAnonymous()) {
|
||||
// Note: if an anonymous class ever has more than
|
||||
// one supertype for some reason, this will
|
||||
// incorrectly attach tree.mods.annotations to ALL
|
||||
// supertypes, not just the first.
|
||||
annotate.annotateAnonClassDefLater(iface, tree.mods.annotations,
|
||||
baseEnv, sym, tree.pos(),
|
||||
annotate.implementsCreator(i++));
|
||||
} else {
|
||||
annotate.annotateTypeLater(iface, baseEnv, sym, tree.pos(),
|
||||
annotate.implementsCreator(i++));
|
||||
}
|
||||
}
|
||||
|
||||
if ((c.flags_field & ANNOTATION) != 0) {
|
||||
@ -1157,6 +1174,7 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
|
||||
} finally {
|
||||
deferredLintHandler.setPos(prevLintPos);
|
||||
log.useSource(prev);
|
||||
dependencies.pop();
|
||||
}
|
||||
|
||||
// Enter all member fields and methods of a set of half completed
|
||||
|
@ -488,6 +488,10 @@ public class Main {
|
||||
}
|
||||
}
|
||||
|
||||
if (options.isSet("completionDeps")) {
|
||||
Dependencies.GraphDependencies.preRegister(context);
|
||||
}
|
||||
|
||||
comp = JavaCompiler.instance(context);
|
||||
|
||||
// FIXME: this code will not be invoked if using JavacTask.parse/analyze/generate
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2014, 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,6 @@
|
||||
|
||||
package com.sun.tools.javac.util;
|
||||
|
||||
|
||||
/**
|
||||
* Simple facility for unconditional assertions.
|
||||
* The methods in this class are described in terms of equivalent assert
|
||||
@ -87,7 +86,7 @@ public class Assert {
|
||||
}
|
||||
|
||||
/** Equivalent to
|
||||
* assert cond : value;
|
||||
* assert cond : msg;
|
||||
*/
|
||||
public static void check(boolean cond, String msg) {
|
||||
if (!cond)
|
||||
@ -103,7 +102,7 @@ public class Assert {
|
||||
}
|
||||
|
||||
/** Equivalent to
|
||||
* assert (o == null) : value;
|
||||
* assert (o == null) : msg;
|
||||
*/
|
||||
public static void checkNull(Object o, String msg) {
|
||||
if (o != null)
|
||||
@ -111,7 +110,7 @@ public class Assert {
|
||||
}
|
||||
|
||||
/** Equivalent to
|
||||
* assert (o != null) : value;
|
||||
* assert (o != null) : msg;
|
||||
*/
|
||||
public static <T> T checkNonNull(T t, String msg) {
|
||||
if (t == null)
|
||||
|
@ -183,7 +183,7 @@ public class Bits {
|
||||
*/
|
||||
public void incl(int x) {
|
||||
Assert.check(currentState != BitsState.UNKNOWN);
|
||||
Assert.check(x >= 0, "Value of x " + x);
|
||||
Assert.check(x >= 0);
|
||||
sizeTo((x >>> wordshift) + 1);
|
||||
bits[x >>> wordshift] = bits[x >>> wordshift] |
|
||||
(1 << (x & wordmask));
|
||||
|
@ -0,0 +1,559 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.tools.javac.util;
|
||||
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
||||
import com.sun.tools.javac.code.Symbol.Completer;
|
||||
import com.sun.tools.javac.code.Symbol.CompletionFailure;
|
||||
import com.sun.tools.javac.main.JavaCompiler;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.util.GraphUtils.DotVisitor;
|
||||
import com.sun.tools.javac.util.GraphUtils.NodeVisitor;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
/**
|
||||
* This class is used to track dependencies in the javac symbol completion process.
|
||||
*
|
||||
* <p><b>This is NOT part of any supported API.
|
||||
* If you write code that depends on this, you do so at your own risk.
|
||||
* This code and its internal interfaces are subject to change or
|
||||
* deletion without notice.</b>
|
||||
*/
|
||||
public abstract class Dependencies {
|
||||
|
||||
protected static final Context.Key<Dependencies> dependenciesKey = new Context.Key<>();
|
||||
|
||||
public static Dependencies instance(Context context) {
|
||||
Dependencies instance = context.get(dependenciesKey);
|
||||
if (instance == null) {
|
||||
//use a do-nothing implementation in case no other implementation has been set by preRegister
|
||||
instance = new DummyDependencies(context);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
Dependencies(Context context) {
|
||||
context.put(dependenciesKey, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* This enum models different kinds of attribution actions triggered during
|
||||
* symbol completion.
|
||||
*/
|
||||
public enum AttributionKind {
|
||||
/**
|
||||
* Attribution of superclass (i.e. @{code extends} clause).
|
||||
*/
|
||||
EXTENDS {
|
||||
@Override
|
||||
String format(JCTree tree) {
|
||||
return "extends " + super.format(tree);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Attribution of superinterface (i.e. an type in the @{code interface} clause).
|
||||
*/
|
||||
IMPLEMENTS {
|
||||
@Override
|
||||
String format(JCTree tree) {
|
||||
return "implements " + super.format(tree);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Attribution of an import statement
|
||||
*/
|
||||
IMPORT,
|
||||
/**
|
||||
* Attribution of type-variable bound
|
||||
*/
|
||||
TVAR {
|
||||
@Override
|
||||
String format(JCTree tree) {
|
||||
return "<" + super.format(tree) + ">";
|
||||
}
|
||||
};
|
||||
|
||||
String format(JCTree tree) {
|
||||
return tree.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a new completion node on the stack.
|
||||
*/
|
||||
abstract public void push(ClassSymbol s);
|
||||
|
||||
/**
|
||||
* Push a new attribution node on the stack.
|
||||
*/
|
||||
abstract public void push(AttributionKind ak, JCTree t);
|
||||
|
||||
/**
|
||||
* Remove current dependency node from the stack.
|
||||
*/
|
||||
abstract public void pop();
|
||||
|
||||
/**
|
||||
* This class creates a graph of all dependencies as symbols are completed;
|
||||
* when compilation finishes, the resulting dependecy graph is then dumped
|
||||
* onto a dot file. Several options are provided to customise the output of the graph.
|
||||
*/
|
||||
public static class GraphDependencies extends Dependencies implements Closeable, Completer {
|
||||
|
||||
/**
|
||||
* set of enabled dependencies modes
|
||||
*/
|
||||
private EnumSet<DependenciesMode> dependenciesModes;
|
||||
|
||||
/**
|
||||
* file in which the dependency graph should be written
|
||||
*/
|
||||
private String dependenciesFile;
|
||||
|
||||
/**
|
||||
* Register a Context.Factory to create a Dependencies.
|
||||
*/
|
||||
public static void preRegister(final Context context) {
|
||||
context.put(dependenciesKey, new Context.Factory<Dependencies>() {
|
||||
public Dependencies make(Context c) {
|
||||
Dependencies deps = new GraphDependencies(context);
|
||||
return deps;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a Dependencies instance.
|
||||
*/
|
||||
GraphDependencies(Context context) {
|
||||
super(context);
|
||||
Options options = Options.instance(context);
|
||||
//fetch filename
|
||||
String[] modes = options.get("completionDeps").split(",");
|
||||
for (String mode : modes) {
|
||||
if (mode.startsWith("file=")) {
|
||||
dependenciesFile = mode.substring(5);
|
||||
}
|
||||
}
|
||||
//parse modes
|
||||
dependenciesModes = DependenciesMode.getDependenciesModes(modes);
|
||||
//add to closeables
|
||||
JavaCompiler compiler = JavaCompiler.instance(context);
|
||||
compiler.closeables = compiler.closeables.prepend(this);
|
||||
}
|
||||
|
||||
enum DependenciesMode {
|
||||
SOURCE("source"),
|
||||
CLASS("class"),
|
||||
REDUNDANT("redundant"),
|
||||
SIDE_EFFECTS("side-effects");
|
||||
|
||||
final String opt;
|
||||
|
||||
private DependenciesMode(String opt) {
|
||||
this.opt = opt;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to parse the {@code completionDeps} option.
|
||||
* Possible modes are separated by colon; a mode can be excluded by
|
||||
* prepending '-' to its name. Finally, the special mode 'all' can be used to
|
||||
* add all modes to the resulting enum.
|
||||
*/
|
||||
static EnumSet<DependenciesMode> getDependenciesModes(String[] modes) {
|
||||
EnumSet<DependenciesMode> res = EnumSet.noneOf(DependenciesMode.class);
|
||||
Collection<String> args = Arrays.asList(modes);
|
||||
if (args.contains("all")) {
|
||||
res = EnumSet.allOf(DependenciesMode.class);
|
||||
}
|
||||
for (DependenciesMode mode : values()) {
|
||||
if (args.contains(mode.opt)) {
|
||||
res.add(mode);
|
||||
} else if (args.contains("-" + mode.opt)) {
|
||||
res.remove(mode);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing a node in the dependency graph. Nodes are of two main
|
||||
* kinds: (i) symbol nodes, corresponding to symbol completion requests
|
||||
* (either from source or classfile); (ii) attribution nodes, corresponding to
|
||||
* attribution actions triggered during (source) completion.
|
||||
*/
|
||||
static abstract class Node extends GraphUtils.AbstractNode<String, Node>
|
||||
implements GraphUtils.DottableNode<String, Node> {
|
||||
|
||||
/**
|
||||
* Model the dependencies between nodes.
|
||||
*/
|
||||
enum DependencyKind implements GraphUtils.DependencyKind {
|
||||
/**
|
||||
* standard dependency - i.e. completion of the source node depends
|
||||
* on completion of the sink node.
|
||||
*/
|
||||
REQUIRES("solid"),
|
||||
/**
|
||||
* soft dependencies - i.e. completion of the source node depends
|
||||
* on side-effects of the source node. These dependencies are meant
|
||||
* to capture the order in which javac processes all dependants of a given node.
|
||||
*/
|
||||
SIDE_EFFECTS("dashed");
|
||||
|
||||
final String dotStyle;
|
||||
|
||||
DependencyKind(String dotStyle) {
|
||||
this.dotStyle = dotStyle;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dependant nodes grouped by kind
|
||||
*/
|
||||
EnumMap<DependencyKind, List<Node>> depsByKind;
|
||||
|
||||
Node(String value) {
|
||||
super(value);
|
||||
this.depsByKind = new EnumMap<>(DependencyKind.class);
|
||||
for (DependencyKind depKind : DependencyKind.values()) {
|
||||
depsByKind.put(depKind, new ArrayList<Node>());
|
||||
}
|
||||
}
|
||||
|
||||
void addDependency(DependencyKind depKind, Node dep) {
|
||||
List<Node> deps = depsByKind.get(depKind);
|
||||
if (!deps.contains(dep)) {
|
||||
deps.add(dep);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Node &&
|
||||
data.equals(((Node) obj).data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return data.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphUtils.DependencyKind[] getSupportedDependencyKinds() {
|
||||
return DependencyKind.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.util.Collection<? extends Node> getDependenciesByKind(GraphUtils.DependencyKind dk) {
|
||||
List<Node> deps = depsByKind.get(dk);
|
||||
if (dk == DependencyKind.REQUIRES) {
|
||||
return deps;
|
||||
} else {
|
||||
Set<Node> temp = new HashSet<>(deps);
|
||||
temp.removeAll(depsByKind.get(DependencyKind.REQUIRES));
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties nodeAttributes() {
|
||||
Properties p = new Properties();
|
||||
p.put("label", DotVisitor.wrap(toString()));
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties dependencyAttributes(Node to, GraphUtils.DependencyKind dk) {
|
||||
Properties p = new Properties();
|
||||
p.put("style", ((DependencyKind) dk).dotStyle);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a dependency node used to model symbol completion requests.
|
||||
* Completion requests can come from either source or class.
|
||||
*/
|
||||
static class CompletionNode extends Node {
|
||||
|
||||
/**
|
||||
* Completion kind (source vs. classfile)
|
||||
*/
|
||||
enum Kind {
|
||||
/**
|
||||
* Source completion request
|
||||
*/
|
||||
SOURCE("solid"),
|
||||
/**
|
||||
* Classfile completion request
|
||||
*/
|
||||
CLASS("dotted");
|
||||
|
||||
final String dotStyle;
|
||||
|
||||
Kind(String dotStyle) {
|
||||
this.dotStyle = dotStyle;
|
||||
}
|
||||
}
|
||||
|
||||
final Kind ck;
|
||||
|
||||
CompletionNode(ClassSymbol sym) {
|
||||
super(sym.getQualifiedName().toString());
|
||||
//infer completion kind by looking at the symbol fields
|
||||
boolean fromClass = (sym.classfile == null && sym.sourcefile == null) ||
|
||||
(sym.classfile != null && sym.classfile.getKind() == JavaFileObject.Kind.CLASS);
|
||||
ck = fromClass ?
|
||||
CompletionNode.Kind.CLASS :
|
||||
CompletionNode.Kind.SOURCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties nodeAttributes() {
|
||||
Properties p = super.nodeAttributes();
|
||||
p.put("style", ck.dotStyle);
|
||||
p.put("shape", "ellipse");
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a dependency node used to model attribution actions triggered during
|
||||
* source symbol completion. The possible kinds of attribution actions are
|
||||
* captured in {@link AttributionNode}.
|
||||
*/
|
||||
static class AttributionNode extends Node {
|
||||
|
||||
AttributionNode(AttributionKind ak, JCTree tree) {
|
||||
super(ak.format(tree));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties nodeAttributes() {
|
||||
Properties p = super.nodeAttributes();
|
||||
p.put("shape", "box");
|
||||
p.put("style", "solid");
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* stack of dependency nodes currently being processed
|
||||
*/
|
||||
Stack<Node> nodeStack = new Stack<>();
|
||||
|
||||
/**
|
||||
* map containing all dependency nodes seen so far
|
||||
*/
|
||||
Map<String, Node> dependencyNodeMap = new LinkedHashMap<>();
|
||||
|
||||
@Override
|
||||
public void push(ClassSymbol s) {
|
||||
Node n = new CompletionNode(s);
|
||||
if (n == push(n)) {
|
||||
s.completer = this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void push(AttributionKind ak, JCTree t) {
|
||||
push(new AttributionNode(ak, t));
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a new dependency on the stack.
|
||||
*/
|
||||
protected Node push(Node newNode) {
|
||||
Node cachedNode = dependencyNodeMap.get(newNode.data);
|
||||
if (cachedNode == null) {
|
||||
dependencyNodeMap.put(newNode.data, newNode);
|
||||
} else {
|
||||
newNode = cachedNode;
|
||||
}
|
||||
if (!nodeStack.isEmpty()) {
|
||||
Node currentNode = nodeStack.peek();
|
||||
currentNode.addDependency(Node.DependencyKind.REQUIRES, newNode);
|
||||
}
|
||||
nodeStack.push(newNode);
|
||||
return newNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pop() {
|
||||
nodeStack.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (dependenciesFile != null) {
|
||||
if (!dependenciesModes.contains(DependenciesMode.REDUNDANT)) {
|
||||
//prune spurious edges
|
||||
new PruneVisitor().visit(dependencyNodeMap.values(), null);
|
||||
}
|
||||
if (!dependenciesModes.contains(DependenciesMode.CLASS)) {
|
||||
//filter class completions
|
||||
new FilterVisitor(CompletionNode.Kind.SOURCE).visit(dependencyNodeMap.values(), null);
|
||||
}
|
||||
if (!dependenciesModes.contains(DependenciesMode.SOURCE)) {
|
||||
//filter source completions
|
||||
new FilterVisitor(CompletionNode.Kind.CLASS).visit(dependencyNodeMap.values(), null);
|
||||
}
|
||||
if (dependenciesModes.contains(DependenciesMode.SIDE_EFFECTS)) {
|
||||
//add side-effects edges
|
||||
new SideEffectVisitor().visit(dependencyNodeMap.values(), null);
|
||||
}
|
||||
//write to file
|
||||
try (FileWriter fw = new FileWriter(dependenciesFile)) {
|
||||
fw.append(GraphUtils.toDot(dependencyNodeMap.values(), "CompletionDeps", ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(Symbol sym) throws CompletionFailure {
|
||||
push((ClassSymbol) sym);
|
||||
pop();
|
||||
sym.completer = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This visitor is used to generate the special side-effect dependencies
|
||||
* given a graph containing only standard dependencies.
|
||||
*/
|
||||
private static class SideEffectVisitor extends NodeVisitor<String, Node, Void> {
|
||||
@Override
|
||||
public void visitNode(Node node, Void arg) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDependency(GraphUtils.DependencyKind dk, Node from, Node to, Void arg) {
|
||||
//if we are adding multiple dependencies to same node
|
||||
//make order explicit via special 'side-effect' dependencies
|
||||
List<Node> deps = from.depsByKind.get(dk);
|
||||
int pos = deps.indexOf(to);
|
||||
if (dk == Node.DependencyKind.REQUIRES && pos > 0) {
|
||||
to.addDependency(Node.DependencyKind.SIDE_EFFECTS, deps.get(pos - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This visitor is used to prune the graph from spurious edges using some heuristics.
|
||||
*/
|
||||
private static class PruneVisitor extends NodeVisitor<String, Node, Void> {
|
||||
@Override
|
||||
public void visitNode(Node node, Void arg) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDependency(GraphUtils.DependencyKind dk, Node from, Node to, Void arg) {
|
||||
//heuristic - skips dependencies that are likely to be fake
|
||||
if (from.equals(to) ||
|
||||
from.depsByKind.get(Node.DependencyKind.REQUIRES).contains(to)) {
|
||||
to.depsByKind.get(dk).remove(from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This visitor is used to retain only completion nodes with given kind.
|
||||
*/
|
||||
private class FilterVisitor extends NodeVisitor<String, Node, Void> {
|
||||
|
||||
CompletionNode.Kind ck;
|
||||
|
||||
private FilterVisitor(CompletionNode.Kind ck) {
|
||||
this.ck = ck;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNode(Node node, Void arg) {
|
||||
if (node instanceof CompletionNode) {
|
||||
if (((CompletionNode) node).ck != ck) {
|
||||
dependencyNodeMap.remove(node.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDependency(GraphUtils.DependencyKind dk, Node from, Node to, Void arg) {
|
||||
if (to instanceof CompletionNode) {
|
||||
if (((CompletionNode) to).ck != ck) {
|
||||
from.depsByKind.get(dk).remove(to);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy class to be used when dependencies options are not set. This keeps
|
||||
* performance cost of calling push/pop methods during completion marginally low.
|
||||
*/
|
||||
private static class DummyDependencies extends Dependencies {
|
||||
|
||||
private DummyDependencies(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void push(ClassSymbol s) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void push(AttributionKind ak, JCTree t) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pop() {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,10 @@
|
||||
|
||||
package com.sun.tools.javac.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Properties;
|
||||
|
||||
/** <p><b>This is NOT part of any supported API.
|
||||
* If you write code that depends on this, you do so at your own risk.
|
||||
* This code and its internal interfaces are subject to change or
|
||||
@ -33,25 +37,65 @@ package com.sun.tools.javac.util;
|
||||
public class GraphUtils {
|
||||
|
||||
/**
|
||||
* Basic interface for defining various dependency kinds. All dependency kinds
|
||||
* must at least support basic capabilities to tell the DOT engine how to render them.
|
||||
* Basic interface for defining various dependency kinds.
|
||||
*/
|
||||
public interface DependencyKind {
|
||||
public interface DependencyKind { }
|
||||
|
||||
/**
|
||||
* Common superinterfaces to all graph nodes.
|
||||
*/
|
||||
public interface Node<D, N extends Node<D, N>> {
|
||||
/**
|
||||
* Returns the DOT representation (to be used in a {@code style} attribute
|
||||
* that's most suited for this dependency kind.
|
||||
* visitor method.
|
||||
*/
|
||||
String getDotStyle();
|
||||
<A> void accept(NodeVisitor<D, N, A> visitor, A arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visitor for graph nodes.
|
||||
*/
|
||||
static abstract class NodeVisitor<D, N extends Node<D, N>, A> {
|
||||
/**
|
||||
* Visitor action for nodes.
|
||||
*/
|
||||
public abstract void visitNode(N node, A arg);
|
||||
/**
|
||||
* Visitor action for a dependency between 'from' and 'to' with given kind.
|
||||
*/
|
||||
public abstract void visitDependency(DependencyKind dk, N from, N to, A arg);
|
||||
|
||||
/**
|
||||
* Visitor entry point.
|
||||
*/
|
||||
public void visit(Collection<? extends N> nodes, A arg) {
|
||||
for (N n : new ArrayList<>(nodes)) {
|
||||
n.accept(this, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional interface for nodes supporting dot-based representation.
|
||||
*/
|
||||
public interface DottableNode<D, N extends DottableNode<D, N>> extends Node<D, N> {
|
||||
/**
|
||||
* Retrieves the set of dot attributes associated with the node.
|
||||
*/
|
||||
Properties nodeAttributes();
|
||||
/**
|
||||
* Retrieves the set of dot attributes associated with a given dependency.
|
||||
*/
|
||||
Properties dependencyAttributes(N to, DependencyKind dk);
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is a basic abstract class for representing a node.
|
||||
* A node is associated with a given data.
|
||||
*/
|
||||
public static abstract class Node<D> {
|
||||
public static abstract class AbstractNode<D, N extends AbstractNode<D, N>> implements Node<D, N> {
|
||||
public final D data;
|
||||
|
||||
public Node(D data) {
|
||||
public AbstractNode(D data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@ -61,26 +105,32 @@ public class GraphUtils {
|
||||
public abstract DependencyKind[] getSupportedDependencyKinds();
|
||||
|
||||
/**
|
||||
* Get all dependencies, regardless of their kind.
|
||||
* Get all dependencies of a given kind
|
||||
*/
|
||||
public abstract Iterable<? extends Node<D>> getAllDependencies();
|
||||
|
||||
/**
|
||||
* Get a name for the dependency (of given kind) linking this node to a given node
|
||||
*/
|
||||
public abstract String getDependencyName(Node<D> to, DependencyKind dk);
|
||||
public abstract Collection<? extends N> getDependenciesByKind(DependencyKind dk);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return data.toString();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <A> void accept(NodeVisitor<D, N, A> visitor, A arg) {
|
||||
visitor.visitNode((N)this, arg);
|
||||
for (DependencyKind dk : getSupportedDependencyKinds()) {
|
||||
for (N dep : new ArrayList<>(getDependenciesByKind(dk))) {
|
||||
visitor.visitDependency(dk, (N)this, dep, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class specialized Node, by adding elements that are required in order
|
||||
* to perform Tarjan computation of strongly connected components.
|
||||
*/
|
||||
public static abstract class TarjanNode<D> extends Node<D> implements Comparable<TarjanNode<D>> {
|
||||
public static abstract class TarjanNode<D, N extends TarjanNode<D, N>> extends AbstractNode<D, N>
|
||||
implements Comparable<N> {
|
||||
int index = -1;
|
||||
int lowlink;
|
||||
boolean active;
|
||||
@ -89,11 +139,9 @@ public class GraphUtils {
|
||||
super(data);
|
||||
}
|
||||
|
||||
public abstract Iterable<? extends TarjanNode<D>> getAllDependencies();
|
||||
public abstract Iterable<? extends N> getAllDependencies();
|
||||
|
||||
public abstract Iterable<? extends TarjanNode<D>> getDependenciesByKind(DependencyKind dk);
|
||||
|
||||
public int compareTo(TarjanNode<D> o) {
|
||||
public int compareTo(N o) {
|
||||
return (index < o.index) ? -1 : (index == o.index) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
@ -102,7 +150,7 @@ public class GraphUtils {
|
||||
* Tarjan's algorithm to determine strongly connected components of a
|
||||
* directed graph in linear time. Works on TarjanNode.
|
||||
*/
|
||||
public static <D, N extends TarjanNode<D>> List<? extends List<? extends N>> tarjan(Iterable<? extends N> nodes) {
|
||||
public static <D, N extends TarjanNode<D, N>> List<? extends List<? extends N>> tarjan(Iterable<? extends N> nodes) {
|
||||
ListBuffer<List<N>> cycles = new ListBuffer<>();
|
||||
ListBuffer<N> stack = new ListBuffer<>();
|
||||
int index = 0;
|
||||
@ -114,15 +162,13 @@ public class GraphUtils {
|
||||
return cycles.toList();
|
||||
}
|
||||
|
||||
private static <D, N extends TarjanNode<D>> int tarjan(N v, int index, ListBuffer<N> stack, ListBuffer<List<N>> cycles) {
|
||||
private static <D, N extends TarjanNode<D, N>> int tarjan(N v, int index, ListBuffer<N> stack, ListBuffer<List<N>> cycles) {
|
||||
v.index = index;
|
||||
v.lowlink = index;
|
||||
index++;
|
||||
stack.prepend(v);
|
||||
v.active = true;
|
||||
for (TarjanNode<D> nd: v.getAllDependencies()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
N n = (N)nd;
|
||||
for (N n: v.getAllDependencies()) {
|
||||
if (n.index == -1) {
|
||||
tarjan(n, index, stack, cycles);
|
||||
v.lowlink = Math.min(v.lowlink, n.lowlink);
|
||||
@ -149,24 +195,45 @@ public class GraphUtils {
|
||||
* and {@code Node.printDependency} to display edge labels. The resulting
|
||||
* representation is also customizable with a graph name and a header.
|
||||
*/
|
||||
public static <D> String toDot(Iterable<? extends TarjanNode<D>> nodes, String name, String header) {
|
||||
public static <D, N extends DottableNode<D, N>> String toDot(Collection<? extends N> nodes, String name, String header) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append(String.format("digraph %s {\n", name));
|
||||
buf.append(String.format("label = \"%s\";\n", header));
|
||||
//dump nodes
|
||||
for (TarjanNode<D> n : nodes) {
|
||||
buf.append(String.format("%s [label = \"%s\"];\n", n.hashCode(), n.toString()));
|
||||
}
|
||||
//dump arcs
|
||||
for (TarjanNode<D> from : nodes) {
|
||||
for (DependencyKind dk : from.getSupportedDependencyKinds()) {
|
||||
for (TarjanNode<D> to : from.getDependenciesByKind(dk)) {
|
||||
buf.append(String.format("%s -> %s [label = \" %s \" style = %s ];\n",
|
||||
from.hashCode(), to.hashCode(), from.getDependencyName(to, dk), dk.getDotStyle()));
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.append(String.format("label = %s;\n", DotVisitor.wrap(header)));
|
||||
DotVisitor<D, N> dotVisitor = new DotVisitor<>();
|
||||
dotVisitor.visit(nodes, buf);
|
||||
buf.append("}\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* This visitor is used to dump the contents of a set of nodes of type {@link DottableNode}
|
||||
* onto a string builder.
|
||||
*/
|
||||
public static class DotVisitor<D, N extends DottableNode<D, N>> extends NodeVisitor<D, N, StringBuilder> {
|
||||
|
||||
@Override
|
||||
public void visitDependency(DependencyKind dk, N from, N to, StringBuilder buf) {
|
||||
buf.append(String.format("%s -> %s", from.hashCode(), to.hashCode()));
|
||||
buf.append(formatProperties(from.dependencyAttributes(to, dk)));
|
||||
buf.append('\n');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNode(N node, StringBuilder buf) {
|
||||
buf.append(String.format("%s ", node.hashCode()));
|
||||
buf.append(formatProperties(node.nodeAttributes()));
|
||||
buf.append('\n');
|
||||
}
|
||||
|
||||
protected String formatProperties(Properties p) {
|
||||
return p.toString().replaceAll(",", " ")
|
||||
.replaceAll("\\{", "[")
|
||||
.replaceAll("\\}", "]");
|
||||
}
|
||||
|
||||
protected static String wrap(String s) {
|
||||
String res = "\"" + s + "\"";
|
||||
return res.replaceAll("\n", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -575,6 +575,9 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask, Messages {
|
||||
}
|
||||
reportError("err.ioerror", className, msg);
|
||||
result = EXIT_ERROR;
|
||||
} catch (OutOfMemoryError e) {
|
||||
reportError("err.nomem");
|
||||
result = EXIT_ERROR;
|
||||
} catch (Throwable t) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
|
@ -18,6 +18,7 @@ err.unknown.option=unknown option: {0}
|
||||
err.no.SourceFile.attribute=no SourceFile attribute
|
||||
err.source.file.not.found=source file not found
|
||||
err.bad.innerclasses.attribute=bad InnerClasses attribute for {0}
|
||||
err.nomem=Insufficient memory. To increase memory use -J-Xmx option.
|
||||
|
||||
main.usage.summary=\
|
||||
Usage: {0} <options> <classes>\n\
|
||||
|
111
langtools/test/tools/all/RunCodingRules.java
Normal file
111
langtools/test/tools/all/RunCodingRules.java
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8043643
|
||||
* @summary Run the langtools coding rules over the langtools source code.
|
||||
*/
|
||||
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.DiagnosticListener;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
import com.sun.tools.javac.util.Assert;
|
||||
|
||||
public class RunCodingRules {
|
||||
public static void main(String... args) throws Exception {
|
||||
new RunCodingRules().run();
|
||||
}
|
||||
|
||||
public void run() throws Exception {
|
||||
File testSrc = new File(System.getProperty("test.src", "."));
|
||||
File targetDir = new File(System.getProperty("test.classes", "."));
|
||||
File sourceDir = null;
|
||||
File crulesDir = null;
|
||||
for (File d = testSrc; d != null; d = d.getParentFile()) {
|
||||
if (new File(d, "TEST.ROOT").exists()) {
|
||||
d = d.getParentFile();
|
||||
File f = new File(d, "src/share/classes");
|
||||
if (f.exists()) {
|
||||
sourceDir = f;
|
||||
f = new File(d, "make/tools");
|
||||
if (f.exists())
|
||||
crulesDir = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceDir == null || crulesDir == null) {
|
||||
System.err.println("Warning: sources not found, test skipped.");
|
||||
return ;
|
||||
}
|
||||
|
||||
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
|
||||
StandardJavaFileManager fm = javaCompiler.getStandardFileManager(null, null, null);
|
||||
DiagnosticListener<JavaFileObject> noErrors = diagnostic -> {
|
||||
Assert.check(diagnostic.getKind() != Diagnostic.Kind.ERROR, diagnostic.toString());
|
||||
};
|
||||
|
||||
List<File> crulesFiles = Files.walk(crulesDir.toPath())
|
||||
.map(entry -> entry.toFile())
|
||||
.filter(entry -> entry.getName().endsWith(".java"))
|
||||
.filter(entry -> entry.getParentFile().getName().equals("crules"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
File crulesTarget = new File(targetDir, "crules");
|
||||
crulesTarget.mkdirs();
|
||||
List<String> crulesOptions = Arrays.asList("-d", crulesTarget.getAbsolutePath());
|
||||
javaCompiler.getTask(null, fm, noErrors, crulesOptions, null,
|
||||
fm.getJavaFileObjectsFromFiles(crulesFiles)).call();
|
||||
File registration = new File(crulesTarget, "META-INF/services/com.sun.source.util.Plugin");
|
||||
registration.getParentFile().mkdirs();
|
||||
try (Writer metaInfServices = new FileWriter(registration)) {
|
||||
metaInfServices.write("crules.CodingRulesAnalyzerPlugin\n");
|
||||
}
|
||||
|
||||
List<File> sources = Files.walk(sourceDir.toPath())
|
||||
.map(entry -> entry.toFile())
|
||||
.filter(entry -> entry.getName().endsWith(".java"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
File sourceTarget = new File(targetDir, "classes");
|
||||
sourceTarget.mkdirs();
|
||||
String processorPath = crulesTarget.getAbsolutePath() + File.pathSeparator +
|
||||
crulesDir.getAbsolutePath();
|
||||
List<String> options = Arrays.asList("-d", sourceTarget.getAbsolutePath(),
|
||||
"-processorpath", processorPath, "-Xplugin:coding_rules");
|
||||
javaCompiler.getTask(null, fm, noErrors, options, null,
|
||||
fm.getJavaFileObjectsFromFiles(sources)).call();
|
||||
}
|
||||
}
|
@ -36,14 +36,26 @@ import java.util.stream.Stream;
|
||||
import static java.lang.String.format;
|
||||
import static java.util.stream.Collectors.*;
|
||||
|
||||
|
||||
/**
|
||||
* Base class for LocalVariableTable and LocalVariableTypeTable attributes tests.
|
||||
* To add tests cases you should extend this class.
|
||||
* Then implement {@link #getVariableTables} to get LocalVariableTable or LocalVariableTypeTable attribute.
|
||||
* Then add method with local variables.
|
||||
* Finally, annotate method with information about expected variables and their types
|
||||
* by several {@link LocalVariableTestBase.ExpectedLocals} annotations.
|
||||
* To run test invoke {@link #test()} method.
|
||||
* If there are variables with the same name, set different scopes for them.
|
||||
*
|
||||
* @see #test()
|
||||
*/
|
||||
public abstract class LocalVariableTestBase extends TestBase {
|
||||
public static final int DEFAULT_SCOPE = 0;
|
||||
private final ClassFile classFile;
|
||||
private final Class<?> clazz;
|
||||
|
||||
protected abstract List<VariableTable> getVariableTables(Code_attribute codeAttribute);
|
||||
|
||||
/**
|
||||
* @param clazz class to test. Must contains annotated methods with expected results.
|
||||
*/
|
||||
public LocalVariableTestBase(Class<?> clazz) {
|
||||
this.clazz = clazz;
|
||||
try {
|
||||
@ -53,8 +65,12 @@ public abstract class LocalVariableTestBase extends TestBase {
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract List<VariableTable> getVariableTables(Code_attribute codeAttribute);
|
||||
|
||||
//info in the LocalVariableTable attribute is compared against expected info stored in annotations
|
||||
/**
|
||||
* Finds expected variables with their type in VariableTable.
|
||||
* Also does consistency checks, like variables from the same scope must point to different indexes.
|
||||
*/
|
||||
public void test() throws IOException {
|
||||
List<java.lang.reflect.Method> testMethods = Stream.of(clazz.getDeclaredMethods())
|
||||
.filter(m -> m.getAnnotationsByType(ExpectedLocals.class).length > 0)
|
||||
@ -198,7 +214,10 @@ public abstract class LocalVariableTestBase extends TestBase {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* LocalVariableTable and LocalVariableTypeTable are similar.
|
||||
* VariableTable interface is introduced to test this attributes in the same way without code duplication.
|
||||
*/
|
||||
interface VariableTable {
|
||||
|
||||
int localVariableTableLength();
|
||||
@ -231,14 +250,23 @@ public abstract class LocalVariableTestBase extends TestBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to store expected results in sources
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(Container.class)
|
||||
@interface ExpectedLocals {
|
||||
/**
|
||||
* @return name of a local variable
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* @return type of local variable in the internal format.
|
||||
*/
|
||||
String type();
|
||||
|
||||
//variables from different scopes can share local variable table index and/or name.
|
||||
//variables from different scopes can share the local variable table index and/or name.
|
||||
int scope() default DEFAULT_SCOPE;
|
||||
}
|
||||
|
||||
|
@ -30,18 +30,41 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
/**
|
||||
* Base class for Source file attribute tests. Checks expected file name for specified classes in the SourceFile attribute.
|
||||
* To add new tests you should extend the SourceFileTestBase class and invoke {@link #test} for static sources
|
||||
* or {@link #compileAndTest} for generated sources. For more information see corresponding methods.
|
||||
*
|
||||
* @see #test
|
||||
* @see #compileAndTest
|
||||
*/
|
||||
public class SourceFileTestBase extends TestBase {
|
||||
|
||||
/**
|
||||
* Checks expected fileName for the specified class in the SourceFile attribute.
|
||||
*
|
||||
* @param classToTest class to check its SourceFile attribute
|
||||
* @param fileName expected name of the file from which the test file is compiled.
|
||||
*/
|
||||
protected void test(Class<?> classToTest, String fileName) throws Exception {
|
||||
assertAttributePresent(ClassFile.read(getClassFile(classToTest)), fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks expected fileName for the specified class in the SourceFile attribute.
|
||||
*
|
||||
* @param classToTest class name to check its SourceFile attribute
|
||||
* @param fileName expected name of the file from which the test file is compiled.
|
||||
*/
|
||||
protected void test(String classToTest, String fileName) throws Exception {
|
||||
assertAttributePresent(ClassFile.read(getClassFile(classToTest + ".class")), fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile sourceCode and for all "classesToTest" checks SourceFile attribute.
|
||||
* Compiles sourceCode and for each specified class name checks the SourceFile attribute.
|
||||
* The file name is extracted from source code.
|
||||
*
|
||||
* @param sourceCode source code to compile
|
||||
* @param classesToTest class names to check their SourceFile attribute.
|
||||
*/
|
||||
protected void compileAndTest(String sourceCode, String... classesToTest) throws Exception {
|
||||
|
||||
|
@ -23,11 +23,14 @@
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import javax.tools.*;
|
||||
import javax.tools.DiagnosticCollector;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.System.lineSeparator;
|
||||
@ -36,6 +39,11 @@ import static java.util.Collections.emptyList;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
* Base class for class file attribute tests.
|
||||
* Contains methods for compiling generated sources in memory,
|
||||
* for reading files from disk and a lot of assert* methods.
|
||||
*/
|
||||
public class TestBase {
|
||||
|
||||
public static final String LINE_SEPARATOR = lineSeparator();
|
||||
@ -66,35 +74,48 @@ public class TestBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles sources in memory.
|
||||
*
|
||||
* @param sources to compile.
|
||||
* @return memory file manager which contains class files and class loader.
|
||||
*/
|
||||
public InMemoryFileManager compile(String... sources)
|
||||
throws IOException, CompilationException {
|
||||
return compile(emptyList(), sources);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options - compiler options
|
||||
* @param sources
|
||||
* Compiles sources in memory.
|
||||
*
|
||||
* @param options compiler options.
|
||||
* @param sources sources to compile.
|
||||
* @return map where key is className, value is corresponding ClassFile.
|
||||
* @throws IOException
|
||||
*/
|
||||
public InMemoryFileManager compile(List<String> options, String...sources)
|
||||
public InMemoryFileManager compile(List<String> options, String... sources)
|
||||
throws IOException, CompilationException {
|
||||
return compile(options, ToolBox.JavaSource::new, asList(sources));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles sources in memory.
|
||||
*
|
||||
* @param sources sources[i][0] - name of file, sources[i][1] - sources.
|
||||
* @return map where key is className, value is corresponding ClassFile.
|
||||
*/
|
||||
public InMemoryFileManager compile(String[]... sources) throws IOException,
|
||||
CompilationException {
|
||||
return compile(emptyList(), sources);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options - compiler options
|
||||
* @param sources - sources[i][0] - name of file, sources[i][1] - sources
|
||||
* Compiles sources in memory.
|
||||
*
|
||||
* @param options compiler options
|
||||
* @param sources sources[i][0] - name of file, sources[i][1] - sources.
|
||||
* @return map where key is className, value is corresponding ClassFile.
|
||||
* @throws IOException
|
||||
* @throws CompilationException
|
||||
*/
|
||||
public InMemoryFileManager compile(List<String> options, String[]...sources)
|
||||
public InMemoryFileManager compile(List<String> options, String[]... sources)
|
||||
throws IOException, CompilationException {
|
||||
return compile(options, src -> new ToolBox.JavaSource(src[0], src[1]), asList(sources));
|
||||
}
|
||||
@ -142,11 +163,22 @@ public class TestBase {
|
||||
return getClassFile(clazz.getName().replace(".", "/") + ".class");
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints message to standard error. New lines are converted to system dependent NL.
|
||||
*
|
||||
* @param message string to print.
|
||||
*/
|
||||
public void echo(String message) {
|
||||
System.err.println(message.replace("\n", LINE_SEPARATOR));
|
||||
}
|
||||
|
||||
public void printf(String template, Object...args) {
|
||||
/**
|
||||
* Substitutes args in template and prints result to standard error. New lines are converted to system dependent NL.
|
||||
*
|
||||
* @param template template in standard String.format(...) format.
|
||||
* @param args arguments to substitute in template.
|
||||
*/
|
||||
public void printf(String template, Object... args) {
|
||||
System.err.printf(template, Stream.of(args)
|
||||
.map(Objects::toString)
|
||||
.map(m -> m.replace("\n", LINE_SEPARATOR))
|
||||
|
71
langtools/test/tools/javac/lambda/8051958/T8051958.java
Normal file
71
langtools/test/tools/javac/lambda/8051958/T8051958.java
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8051958
|
||||
* @summary Cannot assign a value to final variable in lambda
|
||||
* @compile T8051958.java
|
||||
*/
|
||||
|
||||
class T8051958 {
|
||||
Runnable inst_r = ()-> {
|
||||
final int x;
|
||||
x = 1;
|
||||
};
|
||||
|
||||
Runnable static_r = ()-> {
|
||||
final int x;
|
||||
x = 1;
|
||||
};
|
||||
|
||||
{
|
||||
Runnable inst_r = ()-> {
|
||||
final int x;
|
||||
x = 1;
|
||||
};
|
||||
}
|
||||
|
||||
static {
|
||||
Runnable static_r = ()-> {
|
||||
final int x;
|
||||
x = 1;
|
||||
};
|
||||
}
|
||||
|
||||
void instTest() {
|
||||
Runnable static_r = ()-> {
|
||||
final int x;
|
||||
x = 1;
|
||||
};
|
||||
}
|
||||
|
||||
static void staticTest() {
|
||||
Runnable static_r = ()-> {
|
||||
final int x;
|
||||
x = 1;
|
||||
};
|
||||
}
|
||||
}
|
169
langtools/test/tools/javap/BadAttributeLength.java
Normal file
169
langtools/test/tools/javap/BadAttributeLength.java
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8047072
|
||||
* @summary javap OOM on fuzzed classfile
|
||||
* @run main BadAttributeLength
|
||||
*/
|
||||
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class BadAttributeLength {
|
||||
|
||||
public static String source = "public class Test {\n" +
|
||||
" public static void main(String[] args) {}\n" +
|
||||
"}";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
final File sourceFile = new File("Test.java");
|
||||
if (sourceFile.exists()) {
|
||||
if (!sourceFile.delete()) {
|
||||
throw new IOException("Can't override the Test.java file. " +
|
||||
"Check permissions.");
|
||||
}
|
||||
}
|
||||
try (FileWriter fw = new FileWriter(sourceFile)) {
|
||||
fw.write(source);
|
||||
}
|
||||
|
||||
final String[] javacOpts = {"Test.java"};
|
||||
|
||||
if (com.sun.tools.javac.Main.compile(javacOpts) != 0) {
|
||||
throw new Exception("Can't compile embedded test.");
|
||||
}
|
||||
|
||||
RandomAccessFile raf = new RandomAccessFile("Test.class", "rw");
|
||||
long attPos = getFirstAttributePos(raf);
|
||||
if (attPos < 0) {
|
||||
throw new Exception("The class file contains no attributes at all.");
|
||||
}
|
||||
raf.seek(attPos + 2); // Jump to the attribute length
|
||||
raf.writeInt(Integer.MAX_VALUE - 1);
|
||||
raf.close();
|
||||
|
||||
String[] opts = { "-v", "Test.class" };
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pout = new PrintWriter(sw);
|
||||
|
||||
com.sun.tools.javap.Main.run(opts, pout);
|
||||
pout.flush();
|
||||
|
||||
if (sw.getBuffer().indexOf("OutOfMemoryError") != -1) {
|
||||
throw new Exception("javap exited with OutOfMemoryError " +
|
||||
"instead of giving the proper error message.");
|
||||
}
|
||||
}
|
||||
|
||||
private static long getFirstAttributePos(RandomAccessFile cfile) throws Exception {
|
||||
cfile.seek(0);
|
||||
int v1, v2;
|
||||
v1 = cfile.readInt();
|
||||
// System.out.println("Magic: " + String.format("%X", v1));
|
||||
|
||||
v1 = cfile.readUnsignedShort();
|
||||
v2 = cfile.readUnsignedShort();
|
||||
// System.out.println("Version: " + String.format("%d.%d", v1, v2));
|
||||
|
||||
v1 = cfile.readUnsignedShort();
|
||||
// System.out.println("CPool size: " + v1);
|
||||
// Exhaust the constant pool
|
||||
for (; v1 > 1; v1--) {
|
||||
// System.out.print(".");
|
||||
byte tag = cfile.readByte();
|
||||
switch (tag) {
|
||||
case 7 : // Class
|
||||
case 8 : // String
|
||||
// Data is 2 bytes long
|
||||
cfile.skipBytes(2);
|
||||
break;
|
||||
case 3 : // Integer
|
||||
case 4 : // Float
|
||||
case 9 : // FieldRef
|
||||
case 10 : // MethodRef
|
||||
case 11 : // InterfaceMethodRef
|
||||
case 12 : // Name and Type
|
||||
// Data is 4 bytes long
|
||||
cfile.skipBytes(4);
|
||||
break;
|
||||
case 5 : // Long
|
||||
case 6 : // Double
|
||||
// Data is 8 bytes long
|
||||
cfile.skipBytes(8);
|
||||
break;
|
||||
case 1 : // Utf8
|
||||
v2 = cfile.readUnsignedShort(); // Read buffer size
|
||||
cfile.skipBytes(v2); // Skip buffer
|
||||
break;
|
||||
default :
|
||||
throw new Exception("Unexpected tag in CPool: [" + tag + "] at "
|
||||
+ Long.toHexString(cfile.getFilePointer()));
|
||||
}
|
||||
}
|
||||
// System.out.println();
|
||||
|
||||
cfile.skipBytes(6); // Access flags, this_class and super_class
|
||||
v1 = cfile.readUnsignedShort(); // Number of interfaces
|
||||
// System.out.println("Interfaces: " + v1);
|
||||
cfile.skipBytes(3 * v1); // Each interface_info record is 3 bytes long
|
||||
v1 = cfile.readUnsignedShort(); // Number of fields
|
||||
// System.out.println("Fields: " + v1);
|
||||
// Exhaust the fields table
|
||||
for (; v1 > 0; v1--) {
|
||||
// System.out.print(".");
|
||||
cfile.skipBytes(6); // Skip access_flags, name_index and descriptor_index
|
||||
v2 = cfile.readUnsignedShort(); // Field attributes count
|
||||
if (v2 > 0) {
|
||||
// This field has some attributes - suits our needs
|
||||
// System.out.println();
|
||||
return cfile.getFilePointer();
|
||||
}
|
||||
}
|
||||
// System.out.println();
|
||||
v1 = cfile.readUnsignedShort(); // Number of methods
|
||||
// System.out.println("Methods: " + v1);
|
||||
// Exhaust the methods table
|
||||
for (; v1 > 0; v1--) {
|
||||
// System.out.print(".");
|
||||
cfile.skipBytes(6); // Skip access_flags, name_index and descriptor_index
|
||||
v2 = cfile.readUnsignedShort(); // Method attributes count
|
||||
if (v2 > 0) {
|
||||
// This method got attributes - Ok with us,
|
||||
// return position of the first one
|
||||
// System.out.println();
|
||||
return cfile.getFilePointer();
|
||||
}
|
||||
}
|
||||
// System.out.println();
|
||||
// Class attributes section
|
||||
v1 = cfile.readUnsignedShort(); // Counts of attributes in class
|
||||
if (v1 > 0) {
|
||||
// Class has some attributes, return position of the first one
|
||||
return cfile.getFilePointer();
|
||||
}
|
||||
// Bummer! No attributes in the entire class file. Not fair!
|
||||
return -1L;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user