blob: d377ca3f301f374605db01f660f8b371180b4aef [file] [log] [blame]
/*
* 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.api.debugger.jpda;
import com.sun.source.tree.ArrayAccessTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.ParenthesizedTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.util.TreePath;
import org.netbeans.api.java.source.support.ErrorAwareTreeScanner;
import com.sun.source.util.Trees;
import java.util.List;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.platform.JavaPlatformManager;
import org.netbeans.api.java.source.ClasspathInfo;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.JavaSource.Phase;
import org.netbeans.api.java.source.Task;
import org.netbeans.junit.NbTestCase;
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
/**
* Test that all JDI calls are wrapped.
*
* @author Martin Entlicher
*/
public class JDIWrappersTest extends NbTestCase {
public JDIWrappersTest (String s) {
super (s);
}
public void testWrappers() throws Exception {
java.io.File srcFile = new java.io.File("src");
System.out.println("SRC = "+srcFile.getCanonicalPath());
System.out.println("WorkDir = "+getWorkDirPath());
//System.out.println("Java CP = "+System.getProperty("java.class.path"));
//System.out.println("Java Library CP = "+System.getProperty("java.library.path"));
FileObject src = FileUtil.toFileObject(getWorkDir());
while (src != null && src.getFileObject("src") == null) {
src = src.getParent();
}
assertNotNull(src);
src = src.getFileObject("src");
assertNotNull(src);
if (!testWrappersRecursively(src, src)) {
fail("Some JDI invocations were found.");
}
}
private boolean isIgnored(FileObject root, FileObject c) {
String relPath = FileUtil.getRelativePath(root, c);
if (relPath.startsWith("org/netbeans/modules/debugger/jpda/jdi")) {
return true;
}
if (relPath.equals("org/netbeans/modules/debugger/jpda/expr/EvaluatorVisitor.java")) {
return true;
}
return false;
}
private boolean testWrappersRecursively(FileObject root, FileObject fo) throws Exception {
boolean status = true;
FileObject[] ch = fo.getChildren();
for (FileObject c : ch) {
if (c.isData() && !isIgnored(root, c)) {
status = testWrappers(c) && status;
} else {
status = testWrappersRecursively(root, c) && status;
}
}
return status;
}
private boolean testWrappers(FileObject fo) throws Exception {
System.out.println("testWrappers("+fo+")");
ClassPath bootPath = ClassPath.getClassPath(fo, ClassPath.BOOT);
if (bootPath == null) {
bootPath = JavaPlatformManager.getDefault().getDefaultPlatform().getBootstrapLibraries();
}
ClassPath srcPath = ClassPath.getClassPath(fo, ClassPath.SOURCE);
ClassPath compilePath = ClassPathSupport.createClassPath(System.getProperty("java.class.path"));
ClasspathInfo cpi = ClasspathInfo.create(bootPath, compilePath, srcPath);
JavaSource source = JavaSource.create(cpi, fo);//forFileObject(fo);
if (source == null) {
return true;
}
final boolean[] successPtr = new boolean[] { true };
source.runUserActionTask(new Task<CompilationController>() {
@Override
public void run(CompilationController ci) throws Exception {
if (ci.toPhase(Phase.UP_TO_DATE).compareTo(Phase.UP_TO_DATE) < 0) {
fail("Unable to resolve "+ci.getFileObject()+" to phase "+Phase.UP_TO_DATE+", current phase = "+ci.getPhase()+
"\nDiagnostics = "+ci.getDiagnostics()+
"\nFree memory = "+Runtime.getRuntime().freeMemory());
return;
}
List<? extends TypeElement> topElements = ci.getTopLevelElements();
for (TypeElement el : topElements) {
ClassTree ct = ci.getTrees().getTree(el);
JDICallsScanner scanner =
new JDICallsScanner(ci.getTrees(), ci.getTypes(), ci.getElements(), ci.getCompilationUnit());
ct.accept(scanner, null);
successPtr[0] = scanner.isFailed() && successPtr[0];
}
}
}, true);
return successPtr[0];
}
private static class JDICallsScanner extends ErrorAwareTreeScanner<Void, Object> {
private Trees trees;
private Types types;
private Elements elements;
private CompilationUnitTree cut;
private boolean failure = false;
JDICallsScanner(Trees trees, Types types, Elements elements, CompilationUnitTree cut) {
this.trees = trees;
this.types = types;
this.elements = elements;
this.cut = cut;
}
@Override
public Void visitMethodInvocation(MethodInvocationTree node, Object p) {
ExpressionTree expr = node.getMethodSelect();
if (expr.getKind() == Tree.Kind.MEMBER_SELECT) {
MemberSelectTree mst = (MemberSelectTree) expr;
//Object object = mst.getExpression().accept(this, p);
Element expEl = getElement(mst.getExpression());
String type = null;
if (expEl == null) {
TreePath expPath = TreePath.getPath(cut, mst.getExpression());
TypeMirror tm = null;
try {
tm = trees.getTypeMirror(expPath);
} catch (IllegalArgumentException iaex) {}
if (tm != null) {
type = tm.toString();
}
/*
if (mst.getExpression().getKind() == Tree.Kind.STRING_LITERAL) {
// Skip Strings
} else {
System.err.println("Null element for '"+mst.getExpression()+"' in "+node);
}
*/
} else {
TypeMirror tm = expEl.asType();
tm = adjustTypeMirror(tm);
Element typeEl = types.asElement(tm);
if (typeEl instanceof TypeElement) {
type = ((TypeElement) typeEl).getQualifiedName().toString();
} else {
// Get it from the tree:
TreePath expPath = TreePath.getPath(cut, mst.getExpression());
tm = null;
try {
tm = trees.getTypeMirror(expPath);
} catch (IllegalArgumentException iaex) {}
if (tm != null) {
type = tm.toString();
}
/*
String typeStr = expEl.asType().toString();
TypeElement typeElm = elements.getTypeElement(typeStr);
if (typeElm == null) {
System.err.println("NULL type element for '"+typeStr+"'");
} else {
String binaryType = ElementUtilities.getBinaryName(typeElm);
System.err.println("\nBinary type = "+binaryType);
}
//ElementUtilities.getBinaryName(expEl);
System.err.println(" Type element "+typeEl+" for "+expEl+" of TypeMirror: "+expEl.asType()+"\n");
type = expEl.asType().toString();
TreePath expPath = TreePath.getPath(cut, mst.getExpression());
tm = null;
try {
tm = trees.getTypeMirror(expPath);
} catch (IllegalArgumentException iaex) {}
if (tm != null) {
System.err.println(" Tree Type Mirror = '"+tm.toString()+"'");
} else {
System.err.println(" Tree Type Mirror = null. :-(");
}
*/
}
}
if (type == null) {
System.err.println("Unknown type for '"+mst.getExpression()+"' in "+node);
} else if (isJDIType(type)) {
long offset = trees.getSourcePositions().getStartPosition(cut, node);
long line = cut.getLineMap().getLineNumber(offset);
//int line = NbDocument.findLineNumber(doc, offset);
System.err.println("Method "+mst.getIdentifier().toString()+" is invoked on "+type+" in MethodInvocationTree '"+node+"' in file "+cut.getSourceFile()+", line = "+line);
failure = true;
}
}
return super.visitMethodInvocation(node, p);
}
private Element getElement(Tree tree) {
TreePath expPath = TreePath.getPath(cut, tree);
Element e = trees.getElement(expPath);
if (e == null) {
if (tree instanceof ParenthesizedTree) {
e = getElement(((ParenthesizedTree) tree).getExpression());
//if (e == null) {
// System.err.println("Have null element for "+tree);
//}
//System.err.println("\nHAVE "+e.asType().toString()+" for ParenthesizedTree "+tree);
}
else if (tree instanceof TypeCastTree) {
e = getElement(((TypeCastTree) tree).getType());
//if (e == null) {
// System.err.println("Have null element for "+tree);
//}
//System.err.println("\nHAVE "+e.asType().toString()+" for TypeCastTree "+tree);
}
else if (tree instanceof AssignmentTree) {
e = getElement(((AssignmentTree) tree).getVariable());
}
else if (tree instanceof ArrayAccessTree) {
e = getElement(((ArrayAccessTree) tree).getExpression());
if (e != null) {
TypeMirror tm = e.asType();
if (tm.getKind() == TypeKind.ARRAY) {
tm = ((ArrayType) tm).getComponentType();
e = types.asElement(tm);
}
}
//System.err.println("ArrayAccessTree = "+((ArrayAccessTree) tree).getExpression()+", element = "+getElement(((ArrayAccessTree) tree).getExpression())+", type = "+getElement(((ArrayAccessTree) tree).getExpression()).asType());
}
}
return e;
}
private TypeMirror adjustTypeMirror(TypeMirror tm) {
if (tm.getKind() == TypeKind.EXECUTABLE) {
tm = ((ExecutableType) tm).getReturnType();
tm = adjustTypeMirror(tm);
} else if (tm.getKind() == TypeKind.ARRAY) {
tm = ((ArrayType) tm).getComponentType();
tm = adjustTypeMirror(tm);
}
return tm;
}
public boolean isFailed() {
return failure;
}
private boolean isJDIType(String type) {
boolean isJDI = type.startsWith("com.sun.jdi");
if (isJDI) {
if (type.endsWith("InternalException")) {
return false;
}
} else {
//System.out.println("Not JDI type: '"+type+"'");
}
return isJDI;
}
}
}