blob: a5a883d75f4f5781ae5bf8d1b3e960b8b703d30c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.pde.api.tools.internal.model.cache;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.eclipse.pde.api.tools.internal.model.ApiMethod;
import org.eclipse.pde.api.tools.internal.model.ApiType;
import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
import org.eclipse.pde.api.tools.internal.provisional.IApiComponent;
import org.eclipse.pde.api.tools.internal.provisional.IClassFile;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiType;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.util.TraceAnnotationVisitor;
/**
* Class adapter used to create an API type structure
*/
class TypeStructureBuilder extends ClassAdapter {
ApiType fType;
IApiComponent fComponent;
IClassFile fFile;
/**
* Builds a type structure for a class file. Note that if an API
* component is not specified, then some operations on the resulting
* {@link IApiType} will not be available (navigating super types,
* member types, etc).
*
* @param cv class file visitor
* @param component originating API component or <code>null</code> if unknown
*/
TypeStructureBuilder(ClassVisitor cv, IApiComponent component, IClassFile file) {
super(cv);
fComponent = component;
fFile = file;
}
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
StringBuffer simpleSig = new StringBuffer();
simpleSig.append('L');
simpleSig.append(name);
simpleSig.append(';');
String enclosingName = null;
int index = name.lastIndexOf('$');
if (index > -1) {
enclosingName = name.substring(0, index);
}
fType = new ApiType(fComponent, name.replace('/', '.'), simpleSig.toString(), signature, access, enclosingName, fFile);
if (superName != null) {
fType.setSuperclassName(superName.replace('/', '.'));
}
if (interfaces != null && interfaces.length > 0) {
String[] names = new String[interfaces.length];
for (int i = 0; i < names.length; i++) {
names[i] = interfaces[i].replace('/', '.');
}
fType.setSuperInterfaceNames(names);
}
super.visit(version, access, name, signature, superName, interfaces);
}
public void visitInnerClass(String name, String outerName, String innerName, int access) {
super.visitInnerClass(name, outerName, innerName, access);
String currentName = name.replace('/', '.');
if (fType == null) {
// TODO: signature? generic signature?
fType = new ApiType(fComponent, currentName, null, null, access, outerName, fFile);
// this is a nested type
if (outerName == null) {
// this is a local or an anonymous type
if (innerName == null) {
this.fType.setAnonymous();
} else {
this.fType.setLocal();
}
}
} else if (outerName != null && innerName != null) {
// technically speaking innerName != null is not necessary, but this is a workaround for some
// bogus synthetic types created by another compiler
String currentOuterName = outerName.replace('/', '.');
if (currentOuterName.equals(fType.getName())) {
// this is a real type member defined in the descriptor (not just a reference to a type member)
fType.addMemberType(currentName, access);
} else if (currentName.equals(fType.getName())) {
fType.setModifiers(access);
}
}
}
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
fType.addField(name, desc, signature, access, value);
return null;
}
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
String[] names = null;
if (exceptions != null && exceptions.length > 0) {
names = new String[exceptions.length];
for (int i = 0; i < names.length; i++) {
names[i] = exceptions[i].replace('/', '.');
}
}
final ApiMethod method = fType.addMethod(name, desc, signature, access, names);
return new MethodAdapter(super.visitMethod(access, name, desc, signature, exceptions)) {
public AnnotationVisitor visitAnnotationDefault() {
return new TraceAnnotationVisitor() {
public void visitEnd() {
super.visitEnd();
StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(stringWriter);
print(writer);
writer.flush();
writer.close();
String def = String.valueOf(stringWriter.getBuffer());
method.setDefaultValue(def);
}
};
}
};
}
/**
* Builds a type structure with the given .class file bytes in the specified
* API component.
*
* @param bytes class file bytes
* @param component originating API component
* @param file associated class file
* @return
*/
public static IApiType buildTypeStructure(byte[] bytes, IApiComponent component, IClassFile file) {
TypeStructureBuilder visitor = new TypeStructureBuilder(new ClassNode(), component, file);
try {
ClassReader classReader = new ClassReader(bytes);
classReader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
} catch (ArrayIndexOutOfBoundsException e) {
ApiPlugin.log(e);
}
return visitor.fType;
}
}