package org.eclipse.jdt.internal.compiler; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved. | |
*/ | |
import java.io.*; | |
import java.util.*; | |
import org.eclipse.jdt.internal.compiler.impl.*; | |
import org.eclipse.jdt.core.compiler.*; | |
import org.eclipse.jdt.internal.compiler.ast.*; | |
import org.eclipse.jdt.internal.compiler.codegen.*; | |
import org.eclipse.jdt.internal.compiler.lookup.*; | |
import org.eclipse.jdt.internal.compiler.problem.*; | |
import org.eclipse.jdt.internal.compiler.util.*; | |
/** | |
* Represents a class file wrapper on bytes, it is aware of its actual | |
* type name. | |
* | |
* Public APIs are listed below: | |
* | |
* byte[] getBytes(); | |
* Answer the actual bytes of the class file | |
* | |
* char[][] getCompoundName(); | |
* Answer the compound name of the class file. | |
* For example, {{java}, {util}, {Hashtable}}. | |
* | |
* byte[] getReducedBytes(); | |
* Answer a smaller byte format, which is only contains some structural | |
* information. Those bytes are decodable with a regular class file reader, | |
* such as DietClassFileReader | |
*/ | |
public class ClassFile | |
implements AttributeNamesConstants, CompilerModifiers, TypeConstants, TypeIds { | |
public SourceTypeBinding referenceBinding; | |
public ConstantPool constantPool; | |
public ClassFile enclosingClassFile; | |
// used to generate private access methods | |
public int produceDebugAttributes; | |
public ReferenceBinding[] innerClassesBindings; | |
public int numberOfInnerClasses; | |
public byte[] header; | |
// the header contains all the bytes till the end of the constant pool | |
public byte[] contents; | |
// that collection contains all the remaining bytes of the .class file | |
public int headerOffset; | |
public int contentsOffset; | |
public int constantPoolOffset; | |
public int methodCountOffset; | |
public int methodCount; | |
protected boolean creatingProblemType; | |
public static final int INITIAL_CONTENTS_SIZE = 1000; | |
public static final int INITIAL_HEADER_SIZE = 1000; | |
public static final int INCREMENT_SIZE = 1000; | |
public static final int INNER_CLASSES_SIZE = 5; | |
protected HashtableOfType nameUsage; | |
public CodeStream codeStream; | |
protected int problemLine; // used to create line number attributes for problem methods | |
/** | |
* INTERNAL USE-ONLY | |
* This methods creates a new instance of the receiver. | |
*/ | |
public ClassFile() { | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* This methods creates a new instance of the receiver. | |
* | |
* @param aType org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding | |
* @param enclosingClassFile org.eclipse.jdt.internal.compiler.ClassFile | |
* @param creatingProblemType <CODE>boolean</CODE> | |
*/ | |
public ClassFile( | |
SourceTypeBinding aType, | |
ClassFile enclosingClassFile, | |
boolean creatingProblemType) { | |
referenceBinding = aType; | |
header = new byte[INITIAL_HEADER_SIZE]; | |
// generate the magic numbers inside the header | |
header[headerOffset++] = (byte) (0xCAFEBABEL >> 24); | |
header[headerOffset++] = (byte) (0xCAFEBABEL >> 16); | |
header[headerOffset++] = (byte) (0xCAFEBABEL >> 8); | |
header[headerOffset++] = (byte) (0xCAFEBABEL >> 0); | |
switch(((SourceTypeBinding) referenceBinding).scope.environment().options.targetJDK) { | |
case CompilerOptions.JDK1_4 : | |
// Compatible with JDK 1.4 | |
header[headerOffset++] = 0; | |
header[headerOffset++] = 0; | |
header[headerOffset++] = 0; | |
header[headerOffset++] = 48; | |
break; | |
case CompilerOptions.JDK1_3 : | |
// Compatible with JDK 1.3 | |
header[headerOffset++] = 0; | |
header[headerOffset++] = 0; | |
header[headerOffset++] = 0; | |
header[headerOffset++] = 47; | |
break; | |
case CompilerOptions.JDK1_2 : | |
// Compatible with JDK 1.2 | |
header[headerOffset++] = 0; | |
header[headerOffset++] = 0; | |
header[headerOffset++] = 0; | |
header[headerOffset++] = 46; | |
break; | |
case CompilerOptions.JDK1_1 : | |
// Compatible with JDK 1.1 | |
header[headerOffset++] = 0; | |
header[headerOffset++] = 3; | |
header[headerOffset++] = 0; | |
header[headerOffset++] = 45; | |
} | |
constantPoolOffset = headerOffset; | |
headerOffset += 2; | |
constantPool = new ConstantPool(this); | |
// Modifier manipulations for classfile | |
int accessFlags = aType.getAccessFlags(); | |
if (aType.isPrivate()) { // rewrite private to non-public | |
accessFlags &= ~AccPublic; | |
} | |
if (aType.isProtected()) { // rewrite protected into public | |
accessFlags |= AccPublic; | |
} | |
// clear all bits that are illegal for a class or an interface | |
accessFlags | |
&= ~( | |
AccStrictfp | |
| AccProtected | |
| AccPrivate | |
| AccStatic | |
| AccSynchronized | |
| AccNative); | |
// set the AccSuper flag (has to be done after clearing AccSynchronized - since same value) | |
accessFlags |= AccSuper; | |
this.enclosingClassFile = enclosingClassFile; | |
// innerclasses get their names computed at code gen time | |
if (aType.isLocalType()) { | |
((LocalTypeBinding) aType).constantPoolName( | |
computeConstantPoolName((LocalTypeBinding) aType)); | |
ReferenceBinding[] memberTypes = aType.memberTypes(); | |
for (int i = 0, max = memberTypes.length; i < max; i++) { | |
((LocalTypeBinding) memberTypes[i]).constantPoolName( | |
computeConstantPoolName((LocalTypeBinding) memberTypes[i])); | |
} | |
} | |
contents = new byte[INITIAL_CONTENTS_SIZE]; | |
// now we continue to generate the bytes inside the contents array | |
contents[contentsOffset++] = (byte) (accessFlags >> 8); | |
contents[contentsOffset++] = (byte) accessFlags; | |
int classNameIndex = constantPool.literalIndex(aType); | |
contents[contentsOffset++] = (byte) (classNameIndex >> 8); | |
contents[contentsOffset++] = (byte) classNameIndex; | |
int superclassNameIndex; | |
if (aType.isInterface()) { | |
superclassNameIndex = constantPool.literalIndexForJavaLangObject(); | |
} else { | |
superclassNameIndex = | |
(aType.superclass == null ? 0 : constantPool.literalIndex(aType.superclass)); | |
} | |
contents[contentsOffset++] = (byte) (superclassNameIndex >> 8); | |
contents[contentsOffset++] = (byte) superclassNameIndex; | |
ReferenceBinding[] superInterfacesBinding = aType.superInterfaces(); | |
int interfacesCount = superInterfacesBinding.length; | |
contents[contentsOffset++] = (byte) (interfacesCount >> 8); | |
contents[contentsOffset++] = (byte) interfacesCount; | |
if (superInterfacesBinding != null) { | |
for (int i = 0; i < interfacesCount; i++) { | |
int interfaceIndex = constantPool.literalIndex(superInterfacesBinding[i]); | |
contents[contentsOffset++] = (byte) (interfaceIndex >> 8); | |
contents[contentsOffset++] = (byte) interfaceIndex; | |
} | |
} | |
produceDebugAttributes = | |
((SourceTypeBinding) referenceBinding) | |
.scope | |
.environment() | |
.options | |
.produceDebugAttributes; | |
innerClassesBindings = new ReferenceBinding[INNER_CLASSES_SIZE]; | |
this.creatingProblemType = creatingProblemType; | |
codeStream = new CodeStream(this); | |
// retrieve the enclosing one guaranteed to be the one matching the propagated flow info | |
// 1FF9ZBU: LFCOM:ALL - Local variable attributes busted (Sanity check) | |
ClassFile outermostClassFile = this.outerMostEnclosingClassFile(); | |
if (this == outermostClassFile) { | |
codeStream.maxFieldCount = aType.scope.referenceType().maxFieldCount; | |
} else { | |
codeStream.maxFieldCount = outermostClassFile.codeStream.maxFieldCount; | |
} | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Generate the byte for a problem method info that correspond to a boggus method. | |
* | |
* @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration | |
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding | |
*/ | |
public void addAbstractMethod( | |
AbstractMethodDeclaration method, | |
MethodBinding methodBinding) { | |
// force the modifiers to be public and abstract | |
methodBinding.modifiers = AccPublic | AccAbstract; | |
this.generateMethodInfoHeader(methodBinding); | |
int methodAttributeOffset = this.contentsOffset; | |
int attributeNumber = this.generateMethodInfoAttribute(methodBinding); | |
this.completeMethodInfo(methodAttributeOffset, attributeNumber); | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* This methods generate all the attributes for the receiver. | |
* For a class they could be: | |
* - source file attribute | |
* - inner classes attribute | |
* - deprecated attribute | |
*/ | |
public void addAttributes() { | |
// update the method count | |
contents[methodCountOffset++] = (byte) (methodCount >> 8); | |
contents[methodCountOffset] = (byte) methodCount; | |
int attributeNumber = 0; | |
// leave two bytes for the number of attributes and store the current offset | |
int attributeOffset = contentsOffset; | |
contentsOffset += 2; | |
int contentsLength; | |
// source attribute | |
if ((produceDebugAttributes & CompilerOptions.Source) != 0) { | |
String fullFileName = | |
new String(referenceBinding.scope.referenceCompilationUnit().getFileName()); | |
fullFileName = fullFileName.replace('\\', '/'); | |
int lastIndex = fullFileName.lastIndexOf('/'); | |
if (lastIndex != -1) { | |
fullFileName = fullFileName.substring(lastIndex + 1, fullFileName.length()); | |
} | |
// check that there is enough space to write all the bytes for the field info corresponding | |
// to the @fieldBinding | |
if (contentsOffset + 8 >= (contentsLength = contents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
int sourceAttributeNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.SourceName); | |
contents[contentsOffset++] = (byte) (sourceAttributeNameIndex >> 8); | |
contents[contentsOffset++] = (byte) sourceAttributeNameIndex; | |
// The length of a source file attribute is 2. This is a fixed-length | |
// attribute | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 2; | |
// write the source file name | |
int fileNameIndex = constantPool.literalIndex(fullFileName.toCharArray()); | |
contents[contentsOffset++] = (byte) (fileNameIndex >> 8); | |
contents[contentsOffset++] = (byte) fileNameIndex; | |
attributeNumber++; | |
} | |
// Deprecated attribute | |
if (referenceBinding.isDeprecated()) { | |
// check that there is enough space to write all the bytes for the field info corresponding | |
// to the @fieldBinding | |
if (contentsOffset + 6 >= (contentsLength = contents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
int deprecatedAttributeNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.DeprecatedName); | |
contents[contentsOffset++] = (byte) (deprecatedAttributeNameIndex >> 8); | |
contents[contentsOffset++] = (byte) deprecatedAttributeNameIndex; | |
// the length of a deprecated attribute is equals to 0 | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
attributeNumber++; | |
} | |
// Inner class attribute | |
if (numberOfInnerClasses != 0) { | |
// Generate the inner class attribute | |
int exSize; | |
if (contentsOffset + (exSize = (8 * numberOfInnerClasses + 8)) | |
>= (contentsLength = contents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(contents = | |
new byte[contentsLength | |
+ (exSize >= INCREMENT_SIZE ? exSize : INCREMENT_SIZE)]), | |
0, | |
contentsLength); | |
} | |
// Now we now the size of the attribute and the number of entries | |
// attribute name | |
int attributeNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.InnerClassName); | |
contents[contentsOffset++] = (byte) (attributeNameIndex >> 8); | |
contents[contentsOffset++] = (byte) attributeNameIndex; | |
int value = (numberOfInnerClasses << 3) + 2; | |
contents[contentsOffset++] = (byte) (value >> 24); | |
contents[contentsOffset++] = (byte) (value >> 16); | |
contents[contentsOffset++] = (byte) (value >> 8); | |
contents[contentsOffset++] = (byte) value; | |
contents[contentsOffset++] = (byte) (numberOfInnerClasses >> 8); | |
contents[contentsOffset++] = (byte) numberOfInnerClasses; | |
for (int i = 0; i < numberOfInnerClasses; i++) { | |
ReferenceBinding innerClass = innerClassesBindings[i]; | |
int accessFlags = innerClass.getAccessFlags(); | |
int innerClassIndex = constantPool.literalIndex(innerClass); | |
// inner class index | |
contents[contentsOffset++] = (byte) (innerClassIndex >> 8); | |
contents[contentsOffset++] = (byte) innerClassIndex; | |
// outer class index: anonymous and local have no outer class index | |
if (innerClass.isMemberType()) { | |
// member or member of local | |
int outerClassIndex = constantPool.literalIndex(innerClass.enclosingType()); | |
contents[contentsOffset++] = (byte) (outerClassIndex >> 8); | |
contents[contentsOffset++] = (byte) outerClassIndex; | |
} else { | |
// equals to 0 if the innerClass is not a member type | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
} | |
// name index | |
if (!innerClass.isAnonymousType()) { | |
int nameIndex = constantPool.literalIndex(innerClass.sourceName()); | |
contents[contentsOffset++] = (byte) (nameIndex >> 8); | |
contents[contentsOffset++] = (byte) nameIndex; | |
} else { | |
// equals to 0 if the innerClass is an anonymous type | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
} | |
// access flag | |
if (innerClass.isAnonymousType()) { | |
accessFlags |= AccPrivate; | |
} else | |
if (innerClass.isLocalType() && !innerClass.isMemberType()) { | |
accessFlags |= AccPrivate; | |
} | |
contents[contentsOffset++] = (byte) (accessFlags >> 8); | |
contents[contentsOffset++] = (byte) accessFlags; | |
} | |
attributeNumber++; | |
} | |
// update the number of attributes | |
contentsLength = contents.length; | |
if (attributeOffset + 2 >= contentsLength) { | |
System.arraycopy( | |
contents, | |
0, | |
(contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
contents[attributeOffset++] = (byte) (attributeNumber >> 8); | |
contents[attributeOffset] = (byte) attributeNumber; | |
// resynchronize all offsets of the classfile | |
header = constantPool.poolContent; | |
headerOffset = constantPool.currentOffset; | |
int constantPoolCount = constantPool.currentIndex; | |
header[constantPoolOffset++] = (byte) (constantPoolCount >> 8); | |
header[constantPoolOffset] = (byte) constantPoolCount; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* This methods generate all the default abstract method infos that correpond to | |
* the abstract methods inherited from superinterfaces. | |
*/ | |
public void addDefaultAbstractMethods() { // default abstract methods | |
MethodBinding[] defaultAbstractMethods = | |
referenceBinding.getDefaultAbstractMethods(); | |
for (int i = 0, max = defaultAbstractMethods.length; i < max; i++) { | |
generateMethodInfoHeader(defaultAbstractMethods[i]); | |
int methodAttributeOffset = contentsOffset; | |
int attributeNumber = generateMethodInfoAttribute(defaultAbstractMethods[i]); | |
completeMethodInfo(methodAttributeOffset, attributeNumber); | |
} | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* This methods generates the bytes for the field binding passed like a parameter | |
* @param fieldBinding org.eclipse.jdt.internal.compiler.lookup.FieldBinding | |
*/ | |
public void addFieldInfo(FieldBinding fieldBinding) { | |
int attributeNumber = 0; | |
// check that there is enough space to write all the bytes for the field info corresponding | |
// to the @fieldBinding | |
int contentsLength; | |
if (contentsOffset + 30 >= (contentsLength = contents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
// Generate two attribute: constantValueAttribute and SyntheticAttribute | |
// Now we can generate all entries into the byte array | |
// First the accessFlags | |
int accessFlags = fieldBinding.getAccessFlags(); | |
contents[contentsOffset++] = (byte) (accessFlags >> 8); | |
contents[contentsOffset++] = (byte) accessFlags; | |
// Then the nameIndex | |
int nameIndex = constantPool.literalIndex(fieldBinding.name); | |
contents[contentsOffset++] = (byte) (nameIndex >> 8); | |
contents[contentsOffset++] = (byte) nameIndex; | |
// Then the descriptorIndex | |
int descriptorIndex = constantPool.literalIndex(fieldBinding.type.signature()); | |
contents[contentsOffset++] = (byte) (descriptorIndex >> 8); | |
contents[contentsOffset++] = (byte) descriptorIndex; | |
// leave some space for the number of attributes | |
int fieldAttributeOffset = contentsOffset; | |
contentsOffset += 2; | |
// 4.7.2 only static constant fields get a ConstantAttribute | |
if (fieldBinding.constant != Constant.NotAConstant | |
&& fieldBinding.constant.typeID() != T_null) { | |
// Now we generate the constant attribute corresponding to the fieldBinding | |
int constantValueNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.ConstantValueName); | |
contents[contentsOffset++] = (byte) (constantValueNameIndex >> 8); | |
contents[contentsOffset++] = (byte) constantValueNameIndex; | |
// The attribute length = 2 in case of a constantValue attribute | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 2; | |
attributeNumber++; | |
// Need to add the constant_value_index | |
switch (fieldBinding.constant.typeID()) { | |
case T_boolean : | |
int booleanValueIndex = | |
constantPool.literalIndex(fieldBinding.constant.booleanValue() ? 1 : 0); | |
contents[contentsOffset++] = (byte) (booleanValueIndex >> 8); | |
contents[contentsOffset++] = (byte) booleanValueIndex; | |
break; | |
case T_byte : | |
case T_char : | |
case T_int : | |
case T_short : | |
int integerValueIndex = | |
constantPool.literalIndex(fieldBinding.constant.intValue()); | |
contents[contentsOffset++] = (byte) (integerValueIndex >> 8); | |
contents[contentsOffset++] = (byte) integerValueIndex; | |
break; | |
case T_float : | |
int floatValueIndex = | |
constantPool.literalIndex(fieldBinding.constant.floatValue()); | |
contents[contentsOffset++] = (byte) (floatValueIndex >> 8); | |
contents[contentsOffset++] = (byte) floatValueIndex; | |
break; | |
case T_double : | |
int doubleValueIndex = | |
constantPool.literalIndex(fieldBinding.constant.doubleValue()); | |
contents[contentsOffset++] = (byte) (doubleValueIndex >> 8); | |
contents[contentsOffset++] = (byte) doubleValueIndex; | |
break; | |
case T_long : | |
int longValueIndex = | |
constantPool.literalIndex(fieldBinding.constant.longValue()); | |
contents[contentsOffset++] = (byte) (longValueIndex >> 8); | |
contents[contentsOffset++] = (byte) longValueIndex; | |
break; | |
case T_String : | |
int stringValueIndex = | |
constantPool.literalIndex( | |
((StringConstant) fieldBinding.constant).stringValue()); | |
if (stringValueIndex == -1) { | |
if (!creatingProblemType) { | |
// report an error and abort: will lead to a problem type classfile creation | |
TypeDeclaration typeDeclaration = referenceBinding.scope.referenceContext; | |
FieldDeclaration[] fieldDecls = typeDeclaration.fields; | |
for (int i = 0, max = fieldDecls.length; i < max; i++) { | |
if (fieldDecls[i].binding == fieldBinding) { | |
// problem should abort | |
typeDeclaration.scope.problemReporter().stringConstantIsExceedingUtf8Limit( | |
fieldDecls[i]); | |
} | |
} | |
} else { | |
// already inside a problem type creation : no constant for this field | |
contentsOffset = fieldAttributeOffset + 2; | |
// +2 is necessary to keep the two byte space for the attribute number | |
attributeNumber--; | |
} | |
} else { | |
contents[contentsOffset++] = (byte) (stringValueIndex >> 8); | |
contents[contentsOffset++] = (byte) stringValueIndex; | |
} | |
} | |
} | |
if (fieldBinding.isSynthetic()) { | |
int syntheticAttributeNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.SyntheticName); | |
contents[contentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8); | |
contents[contentsOffset++] = (byte) syntheticAttributeNameIndex; | |
// the length of a synthetic attribute is equals to 0 | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
attributeNumber++; | |
} | |
if (fieldBinding.isDeprecated()) { | |
int deprecatedAttributeNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.DeprecatedName); | |
contents[contentsOffset++] = (byte) (deprecatedAttributeNameIndex >> 8); | |
contents[contentsOffset++] = (byte) deprecatedAttributeNameIndex; | |
// the length of a deprecated attribute is equals to 0 | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
attributeNumber++; | |
} | |
contents[fieldAttributeOffset++] = (byte) (attributeNumber >> 8); | |
contents[fieldAttributeOffset] = (byte) attributeNumber; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* This methods generate all the fields infos for the receiver. | |
* This includes: | |
* - a field info for each defined field of that class | |
* - a field info for each synthetic field (e.g. this$0) | |
*/ | |
public void addFieldInfos() { | |
SourceTypeBinding currentBinding = referenceBinding; | |
FieldBinding[] syntheticFields = currentBinding.syntheticFields(); | |
int fieldCount = | |
currentBinding.fieldCount() | |
+ (syntheticFields == null ? 0 : syntheticFields.length); | |
// write the number of fields | |
contents[contentsOffset++] = (byte) (fieldCount >> 8); | |
contents[contentsOffset++] = (byte) fieldCount; | |
FieldBinding[] fieldBindings = currentBinding.fields(); | |
for (int i = 0, max = fieldBindings.length; i < max; i++) { | |
addFieldInfo(fieldBindings[i]); | |
} | |
if (syntheticFields != null) { | |
for (int i = 0, max = syntheticFields.length; i < max; i++) { | |
addFieldInfo(syntheticFields[i]); | |
} | |
} | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* This methods stores the bindings for each inner class. They will be used to know which entries | |
* have to be generated for the inner classes attributes. | |
* @param referenceBinding org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding | |
*/ | |
public void addInnerClasses(ReferenceBinding referenceBinding) { | |
// check first if that reference binding is there | |
for (int i = 0; i < numberOfInnerClasses; i++) { | |
if (innerClassesBindings[i] == referenceBinding) | |
return; | |
} | |
int length = innerClassesBindings.length; | |
if (numberOfInnerClasses == length) { | |
System.arraycopy( | |
innerClassesBindings, | |
0, | |
(innerClassesBindings = new ReferenceBinding[length * 2]), | |
0, | |
length); | |
} | |
innerClassesBindings[numberOfInnerClasses++] = referenceBinding; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Generate the byte for a problem clinit method info that correspond to a boggus method. | |
* | |
* @param problem org.eclipse.jdt.internal.compiler.problem.Problem[] | |
*/ | |
public void addProblemClinit(IProblem[] problems) { | |
generateMethodInfoHeaderForClinit(); | |
// leave two spaces for the number of attributes | |
contentsOffset -= 2; | |
int attributeOffset = contentsOffset; | |
contentsOffset += 2; | |
int attributeNumber = 0; | |
int codeAttributeOffset = contentsOffset; | |
generateCodeAttributeHeader(); | |
codeStream.resetForProblemClinit(this); | |
String problemString = "" ; //$NON-NLS-1$ | |
if (problems != null) { | |
int max = problems.length; | |
StringBuffer buffer = new StringBuffer(25); | |
int count = 0; | |
for (int i = 0; i < max; i++) { | |
IProblem problem = problems[i]; | |
if ((problem != null) && (problem.isError())) { | |
buffer.append("\t" +problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$ | |
count++; | |
if (problemLine == 0) { | |
problemLine = problem.getSourceLineNumber(); | |
} | |
problems[i] = null; | |
} | |
} // insert the top line afterwards, once knowing how many problems we have to consider | |
if (count > 1) { | |
buffer.insert(0, Util.bind("compilation.unresolvedProblems" )); //$NON-NLS-1$ | |
} else { | |
buffer.insert(0, Util.bind("compilation.unresolvedProblem" )); //$NON-NLS-1$ | |
} | |
problemString = buffer.toString(); | |
} | |
// return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "") | |
int[] exceptionHandler = | |
codeStream.generateCodeAttributeForProblemMethod( | |
referenceBinding | |
.scope | |
.environment() | |
.options | |
.runtimeExceptionNameForCompileError, | |
problemString); | |
attributeNumber++; // code attribute | |
completeCodeAttributeForClinit( | |
codeAttributeOffset, | |
exceptionHandler, | |
referenceBinding | |
.scope | |
.referenceCompilationUnit() | |
.compilationResult | |
.lineSeparatorPositions); | |
contents[attributeOffset++] = (byte) (attributeNumber >> 8); | |
contents[attributeOffset] = (byte) attributeNumber; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Generate the byte for a problem method info that correspond to a boggus constructor. | |
* | |
* @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration | |
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding | |
* @param problem org.eclipse.jdt.internal.compiler.problem.Problem[] | |
*/ | |
public void addProblemConstructor( | |
AbstractMethodDeclaration method, | |
MethodBinding methodBinding, | |
IProblem[] problems) { | |
// always clear the strictfp/native/abstract bit for a problem method | |
methodBinding.modifiers &= ~(AccStrictfp | AccNative | AccAbstract); | |
generateMethodInfoHeader(methodBinding); | |
int methodAttributeOffset = contentsOffset; | |
int attributeNumber = generateMethodInfoAttribute(methodBinding); | |
// Code attribute | |
attributeNumber++; | |
int codeAttributeOffset = contentsOffset; | |
generateCodeAttributeHeader(); | |
final ProblemReporter problemReporter = method.scope.problemReporter(); | |
codeStream.reset(method, this); | |
String problemString = "" ; //$NON-NLS-1$ | |
if (problems != null) { | |
int max = problems.length; | |
StringBuffer buffer = new StringBuffer(25); | |
int count = 0; | |
for (int i = 0; i < max; i++) { | |
IProblem problem = problems[i]; | |
if ((problem != null) && (problem.isError())) { | |
buffer.append("\t" +problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$ | |
count++; | |
if (problemLine == 0) { | |
problemLine = problem.getSourceLineNumber(); | |
} | |
} | |
} // insert the top line afterwards, once knowing how many problems we have to consider | |
if (count > 1) { | |
buffer.insert(0, Util.bind("compilation.unresolvedProblems" )); //$NON-NLS-1$ | |
} else { | |
buffer.insert(0, Util.bind("compilation.unresolvedProblem" )); //$NON-NLS-1$ | |
} | |
problemString = buffer.toString(); | |
} | |
// return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "") | |
int[] exceptionHandler = | |
codeStream.generateCodeAttributeForProblemMethod( | |
problemReporter.options.runtimeExceptionNameForCompileError, | |
problemString); | |
completeCodeAttributeForProblemMethod( | |
method, | |
methodBinding, | |
codeAttributeOffset, | |
exceptionHandler, | |
((SourceTypeBinding) methodBinding.declaringClass) | |
.scope | |
.referenceCompilationUnit() | |
.compilationResult | |
.lineSeparatorPositions); | |
completeMethodInfo(methodAttributeOffset, attributeNumber); | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Generate the byte for a problem method info that correspond to a boggus constructor. | |
* Reset the position inside the contents byte array to the savedOffset. | |
* | |
* @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration | |
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding | |
* @param problem org.eclipse.jdt.internal.compiler.problem.Problem[] | |
* @param savedOffset <CODE>int</CODE> | |
*/ | |
public void addProblemConstructor( | |
AbstractMethodDeclaration method, | |
MethodBinding methodBinding, | |
IProblem[] problems, | |
int savedOffset) { | |
// we need to move back the contentsOffset to the value at the beginning of the method | |
contentsOffset = savedOffset; | |
methodCount--; // we need to remove the method that causes the problem | |
addProblemConstructor(method, methodBinding, problems); | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Generate the byte for a problem method info that correspond to a boggus method. | |
* | |
* @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration | |
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding | |
* @param problem org.eclipse.jdt.internal.compiler.problem.Problem[] | |
*/ | |
public void addProblemMethod( | |
AbstractMethodDeclaration method, | |
MethodBinding methodBinding, | |
IProblem[] problems) { | |
if (methodBinding.isAbstract() && methodBinding.declaringClass.isInterface()) { | |
method.abort(AbstractMethodDeclaration.AbortType); | |
} | |
// always clear the strictfp/native/abstract bit for a problem method | |
methodBinding.modifiers &= ~(AccStrictfp | AccNative | AccAbstract); | |
generateMethodInfoHeader(methodBinding); | |
int methodAttributeOffset = contentsOffset; | |
int attributeNumber = generateMethodInfoAttribute(methodBinding); | |
// Code attribute | |
attributeNumber++; | |
int codeAttributeOffset = contentsOffset; | |
generateCodeAttributeHeader(); | |
final ProblemReporter problemReporter = method.scope.problemReporter(); | |
codeStream.reset(method, this); | |
String problemString = "" ; //$NON-NLS-1$ | |
if (problems != null) { | |
int max = problems.length; | |
StringBuffer buffer = new StringBuffer(25); | |
int count = 0; | |
for (int i = 0; i < max; i++) { | |
IProblem problem = problems[i]; | |
if ((problem != null) | |
&& (problem.isError()) | |
&& (problem.getSourceStart() >= method.declarationSourceStart) | |
&& (problem.getSourceEnd() <= method.declarationSourceEnd)) { | |
buffer.append("\t" +problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$ | |
count++; | |
if (problemLine == 0) { | |
problemLine = problem.getSourceLineNumber(); | |
} | |
problems[i] = null; | |
} | |
} // insert the top line afterwards, once knowing how many problems we have to consider | |
if (count > 1) { | |
buffer.insert(0, Util.bind("compilation.unresolvedProblems" )); //$NON-NLS-1$ | |
} else { | |
buffer.insert(0, Util.bind("compilation.unresolvedProblem" )); //$NON-NLS-1$ | |
} | |
problemString = buffer.toString(); | |
} | |
// return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "") | |
int[] exceptionHandler = | |
codeStream.generateCodeAttributeForProblemMethod( | |
problemReporter.options.runtimeExceptionNameForCompileError, | |
problemString); | |
completeCodeAttributeForProblemMethod( | |
method, | |
methodBinding, | |
codeAttributeOffset, | |
exceptionHandler, | |
((SourceTypeBinding) methodBinding.declaringClass) | |
.scope | |
.referenceCompilationUnit() | |
.compilationResult | |
.lineSeparatorPositions); | |
completeMethodInfo(methodAttributeOffset, attributeNumber); | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Generate the byte for a problem method info that correspond to a boggus method. | |
* Reset the position inside the contents byte array to the savedOffset. | |
* | |
* @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration | |
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding | |
* @param problem org.eclipse.jdt.internal.compiler.problem.Problem[] | |
* @param savedOffset <CODE>int</CODE> | |
*/ | |
public void addProblemMethod( | |
AbstractMethodDeclaration method, | |
MethodBinding methodBinding, | |
IProblem[] problems, | |
int savedOffset) { | |
// we need to move back the contentsOffset to the value at the beginning of the method | |
contentsOffset = savedOffset; | |
methodCount--; // we need to remove the method that causes the problem | |
addProblemMethod(method, methodBinding, problems); | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Generate the byte for all the special method infos. | |
* They are: | |
* - synthetic access methods | |
* - default abstract methods | |
*/ | |
public void addSpecialMethods() { | |
// add all methods (default abstract methods and synthetic) | |
// default abstract methods | |
SourceTypeBinding currentBinding = referenceBinding; | |
MethodBinding[] defaultAbstractMethods = | |
currentBinding.getDefaultAbstractMethods(); | |
for (int i = 0, max = defaultAbstractMethods.length; i < max; i++) { | |
generateMethodInfoHeader(defaultAbstractMethods[i]); | |
int methodAttributeOffset = contentsOffset; | |
int attributeNumber = generateMethodInfoAttribute(defaultAbstractMethods[i]); | |
completeMethodInfo(methodAttributeOffset, attributeNumber); | |
} | |
// add synthetic methods infos | |
SyntheticAccessMethodBinding[] syntheticAccessMethods = | |
currentBinding.syntheticAccessMethods(); | |
if (syntheticAccessMethods != null) { | |
for (int i = 0, max = syntheticAccessMethods.length; i < max; i++) { | |
SyntheticAccessMethodBinding accessMethodBinding = syntheticAccessMethods[i]; | |
switch (accessMethodBinding.accessType) { | |
case SyntheticAccessMethodBinding.FieldReadAccess : | |
// generate a method info to emulate an reading access to | |
// a private field | |
addSyntheticFieldReadAccessMethod(syntheticAccessMethods[i]); | |
break; | |
case SyntheticAccessMethodBinding.FieldWriteAccess : | |
// generate a method info to emulate an writing access to | |
// a private field | |
addSyntheticFieldWriteAccessMethod(syntheticAccessMethods[i]); | |
break; | |
case SyntheticAccessMethodBinding.MethodAccess : | |
// generate a method info to emulate an access to a private method | |
addSyntheticMethodAccessMethod(syntheticAccessMethods[i]); | |
break; | |
case SyntheticAccessMethodBinding.ConstructorAccess : | |
// generate a method info to emulate an access to a private method | |
addSyntheticConstructorAccessMethod(syntheticAccessMethods[i]); | |
} | |
} | |
} | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Generate the byte for problem method infos that correspond to missing abstract methods. | |
* http://dev.eclipse.org/bugs/show_bug.cgi?id=3179 | |
* | |
* @param methodDeclarations Array of all missing abstract methods | |
*/ | |
public void generateMissingAbstractMethods(MethodDeclaration[] methodDeclarations, CompilationResult compilationResult) { | |
if (methodDeclarations != null) { | |
for (int i = 0, max = methodDeclarations.length; i < max; i++) { | |
MethodDeclaration methodDeclaration = methodDeclarations[i]; | |
MethodBinding methodBinding = methodDeclaration.binding; | |
String readableName = new String(methodBinding.readableName()); | |
IProblem[] problems = compilationResult.problems; | |
int problemsCount = compilationResult.problemCount; | |
for (int j = 0; j < problemsCount; j++) { | |
IProblem problem = problems[j]; | |
if (problem != null | |
&& problem.getID() == IProblem.AbstractMethodMustBeImplemented | |
&& problem.getMessage().indexOf(readableName) != -1) { | |
// we found a match | |
addMissingAbstractProblemMethod(methodDeclaration, methodBinding, problem, compilationResult); | |
} | |
} | |
} | |
} | |
} | |
private void addMissingAbstractProblemMethod(MethodDeclaration methodDeclaration, MethodBinding methodBinding, IProblem problem, CompilationResult compilationResult) { | |
// always clear the strictfp/native/abstract bit for a problem method | |
methodBinding.modifiers &= ~(AccStrictfp | AccNative | AccAbstract); | |
generateMethodInfoHeader(methodBinding); | |
int methodAttributeOffset = contentsOffset; | |
int attributeNumber = generateMethodInfoAttribute(methodBinding); | |
// Code attribute | |
attributeNumber++; | |
int codeAttributeOffset = contentsOffset; | |
generateCodeAttributeHeader(); | |
StringBuffer buffer = new StringBuffer(25); | |
buffer.append("\t" + problem.getMessage() + "\n" ); //$NON-NLS-1$ //$NON-NLS-2$ | |
buffer.insert(0, Util.bind("compilation.unresolvedProblem" )); //$NON-NLS-1$ | |
String problemString = buffer.toString(); | |
this.problemLine = problem.getSourceLineNumber(); | |
final ProblemReporter problemReporter = methodDeclaration.scope.problemReporter(); | |
codeStream.init(this); | |
codeStream.preserveUnusedLocals = true; | |
codeStream.initializeMaxLocals(methodBinding); | |
// return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "") | |
int[] exceptionHandler = | |
codeStream.generateCodeAttributeForProblemMethod( | |
problemReporter.options.runtimeExceptionNameForCompileError, | |
problemString); | |
completeCodeAttributeForMissingAbstractProblemMethod( | |
methodBinding, | |
codeAttributeOffset, | |
exceptionHandler, | |
compilationResult.lineSeparatorPositions); | |
completeMethodInfo(methodAttributeOffset, attributeNumber); | |
} | |
/** | |
* | |
*/ | |
public void completeCodeAttributeForMissingAbstractProblemMethod( | |
MethodBinding binding, | |
int codeAttributeOffset, | |
int[] exceptionHandler, | |
int[] startLineIndexes) { | |
// reinitialize the localContents with the byte modified by the code stream | |
byte[] localContents = contents = codeStream.bCodeStream; | |
int localContentsOffset = codeStream.classFileOffset; | |
// codeAttributeOffset is the position inside localContents byte array before we started to write// any information about the codeAttribute// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset// to get the right position, 6 for the max_stack etc... | |
int max_stack = codeStream.stackMax; | |
localContents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); | |
localContents[codeAttributeOffset + 7] = (byte) max_stack; | |
int max_locals = codeStream.maxLocals; | |
localContents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); | |
localContents[codeAttributeOffset + 9] = (byte) max_locals; | |
int code_length = codeStream.position; | |
localContents[codeAttributeOffset + 10] = (byte) (code_length >> 24); | |
localContents[codeAttributeOffset + 11] = (byte) (code_length >> 16); | |
localContents[codeAttributeOffset + 12] = (byte) (code_length >> 8); | |
localContents[codeAttributeOffset + 13] = (byte) code_length; | |
// write the exception table | |
int contentsLength; | |
if (localContentsOffset + 50 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 1; | |
int start = exceptionHandler[0]; | |
localContents[localContentsOffset++] = (byte) (start >> 8); | |
localContents[localContentsOffset++] = (byte) start; | |
int end = exceptionHandler[1]; | |
localContents[localContentsOffset++] = (byte) (end >> 8); | |
localContents[localContentsOffset++] = (byte) end; | |
int handlerPC = exceptionHandler[2]; | |
localContents[localContentsOffset++] = (byte) (handlerPC >> 8); | |
localContents[localContentsOffset++] = (byte) handlerPC; | |
int nameIndex = constantPool.literalIndexForJavaLangException(); | |
localContents[localContentsOffset++] = (byte) (nameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) nameIndex; // debug attributes | |
int codeAttributeAttributeOffset = localContentsOffset; | |
int attributeNumber = 0; // leave two bytes for the attribute_length | |
localContentsOffset += 2; // first we handle the linenumber attribute | |
if (codeStream.generateLineNumberAttributes) { | |
/* Create and add the line number attribute (used for debugging) | |
* Build the pairs of: | |
* (bytecodePC lineNumber) | |
* according to the table of start line indexes and the pcToSourceMap table | |
* contained into the codestream | |
*/ | |
int lineNumberNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName); | |
localContents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) lineNumberNameIndex; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 6; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 1; | |
if (problemLine == 0) { | |
problemLine = searchLineNumber(startLineIndexes, binding.sourceStart()); | |
} | |
// first entry at pc = 0 | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = (byte) (problemLine >> 8); | |
localContents[localContentsOffset++] = (byte) problemLine; | |
// now we change the size of the line number attribute | |
attributeNumber++; | |
} | |
// then we do the local variable attribute | |
// update the number of attributes// ensure first that there is enough space available inside the localContents array | |
if (codeAttributeAttributeOffset + 2 | |
>= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
localContents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8); | |
localContents[codeAttributeAttributeOffset] = (byte) attributeNumber; | |
// update the attribute length | |
int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6); | |
localContents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24); | |
localContents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16); | |
localContents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8); | |
localContents[codeAttributeOffset + 5] = (byte) codeAttributeLength; | |
contentsOffset = localContentsOffset; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Generate the byte for a problem method info that correspond to a synthetic method that | |
* generate an access to a private constructor. | |
* | |
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding | |
*/ | |
public void addSyntheticConstructorAccessMethod(SyntheticAccessMethodBinding methodBinding) { | |
generateMethodInfoHeader(methodBinding); | |
// We know that we won't get more than 2 attribute: the code attribute + synthetic attribute | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 2; | |
// Code attribute | |
int codeAttributeOffset = contentsOffset; | |
generateCodeAttributeHeader(); | |
codeStream.init(this); | |
codeStream.generateSyntheticBodyForConstructorAccess(methodBinding); | |
completeCodeAttributeForSyntheticAccessMethod( | |
methodBinding, | |
codeAttributeOffset, | |
((SourceTypeBinding) methodBinding.declaringClass) | |
.scope | |
.referenceCompilationUnit() | |
.compilationResult | |
.lineSeparatorPositions); | |
// add the synthetic attribute | |
int syntheticAttributeNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.SyntheticName); | |
contents[contentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8); | |
contents[contentsOffset++] = (byte) syntheticAttributeNameIndex; | |
// the length of a synthetic attribute is equals to 0 | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Generate the byte for a problem method info that correspond to a synthetic method that | |
* generate an read access to a private field. | |
* | |
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding | |
*/ | |
public void addSyntheticFieldReadAccessMethod(SyntheticAccessMethodBinding methodBinding) { | |
generateMethodInfoHeader(methodBinding); | |
// We know that we won't get more than 2 attribute: the code attribute + synthetic attribute | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 2; | |
// Code attribute | |
int codeAttributeOffset = contentsOffset; | |
generateCodeAttributeHeader(); | |
codeStream.init(this); | |
codeStream.generateSyntheticBodyForFieldReadAccess(methodBinding); | |
completeCodeAttributeForSyntheticAccessMethod( | |
methodBinding, | |
codeAttributeOffset, | |
((SourceTypeBinding) methodBinding.declaringClass) | |
.scope | |
.referenceCompilationUnit() | |
.compilationResult | |
.lineSeparatorPositions); | |
// add the synthetic attribute | |
int syntheticAttributeNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.SyntheticName); | |
contents[contentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8); | |
contents[contentsOffset++] = (byte) syntheticAttributeNameIndex; | |
// the length of a synthetic attribute is equals to 0 | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Generate the byte for a problem method info that correspond to a synthetic method that | |
* generate an write access to a private field. | |
* | |
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding | |
*/ | |
public void addSyntheticFieldWriteAccessMethod(SyntheticAccessMethodBinding methodBinding) { | |
generateMethodInfoHeader(methodBinding); | |
// We know that we won't get more than 2 attribute: the code attribute + synthetic attribute | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 2; | |
// Code attribute | |
int codeAttributeOffset = contentsOffset; | |
generateCodeAttributeHeader(); | |
codeStream.init(this); | |
codeStream.generateSyntheticBodyForFieldWriteAccess(methodBinding); | |
completeCodeAttributeForSyntheticAccessMethod( | |
methodBinding, | |
codeAttributeOffset, | |
((SourceTypeBinding) methodBinding.declaringClass) | |
.scope | |
.referenceCompilationUnit() | |
.compilationResult | |
.lineSeparatorPositions); | |
// add the synthetic attribute | |
int syntheticAttributeNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.SyntheticName); | |
contents[contentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8); | |
contents[contentsOffset++] = (byte) syntheticAttributeNameIndex; | |
// the length of a synthetic attribute is equals to 0 | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Generate the byte for a problem method info that correspond to a synthetic method that | |
* generate an access to a private method. | |
* | |
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding | |
*/ | |
public void addSyntheticMethodAccessMethod(SyntheticAccessMethodBinding methodBinding) { | |
generateMethodInfoHeader(methodBinding); | |
// We know that we won't get more than 2 attribute: the code attribute + synthetic attribute | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 2; | |
// Code attribute | |
int codeAttributeOffset = contentsOffset; | |
generateCodeAttributeHeader(); | |
codeStream.init(this); | |
codeStream.generateSyntheticBodyForMethodAccess(methodBinding); | |
completeCodeAttributeForSyntheticAccessMethod( | |
methodBinding, | |
codeAttributeOffset, | |
((SourceTypeBinding) methodBinding.declaringClass) | |
.scope | |
.referenceCompilationUnit() | |
.compilationResult | |
.lineSeparatorPositions); | |
// add the synthetic attribute | |
int syntheticAttributeNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.SyntheticName); | |
contents[contentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8); | |
contents[contentsOffset++] = (byte) syntheticAttributeNameIndex; | |
// the length of a synthetic attribute is equals to 0 | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Build all the directories and subdirectories corresponding to the packages names | |
* into the directory specified in parameters. | |
* | |
* outputPath is formed like: | |
* c:\temp\ the last character is a file separator | |
* relativeFileName is formed like: | |
* java\lang\String.class * | |
* | |
* @param outputPath java.lang.String | |
* @param relativeFileName java.lang.String | |
* @return java.lang.String | |
*/ | |
public static String buildAllDirectoriesInto( | |
String outputPath, | |
String relativeFileName) | |
throws IOException { | |
char fileSeparatorChar = File.separatorChar; | |
String fileSeparator = File.separator; | |
File f; | |
// First we ensure that the outputPath exists | |
outputPath = outputPath.replace('/', fileSeparatorChar); | |
// To be able to pass the mkdirs() method we need to remove the extra file separator at the end of the outDir name | |
if (outputPath.endsWith(fileSeparator)) { | |
outputPath = outputPath.substring(0, outputPath.length() - 1); | |
} | |
f = new File(outputPath); | |
if (f.exists()) { | |
if (!f.isDirectory()) { | |
System.out.println(Util.bind("output.isFile" , f.getAbsolutePath())); //$NON-NLS-1$ | |
throw new IOException(Util.bind("output.isFileNotDirectory" )); //$NON-NLS-1$ | |
} | |
} else { | |
// we have to create that directory | |
if (!f.mkdirs()) { | |
System.out.println(Util.bind("output.dirName" , f.getAbsolutePath())); //$NON-NLS-1$ | |
throw new IOException(Util.bind("output.notValidAll" )); //$NON-NLS-1$ | |
} | |
} | |
StringBuffer outDir = new StringBuffer(outputPath); | |
outDir.append(fileSeparator); | |
StringTokenizer tokenizer = | |
new StringTokenizer(relativeFileName, fileSeparator); | |
String token = tokenizer.nextToken(); | |
while (tokenizer.hasMoreTokens()) { | |
f = new File(outDir.append(token).append(fileSeparator).toString()); | |
if (f.exists()) { | |
// The outDir already exists, so we proceed the next entry | |
// System.out.println("outDir: " + outDir + " already exists."); | |
} else { | |
// Need to add the outDir | |
if (!f.mkdir()) { | |
System.out.println(Util.bind("output.fileName" , f.getName())); //$NON-NLS-1$ | |
throw new IOException(Util.bind("output.notValid" )); //$NON-NLS-1$ | |
} | |
} | |
token = tokenizer.nextToken(); | |
} | |
// token contains the last one | |
return outDir.append(token).toString(); | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* That method completes the creation of the code attribute by setting | |
* - the attribute_length | |
* - max_stack | |
* - max_locals | |
* - code_length | |
* - exception table | |
* - and debug attributes if necessary. | |
* | |
* @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream | |
* @param codeAttributeOffset <CODE>int</CODE> | |
*/ | |
public void completeCodeAttribute(int codeAttributeOffset) { | |
// reinitialize the localContents with the byte modified by the code stream | |
byte[] localContents = contents = codeStream.bCodeStream; | |
int localContentsOffset = codeStream.classFileOffset; | |
// codeAttributeOffset is the position inside localContents byte array before we started to write | |
// any information about the codeAttribute | |
// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset | |
// to get the right position, 6 for the max_stack etc... | |
int contentsLength; | |
int code_length = codeStream.position; | |
if (code_length > 65535) { | |
codeStream.methodDeclaration.scope.problemReporter().bytecodeExceeds64KLimit( | |
codeStream.methodDeclaration); | |
} | |
if (localContentsOffset + 20 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
int max_stack = codeStream.stackMax; | |
localContents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); | |
localContents[codeAttributeOffset + 7] = (byte) max_stack; | |
int max_locals = codeStream.maxLocals; | |
localContents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); | |
localContents[codeAttributeOffset + 9] = (byte) max_locals; | |
localContents[codeAttributeOffset + 10] = (byte) (code_length >> 24); | |
localContents[codeAttributeOffset + 11] = (byte) (code_length >> 16); | |
localContents[codeAttributeOffset + 12] = (byte) (code_length >> 8); | |
localContents[codeAttributeOffset + 13] = (byte) code_length; | |
// write the exception table | |
int exceptionHandlersNumber = codeStream.exceptionHandlersNumber; | |
ExceptionLabel[] exceptionHandlers = codeStream.exceptionHandlers; | |
int exSize; | |
if (localContentsOffset + (exSize = (exceptionHandlersNumber * 8 + 2)) | |
>= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = | |
contents = | |
new byte[contentsLength + (exSize > INCREMENT_SIZE ? exSize : INCREMENT_SIZE)]), | |
0, | |
contentsLength); | |
} | |
// there is no exception table, so we need to offset by 2 the current offset and move | |
// on the attribute generation | |
localContents[localContentsOffset++] = (byte) (exceptionHandlersNumber >> 8); | |
localContents[localContentsOffset++] = (byte) exceptionHandlersNumber; | |
for (int i = 0; i < exceptionHandlersNumber; i++) { | |
ExceptionLabel exceptionHandler = exceptionHandlers[i]; | |
int start = exceptionHandler.start; | |
localContents[localContentsOffset++] = (byte) (start >> 8); | |
localContents[localContentsOffset++] = (byte) start; | |
int end = exceptionHandler.end; | |
localContents[localContentsOffset++] = (byte) (end >> 8); | |
localContents[localContentsOffset++] = (byte) end; | |
int handlerPC = exceptionHandler.position; | |
localContents[localContentsOffset++] = (byte) (handlerPC >> 8); | |
localContents[localContentsOffset++] = (byte) handlerPC; | |
if (exceptionHandler.exceptionType == null) { | |
// any exception handler | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
} else { | |
int nameIndex; | |
if (exceptionHandler.exceptionType == TypeBinding.NullBinding) { | |
/* represents ClassNotFoundException, see class literal access*/ | |
nameIndex = constantPool.literalIndexForJavaLangClassNotFoundException(); | |
} else { | |
nameIndex = constantPool.literalIndex(exceptionHandler.exceptionType); | |
} | |
localContents[localContentsOffset++] = (byte) (nameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) nameIndex; | |
} | |
} | |
// debug attributes | |
int codeAttributeAttributeOffset = localContentsOffset; | |
int attributeNumber = 0; | |
// leave two bytes for the attribute_length | |
localContentsOffset += 2; | |
// first we handle the linenumber attribute | |
if (codeStream.generateLineNumberAttributes) { | |
/* Create and add the line number attribute (used for debugging) | |
* Build the pairs of: | |
* (bytecodePC lineNumber) | |
* according to the table of start line indexes and the pcToSourceMap table | |
* contained into the codestream | |
*/ | |
int[] pcToSourceMapTable; | |
if (((pcToSourceMapTable = codeStream.pcToSourceMap) != null) | |
&& (codeStream.pcToSourceMapSize != 0)) { | |
int lineNumberNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName); | |
if (localContentsOffset + 8 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
localContents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) lineNumberNameIndex; | |
int lineNumberTableOffset = localContentsOffset; | |
localContentsOffset += 6; | |
// leave space for attribute_length and line_number_table_length | |
int numberOfEntries = 0; | |
int length = codeStream.pcToSourceMapSize; | |
for (int i = 0; i < length;) { | |
// write the entry | |
if (localContentsOffset + 4 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
int pc = pcToSourceMapTable[i++]; | |
localContents[localContentsOffset++] = (byte) (pc >> 8); | |
localContents[localContentsOffset++] = (byte) pc; | |
int lineNumber = pcToSourceMapTable[i++]; | |
localContents[localContentsOffset++] = (byte) (lineNumber >> 8); | |
localContents[localContentsOffset++] = (byte) lineNumber; | |
numberOfEntries++; | |
} | |
// now we change the size of the line number attribute | |
int lineNumberAttr_length = numberOfEntries * 4 + 2; | |
localContents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 24); | |
localContents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 16); | |
localContents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 8); | |
localContents[lineNumberTableOffset++] = (byte) lineNumberAttr_length; | |
localContents[lineNumberTableOffset++] = (byte) (numberOfEntries >> 8); | |
localContents[lineNumberTableOffset++] = (byte) numberOfEntries; | |
attributeNumber++; | |
} | |
} | |
// then we do the local variable attribute | |
if (codeStream.generateLocalVariableTableAttributes) { | |
int localVariableTableOffset = localContentsOffset; | |
int numberOfEntries = 0; | |
int localVariableNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName); | |
if (localContentsOffset + 8 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
localContents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) localVariableNameIndex; | |
localContentsOffset += 6; | |
// leave space for attribute_length and local_variable_table_length | |
int nameIndex; | |
int descriptorIndex; | |
if (!codeStream.methodDeclaration.isStatic()) { | |
numberOfEntries++; | |
if (localContentsOffset + 10 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
localContentsOffset += 2; // the startPC for this is always 0 | |
localContents[localContentsOffset++] = (byte) (code_length >> 8); | |
localContents[localContentsOffset++] = (byte) code_length; | |
nameIndex = constantPool.literalIndex(QualifiedNamesConstants.This); | |
localContents[localContentsOffset++] = (byte) (nameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) nameIndex; | |
descriptorIndex = | |
constantPool.literalIndex( | |
codeStream.methodDeclaration.binding.declaringClass.signature()); | |
localContents[localContentsOffset++] = (byte) (descriptorIndex >> 8); | |
localContents[localContentsOffset++] = (byte) descriptorIndex; | |
localContentsOffset += 2; // the resolved position for this is always 0 | |
} | |
for (int i = 0; i < codeStream.allLocalsCounter; i++) { | |
LocalVariableBinding localVariable = codeStream.locals[i]; | |
for (int j = 0; j < localVariable.initializationCount; j++) { | |
int startPC = localVariable.initializationPCs[j << 1]; | |
int endPC = localVariable.initializationPCs[(j << 1) + 1]; | |
if (startPC != endPC) { // only entries for non zero length | |
if (endPC == -1) { | |
localVariable.declaringScope.problemReporter().abortDueToInternalError( | |
Util.bind("abort.invalidAttribute" , new String(localVariable.name)), //$NON-NLS-1$ | |
(AstNode) localVariable.declaringScope.methodScope().referenceContext); | |
} | |
if (localContentsOffset + 10 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
// now we can safely add the local entry | |
numberOfEntries++; | |
localContents[localContentsOffset++] = (byte) (startPC >> 8); | |
localContents[localContentsOffset++] = (byte) startPC; | |
int length = endPC - startPC; | |
localContents[localContentsOffset++] = (byte) (length >> 8); | |
localContents[localContentsOffset++] = (byte) length; | |
nameIndex = constantPool.literalIndex(localVariable.name); | |
localContents[localContentsOffset++] = (byte) (nameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) nameIndex; | |
descriptorIndex = constantPool.literalIndex(localVariable.type.signature()); | |
localContents[localContentsOffset++] = (byte) (descriptorIndex >> 8); | |
localContents[localContentsOffset++] = (byte) descriptorIndex; | |
int resolvedPosition = localVariable.resolvedPosition; | |
localContents[localContentsOffset++] = (byte) (resolvedPosition >> 8); | |
localContents[localContentsOffset++] = (byte) resolvedPosition; | |
} | |
} | |
} | |
int value = numberOfEntries * 10 + 2; | |
localVariableTableOffset += 2; | |
localContents[localVariableTableOffset++] = (byte) (value >> 24); | |
localContents[localVariableTableOffset++] = (byte) (value >> 16); | |
localContents[localVariableTableOffset++] = (byte) (value >> 8); | |
localContents[localVariableTableOffset++] = (byte) value; | |
localContents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8); | |
localContents[localVariableTableOffset] = (byte) numberOfEntries; | |
attributeNumber++; | |
} | |
// update the number of attributes | |
// ensure first that there is enough space available inside the localContents array | |
if (codeAttributeAttributeOffset + 2 | |
>= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
localContents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8); | |
localContents[codeAttributeAttributeOffset] = (byte) attributeNumber; | |
// update the attribute length | |
int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6); | |
localContents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24); | |
localContents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16); | |
localContents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8); | |
localContents[codeAttributeOffset + 5] = (byte) codeAttributeLength; | |
contentsOffset = localContentsOffset; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* That method completes the creation of the code attribute by setting | |
* - the attribute_length | |
* - max_stack | |
* - max_locals | |
* - code_length | |
* - exception table | |
* - and debug attributes if necessary. | |
* | |
* @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream | |
* @param codeAttributeOffset <CODE>int</CODE> | |
*/ | |
public void completeCodeAttributeForClinit(int codeAttributeOffset) { | |
// reinitialize the contents with the byte modified by the code stream | |
byte[] localContents = contents = codeStream.bCodeStream; | |
int localContentsOffset = codeStream.classFileOffset; | |
// codeAttributeOffset is the position inside contents byte array before we started to write | |
// any information about the codeAttribute | |
// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset | |
// to get the right position, 6 for the max_stack etc... | |
int contentsLength; | |
int code_length = codeStream.position; | |
if (code_length > 65535) { | |
codeStream.methodDeclaration.scope.problemReporter().bytecodeExceeds64KLimit( | |
codeStream.methodDeclaration.scope.referenceType()); | |
} | |
if (localContentsOffset + 20 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
int max_stack = codeStream.stackMax; | |
localContents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); | |
localContents[codeAttributeOffset + 7] = (byte) max_stack; | |
int max_locals = codeStream.maxLocals; | |
localContents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); | |
localContents[codeAttributeOffset + 9] = (byte) max_locals; | |
localContents[codeAttributeOffset + 10] = (byte) (code_length >> 24); | |
localContents[codeAttributeOffset + 11] = (byte) (code_length >> 16); | |
localContents[codeAttributeOffset + 12] = (byte) (code_length >> 8); | |
localContents[codeAttributeOffset + 13] = (byte) code_length; | |
// write the exception table | |
int exceptionHandlersNumber = codeStream.exceptionHandlersNumber; | |
ExceptionLabel[] exceptionHandlers = codeStream.exceptionHandlers; | |
int exSize; | |
if (localContentsOffset + (exSize = (exceptionHandlersNumber * 8 + 2)) | |
>= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = | |
contents = | |
new byte[contentsLength + (exSize > INCREMENT_SIZE ? exSize : INCREMENT_SIZE)]), | |
0, | |
contentsLength); | |
} | |
// there is no exception table, so we need to offset by 2 the current offset and move | |
// on the attribute generation | |
localContents[localContentsOffset++] = (byte) (exceptionHandlersNumber >> 8); | |
localContents[localContentsOffset++] = (byte) exceptionHandlersNumber; | |
for (int i = 0; i < exceptionHandlersNumber; i++) { | |
ExceptionLabel exceptionHandler = exceptionHandlers[i]; | |
int start = exceptionHandler.start; | |
localContents[localContentsOffset++] = (byte) (start >> 8); | |
localContents[localContentsOffset++] = (byte) start; | |
int end = exceptionHandler.end; | |
localContents[localContentsOffset++] = (byte) (end >> 8); | |
localContents[localContentsOffset++] = (byte) end; | |
int handlerPC = exceptionHandler.position; | |
localContents[localContentsOffset++] = (byte) (handlerPC >> 8); | |
localContents[localContentsOffset++] = (byte) handlerPC; | |
if (exceptionHandler.exceptionType == null) { | |
// any exception handler | |
localContentsOffset += 2; | |
} else { | |
int nameIndex; | |
if (exceptionHandler.exceptionType == TypeBinding.NullBinding) { | |
/* represents denote ClassNotFoundException, see class literal access*/ | |
nameIndex = constantPool.literalIndexForJavaLangClassNotFoundException(); | |
} else { | |
nameIndex = constantPool.literalIndex(exceptionHandler.exceptionType); | |
} | |
localContents[localContentsOffset++] = (byte) (nameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) nameIndex; | |
} | |
} | |
// debug attributes | |
int codeAttributeAttributeOffset = localContentsOffset; | |
int attributeNumber = 0; | |
// leave two bytes for the attribute_length | |
localContentsOffset += 2; | |
// first we handle the linenumber attribute | |
if (codeStream.generateLineNumberAttributes) { | |
/* Create and add the line number attribute (used for debugging) | |
* Build the pairs of: | |
* (bytecodePC lineNumber) | |
* according to the table of start line indexes and the pcToSourceMap table | |
* contained into the codestream | |
*/ | |
int[] pcToSourceMapTable; | |
if (((pcToSourceMapTable = codeStream.pcToSourceMap) != null) | |
&& (codeStream.pcToSourceMapSize != 0)) { | |
int lineNumberNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName); | |
if (localContentsOffset + 8 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
localContents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) lineNumberNameIndex; | |
int lineNumberTableOffset = localContentsOffset; | |
localContentsOffset += 6; | |
// leave space for attribute_length and line_number_table_length | |
int numberOfEntries = 0; | |
int length = codeStream.pcToSourceMapSize; | |
for (int i = 0; i < length;) { | |
// write the entry | |
if (localContentsOffset + 4 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
int pc = pcToSourceMapTable[i++]; | |
localContents[localContentsOffset++] = (byte) (pc >> 8); | |
localContents[localContentsOffset++] = (byte) pc; | |
int lineNumber = pcToSourceMapTable[i++]; | |
localContents[localContentsOffset++] = (byte) (lineNumber >> 8); | |
localContents[localContentsOffset++] = (byte) lineNumber; | |
numberOfEntries++; | |
} | |
// now we change the size of the line number attribute | |
int lineNumberAttr_length = numberOfEntries * 4 + 2; | |
localContents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 24); | |
localContents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 16); | |
localContents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 8); | |
localContents[lineNumberTableOffset++] = (byte) lineNumberAttr_length; | |
localContents[lineNumberTableOffset++] = (byte) (numberOfEntries >> 8); | |
localContents[lineNumberTableOffset++] = (byte) numberOfEntries; | |
attributeNumber++; | |
} | |
} | |
// then we do the local variable attribute | |
if (codeStream.generateLocalVariableTableAttributes) { | |
int localVariableTableOffset = localContentsOffset; | |
int numberOfEntries = 0; | |
// codeAttribute.addLocalVariableTableAttribute(this); | |
if ((codeStream.pcToSourceMap != null) | |
&& (codeStream.pcToSourceMapSize != 0)) { | |
int localVariableNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName); | |
if (localContentsOffset + 8 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
localContents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) localVariableNameIndex; | |
localContentsOffset += 6; | |
// leave space for attribute_length and local_variable_table_length | |
int nameIndex; | |
int descriptorIndex; | |
for (int i = 0; i < codeStream.allLocalsCounter; i++) { | |
LocalVariableBinding localVariable = codeStream.locals[i]; | |
for (int j = 0; j < localVariable.initializationCount; j++) { | |
int startPC = localVariable.initializationPCs[j << 1]; | |
int endPC = localVariable.initializationPCs[(j << 1) + 1]; | |
if (startPC != endPC) { // only entries for non zero length | |
if (endPC == -1) { | |
localVariable.declaringScope.problemReporter().abortDueToInternalError( | |
Util.bind("abort.invalidAttribute" , new String(localVariable.name)), //$NON-NLS-1$ | |
(AstNode) localVariable.declaringScope.methodScope().referenceContext); | |
} | |
if (localContentsOffset + 10 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
// now we can safely add the local entry | |
numberOfEntries++; | |
localContents[localContentsOffset++] = (byte) (startPC >> 8); | |
localContents[localContentsOffset++] = (byte) startPC; | |
int length = endPC - startPC; | |
localContents[localContentsOffset++] = (byte) (length >> 8); | |
localContents[localContentsOffset++] = (byte) length; | |
nameIndex = constantPool.literalIndex(localVariable.name); | |
localContents[localContentsOffset++] = (byte) (nameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) nameIndex; | |
descriptorIndex = constantPool.literalIndex(localVariable.type.signature()); | |
localContents[localContentsOffset++] = (byte) (descriptorIndex >> 8); | |
localContents[localContentsOffset++] = (byte) descriptorIndex; | |
int resolvedPosition = localVariable.resolvedPosition; | |
localContents[localContentsOffset++] = (byte) (resolvedPosition >> 8); | |
localContents[localContentsOffset++] = (byte) resolvedPosition; | |
} | |
} | |
} | |
int value = numberOfEntries * 10 + 2; | |
localVariableTableOffset += 2; | |
localContents[localVariableTableOffset++] = (byte) (value >> 24); | |
localContents[localVariableTableOffset++] = (byte) (value >> 16); | |
localContents[localVariableTableOffset++] = (byte) (value >> 8); | |
localContents[localVariableTableOffset++] = (byte) value; | |
localContents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8); | |
localContents[localVariableTableOffset] = (byte) numberOfEntries; | |
attributeNumber++; | |
} | |
} | |
// update the number of attributes | |
// ensure first that there is enough space available inside the contents array | |
if (codeAttributeAttributeOffset + 2 | |
>= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
localContents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8); | |
localContents[codeAttributeAttributeOffset] = (byte) attributeNumber; | |
// update the attribute length | |
int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6); | |
localContents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24); | |
localContents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16); | |
localContents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8); | |
localContents[codeAttributeOffset + 5] = (byte) codeAttributeLength; | |
contentsOffset = localContentsOffset; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* That method completes the creation of the code attribute by setting | |
* - the attribute_length | |
* - max_stack | |
* - max_locals | |
* - code_length | |
* - exception table | |
* - and debug attributes if necessary. | |
* | |
* @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream | |
* @param codeAttributeOffset <CODE>int</CODE> | |
* @param exceptionHandler int[] | |
* @param startIndexes int[] | |
*/ | |
public void completeCodeAttributeForClinit( | |
int codeAttributeOffset, | |
int[] exceptionHandler, | |
int[] startLineIndexes) { | |
// reinitialize the contents with the byte modified by the code stream | |
byte[] localContents = contents = codeStream.bCodeStream; | |
int localContentsOffset = codeStream.classFileOffset; | |
// codeAttributeOffset is the position inside contents byte array before we started to write | |
// any information about the codeAttribute | |
// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset | |
// to get the right position, 6 for the max_stack etc... | |
int contentsLength; | |
int code_length = codeStream.position; | |
if (code_length > 65535) { | |
codeStream.methodDeclaration.scope.problemReporter().bytecodeExceeds64KLimit( | |
codeStream.methodDeclaration.scope.referenceType()); | |
} | |
if (localContentsOffset + 20 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
int max_stack = codeStream.stackMax; | |
localContents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); | |
localContents[codeAttributeOffset + 7] = (byte) max_stack; | |
int max_locals = codeStream.maxLocals; | |
localContents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); | |
localContents[codeAttributeOffset + 9] = (byte) max_locals; | |
localContents[codeAttributeOffset + 10] = (byte) (code_length >> 24); | |
localContents[codeAttributeOffset + 11] = (byte) (code_length >> 16); | |
localContents[codeAttributeOffset + 12] = (byte) (code_length >> 8); | |
localContents[codeAttributeOffset + 13] = (byte) code_length; | |
// write the exception table | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 1; | |
int start = exceptionHandler[0]; | |
localContents[localContentsOffset++] = (byte) (start >> 8); | |
localContents[localContentsOffset++] = (byte) start; | |
int end = exceptionHandler[1]; | |
localContents[localContentsOffset++] = (byte) (end >> 8); | |
localContents[localContentsOffset++] = (byte) end; | |
int handlerPC = exceptionHandler[2]; | |
localContents[localContentsOffset++] = (byte) (handlerPC >> 8); | |
localContents[localContentsOffset++] = (byte) handlerPC; | |
int nameIndex = constantPool.literalIndexForJavaLangException(); | |
localContents[localContentsOffset++] = (byte) (nameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) nameIndex; | |
// debug attributes | |
int codeAttributeAttributeOffset = localContentsOffset; | |
int attributeNumber = 0; // leave two bytes for the attribute_length | |
localContentsOffset += 2; // first we handle the linenumber attribute | |
// first we handle the linenumber attribute | |
if (codeStream.generateLineNumberAttributes) { | |
/* Create and add the line number attribute (used for debugging) | |
* Build the pairs of: | |
* (bytecodePC lineNumber) | |
* according to the table of start line indexes and the pcToSourceMap table | |
* contained into the codestream | |
*/ | |
int lineNumberNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName); | |
localContents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) lineNumberNameIndex; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 6; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 1; | |
// first entry at pc = 0 | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = (byte) (problemLine >> 8); | |
localContents[localContentsOffset++] = (byte) problemLine; | |
// now we change the size of the line number attribute | |
attributeNumber++; | |
} | |
// then we do the local variable attribute | |
if (codeStream.generateLocalVariableTableAttributes) { | |
int localVariableNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName); | |
if (localContentsOffset + 8 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
localContents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) localVariableNameIndex; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 2; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
attributeNumber++; | |
} | |
// update the number of attributes | |
// ensure first that there is enough space available inside the contents array | |
if (codeAttributeAttributeOffset + 2 | |
>= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
localContents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8); | |
localContents[codeAttributeAttributeOffset] = (byte) attributeNumber; | |
// update the attribute length | |
int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6); | |
localContents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24); | |
localContents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16); | |
localContents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8); | |
localContents[codeAttributeOffset + 5] = (byte) codeAttributeLength; | |
contentsOffset = localContentsOffset; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* That method completes the creation of the code attribute by setting | |
* - the attribute_length | |
* - max_stack | |
* - max_locals | |
* - code_length | |
* - exception table | |
* - and debug attributes if necessary. | |
* | |
* @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream | |
* @param codeAttributeOffset <CODE>int</CODE> | |
* @param exceptionHandler int[] | |
*/ | |
public void completeCodeAttributeForProblemMethod( | |
AbstractMethodDeclaration method, | |
MethodBinding binding, | |
int codeAttributeOffset, | |
int[] exceptionHandler, | |
int[] startLineIndexes) { | |
// reinitialize the localContents with the byte modified by the code stream | |
byte[] localContents = contents = codeStream.bCodeStream; | |
int localContentsOffset = codeStream.classFileOffset; | |
// codeAttributeOffset is the position inside localContents byte array before we started to write// any information about the codeAttribute// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset// to get the right position, 6 for the max_stack etc... | |
int max_stack = codeStream.stackMax; | |
localContents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); | |
localContents[codeAttributeOffset + 7] = (byte) max_stack; | |
int max_locals = codeStream.maxLocals; | |
localContents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); | |
localContents[codeAttributeOffset + 9] = (byte) max_locals; | |
int code_length = codeStream.position; | |
localContents[codeAttributeOffset + 10] = (byte) (code_length >> 24); | |
localContents[codeAttributeOffset + 11] = (byte) (code_length >> 16); | |
localContents[codeAttributeOffset + 12] = (byte) (code_length >> 8); | |
localContents[codeAttributeOffset + 13] = (byte) code_length; | |
// write the exception table | |
int contentsLength; | |
if (localContentsOffset + 50 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 1; | |
int start = exceptionHandler[0]; | |
localContents[localContentsOffset++] = (byte) (start >> 8); | |
localContents[localContentsOffset++] = (byte) start; | |
int end = exceptionHandler[1]; | |
localContents[localContentsOffset++] = (byte) (end >> 8); | |
localContents[localContentsOffset++] = (byte) end; | |
int handlerPC = exceptionHandler[2]; | |
localContents[localContentsOffset++] = (byte) (handlerPC >> 8); | |
localContents[localContentsOffset++] = (byte) handlerPC; | |
int nameIndex = constantPool.literalIndexForJavaLangException(); | |
localContents[localContentsOffset++] = (byte) (nameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) nameIndex; // debug attributes | |
int codeAttributeAttributeOffset = localContentsOffset; | |
int attributeNumber = 0; // leave two bytes for the attribute_length | |
localContentsOffset += 2; // first we handle the linenumber attribute | |
if (codeStream.generateLineNumberAttributes) { | |
/* Create and add the line number attribute (used for debugging) | |
* Build the pairs of: | |
* (bytecodePC lineNumber) | |
* according to the table of start line indexes and the pcToSourceMap table | |
* contained into the codestream | |
*/ | |
int lineNumberNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName); | |
localContents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) lineNumberNameIndex; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 6; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 1; | |
if (problemLine == 0) { | |
problemLine = searchLineNumber(startLineIndexes, binding.sourceStart()); | |
} | |
// first entry at pc = 0 | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = (byte) (problemLine >> 8); | |
localContents[localContentsOffset++] = (byte) problemLine; | |
// now we change the size of the line number attribute | |
attributeNumber++; | |
} | |
// then we do the local variable attribute | |
if (codeStream.generateLocalVariableTableAttributes) { | |
// compute the resolved position for the arguments of the method | |
int argSize; | |
int localVariableTableOffset = localContentsOffset; | |
int numberOfEntries = 0; | |
// codeAttribute.addLocalVariableTableAttribute(this); | |
int localVariableNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName); | |
if (localContentsOffset + 8 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
localContents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) localVariableNameIndex; | |
localContentsOffset += 6; | |
// leave space for attribute_length and local_variable_table_length | |
int descriptorIndex; | |
if (!codeStream.methodDeclaration.isStatic()) { | |
numberOfEntries++; | |
if (localContentsOffset + 10 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = (byte) (code_length >> 8); | |
localContents[localContentsOffset++] = (byte) code_length; | |
nameIndex = constantPool.literalIndex(QualifiedNamesConstants.This); | |
localContents[localContentsOffset++] = (byte) (nameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) nameIndex; | |
descriptorIndex = | |
constantPool.literalIndex( | |
codeStream.methodDeclaration.binding.declaringClass.signature()); | |
localContents[localContentsOffset++] = (byte) (descriptorIndex >> 8); | |
localContents[localContentsOffset++] = (byte) descriptorIndex; | |
// the resolved position for this is always 0 | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
} | |
if (binding.isConstructor()) { | |
ReferenceBinding declaringClass = binding.declaringClass; | |
if (declaringClass.isNestedType()) { | |
NestedTypeBinding methodDeclaringClass = (NestedTypeBinding) declaringClass; | |
argSize = methodDeclaringClass.syntheticArgumentsOffset; | |
SyntheticArgumentBinding[] syntheticArguments; | |
if ((syntheticArguments = methodDeclaringClass.syntheticEnclosingInstances()) | |
!= null) { | |
for (int i = 0, max = syntheticArguments.length; i < max; i++) { | |
LocalVariableBinding localVariable = syntheticArguments[i]; | |
if (localContentsOffset + 10 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
// now we can safely add the local entry | |
numberOfEntries++; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = (byte) (code_length >> 8); | |
localContents[localContentsOffset++] = (byte) code_length; | |
nameIndex = constantPool.literalIndex(localVariable.name); | |
localContents[localContentsOffset++] = (byte) (nameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) nameIndex; | |
descriptorIndex = constantPool.literalIndex(localVariable.type.signature()); | |
localContents[localContentsOffset++] = (byte) (descriptorIndex >> 8); | |
localContents[localContentsOffset++] = (byte) descriptorIndex; | |
int resolvedPosition = localVariable.resolvedPosition; | |
localContents[localContentsOffset++] = (byte) (resolvedPosition >> 8); | |
localContents[localContentsOffset++] = (byte) resolvedPosition; | |
} | |
} | |
} else { | |
argSize = 1; | |
} | |
} else { | |
argSize = binding.isStatic() ? 0 : 1; | |
} | |
if (method.binding != null) { | |
TypeBinding[] parameters = method.binding.parameters; | |
Argument[] arguments = method.arguments; | |
if ((parameters != null) && (arguments != null)) { | |
for (int i = 0, max = parameters.length; i < max; i++) { | |
TypeBinding argumentBinding = parameters[i]; | |
if (localContentsOffset + 10 >= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
// now we can safely add the local entry | |
numberOfEntries++; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = 0; | |
localContents[localContentsOffset++] = (byte) (code_length >> 8); | |
localContents[localContentsOffset++] = (byte) code_length; | |
nameIndex = constantPool.literalIndex(arguments[i].name); | |
localContents[localContentsOffset++] = (byte) (nameIndex >> 8); | |
localContents[localContentsOffset++] = (byte) nameIndex; | |
descriptorIndex = constantPool.literalIndex(argumentBinding.signature()); | |
localContents[localContentsOffset++] = (byte) (descriptorIndex >> 8); | |
localContents[localContentsOffset++] = (byte) descriptorIndex; | |
int resolvedPosition = argSize; | |
if ((argumentBinding == TypeBinding.LongBinding) | |
|| (argumentBinding == TypeBinding.DoubleBinding)) | |
argSize += 2; | |
else | |
argSize++; | |
localContents[localContentsOffset++] = (byte) (resolvedPosition >> 8); | |
localContents[localContentsOffset++] = (byte) resolvedPosition; | |
} | |
} | |
} | |
int value = numberOfEntries * 10 + 2; | |
localVariableTableOffset += 2; | |
localContents[localVariableTableOffset++] = (byte) (value >> 24); | |
localContents[localVariableTableOffset++] = (byte) (value >> 16); | |
localContents[localVariableTableOffset++] = (byte) (value >> 8); | |
localContents[localVariableTableOffset++] = (byte) value; | |
localContents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8); | |
localContents[localVariableTableOffset] = (byte) numberOfEntries; | |
attributeNumber++; | |
} | |
// update the number of attributes// ensure first that there is enough space available inside the localContents array | |
if (codeAttributeAttributeOffset + 2 | |
>= (contentsLength = localContents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(localContents = contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
localContents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8); | |
localContents[codeAttributeAttributeOffset] = (byte) attributeNumber; | |
// update the attribute length | |
int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6); | |
localContents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24); | |
localContents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16); | |
localContents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8); | |
localContents[codeAttributeOffset + 5] = (byte) codeAttributeLength; | |
contentsOffset = localContentsOffset; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* That method completes the creation of the code attribute by setting | |
* - the attribute_length | |
* - max_stack | |
* - max_locals | |
* - code_length | |
* - exception table | |
* - and debug attributes if necessary. | |
* | |
* @param binding org.eclipse.jdt.internal.compiler.lookup.SyntheticAccessMethodBinding | |
* @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream | |
* @param codeAttributeOffset <CODE>int</CODE> | |
*/ | |
public void completeCodeAttributeForSyntheticAccessMethod( | |
SyntheticAccessMethodBinding binding, | |
int codeAttributeOffset, | |
int[] startLineIndexes) { | |
// reinitialize the contents with the byte modified by the code stream | |
contents = codeStream.bCodeStream; | |
int localContentsOffset = codeStream.classFileOffset; | |
// codeAttributeOffset is the position inside contents byte array before we started to write | |
// any information about the codeAttribute | |
// That means that to write the attribute_length you need to offset by 2 the value of codeAttributeOffset | |
// to get the right position, 6 for the max_stack etc... | |
int max_stack = codeStream.stackMax; | |
contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8); | |
contents[codeAttributeOffset + 7] = (byte) max_stack; | |
int max_locals = codeStream.maxLocals; | |
contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8); | |
contents[codeAttributeOffset + 9] = (byte) max_locals; | |
int code_length = codeStream.position; | |
contents[codeAttributeOffset + 10] = (byte) (code_length >> 24); | |
contents[codeAttributeOffset + 11] = (byte) (code_length >> 16); | |
contents[codeAttributeOffset + 12] = (byte) (code_length >> 8); | |
contents[codeAttributeOffset + 13] = (byte) code_length; | |
int contentsLength; | |
if ((localContentsOffset + 40) >= (contentsLength = contents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
// there is no exception table, so we need to offset by 2 the current offset and move | |
// on the attribute generation | |
localContentsOffset += 2; | |
// debug attributes | |
int codeAttributeAttributeOffset = localContentsOffset; | |
int attributeNumber = 0; | |
// leave two bytes for the attribute_length | |
localContentsOffset += 2; | |
// first we handle the linenumber attribute | |
if (codeStream.generateLineNumberAttributes) { | |
int index = 0; | |
int lineNumberNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.LineNumberTableName); | |
contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8); | |
contents[localContentsOffset++] = (byte) lineNumberNameIndex; | |
int lineNumberTableOffset = localContentsOffset; | |
localContentsOffset += 6; | |
// leave space for attribute_length and line_number_table_length | |
// Seems like do would be better, but this preserves the existing behavior. | |
index = searchLineNumber(startLineIndexes, binding.sourceStart); | |
contents[localContentsOffset++] = 0; | |
contents[localContentsOffset++] = 0; | |
contents[localContentsOffset++] = (byte) (index >> 8); | |
contents[localContentsOffset++] = (byte) index; | |
// now we change the size of the line number attribute | |
contents[lineNumberTableOffset++] = 0; | |
contents[lineNumberTableOffset++] = 0; | |
contents[lineNumberTableOffset++] = 0; | |
contents[lineNumberTableOffset++] = 6; | |
contents[lineNumberTableOffset++] = 0; | |
contents[lineNumberTableOffset++] = 1; | |
attributeNumber++; | |
} | |
// then we do the local variable attribute | |
if (codeStream.generateLocalVariableTableAttributes) { | |
int localVariableTableOffset = localContentsOffset; | |
int numberOfEntries = 0; | |
int localVariableNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.LocalVariableTableName); | |
if (localContentsOffset + 8 > (contentsLength = contents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8); | |
contents[localContentsOffset++] = (byte) localVariableNameIndex; | |
localContentsOffset += 6; | |
// leave space for attribute_length and local_variable_table_length | |
int nameIndex; | |
int descriptorIndex; | |
for (int i = 0; i < codeStream.allLocalsCounter; i++) { | |
LocalVariableBinding localVariable = codeStream.locals[i]; | |
for (int j = 0; j < localVariable.initializationCount; j++) { | |
int startPC = localVariable.initializationPCs[j << 1]; | |
int endPC = localVariable.initializationPCs[(j << 1) + 1]; | |
if (startPC != endPC) { // only entries for non zero length | |
if (endPC == -1) { | |
localVariable.declaringScope.problemReporter().abortDueToInternalError( | |
Util.bind("abort.invalidAttribute" , new String(localVariable.name)), //$NON-NLS-1$ | |
(AstNode) localVariable.declaringScope.methodScope().referenceContext); | |
} | |
if (localContentsOffset + 10 > (contentsLength = contents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
// now we can safely add the local entry | |
numberOfEntries++; | |
contents[localContentsOffset++] = (byte) (startPC >> 8); | |
contents[localContentsOffset++] = (byte) startPC; | |
int length = endPC - startPC; | |
contents[localContentsOffset++] = (byte) (length >> 8); | |
contents[localContentsOffset++] = (byte) length; | |
nameIndex = constantPool.literalIndex(localVariable.name); | |
contents[localContentsOffset++] = (byte) (nameIndex >> 8); | |
contents[localContentsOffset++] = (byte) nameIndex; | |
descriptorIndex = constantPool.literalIndex(localVariable.type.signature()); | |
contents[localContentsOffset++] = (byte) (descriptorIndex >> 8); | |
contents[localContentsOffset++] = (byte) descriptorIndex; | |
int resolvedPosition = localVariable.resolvedPosition; | |
contents[localContentsOffset++] = (byte) (resolvedPosition >> 8); | |
contents[localContentsOffset++] = (byte) resolvedPosition; | |
} | |
} | |
} | |
int value = numberOfEntries * 10 + 2; | |
localVariableTableOffset += 2; | |
contents[localVariableTableOffset++] = (byte) (value >> 24); | |
contents[localVariableTableOffset++] = (byte) (value >> 16); | |
contents[localVariableTableOffset++] = (byte) (value >> 8); | |
contents[localVariableTableOffset++] = (byte) value; | |
contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8); | |
contents[localVariableTableOffset] = (byte) numberOfEntries; | |
attributeNumber++; | |
} | |
// update the number of attributes | |
// ensure first that there is enough space available inside the contents array | |
if (codeAttributeAttributeOffset + 2 >= (contentsLength = contents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
contents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8); | |
contents[codeAttributeAttributeOffset] = (byte) attributeNumber; | |
// update the attribute length | |
int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6); | |
contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24); | |
contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16); | |
contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8); | |
contents[codeAttributeOffset + 5] = (byte) codeAttributeLength; | |
contentsOffset = localContentsOffset; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Complete the creation of a method info by setting up the number of attributes at the right offset. | |
* | |
* @param methodAttributeOffset <CODE>int</CODE> | |
* @param attributeNumber <CODE>int</CODE> | |
*/ | |
public void completeMethodInfo( | |
int methodAttributeOffset, | |
int attributeNumber) { | |
// update the number of attributes | |
contents[methodAttributeOffset++] = (byte) (attributeNumber >> 8); | |
contents[methodAttributeOffset] = (byte) attributeNumber; | |
} | |
/* | |
* INTERNAL USE-ONLY | |
* Innerclasses get their name computed as they are generated, since some may not | |
* be actually outputed if sitting inside unreachable code. | |
* | |
* @param localType org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding | |
*/ | |
public char[] computeConstantPoolName(LocalTypeBinding localType) { | |
if (localType.constantPoolName() != null) { | |
return localType.constantPoolName(); | |
} | |
// delegates to the outermost enclosing classfile, since it is the only one with a global vision of its innertypes. | |
if (enclosingClassFile != null) { | |
return this.outerMostEnclosingClassFile().computeConstantPoolName(localType); | |
} | |
if (nameUsage == null) | |
nameUsage = new HashtableOfType(); | |
// ensure there is not already such a local type name defined by the user | |
int index = 0; | |
char[] candidateName; | |
while(true) { | |
if (localType.isMemberType()){ | |
if (index == 0){ | |
candidateName = CharOperation.concat( | |
localType.enclosingType().constantPoolName(), | |
localType.sourceName, | |
'$'); | |
} else { | |
// in case of collision, then member name gets extra $1 inserted | |
// e.g. class X { { class L{} new X(){ class L{} } } } | |
candidateName = CharOperation.concat( | |
localType.enclosingType().constantPoolName(), | |
'$', | |
String.valueOf(index).toCharArray(), | |
'$', | |
localType.sourceName); | |
} | |
} else if (localType.isAnonymousType()){ | |
candidateName = CharOperation.concat( | |
referenceBinding.constantPoolName(), | |
String.valueOf(index+1).toCharArray(), | |
'$'); | |
} else { | |
candidateName = CharOperation.concat( | |
referenceBinding.constantPoolName(), | |
'$', | |
String.valueOf(index+1).toCharArray(), | |
'$', | |
localType.sourceName); | |
} | |
if (nameUsage.get(candidateName) != null) { | |
index ++; | |
} else { | |
nameUsage.put(candidateName, localType); | |
break; | |
} | |
} | |
return candidateName; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Request the creation of a ClassFile compatible representation of a problematic type | |
* | |
* @param typeDeclaration org.eclipse.jdt.internal.compiler.ast.TypeDeclaration | |
* @param unitResult org.eclipse.jdt.internal.compiler.CompilationUnitResult | |
*/ | |
public static void createProblemType( | |
TypeDeclaration typeDeclaration, | |
CompilationResult unitResult) { | |
SourceTypeBinding typeBinding = typeDeclaration.binding; | |
ClassFile classFile = new ClassFile(typeBinding, null, true); | |
// inner attributes | |
if (typeBinding.isMemberType()) | |
classFile.recordEnclosingTypeAttributes(typeBinding); | |
// add its fields | |
FieldBinding[] fields = typeBinding.fields; | |
if ((fields != null) && (fields != NoFields)) { | |
for (int i = 0, max = fields.length; i < max; i++) { | |
if (fields[i].constant == null) { | |
FieldReference.getConstantFor(fields[i], false, null, null, 0); | |
} | |
} | |
classFile.addFieldInfos(); | |
} else { | |
// we have to set the number of fields to be equals to 0 | |
classFile.contents[classFile.contentsOffset++] = 0; | |
classFile.contents[classFile.contentsOffset++] = 0; | |
} | |
// leave some space for the methodCount | |
classFile.setForMethodInfos(); | |
// add its user defined methods | |
MethodBinding[] methods = typeBinding.methods; | |
AbstractMethodDeclaration[] methodDeclarations = typeDeclaration.methods; | |
int maxMethodDecl = methodDeclarations == null ? 0 : methodDeclarations.length; | |
int problemsLength; | |
IProblem[] problems = unitResult.getProblems(); | |
if (problems == null) { | |
problems = new IProblem[0]; | |
} | |
IProblem[] problemsCopy = new IProblem[problemsLength = problems.length]; | |
System.arraycopy(problems, 0, problemsCopy, 0, problemsLength); | |
if (methods != null) { | |
if (typeBinding.isInterface()) { | |
// we cannot create problem methods for an interface. So we have to generate a clinit | |
// which should contain all the problem | |
classFile.addProblemClinit(problemsCopy); | |
for (int i = 0, max = methods.length; i < max; i++) { | |
MethodBinding methodBinding; | |
if ((methodBinding = methods[i]) != null) { | |
// find the corresponding method declaration | |
for (int j = 0; j < maxMethodDecl; j++) { | |
if ((methodDeclarations[j] != null) | |
&& (methodDeclarations[j].binding == methods[i])) { | |
if (!methodBinding.isConstructor()) { | |
classFile.addAbstractMethod(methodDeclarations[j], methodBinding); | |
} | |
break; | |
} | |
} | |
} | |
} | |
} else { | |
for (int i = 0, max = methods.length; i < max; i++) { | |
MethodBinding methodBinding; | |
if ((methodBinding = methods[i]) != null) { | |
// find the corresponding method declaration | |
for (int j = 0; j < maxMethodDecl; j++) { | |
if ((methodDeclarations[j] != null) | |
&& (methodDeclarations[j].binding == methods[i])) { | |
AbstractMethodDeclaration methodDecl; | |
if ((methodDecl = methodDeclarations[j]).isConstructor()) { | |
classFile.addProblemConstructor(methodDecl, methodBinding, problemsCopy); | |
} else { | |
classFile.addProblemMethod(methodDecl, methodBinding, problemsCopy); | |
} | |
break; | |
} | |
} | |
} | |
} | |
} | |
// add abstract methods | |
classFile.addDefaultAbstractMethods(); | |
} | |
// propagate generation of (problem) member types | |
if (typeDeclaration.memberTypes != null) { | |
for (int i = 0, max = typeDeclaration.memberTypes.length; i < max; i++) { | |
TypeDeclaration memberType = typeDeclaration.memberTypes[i]; | |
if (memberType.binding != null) { | |
classFile.recordNestedMemberAttribute(memberType.binding); | |
ClassFile.createProblemType(memberType, unitResult); | |
} | |
} | |
} | |
classFile.addAttributes(); | |
unitResult.record(typeBinding.constantPoolName(), classFile); | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* This methods returns a char[] representing the file name of the receiver | |
* | |
* @return char[] | |
*/ | |
public char[] fileName() { | |
return constantPool.UTF8Cache.returnKeyFor(1); | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* That method generates the header of a code attribute. | |
* - the index inside the constant pool for the attribute name (i.e. Code) | |
* - leave some space for attribute_length(4), max_stack(2), max_locals(2), code_length(4). | |
*/ | |
public void generateCodeAttributeHeader() { | |
int contentsLength; | |
if (contentsOffset + 20 >= (contentsLength = contents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
int constantValueNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.CodeName); | |
contents[contentsOffset++] = (byte) (constantValueNameIndex >> 8); | |
contents[contentsOffset++] = (byte) constantValueNameIndex; | |
// leave space for attribute_length(4), max_stack(2), max_locals(2), code_length(4) | |
contentsOffset += 12; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* That method generates the attributes of a code attribute. | |
* They could be: | |
* - an exception attribute for each try/catch found inside the method | |
* - a deprecated attribute | |
* - a synthetic attribute for synthetic access methods | |
* | |
* It returns the number of attributes created for the code attribute. | |
* | |
* @param methodBinding org.eclipse.jdt.internal.compiler.lookup.MethodBinding | |
* @return <CODE>int</CODE> | |
*/ | |
public int generateMethodInfoAttribute(MethodBinding methodBinding) { | |
// leave two bytes for the attribute_number | |
contentsOffset += 2; | |
// now we can handle all the attribute for that method info: | |
// it could be: | |
// - a CodeAttribute | |
// - a ExceptionAttribute | |
// - a DeprecatedAttribute | |
// - a SyntheticAttribute | |
// Exception attribute | |
ReferenceBinding[] thrownsExceptions; | |
int contentsLength; | |
int attributeNumber = 0; | |
if ((thrownsExceptions = methodBinding.thrownExceptions) != NoExceptions) { | |
// The method has a throw clause. So we need to add an exception attribute | |
// check that there is enough space to write all the bytes for the exception attribute | |
int length = thrownsExceptions.length; | |
if (contentsOffset + (8 + length * 2) >= (contentsLength = contents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(contents = | |
new byte[contentsLength + Math.max(INCREMENT_SIZE, (8 + length * 2))]), | |
0, | |
contentsLength); | |
} | |
int exceptionNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.ExceptionsName); | |
contents[contentsOffset++] = (byte) (exceptionNameIndex >> 8); | |
contents[contentsOffset++] = (byte) exceptionNameIndex; | |
// The attribute length = length * 2 + 2 in case of a exception attribute | |
int attributeLength = length * 2 + 2; | |
contents[contentsOffset++] = (byte) (attributeLength >> 24); | |
contents[contentsOffset++] = (byte) (attributeLength >> 16); | |
contents[contentsOffset++] = (byte) (attributeLength >> 8); | |
contents[contentsOffset++] = (byte) attributeLength; | |
contents[contentsOffset++] = (byte) (length >> 8); | |
contents[contentsOffset++] = (byte) length; | |
for (int i = 0; i < length; i++) { | |
int exceptionIndex = constantPool.literalIndex(thrownsExceptions[i]); | |
contents[contentsOffset++] = (byte) (exceptionIndex >> 8); | |
contents[contentsOffset++] = (byte) exceptionIndex; | |
} | |
attributeNumber++; | |
} | |
// Deprecated attribute | |
// Check that there is enough space to write the deprecated attribute | |
if (contentsOffset + 6 >= (contentsLength = contents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
if (methodBinding.isDeprecated()) { | |
int deprecatedAttributeNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.DeprecatedName); | |
contents[contentsOffset++] = (byte) (deprecatedAttributeNameIndex >> 8); | |
contents[contentsOffset++] = (byte) deprecatedAttributeNameIndex; | |
// the length of a deprecated attribute is equals to 0 | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
attributeNumber++; | |
} | |
// Synthetic attribute | |
// Check that there is enough space to write the deprecated attribute | |
if (contentsOffset + 6 >= (contentsLength = contents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
if (methodBinding.isSynthetic()) { | |
int syntheticAttributeNameIndex = | |
constantPool.literalIndex(AttributeNamesConstants.SyntheticName); | |
contents[contentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8); | |
contents[contentsOffset++] = (byte) syntheticAttributeNameIndex; | |
// the length of a synthetic attribute is equals to 0 | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 0; | |
attributeNumber++; | |
} | |
return attributeNumber; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* That method generates the header of a method info: | |
* The header consists in: | |
* - the access flags | |
* - the name index of the method name inside the constant pool | |
* - the descriptor index of the signature of the method inside the constant pool. | |
* | |
* @param methodBinding org.eclipse.jdt.internal.compiler.lookup.MethodBinding | |
*/ | |
public void generateMethodInfoHeader(MethodBinding methodBinding) { | |
// check that there is enough space to write all the bytes for the method info corresponding | |
// to the @methodBinding | |
int contentsLength; | |
methodCount++; // add one more method | |
if (contentsOffset + 10 >= (contentsLength = contents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
int accessFlags = methodBinding.getAccessFlags(); | |
if (methodBinding.isRequiredToClearPrivateModifier()) { | |
accessFlags &= ~AccPrivate; | |
} | |
contents[contentsOffset++] = (byte) (accessFlags >> 8); | |
contents[contentsOffset++] = (byte) accessFlags; | |
int nameIndex = constantPool.literalIndex(methodBinding.selector); | |
contents[contentsOffset++] = (byte) (nameIndex >> 8); | |
contents[contentsOffset++] = (byte) nameIndex; | |
int descriptorIndex = constantPool.literalIndex(methodBinding.signature()); | |
contents[contentsOffset++] = (byte) (descriptorIndex >> 8); | |
contents[contentsOffset++] = (byte) descriptorIndex; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* That method generates the method info header of a clinit: | |
* The header consists in: | |
* - the access flags (always default access + static) | |
* - the name index of the method name (always <clinit>) inside the constant pool | |
* - the descriptor index of the signature (always ()V) of the method inside the constant pool. | |
* | |
* @param methodBinding org.eclipse.jdt.internal.compiler.lookup.MethodBinding | |
*/ | |
public void generateMethodInfoHeaderForClinit() { | |
// check that there is enough space to write all the bytes for the method info corresponding | |
// to the @methodBinding | |
int contentsLength; | |
methodCount++; // add one more method | |
if (contentsOffset + 10 >= (contentsLength = contents.length)) { | |
System.arraycopy( | |
contents, | |
0, | |
(contents = new byte[contentsLength + INCREMENT_SIZE]), | |
0, | |
contentsLength); | |
} | |
contents[contentsOffset++] = (byte) ((AccDefault | AccStatic) >> 8); | |
contents[contentsOffset++] = (byte) (AccDefault | AccStatic); | |
int nameIndex = constantPool.literalIndex(QualifiedNamesConstants.Clinit); | |
contents[contentsOffset++] = (byte) (nameIndex >> 8); | |
contents[contentsOffset++] = (byte) nameIndex; | |
int descriptorIndex = | |
constantPool.literalIndex(QualifiedNamesConstants.ClinitSignature); | |
contents[contentsOffset++] = (byte) (descriptorIndex >> 8); | |
contents[contentsOffset++] = (byte) descriptorIndex; | |
// We know that we won't get more than 1 attribute: the code attribute | |
contents[contentsOffset++] = 0; | |
contents[contentsOffset++] = 1; | |
} | |
/** | |
* EXTERNAL API | |
* Answer the actual bytes of the class file | |
* | |
* This method encodes the receiver structure into a byte array which is the content of the classfile. | |
* Returns the byte array that represents the encoded structure of the receiver. | |
* | |
* @return byte[] | |
*/ | |
public byte[] getBytes() { | |
byte[] fullContents = new byte[headerOffset + contentsOffset]; | |
System.arraycopy(header, 0, fullContents, 0, headerOffset); | |
System.arraycopy(contents, 0, fullContents, headerOffset, contentsOffset); | |
return fullContents; | |
} | |
/** | |
* EXTERNAL API | |
* Answer the compound name of the class file. | |
* @return char[][] | |
* e.g. {{java}, {util}, {Hashtable}}. | |
*/ | |
public char[][] getCompoundName() { | |
return CharOperation.splitOn('/', fileName()); | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Returns the most enclosing classfile of the receiver. This is used know to store the constant pool name | |
* for all inner types of the receiver. | |
* @return org.eclipse.jdt.internal.compiler.codegen.ClassFile | |
*/ | |
public ClassFile outerMostEnclosingClassFile() { | |
ClassFile current = this; | |
while (current.enclosingClassFile != null) | |
current = current.enclosingClassFile; | |
return current; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* This is used to store a new inner class. It checks that the binding @binding doesn't already exist inside the | |
* collection of inner classes. Add all the necessary classes in the right order to fit to the specifications. | |
* | |
* @param binding org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding | |
*/ | |
public void recordEnclosingTypeAttributes(ReferenceBinding binding) { | |
// add all the enclosing types | |
ReferenceBinding enclosingType = referenceBinding.enclosingType(); | |
int depth = 0; | |
while (enclosingType != null) { | |
depth++; | |
enclosingType = enclosingType.enclosingType(); | |
} | |
enclosingType = referenceBinding; | |
ReferenceBinding enclosingTypes[]; | |
if (depth >= 2) { | |
enclosingTypes = new ReferenceBinding[depth]; | |
for (int i = depth - 1; i >= 0; i--) { | |
enclosingTypes[i] = enclosingType; | |
enclosingType = enclosingType.enclosingType(); | |
} | |
for (int i = 0; i < depth; i++) { | |
addInnerClasses(enclosingTypes[i]); | |
} | |
} else { | |
addInnerClasses(referenceBinding); | |
} | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* This is used to store a new inner class. It checks that the binding @binding doesn't already exist inside the | |
* collection of inner classes. Add all the necessary classes in the right order to fit to the specifications. | |
* | |
* @param binding org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding | |
*/ | |
public void recordNestedLocalAttribute(ReferenceBinding binding) { | |
// add all the enclosing types | |
ReferenceBinding enclosingType = referenceBinding.enclosingType(); | |
int depth = 0; | |
while (enclosingType != null) { | |
depth++; | |
enclosingType = enclosingType.enclosingType(); | |
} | |
enclosingType = referenceBinding; | |
ReferenceBinding enclosingTypes[]; | |
if (depth >= 2) { | |
enclosingTypes = new ReferenceBinding[depth]; | |
for (int i = depth - 1; i >= 0; i--) { | |
enclosingTypes[i] = enclosingType; | |
enclosingType = enclosingType.enclosingType(); | |
} | |
for (int i = 0; i < depth; i++) | |
addInnerClasses(enclosingTypes[i]); | |
} else { | |
addInnerClasses(binding); | |
} | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* This is used to store a new inner class. It checks that the binding @binding doesn't already exist inside the | |
* collection of inner classes. Add all the necessary classes in the right order to fit to the specifications. | |
* | |
* @param binding org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding | |
*/ | |
public void recordNestedMemberAttribute(ReferenceBinding binding) { | |
addInnerClasses(binding); | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* Search the line number corresponding to a specific position | |
* | |
* @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding | |
*/ | |
public static final int searchLineNumber( | |
int[] startLineIndexes, | |
int position) { | |
// this code is completely useless, but it is the same implementation than | |
// org.eclipse.jdt.internal.compiler.problem.ProblemHandler.searchLineNumber(int[], int) | |
// if (startLineIndexes == null) | |
// return 1; | |
int length = startLineIndexes.length; | |
if (length == 0) | |
return 1; | |
int g = 0, d = length - 1; | |
int m = 0; | |
while (g <= d) { | |
m = (g + d) / 2; | |
if (position < startLineIndexes[m]) { | |
d = m - 1; | |
} else | |
if (position > startLineIndexes[m]) { | |
g = m + 1; | |
} else { | |
return m + 1; | |
} | |
} | |
if (position < startLineIndexes[m]) { | |
return m + 1; | |
} | |
return m + 2; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* This methods leaves the space for method counts recording. | |
*/ | |
public void setForMethodInfos() { | |
// leave some space for the methodCount | |
methodCountOffset = contentsOffset; | |
contentsOffset += 2; | |
} | |
/** | |
* INTERNAL USE-ONLY | |
* outputPath is formed like: | |
* c:\temp\ the last character is a file separator | |
* relativeFileName is formed like: | |
* java\lang\String.class | |
* @param generatePackagesStructure a flag to know if the packages structure has to be generated. | |
* @param outputPath the output directory | |
* @param relativeFileName java.lang.String | |
* @param contents byte[] | |
* | |
*/ | |
public static void writeToDisk( | |
boolean generatePackagesStructure, | |
String outputPath, | |
String relativeFileName, | |
byte[] contents) | |
throws IOException { | |
BufferedOutputStream output = null; | |
if (generatePackagesStructure) { | |
output = new BufferedOutputStream( | |
new FileOutputStream( | |
new File(buildAllDirectoriesInto(outputPath, relativeFileName)))); | |
} else { | |
String fileName = null; | |
char fileSeparatorChar = File.separatorChar; | |
String fileSeparator = File.separator; | |
// First we ensure that the outputPath exists | |
outputPath = outputPath.replace('/', fileSeparatorChar); | |
// To be able to pass the mkdirs() method we need to remove the extra file separator at the end of the outDir name | |
int indexOfPackageSeparator = relativeFileName.lastIndexOf(fileSeparatorChar); | |
if (indexOfPackageSeparator == -1) { | |
if (outputPath.endsWith(fileSeparator)) { | |
fileName = outputPath + relativeFileName; | |
} else { | |
fileName = outputPath + fileSeparator + relativeFileName; | |
} | |
} else { | |
int length = relativeFileName.length(); | |
if (outputPath.endsWith(fileSeparator)) { | |
fileName = outputPath + relativeFileName.substring(indexOfPackageSeparator + 1, length); | |
} else { | |
fileName = outputPath + fileSeparator + relativeFileName.substring(indexOfPackageSeparator + 1, length); | |
} | |
} | |
output = new BufferedOutputStream( | |
new FileOutputStream( | |
new File(fileName))); | |
} | |
try { | |
output.write(contents); | |
} finally { | |
output.flush(); | |
output.close(); | |
} | |
} | |
} |