blob: d7f4497b430ed06f1d7a3984fa96b95195c76748 [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed 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 com.android.jack.ecj.loader.jast;
import com.android.jack.ir.ast.HasEnclosingMethod;
import com.android.jack.ir.ast.JAbstractStringLiteral;
import com.android.jack.ir.ast.JAnnotation;
import com.android.jack.ir.ast.JClass;
import com.android.jack.ir.ast.JClassOrInterface;
import com.android.jack.ir.ast.JDefinedClass;
import com.android.jack.ir.ast.JDefinedClassOrInterface;
import com.android.jack.ir.ast.JDefinedInterface;
import com.android.jack.ir.ast.JField;
import com.android.jack.ir.ast.JIntLiteral;
import com.android.jack.ir.ast.JInterface;
import com.android.jack.ir.ast.JLiteral;
import com.android.jack.ir.ast.JMethod;
import com.android.jack.ir.ast.JNameValuePair;
import com.android.jack.ir.ast.JNullLiteral;
import com.android.jack.ir.ast.marker.SimpleName;
import com.android.jack.ir.formatter.TypeFormatter;
import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.jack.util.NamingTools;
import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
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.ITypeAnnotationWalker;
import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.ExternalAnnotationStatus;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* A {@code IBinaryType} for jack.
*/
class JAstBinaryType implements IBinaryType {
@Nonnull
private static final char[] OBJECT = "java/lang/Object".toCharArray();
@Nonnull
private final JDefinedClassOrInterface jDeclaredType;
@Nonnull
private final JAstClasspath classpathLocation;
JAstBinaryType(
@Nonnull JDefinedClassOrInterface jDeclaredType, @Nonnull JAstClasspath classpathLocation) {
this.jDeclaredType = jDeclaredType;
this.classpathLocation = classpathLocation;
}
/**
* {@inheritDoc}
*/
@Override
public int getModifiers() {
int modifiers;
JAnnotation enclosingAnnotation =
AnnotationUtils.getAnnotation(jDeclaredType, AnnotationUtils.INNER_CLASS_ANNOTATION);
if (enclosingAnnotation != null) {
JNameValuePair pair =
enclosingAnnotation.getNameValuePair(AnnotationUtils.INNERCLASS_ACCFLAGS_FIELD);
assert pair != null;
modifiers = ((JIntLiteral) pair.getValue()).getIntValue();
} else {
modifiers = jDeclaredType.getModifier();
}
modifiers = LoaderUtils.convertJAstModifiersToEcj(modifiers, jDeclaredType);
JClassOrInterface enclosingType = jDeclaredType.getEnclosingType();
if (enclosingType != null && !isAnonymous()
&& enclosingType instanceof JDefinedClassOrInterface) {
/* If the enclosing is not in the classpath, just skip. This should be with no bad consequence
* since ECJ is refusing to compile a source referencing an inner class when its enclosing
* class is missing.
*/
JAstBinaryType enclosing =
classpathLocation.findType((JDefinedClassOrInterface) enclosingType);
if (enclosing != null) {
if (LoaderUtils.isDeprecated(enclosing)) {
modifiers |= ExtraCompilerModifiers.AccDeprecatedImplicitly;
}
}
}
return modifiers;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isBinaryType() {
return true;
}
/**
* {@inheritDoc}
*/
@Nonnull
@Override
public char[] getFileName() {
return (classpathLocation.getPath() + "|" + getBinaryName()).toCharArray();
}
/**
* {@inheritDoc}
*/
@CheckForNull
@Override
public IBinaryAnnotation[] getAnnotations() {
return AnnotationUtils.convertJAstAnnotationToEcj(jDeclaredType, true);
}
public boolean hasEnclosingMethod() {
return jDeclaredType instanceof JDefinedClass
&& ((JDefinedClass) jDeclaredType).getEnclosingMethod() != null;
}
/**
* {@inheritDoc}
*/
@CheckForNull
@Override
public char[] getEnclosingTypeName() {
char[] enclosingTypeName = null;
JClassOrInterface enclosingType = jDeclaredType.getEnclosingType();
if (enclosingType == null && jDeclaredType instanceof JDefinedClass) {
JMethod enclosingMethod = ((JDefinedClass) jDeclaredType).getEnclosingMethod();
if (enclosingMethod != null) {
enclosingType = enclosingMethod.getEnclosingType();
}
}
if (enclosingType != null) {
enclosingTypeName =
LoaderUtils.getQualifiedNameFormatter().getName(enclosingType).toCharArray();
}
return enclosingTypeName;
}
/**
* {@inheritDoc}
*/
@CheckForNull
@Override
public IBinaryField[] getFields() {
List<JField> fields = jDeclaredType.getFields();
int totalFields = fields.size();
if (totalFields == 0) {
return null;
}
IBinaryField[] allFields = new IBinaryField[totalFields];
int indexInAllFields = 0;
for (JField field : fields) {
JLiteral initialValue = null;
initialValue = field.getLiteralInitializer();
if (initialValue == null) {
initialValue = field.getInitialValue();
}
allFields[indexInAllFields] = new JAstBinaryField(field, initialValue);
indexInAllFields++;
}
return allFields;
}
/**
* {@inheritDoc}
*/
@CheckForNull
@Override
public char[] getGenericSignature() {
return LoaderUtils.getGenericSignature(jDeclaredType);
}
/**
* {@inheritDoc}
*/
@CheckForNull
@Override
public char[][] getInterfaceNames() {
List<JInterface> implemented = jDeclaredType.getImplements();
char[][] names = null;
int interfaceCount = implemented.size();
if (interfaceCount > 0) {
names = new char[interfaceCount][];
TypeFormatter formatter = LoaderUtils.getQualifiedNameFormatter();
for (int i = 0; i < interfaceCount; i++) {
names[i] = formatter.getName(implemented.get(i)).toCharArray();
}
}
return names;
}
/**
* {@inheritDoc}
*/
@CheckForNull
@Override
public IBinaryNestedType[] getMemberTypes() {
List<JClassOrInterface> members = jDeclaredType.getMemberTypes();
IBinaryNestedType[] nestedTypesArray = null;
if (!members.isEmpty()) {
int nestedTypeCount = members.size();
List<IBinaryNestedType> nestedTypes = new ArrayList<IBinaryNestedType>(nestedTypeCount);
for (JClassOrInterface jNested : members) {
/* If the inner is not in the classpath, just skip it. This should not have consequence in
* Jack context, since we are unable to present the missing class to ECJ anyway. */
if (jNested instanceof JDefinedClassOrInterface) {
/* According to a note in a comment on the interface, we have to filter out local types
* found in the member types list. Tests have shown that, in this note, "local" means also
* anonymous.
*/
JAstBinaryType nested = classpathLocation.findType((JDefinedClassOrInterface) jNested);
assert nested != null;
if (!(nested.isAnonymous() || nested.isLocal())) {
nestedTypes.add(new JAstBinaryNestedType(nested.jDeclaredType));
}
}
}
if (!nestedTypes.isEmpty()) {
nestedTypesArray = nestedTypes.toArray(new IBinaryNestedType[nestedTypes.size()]);
}
}
return nestedTypesArray;
}
/**
* {@inheritDoc}
*/
@CheckForNull
@Override
public IBinaryMethod[] getMethods() {
List<JMethod> jMethods = jDeclaredType.getMethods();
int total = jMethods.size();
IBinaryMethod[] methods = null;
if (total != 0) {
methods = new IBinaryMethod[total];
int indexInMethods = 0;
for (JMethod method: jMethods) {
methods[indexInMethods] = new JAstBinaryMethod(method);
indexInMethods++;
}
}
return methods;
}
/**
* {@inheritDoc}
*/
@CheckForNull
@Override
public char[][][] getMissingTypeNames() {
// N/A (Eclipse compiled classes with errors)
return null;
}
/**
* {@inheritDoc}
*/
@Nonnull
@Override
public char[] getName() {
return getBinaryName().toCharArray();
}
/**
* {@inheritDoc}
*/
@CheckForNull
@Override
public char[] getSourceName() {
SimpleName typeInfo = jDeclaredType.getMarker(SimpleName.class);
if (typeInfo != null) {
return typeInfo.getSimpleName().toCharArray();
}
char[] sourceNameArray;
JAnnotation enclosingAnnotation =
AnnotationUtils.getAnnotation(jDeclaredType, AnnotationUtils.INNER_CLASS_ANNOTATION);
if (enclosingAnnotation != null) {
/* if this class is an inner then return the source name given by the annotation */
JNameValuePair pair =
enclosingAnnotation.getNameValuePair(AnnotationUtils.INNERCLASS_NAME_FIELD);
assert pair != null : "Invalid jayce file";
JLiteral nameValue = pair.getValue();
if (nameValue instanceof JNullLiteral) {
/*
* javadoc says anonymous must return null but ClassFileReader returns a name. Anyway
* ClassFileReader returns weird things with anonymous in inner. Lets stick to the javadoc
* for now. If we decide to stick to the reference instead we could just subtract the
* enclosingTypeName to this type name.
*/
sourceNameArray = null;
} else {
String sourceName = ((JAbstractStringLiteral) nameValue).getValue();
sourceNameArray = sourceName.toCharArray();
}
} else {
String binaryName = getBinaryName();
String simpleName = NamingTools.getSimpleClassNameFromBinaryName(binaryName);
sourceNameArray = simpleName.toCharArray();
}
return sourceNameArray;
}
/**
* {@inheritDoc}
*/
@CheckForNull
@Override
public char[] getSuperclassName() {
JClass superType = jDeclaredType.getSuperClass();
if (superType != null) {
return LoaderUtils.getQualifiedNameFormatter().getName(superType).toCharArray();
} else if (jDeclaredType instanceof JDefinedInterface) {
return OBJECT;
} else {
return null;
}
}
/**
* {@inheritDoc}
*/
@Override
public long getTagBits() {
return AnnotationUtils.getTagBits(jDeclaredType);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isAnonymous() {
boolean isAnonymous = false;
SimpleName simpleNameInfo = jDeclaredType.getMarker(SimpleName.class);
if (simpleNameInfo != null) {
isAnonymous = simpleNameInfo.getSimpleName().isEmpty();
} else {
JAnnotation enclosingAnnotation =
AnnotationUtils.getAnnotation(jDeclaredType, AnnotationUtils.INNER_CLASS_ANNOTATION);
if (enclosingAnnotation != null) {
JNameValuePair pair =
enclosingAnnotation.getNameValuePair(AnnotationUtils.INNERCLASS_NAME_FIELD);
assert pair != null;
isAnonymous = (pair.getValue() instanceof JNullLiteral);
}
}
return isAnonymous;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isLocal() {
return (!isAnonymous()) && hasEnclosingMethod();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isMember() {
return (!isAnonymous())
&& (jDeclaredType.getEnclosingType() != null && !hasEnclosingMethod());
}
/**
* {@inheritDoc}
*/
@CheckForNull
@Override
public char[] sourceFileName() {
if (jDeclaredType.getSourceInfo() == SourceInfo.UNKNOWN) {
return null;
}
String fileName = jDeclaredType.getSourceInfo().getFileName();
int simpleNameIndex = fileName.lastIndexOf('/');
simpleNameIndex = Math.max(simpleNameIndex, fileName.lastIndexOf('\\'));
if (simpleNameIndex > 0) {
fileName = fileName.substring(simpleNameIndex + 1);
}
return fileName.toCharArray();
}
@Override
@Nonnull
public String toString() {
return jDeclaredType.toString();
}
@Nonnull
String getBinaryName() {
return LoaderUtils.getQualifiedNameFormatter().getName(jDeclaredType);
}
@Override
public char[] getEnclosingMethod() {
char[] enclosingMethodName = null;
if (jDeclaredType instanceof HasEnclosingMethod) {
JMethod enclosingMethod = ((HasEnclosingMethod) jDeclaredType).getEnclosingMethod();
if (enclosingMethod != null) {
enclosingMethodName =
LoaderUtils.getSignatureFormatter().getName(enclosingMethod).toCharArray();
}
}
return enclosingMethodName;
}
@Override
public IBinaryTypeAnnotation[] getTypeAnnotations() {
return null;
}
@Override
public ITypeAnnotationWalker enrichWithExternalAnnotationsFor(ITypeAnnotationWalker walker,
Object member, LookupEnvironment environment) {
// Jack does not support ecj external annotation file
return walker;
}
@Override
public ExternalAnnotationStatus getExternalAnnotationStatus() {
return ExternalAnnotationStatus.FROM_SOURCE;
}
}