
org.kohsuke.accmod.impl.Checker Maven / Gradle / Ivy
/*
* The MIT License
*
* Copyright (c) 2010, Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.accmod.impl;
import org.kohsuke.accmod.AccessRestriction;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.impl.Restrictions.Parser;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import static org.objectweb.asm.ClassReader.SKIP_FRAMES;
/**
* Performs check.
*
* @author Kohsuke Kawaguchi
*/
public class Checker {
/**
* Where dependencies are loaded. We don't actually load classes,
* but this is used to search for class files and indexed restrictions.
*/
public final ClassLoader dependencies;
/**
* Where errors and warnings are sent.
*/
private final ErrorListener errorListener;
/**
* Restrictions found from dependencies.
*
* The string is either:
*
* - internal name of a type
*
- internal name of a type + '.' + field name
*
- internal name of a type + '.' + method name + method descriptor
*
*/
private final Map restrictions = new HashMap();
private final AccessRestrictionFactory factory;
public Checker(ClassLoader dependencies, ErrorListener errorListener) throws IOException {
this.dependencies = dependencies;
this.errorListener = errorListener;
this.factory = new AccessRestrictionFactory(dependencies);
// load access restrictions
loadAccessRestrictions();
}
public ErrorListener getErrorListener() {
return errorListener;
}
/**
* Checks a single class file or a directory full of class files (recursively.)
*/
public void check(File f) throws IOException {
if (f.isDirectory()) {
for (File c : f.listFiles())
check(c);
return;
}
if (f.getPath().endsWith(".class"))
checkClass(f);
}
/**
* Loads all the access restrictions defined in our dependencies.
*/
private void loadAccessRestrictions() throws IOException {
final Enumeration res = dependencies.getResources("META-INF/annotations/"+Restricted.class.getName());
while (res.hasMoreElements()) {
URL url = res.nextElement();
loadRestrictions(url.openStream(),false);
}
}
/**
* Loads an additional restriction from the specified "META-INF/annotations/org.kohsuke.accmod.Restricted" file.
*
* @param isInTheInspectedModule
* This value shows up in {@link RestrictedElement#isInTheInspectedModule()}.
* @param stream
*/
public void loadRestrictions(InputStream stream, final boolean isInTheInspectedModule) throws IOException {
if (stream==null) return;
BufferedReader r = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
String className;
while ((className=r.readLine())!=null) {
InputStream is = dependencies.getResourceAsStream(className.replace('.','/') + ".class");
if (is==null) {
errorListener.onWarning(null,null,"Failed to find class file for "+ className);
continue;
}
try {
new ClassReader(is).accept(new ClassVisitor(Opcodes.ASM5) {
private String className;
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.className = name;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return onAnnotationFor(className,desc);
}
@Override
public FieldVisitor visitField(int access, final String name, String desc, String signature, Object value) {
return new FieldVisitor(Opcodes.ASM5) {
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return onAnnotationFor(className+'.'+name,desc);
}
};
}
@Override
public MethodVisitor visitMethod(int access, final String methodName, final String methodDesc, String signature, String[] exceptions) {
return new MethodVisitor(Opcodes.ASM5) {
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return onAnnotationFor(className+'.'+methodName+methodDesc,desc);
}
};
}
/**
* Parse {@link Restricted} annotation on some annotated element.
*/
private AnnotationVisitor onAnnotationFor(final String keyName, String desc) {
if (RESTRICTED_DESCRIPTOR.equals(desc)) {
RestrictedElement target = new RestrictedElement() {
public boolean isInTheInspectedModule() {
return isInTheInspectedModule;
}
public String toString() { return keyName; }
};
return new Parser(target) {
@Override
public void visitEnd() {
try {
restrictions.put(keyName,build(factory));
} catch (ClassNotFoundException e) {
failure(e);
} catch (InstantiationException e) {
failure(e);
} catch (IllegalAccessException e) {
failure(e);
}
}
/**
* Fails to load a {@link AccessRestriction} instance.
*/
private void failure(Exception e) {
errorListener.onError(e,null,"Failed to load restrictions");
}
};
}
return null;
}
}, ClassReader.SKIP_CODE);
} finally {
is.close();
}
}
}
/**
* Inspects a class for the restriction violations.
*/
public void checkClass(File clazz) throws IOException {
FileInputStream in = new FileInputStream(clazz);
try {
ClassReader cr = new ClassReader(in);
cr.accept(new ClassVisitor(Opcodes.ASM5) {
private String className;
private String methodName,methodDesc;
private int line;
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.className = name;
if (superName!=null)
getRestrictions(superName).usedAsSuperType(currentLocation,errorListener);
if (interfaces!=null) {
for (String intf : interfaces)
getRestrictions(intf).usedAsInterface(currentLocation,errorListener);
}
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
this.methodName = name;
this.methodDesc = desc;
return new MethodVisitor(Opcodes.ASM5) {
@Override
public void visitLineNumber(int _line, Label start) {
line = _line;
}
public void visitTypeInsn(int opcode, String type) {
switch (opcode) {
case Opcodes.NEW:
getRestrictions(type).instantiated(currentLocation,errorListener);
}
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
getRestrictions(owner+'.'+name+desc).invoked(currentLocation,errorListener);
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
Restrictions r = getRestrictions(owner + '.' + name);
switch (opcode) {
case Opcodes.GETSTATIC:
case Opcodes.GETFIELD:
r.read(currentLocation,errorListener);
break;
case Opcodes.PUTSTATIC:
case Opcodes.PUTFIELD:
r.written(currentLocation,errorListener);
break;
}
super.visitFieldInsn(opcode, owner, name, desc);
}
};
}
/**
* Constant that represents the current location.
*/
private final Location currentLocation = new Location() {
public String getClassName() {
return className.replace('/','.');
}
public String getMethodName() {
return methodName;
}
public String getMethodDescriptor() {
return methodDesc;
}
public int getLineNumber() {
return line;
}
public String toString() {
return className+':'+line;
}
public ClassLoader getDependencyClassLoader() {
return dependencies;
}
public boolean isInTheSameModuleAs(RestrictedElement e) {
// TODO
throw new UnsupportedOperationException();
}
};
}, SKIP_FRAMES);
} finally {
in.close();
}
}
private Restrictions getRestrictions(String keyName) {
Restrictions r = restrictions.get(keyName);
if (r==null) return Restrictions.NONE;
return r;
}
private static final String RESTRICTED_DESCRIPTOR = Type.getDescriptor(Restricted.class);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy