blob: 8409fc0dccc40410b2e27cf4b9cdfdfa9fcc5358 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2017 Google, Inc 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:
* Stefan Xenos (Google) - Initial implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core.nd.java.model;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.classfmt.BinaryTypeFormatter;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.classfmt.ElementValuePairInfo;
import org.eclipse.jdt.internal.compiler.codegen.AnnotationTargetTypeConstants;
import org.eclipse.jdt.internal.compiler.env.ClassSignature;
import org.eclipse.jdt.internal.compiler.env.EnumConstantSignature;
import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
import org.eclipse.jdt.internal.compiler.env.IBinaryElementValuePair;
import org.eclipse.jdt.internal.compiler.env.IBinaryField;
import org.eclipse.jdt.internal.compiler.env.IBinaryMethod;
import org.eclipse.jdt.internal.compiler.env.IBinaryNestedType;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation;
import org.eclipse.jdt.internal.compiler.env.IDependent;
import org.eclipse.jdt.internal.compiler.env.ITypeAnnotationWalker;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.ExternalAnnotationStatus;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.core.JarPackageFragmentRoot;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jdt.internal.core.nd.IReader;
import org.eclipse.jdt.internal.core.nd.db.IString;
import org.eclipse.jdt.internal.core.nd.java.JavaNames;
import org.eclipse.jdt.internal.core.nd.java.NdAnnotation;
import org.eclipse.jdt.internal.core.nd.java.NdAnnotationValuePair;
import org.eclipse.jdt.internal.core.nd.java.NdConstant;
import org.eclipse.jdt.internal.core.nd.java.NdConstantAnnotation;
import org.eclipse.jdt.internal.core.nd.java.NdConstantArray;
import org.eclipse.jdt.internal.core.nd.java.NdConstantClass;
import org.eclipse.jdt.internal.core.nd.java.NdConstantEnum;
import org.eclipse.jdt.internal.core.nd.java.NdMethod;
import org.eclipse.jdt.internal.core.nd.java.NdMethodException;
import org.eclipse.jdt.internal.core.nd.java.NdMethodParameter;
import org.eclipse.jdt.internal.core.nd.java.NdResourceFile;
import org.eclipse.jdt.internal.core.nd.java.NdType;
import org.eclipse.jdt.internal.core.nd.java.NdTypeAnnotation;
import org.eclipse.jdt.internal.core.nd.java.NdTypeId;
import org.eclipse.jdt.internal.core.nd.java.NdTypeInterface;
import org.eclipse.jdt.internal.core.nd.java.NdTypeParameter;
import org.eclipse.jdt.internal.core.nd.java.NdTypeSignature;
import org.eclipse.jdt.internal.core.nd.java.NdVariable;
import org.eclipse.jdt.internal.core.nd.java.TypeRef;
import org.eclipse.jdt.internal.core.util.CharArrayBuffer;
/**
* Implementation of {@link IBinaryType} that reads all its content from the index
*/
public class IndexBinaryType implements IBinaryType {
private final TypeRef typeRef;
private boolean simpleAttributesInitialized;
private char[] enclosingMethod;
private char[] enclosingType;
private char[] fileName;
private char[] superclassName;
private int modifiers;
private boolean isAnonymous;
private boolean isLocal;
private boolean isMember;
private long tagBits;
private char[] binaryTypeName;
private static final IBinaryAnnotation[] NO_ANNOTATIONS = new IBinaryAnnotation[0];
private static final int[] NO_PATH = new int[0];
public IndexBinaryType(TypeRef type, char[] indexPath) {
this.typeRef = type;
this.fileName = indexPath;
}
public boolean exists() {
return this.typeRef.get() != null;
}
@Override
public int getModifiers() {
initSimpleAttributes();
return this.modifiers;
}
@Override
public boolean isBinaryType() {
return true;
}
@Override
public char[] getFileName() {
return this.fileName;
}
@Override
public IBinaryAnnotation[] getAnnotations() {
try (IReader rl = this.typeRef.lock()) {
NdType type = this.typeRef.get();
if (type != null) {
return toAnnotationArray(this.typeRef.get().getAnnotations());
} else {
return NO_ANNOTATIONS;
}
}
}
private static IBinaryAnnotation[] toAnnotationArray(List<? extends NdAnnotation> annotations) {
if (annotations.isEmpty()) {
return NO_ANNOTATIONS;
}
IBinaryAnnotation[] result = new IBinaryAnnotation[annotations.size()];
for (int idx = 0; idx < result.length; idx++) {
result[idx] = createBinaryAnnotation(annotations.get(idx));
}
return result;
}
@Override
public IBinaryTypeAnnotation[] getTypeAnnotations() {
try (IReader rl = this.typeRef.lock()) {
NdType type = this.typeRef.get();
if (type != null) {
return createBinaryTypeAnnotations(type.getTypeAnnotations());
}
}
return null;
}
@Override
public char[] getEnclosingMethod() {
initSimpleAttributes();
return this.enclosingMethod;
}
@Override
public char[] getEnclosingTypeName() {
initSimpleAttributes();
return this.enclosingType;
}
@Override
public IBinaryField[] getFields() {
try (IReader rl = this.typeRef.lock()) {
NdType type = this.typeRef.get();
if (type != null) {
List<NdVariable> variables = type.getVariables();
if (variables.isEmpty()) {
return null;
}
IBinaryField[] result = new IBinaryField[variables.size()];
for (int fieldIdx = 0; fieldIdx < variables.size(); fieldIdx++) {
result[fieldIdx] = createBinaryField(variables.get(fieldIdx));
}
return result;
} else {
return null;
}
}
}
@Override
public char[] getGenericSignature() {
try (IReader rl = this.typeRef.lock()) {
NdType type = this.typeRef.get();
if (type != null) {
if (!type.getFlag(NdType.FLG_GENERIC_SIGNATURE_PRESENT)) {
return null;
}
CharArrayBuffer buffer = new CharArrayBuffer();
NdTypeParameter.getSignature(buffer, type.getTypeParameters());
NdTypeSignature superclass = type.getSuperclass();
if (superclass != null) {
superclass.getSignature(buffer);
}
for (NdTypeInterface nextInterface : type.getInterfaces()) {
nextInterface.getInterface().getSignature(buffer);
}
return buffer.getContents();
} else {
return null;
}
}
}
@Override
public char[][] getInterfaceNames() {
try (IReader rl = this.typeRef.lock()) {
NdType type = this.typeRef.get();
if (type != null) {
List<NdTypeInterface> interfaces = type.getInterfaces();
if (interfaces.isEmpty()) {
return null;
}
char[][] result = new char[interfaces.size()][];
for (int idx = 0; idx < interfaces.size(); idx++) {
NdTypeSignature nextInterface = interfaces.get(idx).getInterface();
result[idx] = nextInterface.getRawType().getBinaryName();
}
return result;
} else {
return null;
}
}
}
@Override
public IBinaryNestedType[] getMemberTypes() {
try (IReader rl = this.typeRef.lock()) {
NdType type = this.typeRef.get();
if (type != null) {
List<NdType> declaredTypes = type.getTypeId().getDeclaredTypes();
if (declaredTypes.isEmpty()) {
return null;
}
NdResourceFile resFile = type.getResourceFile();
IString javaRoot = resFile.getPackageFragmentRoot();
// Filter out all the declared types which are at different java roots (only keep the ones belonging
// to the same .jar file or to another .class file in the same folder).
List<IBinaryNestedType> result = new ArrayList<>();
for (NdType next : declaredTypes) {
NdResourceFile nextResFile = next.getResourceFile();
if (nextResFile.getPackageFragmentRoot().compare(javaRoot, true) == 0) {
result.add(createBinaryNestedType(next));
}
}
return result.isEmpty() ? null : result.toArray(new IBinaryNestedType[result.size()]);
} else {
return null;
}
}
}
private IBinaryNestedType createBinaryNestedType(NdType next) {
return new IndexBinaryNestedType(next.getTypeId().getBinaryName(), next.getDeclaringType().getBinaryName(),
next.getModifiers());
}
@Override
public IBinaryMethod[] getMethods() {
try (IReader rl = this.typeRef.lock()) {
NdType type = this.typeRef.get();
if (type != null) {
List<NdMethod> methods = type.getMethodsInDeclarationOrder();
if (methods.isEmpty()) {
return null;
}
IBinaryMethod[] result = new IBinaryMethod[methods.size()];
for (int idx = 0; idx < result.length; idx++) {
result[idx] = createBinaryMethod(methods.get(idx));
}
return result;
} else {
return null;
}
}
}
@Override
public char[][][] getMissingTypeNames() {
try (IReader rl = this.typeRef.lock()) {
NdType type = this.typeRef.get();
if (type != null) {
IString string = type.getMissingTypeNames();
if (string.length() == 0) {
return null;
}
char[] missingTypeNames = string.getChars();
char[][] paths = CharOperation.splitOn(',', missingTypeNames);
char[][][] result = new char[paths.length][][];
for (int idx = 0; idx < paths.length; idx++) {
result[idx] = CharOperation.splitOn('/', paths[idx]);
}
return result;
} else {
return null;
}
}
}
@Override
public char[] getName() {
initSimpleAttributes();
return this.binaryTypeName;
}
@Override
public char[] getSourceName() {
try (IReader rl = this.typeRef.lock()) {
NdType type = this.typeRef.get();
if (type != null) {
return type.getSourceName();
} else {
return new char[0];
}
}
}
@Override
public char[] getSuperclassName() {
initSimpleAttributes();
return this.superclassName;
}
@Override
public long getTagBits() {
initSimpleAttributes();
return this.tagBits;
}
@Override
public boolean isAnonymous() {
initSimpleAttributes();
return this.isAnonymous;
}
@Override
public boolean isLocal() {
initSimpleAttributes();
return this.isLocal;
}
@Override
public boolean isMember() {
initSimpleAttributes();
return this.isMember;
}
@Override
public char[] sourceFileName() {
try (IReader rl = this.typeRef.lock()) {
NdType type = this.typeRef.get();
if (type != null) {
char[] result = type.getSourceFileName().getChars();
if (result.length == 0) {
return null;
}
return result;
} else {
return null;
}
}
}
@Override
public ITypeAnnotationWalker enrichWithExternalAnnotationsFor(ITypeAnnotationWalker walker, Object member,
LookupEnvironment environment) {
return walker;
}
private IBinaryMethod createBinaryMethod(NdMethod ndMethod) {
return IndexBinaryMethod.create().setAnnotations(toAnnotationArray(ndMethod.getAnnotations()))
.setModifiers(ndMethod.getModifiers()).setIsConstructor(ndMethod.isConstructor())
.setArgumentNames(getArgumentNames(ndMethod)).setDefaultValue(unpackValue(ndMethod.getDefaultValue()))
.setExceptionTypeNames(getExceptionTypeNames(ndMethod))
.setGenericSignature(getGenericSignatureFor(ndMethod))
.setMethodDescriptor(ndMethod.getMethodDescriptor())
.setParameterAnnotations(getParameterAnnotations(ndMethod))
.setSelector(ndMethod.getSelector()).setTagBits(ndMethod.getTagBits())
.setIsClInit(ndMethod.isClInit()).setTypeAnnotations(createBinaryTypeAnnotations(ndMethod.getTypeAnnotations()));
}
private static IBinaryTypeAnnotation[] createBinaryTypeAnnotations(List<? extends NdTypeAnnotation> typeAnnotations) {
if (typeAnnotations.isEmpty()) {
return null;
}
IBinaryTypeAnnotation[] result = new IBinaryTypeAnnotation[typeAnnotations.size()];
int idx = 0;
for (NdTypeAnnotation next : typeAnnotations) {
IBinaryAnnotation annotation = createBinaryAnnotation(next);
int[] typePath = getTypePath(next.getTypePath());
int info = 0;
int info2 = 0;
switch (next.getTargetType()) {
case AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER:
case AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER:
info = next.getTargetInfoArg0();
break;
case AnnotationTargetTypeConstants.CLASS_EXTENDS:
info = next.getTarget();
break;
case AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER_BOUND:
case AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER_BOUND:
info = next.getTargetInfoArg0();
info2 = next.getTargetInfoArg1();
break;
case AnnotationTargetTypeConstants.FIELD:
case AnnotationTargetTypeConstants.METHOD_RETURN:
case AnnotationTargetTypeConstants.METHOD_RECEIVER:
break;
case AnnotationTargetTypeConstants.METHOD_FORMAL_PARAMETER :
info = next.getTarget();
break;
case AnnotationTargetTypeConstants.THROWS :
info = next.getTarget();
break;
default:
throw new IllegalStateException("Target type not handled " + next.getTargetType()); //$NON-NLS-1$
}
result[idx++] = new IndexBinaryTypeAnnotation(next.getTargetType(), info, info2, typePath, annotation);
}
return result;
}
private static int[] getTypePath(byte[] typePath) {
if (typePath.length == 0) {
return NO_PATH;
}
int[] result = new int[typePath.length];
for (int idx = 0; idx < typePath.length; idx++) {
result[idx] = typePath[idx];
}
return result;
}
private static char[] getGenericSignatureFor(NdMethod method) {
if (!method.hasAllFlags(NdMethod.FLG_GENERIC_SIGNATURE_PRESENT)) {
return null;
}
CharArrayBuffer result = new CharArrayBuffer();
method.getGenericSignature(result, method.hasAllFlags(NdMethod.FLG_THROWS_SIGNATURE_PRESENT));
return result.getContents();
}
private char[][] getArgumentNames(NdMethod ndMethod) {
// Unlike what its JavaDoc says, IBinaryType returns an empty array if no argument names are available, so
// we replicate this weird undocumented corner case here.
char[][] result = ndMethod.getParameterNames();
int lastNonEmpty = -1;
for (int idx = 0; idx < result.length; idx++) {
if (result[idx] != null && result[idx].length != 0) {
lastNonEmpty = idx;
}
}
if (lastNonEmpty != result.length - 1) {
char[][] newResult = new char[lastNonEmpty + 1][];
System.arraycopy(result, 0, newResult, 0, lastNonEmpty + 1);
return newResult;
}
return result;
}
private IBinaryAnnotation[][] getParameterAnnotations(NdMethod ndMethod) {
List<NdMethodParameter> parameters = ndMethod.getMethodParameters();
if (parameters.isEmpty()) {
return null;
}
IBinaryAnnotation[][] result = new IBinaryAnnotation[parameters.size()][];
for (int idx = 0; idx < parameters.size(); idx++) {
NdMethodParameter next = parameters.get(idx);
result[idx] = toAnnotationArray(next.getAnnotations());
}
// int newLength = result.length;
// while (newLength > 0 && result[newLength - 1] == null) {
// --newLength;
// }
//
// if (newLength < result.length) {
// if (newLength == 0) {
// return null;
// }
// IBinaryAnnotation[][] newResult = new IBinaryAnnotation[newLength][];
// System.arraycopy(result, 0, newResult, 0, newLength);
// result = newResult;
// }
return result;
}
private char[][] getExceptionTypeNames(NdMethod ndMethod) {
List<NdMethodException> exceptions = ndMethod.getExceptions();
// Although the JavaDoc for IBinaryMethod says that the exception list will be null if empty,
// the implementation in MethodInfo returns an empty array rather than null. We copy the
// same behavior here in case something is relying on it. Uncomment the following if the "null"
// version is deemed correct.
// if (exceptions.isEmpty()) {
// return null;
// }
char[][] result = new char[exceptions.size()][];
for (int idx = 0; idx < exceptions.size(); idx++) {
NdMethodException next = exceptions.get(idx);
result[idx] = next.getExceptionType().getRawType().getBinaryName();
}
return result;
}
public static IBinaryField createBinaryField(NdVariable ndVariable) {
char[] name = ndVariable.getName().getChars();
Constant constant = null;
NdConstant ndConstant = ndVariable.getConstant();
if (ndConstant != null) {
constant = ndConstant.getConstant();
}
if (constant == null) {
constant = Constant.NotAConstant;
}
NdTypeSignature type = ndVariable.getType();
IBinaryTypeAnnotation[] typeAnnotationArray = createBinaryTypeAnnotations(ndVariable.getTypeAnnotations());
IBinaryAnnotation[] annotations = toAnnotationArray(ndVariable.getAnnotations());
CharArrayBuffer signature = new CharArrayBuffer();
if (ndVariable.hasVariableFlag(NdVariable.FLG_GENERIC_SIGNATURE_PRESENT)) {
type.getSignature(signature);
}
long tagBits = ndVariable.getTagBits();
return new IndexBinaryField(annotations, constant, signature.getContents(), ndVariable.getModifiers(), name,
tagBits, typeAnnotationArray, type.getRawType().getFieldDescriptor().getChars());
}
public static IBinaryAnnotation createBinaryAnnotation(NdAnnotation ndAnnotation) {
List<NdAnnotationValuePair> elementValuePairs = ndAnnotation.getElementValuePairs();
final IBinaryElementValuePair[] resultingPair = new IBinaryElementValuePair[elementValuePairs.size()];
for (int idx = 0; idx < elementValuePairs.size(); idx++) {
NdAnnotationValuePair next = elementValuePairs.get(idx);
resultingPair[idx] = new ElementValuePairInfo(next.getName().getChars(), unpackValue(next.getValue()));
}
final char[] binaryName = JavaNames.fieldDescriptorToBinaryName(
ndAnnotation.getType().getRawType().getFieldDescriptor().getChars());
return new IBinaryAnnotation() {
@Override
public char[] getTypeName() {
return binaryName;
}
@Override
public IBinaryElementValuePair[] getElementValuePairs() {
return resultingPair;
}
@Override
public String toString() {
return BinaryTypeFormatter.annotationToString(this);
}
};
}
public void initSimpleAttributes() {
if (!this.simpleAttributesInitialized) {
this.simpleAttributesInitialized = true;
try (IReader rl = this.typeRef.lock()) {
NdType type = this.typeRef.get();
if (type != null) {
IString declaringMethod = type.getDeclaringMethod();
if (declaringMethod.length() != 0) {
char[] methodName = declaringMethod.getChars();
this.enclosingMethod = methodName;
this.enclosingType = type.getDeclaringType().getBinaryName();
} else {
NdTypeId typeId = type.getDeclaringType();
if (typeId != null) {
this.enclosingType = typeId.getBinaryName();
}
}
this.modifiers = type.getModifiers();
this.isAnonymous = type.isAnonymous();
this.isLocal = type.isLocal();
this.isMember = type.isMember();
this.tagBits = type.getTagBits();
NdTypeSignature superclass = type.getSuperclass();
if (superclass != null) {
this.superclassName = superclass.getRawType().getBinaryName();
} else {
this.superclassName = null;
}
this.binaryTypeName = JavaNames.fieldDescriptorToBinaryName(type.getFieldDescriptor().getChars());
} else {
this.binaryTypeName = JavaNames.fieldDescriptorToBinaryName(this.typeRef.getFieldDescriptor());
}
}
}
}
private static Object unpackValue(NdConstant value) {
if (value == null) {
return null;
}
if (value instanceof NdConstantAnnotation) {
NdConstantAnnotation annotation = (NdConstantAnnotation) value;
return createBinaryAnnotation(annotation.getValue());
}
if (value instanceof NdConstantArray) {
NdConstantArray array = (NdConstantArray) value;
List<NdConstant> arrayContents = array.getValue();
Object[] result = new Object[arrayContents.size()];
for (int idx = 0; idx < arrayContents.size(); idx++) {
result[idx] = unpackValue(arrayContents.get(idx));
}
return result;
}
if (value instanceof NdConstantEnum) {
NdConstantEnum ndConstantEnum = (NdConstantEnum) value;
NdTypeSignature signature = ndConstantEnum.getType();
return new EnumConstantSignature(signature.getRawType().getBinaryName(), ndConstantEnum.getValue());
}
if (value instanceof NdConstantClass) {
NdConstantClass constant = (NdConstantClass) value;
return new ClassSignature(constant.getValue().getRawType().getBinaryName());
}
return value.getConstant();
}
@Override
public ExternalAnnotationStatus getExternalAnnotationStatus() {
return ExternalAnnotationStatus.NOT_EEA_CONFIGURED;
}
//{ObjectTeams: retrieve class file in workspace:
// FIXME: sync this with (a) reading EEA, (b) fileName manipulation for JRT
@Override
public IBinaryType withClassBytes() throws ClassFormatException, IOException, CoreException {
File file = new File(String.valueOf(this.fileName));
if (file.exists())
return ClassFileReader.read(file);
int pos = CharOperation.indexOf(IDependent.JAR_FILE_ENTRY_SEPARATOR, this.fileName);
if (pos != -1) {
String jarIdentifier = String.valueOf(CharOperation.subarray(this.fileName, 0, pos));
String fileInJarName = String.valueOf(CharOperation.subarray(this.fileName, pos+1, -1));
IJavaElement jJar = JavaCore.create(jarIdentifier);
if (jJar.exists()) {
ZipFile jarZip = ((JarPackageFragmentRoot) jJar).getJar();
try {
return ClassFileReader.read(jarZip, fileInJarName);
} finally {
JavaModelManager.getJavaModelManager().closeZipFile(jarZip);
}
}
}
return this; // could not improve
}
// SH}
@Override
public char[] getModule() {
// TODO Auto-generated method stub
return null;
}
}