blob: 157a1089caf8915e5c9659ab29ec9a4f9a04ca5b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.jsdt.internal.compiler;
import java.io.*;
import java.util.StringTokenizer;
import org.eclipse.wst.jsdt.core.compiler.CharOperation;
import org.eclipse.wst.jsdt.core.compiler.IProblem;
import org.eclipse.wst.jsdt.internal.compiler.ast.*;
import org.eclipse.wst.jsdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.wst.jsdt.internal.compiler.codegen.*;
import org.eclipse.wst.jsdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.wst.jsdt.internal.compiler.impl.Constant;
import org.eclipse.wst.jsdt.internal.compiler.impl.StringConstant;
import org.eclipse.wst.jsdt.internal.compiler.lookup.*;
import org.eclipse.wst.jsdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.wst.jsdt.internal.compiler.util.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 = 400;
public static final int INITIAL_HEADER_SIZE = 1500;
public boolean ownSharedArrays = false; // flag set when header/contents are set to shared arrays
public static final int INNER_CLASSES_SIZE = 5;
public CodeStream codeStream;
public long targetJDK;
/**
* INTERNAL USE-ONLY
* This methods creates a new instance of the receiver.
*/
public ClassFile() {
// default constructor for subclasses
}
/**
* INTERNAL USE-ONLY
* This methods creates a new instance of the receiver.
*
* @param aType org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding
* @param enclosingClassFile org.eclipse.wst.jsdt.internal.compiler.ClassFile
* @param creatingProblemType <CODE>boolean</CODE>
*/
public ClassFile(
SourceTypeBinding aType,
ClassFile enclosingClassFile,
boolean creatingProblemType) {
this.referenceBinding = aType;
initByteArrays();
// 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);
final CompilerOptions options = aType.scope.environment().options;
this.targetJDK = options.targetJDK;
header[headerOffset++] = (byte) (this.targetJDK >> 8); // minor high
header[headerOffset++] = (byte) (this.targetJDK >> 0); // minor low
header[headerOffset++] = (byte) (this.targetJDK >> 24); // major high
header[headerOffset++] = (byte) (this.targetJDK >> 16); // major low
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)
if (aType.isClass()) {
accessFlags |= AccSuper;
}
this.enclosingClassFile = enclosingClassFile;
// innerclasses get their names computed at code gen time
// 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;
for (int i = 0; i < interfacesCount; i++) {
int interfaceIndex = constantPool.literalIndex(superInterfacesBinding[i]);
contents[contentsOffset++] = (byte) (interfaceIndex >> 8);
contents[contentsOffset++] = (byte) interfaceIndex;
}
produceDebugAttributes = options.produceDebugAttributes;
innerClassesBindings = new ReferenceBinding[INNER_CLASSES_SIZE];
this.creatingProblemType = creatingProblemType;
codeStream = new CodeStream(this, this.targetJDK);
// 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.wst.jsdt.internal.compiler.ast.AbstractMethodDeclaration
* @param methodBinding org.eclipse.wst.jsdt.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;
// 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 >= contents.length) {
resizeContents(8);
}
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 >= contents.length) {
resizeContents(6);
}
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 = 8 * numberOfInnerClasses + 8;
if (exSize + contentsOffset >= this.contents.length) {
resizeContents(exSize);
}
// 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++;
}
// add signature attribute
char[] genericSignature = referenceBinding.genericSignature();
if (genericSignature != null) {
// check that there is enough space to write all the bytes for the field info corresponding
// to the @fieldBinding
if (contentsOffset + 8 >= contents.length) {
resizeContents(8);
}
int signatureAttributeNameIndex =
constantPool.literalIndex(AttributeNamesConstants.SignatureName);
contents[contentsOffset++] = (byte) (signatureAttributeNameIndex >> 8);
contents[contentsOffset++] = (byte) signatureAttributeNameIndex;
// the length of a signature attribute is equals to 2
contents[contentsOffset++] = 0;
contents[contentsOffset++] = 0;
contents[contentsOffset++] = 0;
contents[contentsOffset++] = 2;
int signatureIndex =
constantPool.literalIndex(genericSignature);
contents[contentsOffset++] = (byte) (signatureIndex >> 8);
contents[contentsOffset++] = (byte) signatureIndex;
attributeNumber++;
}
// update the number of attributes
if (attributeOffset + 2 >= this.contents.length) {
resizeContents(2);
}
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.wst.jsdt.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
if (contentsOffset + 30 >= contents.length) {
resizeContents(30);
}
// Now we can generate all entries into the byte array
// First the accessFlags
int accessFlags = fieldBinding.getAccessFlags();
if (targetJDK < ClassFileConstants.JDK1_5) {
// pre 1.5, synthetic was an attribute, not a modifier
accessFlags &= ~AccSynthetic;
}
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
// Generate the constantValueAttribute
if (fieldBinding.isConstantValue()){
if (contentsOffset + 8 >= contents.length) {
resizeContents(8);
}
// 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
Constant fieldConstant = fieldBinding.constant();
switch (fieldConstant.typeID()) {
case T_boolean :
int booleanValueIndex =
constantPool.literalIndex(fieldConstant.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(fieldConstant.intValue());
contents[contentsOffset++] = (byte) (integerValueIndex >> 8);
contents[contentsOffset++] = (byte) integerValueIndex;
break;
case T_float :
int floatValueIndex =
constantPool.literalIndex(fieldConstant.floatValue());
contents[contentsOffset++] = (byte) (floatValueIndex >> 8);
contents[contentsOffset++] = (byte) floatValueIndex;
break;
case T_double :
int doubleValueIndex =
constantPool.literalIndex(fieldConstant.doubleValue());
contents[contentsOffset++] = (byte) (doubleValueIndex >> 8);
contents[contentsOffset++] = (byte) doubleValueIndex;
break;
case T_long :
int longValueIndex =
constantPool.literalIndex(fieldConstant.longValue());
contents[contentsOffset++] = (byte) (longValueIndex >> 8);
contents[contentsOffset++] = (byte) longValueIndex;
break;
case T_String :
int stringValueIndex =
constantPool.literalIndex(
((StringConstant) fieldConstant).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 (this.targetJDK < ClassFileConstants.JDK1_5 && fieldBinding.isSynthetic()) {
if (contentsOffset + 6 >= contents.length) {
resizeContents(6);
}
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()) {
if (contentsOffset + 6 >= contents.length) {
resizeContents(6);
}
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++;
}
// add signature attribute
char[] genericSignature = fieldBinding.genericSignature();
if (genericSignature != null) {
// check that there is enough space to write all the bytes for the field info corresponding
// to the @fieldBinding
if (contentsOffset + 8 >= contents.length) {
resizeContents(8);
}
int signatureAttributeNameIndex =
constantPool.literalIndex(AttributeNamesConstants.SignatureName);
contents[contentsOffset++] = (byte) (signatureAttributeNameIndex >> 8);
contents[contentsOffset++] = (byte) signatureAttributeNameIndex;
// the length of a signature attribute is equals to 2
contents[contentsOffset++] = 0;
contents[contentsOffset++] = 0;
contents[contentsOffset++] = 0;
contents[contentsOffset++] = 2;
int signatureIndex =
constantPool.literalIndex(genericSignature);
contents[contentsOffset++] = (byte) (signatureIndex >> 8);
contents[contentsOffset++] = (byte) signatureIndex;
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
if (fieldCount > 0xFFFF) {
referenceBinding.scope.problemReporter().tooManyFields(referenceBinding.scope.referenceType());
}
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 refBinding org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding
*/
public void addInnerClasses(ReferenceBinding refBinding) {
// check first if that reference binding is there
for (int i = 0; i < numberOfInnerClasses; i++) {
if (innerClassesBindings[i] == refBinding)
return;
}
int length = innerClassesBindings.length;
if (numberOfInnerClasses == length) {
System.arraycopy(
innerClassesBindings,
0,
innerClassesBindings = new ReferenceBinding[length * 2],
0,
length);
}
innerClassesBindings[numberOfInnerClasses++] = refBinding;
}
/**
* INTERNAL USE-ONLY
* Generate the byte for a problem clinit method info that correspond to a boggus method.
*
* @param problems org.eclipse.wst.jsdt.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$
int problemLine = 0;
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, "")
codeStream.generateCodeAttributeForProblemMethod(problemString);
attributeNumber++; // code attribute
completeCodeAttributeForClinit(
codeAttributeOffset,
referenceBinding
.scope
.referenceCompilationUnit()
.compilationResult
.lineSeparatorPositions,
problemLine);
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.wst.jsdt.internal.compiler.ast.AbstractMethodDeclaration
* @param methodBinding org.eclipse.wst.jsdt.internal.compiler.nameloopkup.MethodBinding
* @param problems org.eclipse.wst.jsdt.internal.compiler.problem.Problem[]
*/
public void addProblemConstructor(
AbstractMethodDeclaration method,
MethodBinding methodBinding,
IProblem[] problems) {
// always clear the strictfp/native/abstract bit for a problem method
generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(AccStrictfp | AccNative | AccAbstract));
int methodAttributeOffset = contentsOffset;
int attributeNumber = generateMethodInfoAttribute(methodBinding);
// Code attribute
attributeNumber++;
int codeAttributeOffset = contentsOffset;
generateCodeAttributeHeader();
codeStream.reset(method, this);
String problemString = "" ; //$NON-NLS-1$
int problemLine = 0;
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, "")
codeStream.generateCodeAttributeForProblemMethod(problemString);
completeCodeAttributeForProblemMethod(
method,
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.lineSeparatorPositions,
problemLine);
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.wst.jsdt.internal.compiler.ast.AbstractMethodDeclaration
* @param methodBinding org.eclipse.wst.jsdt.internal.compiler.nameloopkup.MethodBinding
* @param problems org.eclipse.wst.jsdt.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.wst.jsdt.internal.compiler.ast.AbstractMethodDeclaration
* @param methodBinding org.eclipse.wst.jsdt.internal.compiler.nameloopkup.MethodBinding
* @param problems org.eclipse.wst.jsdt.internal.compiler.problem.Problem[]
*/
public void addProblemMethod(
AbstractMethodDeclaration method,
MethodBinding methodBinding,
IProblem[] problems) {
if (methodBinding.isAbstract() && methodBinding.declaringClass.isInterface()) {
method.abort(ProblemSeverities.AbortType, null);
}
// always clear the strictfp/native/abstract bit for a problem method
generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(AccStrictfp | AccNative | AccAbstract));
int methodAttributeOffset = contentsOffset;
int attributeNumber = generateMethodInfoAttribute(methodBinding);
// Code attribute
attributeNumber++;
int codeAttributeOffset = contentsOffset;
generateCodeAttributeHeader();
codeStream.reset(method, this);
String problemString = "" ; //$NON-NLS-1$
int problemLine = 0;
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, "")
codeStream.generateCodeAttributeForProblemMethod(problemString);
completeCodeAttributeForProblemMethod(
method,
methodBinding,
codeAttributeOffset,
((SourceTypeBinding) methodBinding.declaringClass)
.scope
.referenceCompilationUnit()
.compilationResult
.lineSeparatorPositions,
problemLine);
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.wst.jsdt.internal.compiler.ast.AbstractMethodDeclaration
* @param methodBinding org.eclipse.wst.jsdt.internal.compiler.nameloopkup.MethodBinding
* @param problems org.eclipse.wst.jsdt.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
generateMissingAbstractMethods(referenceBinding.scope.referenceType().missingAbstractMethods, referenceBinding.scope.referenceCompilationUnit().compilationResult);
MethodBinding[] defaultAbstractMethods = this.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);
}
// add synthetic methods infos
SyntheticAccessMethodBinding[] syntheticAccessMethods = this.referenceBinding.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 non-accessible field
addSyntheticFieldReadAccessMethod(accessMethodBinding);
break;
case SyntheticAccessMethodBinding.FieldWriteAccess :
// generate a method info to emulate an writing access to
// a non-accessible field
addSyntheticFieldWriteAccessMethod(accessMethodBinding);
break;
case SyntheticAccessMethodBinding.MethodAccess :
case SyntheticAccessMethodBinding.SuperMethodAccess :
case SyntheticAccessMethodBinding.BridgeMethodAccess :
// generate a method info to emulate an access to a non-accessible method / super-method or bridge method
addSyntheticMethodAccessMethod(accessMethodBinding);
break;
case SyntheticAccessMethodBinding.ConstructorAccess :
// generate a method info to emulate an access to a non-accessible constructor
addSyntheticConstructorAccessMethod(accessMethodBinding);
break;
}
}
}
}
/**
* 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
generateMethodInfoHeader(methodBinding, methodBinding.modifiers & ~(AccStrictfp | AccNative | AccAbstract));
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();
codeStream.init(this);
codeStream.preserveUnusedLocals = true;
codeStream.initializeMaxLocals(methodBinding);
// return codeStream.generateCodeAttributeForProblemMethod(comp.options.runtimeExceptionNameForCompileError, "")
codeStream.generateCodeAttributeForProblemMethod(problemString);
completeCodeAttributeForMissingAbstractProblemMethod(
methodBinding,
codeAttributeOffset,
compilationResult.lineSeparatorPositions,
problem.getSourceLineNumber());
completeMethodInfo(methodAttributeOffset, attributeNumber);
}
/**
*
*/
public void completeCodeAttributeForMissingAbstractProblemMethod(
MethodBinding binding,
int codeAttributeOffset,
int[] startLineIndexes,
int problemLine) {
// reinitialize the localContents with the byte modified by the code stream
this.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;
this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
this.contents[codeAttributeOffset + 7] = (byte) max_stack;
int max_locals = codeStream.maxLocals;
this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
this.contents[codeAttributeOffset + 9] = (byte) max_locals;
int code_length = codeStream.position;
this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
this.contents[codeAttributeOffset + 13] = (byte) code_length;
// write the exception table
if (localContentsOffset + 50 >= this.contents.length) {
resizeContents(50);
}
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
// 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);
this.contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) lineNumberNameIndex;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 6;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 1;
if (problemLine == 0) {
problemLine = searchLineNumber(startLineIndexes, binding.sourceStart());
}
// first entry at pc = 0
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) (problemLine >> 8);
this.contents[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 >= this.contents.length) {
resizeContents(2);
}
this.contents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[codeAttributeAttributeOffset] = (byte) attributeNumber;
// update the attribute length
int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6);
this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
this.contents[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.wst.jsdt.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.wst.jsdt.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.wst.jsdt.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.wst.jsdt.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 codeAttributeOffset <CODE>int</CODE>
*/
public void completeCodeAttribute(int codeAttributeOffset) {
// reinitialize the localContents with the byte modified by the code stream
this.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 code_length = codeStream.position;
if (code_length > 65535) {
codeStream.methodDeclaration.scope.problemReporter().bytecodeExceeds64KLimit(
codeStream.methodDeclaration);
}
if (localContentsOffset + 20 >= this.contents.length) {
resizeContents(20);
}
int max_stack = codeStream.stackMax;
this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
this.contents[codeAttributeOffset + 7] = (byte) max_stack;
int max_locals = codeStream.maxLocals;
this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
this.contents[codeAttributeOffset + 9] = (byte) max_locals;
this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
this.contents[codeAttributeOffset + 13] = (byte) code_length;
// write the exception table
int exceptionHandlersNumber = codeStream.exceptionHandlersCounter;
ExceptionLabel[] exceptionHandlers = codeStream.exceptionHandlers;
int exSize = exceptionHandlersNumber * 8 + 2;
if (exSize + localContentsOffset >= this.contents.length) {
resizeContents(exSize);
}
// there is no exception table, so we need to offset by 2 the current offset and move
// on the attribute generation
this.contents[localContentsOffset++] = (byte) (exceptionHandlersNumber >> 8);
this.contents[localContentsOffset++] = (byte) exceptionHandlersNumber;
for (int i = 0, max = codeStream.exceptionHandlersIndex; i < max; i++) {
ExceptionLabel exceptionHandler = exceptionHandlers[i];
if (exceptionHandler != null) {
int start = exceptionHandler.start;
this.contents[localContentsOffset++] = (byte) (start >> 8);
this.contents[localContentsOffset++] = (byte) start;
int end = exceptionHandler.end;
this.contents[localContentsOffset++] = (byte) (end >> 8);
this.contents[localContentsOffset++] = (byte) end;
int handlerPC = exceptionHandler.position;
this.contents[localContentsOffset++] = (byte) (handlerPC >> 8);
this.contents[localContentsOffset++] = (byte) handlerPC;
if (exceptionHandler.exceptionType == null) {
// any exception handler
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
} else {
int nameIndex;
if (exceptionHandler.exceptionType == BaseTypes.NullBinding) {
/* represents ClassNotFoundException, see class literal access*/
nameIndex = constantPool.literalIndexForJavaLangClassNotFoundException();
} else {
nameIndex = constantPool.literalIndex(exceptionHandler.exceptionType);
}
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[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 >= this.contents.length) {
resizeContents(8);
}
this.contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8);
this.contents[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 >= this.contents.length) {
resizeContents(4);
}
int pc = pcToSourceMapTable[i++];
this.contents[localContentsOffset++] = (byte) (pc >> 8);
this.contents[localContentsOffset++] = (byte) pc;
int lineNumber = pcToSourceMapTable[i++];
this.contents[localContentsOffset++] = (byte) (lineNumber >> 8);
this.contents[localContentsOffset++] = (byte) lineNumber;
numberOfEntries++;
}
// now we change the size of the line number attribute
int lineNumberAttr_length = numberOfEntries * 4 + 2;
this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 24);
this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 16);
this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 8);
this.contents[lineNumberTableOffset++] = (byte) lineNumberAttr_length;
this.contents[lineNumberTableOffset++] = (byte) (numberOfEntries >> 8);
this.contents[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);
final boolean methodDeclarationIsStatic = codeStream.methodDeclaration.isStatic();
int maxOfEntries = 8 + 10 * (methodDeclarationIsStatic ? 0 : 1);
for (int i = 0; i < codeStream.allLocalsCounter; i++) {
maxOfEntries += 10 * codeStream.locals[i].initializationCount;
}
// reserve enough space
if (localContentsOffset + maxOfEntries >= this.contents.length) {
resizeContents(maxOfEntries);
}
this.contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) localVariableNameIndex;
localContentsOffset += 6;
// leave space for attribute_length and local_variable_table_length
int nameIndex;
int descriptorIndex;
SourceTypeBinding declaringClassBinding = null;
if (!methodDeclarationIsStatic) {
numberOfEntries++;
this.contents[localContentsOffset++] = 0; // the startPC for this is always 0
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) (code_length >> 8);
this.contents[localContentsOffset++] = (byte) code_length;
nameIndex = constantPool.literalIndex(QualifiedNamesConstants.This);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
declaringClassBinding = (SourceTypeBinding) codeStream.methodDeclaration.binding.declaringClass;
descriptorIndex =
constantPool.literalIndex(
declaringClassBinding.signature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
this.contents[localContentsOffset++] = 0;// the resolved position for this is always 0
this.contents[localContentsOffset++] = 0;
}
// used to remember the local variable with a generic type
int genericLocalVariablesCounter = 0;
LocalVariableBinding[] genericLocalVariables = null;
int numberOfGenericEntries = 0;
for (int i = 0, max = codeStream.allLocalsCounter; i < max; i++) {
LocalVariableBinding localVariable = codeStream.locals[i];
final TypeBinding localVariableTypeBinding = localVariable.type;
boolean isParameterizedType = localVariableTypeBinding.isParameterizedType() || localVariableTypeBinding.isTypeVariable();
if (localVariable.initializationCount != 0 && isParameterizedType) {
if (genericLocalVariables == null) {
// we cannot have more than max locals
genericLocalVariables = new LocalVariableBinding[max];
}
genericLocalVariables[genericLocalVariablesCounter++] = localVariable;
}
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 (isParameterizedType) {
numberOfGenericEntries++;
}
// now we can safely add the local entry
numberOfEntries++;
this.contents[localContentsOffset++] = (byte) (startPC >> 8);
this.contents[localContentsOffset++] = (byte) startPC;
int length = endPC - startPC;
this.contents[localContentsOffset++] = (byte) (length >> 8);
this.contents[localContentsOffset++] = (byte) length;
nameIndex = constantPool.literalIndex(localVariable.name);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
descriptorIndex = constantPool.literalIndex(localVariableTypeBinding.signature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
int resolvedPosition = localVariable.resolvedPosition;
this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8);
this.contents[localContentsOffset++] = (byte) resolvedPosition;
}
}
}
int value = numberOfEntries * 10 + 2;
localVariableTableOffset += 2;
this.contents[localVariableTableOffset++] = (byte) (value >> 24);
this.contents[localVariableTableOffset++] = (byte) (value >> 16);
this.contents[localVariableTableOffset++] = (byte) (value >> 8);
this.contents[localVariableTableOffset++] = (byte) value;
this.contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8);
this.contents[localVariableTableOffset] = (byte) numberOfEntries;
attributeNumber++;
final boolean currentInstanceIsGeneric =
!methodDeclarationIsStatic
&& declaringClassBinding != null
&& declaringClassBinding.typeVariables != NoTypeVariables;
if (genericLocalVariablesCounter != 0 || currentInstanceIsGeneric) {
// add the local variable type table attribute
numberOfGenericEntries += (currentInstanceIsGeneric ? 1 : 0);
maxOfEntries = 8 + numberOfGenericEntries * 10;
// reserve enough space
if (localContentsOffset + maxOfEntries >= this.contents.length) {
resizeContents(maxOfEntries);
}
int localVariableTypeNameIndex =
constantPool.literalIndex(AttributeNamesConstants.LocalVariableTypeTableName);
this.contents[localContentsOffset++] = (byte) (localVariableTypeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) localVariableTypeNameIndex;
value = numberOfGenericEntries * 10 + 2;
this.contents[localContentsOffset++] = (byte) (value >> 24);
this.contents[localContentsOffset++] = (byte) (value >> 16);
this.contents[localContentsOffset++] = (byte) (value >> 8);
this.contents[localContentsOffset++] = (byte) value;
this.contents[localContentsOffset++] = (byte) (numberOfGenericEntries >> 8);
this.contents[localContentsOffset++] = (byte) numberOfGenericEntries;
if (currentInstanceIsGeneric) {
this.contents[localContentsOffset++] = 0; // the startPC for this is always 0
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) (code_length >> 8);
this.contents[localContentsOffset++] = (byte) code_length;
nameIndex = constantPool.literalIndex(QualifiedNamesConstants.This);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
descriptorIndex = constantPool.literalIndex(declaringClassBinding.genericTypeSignature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
this.contents[localContentsOffset++] = 0;// the resolved position for this is always 0
this.contents[localContentsOffset++] = 0;
}
for (int i = 0; i < genericLocalVariablesCounter; i++) {
LocalVariableBinding localVariable = genericLocalVariables[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
// now we can safely add the local entry
this.contents[localContentsOffset++] = (byte) (startPC >> 8);
this.contents[localContentsOffset++] = (byte) startPC;
int length = endPC - startPC;
this.contents[localContentsOffset++] = (byte) (length >> 8);
this.contents[localContentsOffset++] = (byte) length;
nameIndex = constantPool.literalIndex(localVariable.name);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
descriptorIndex = constantPool.literalIndex(localVariable.type.genericTypeSignature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
int resolvedPosition = localVariable.resolvedPosition;
this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8);
this.contents[localContentsOffset++] = (byte) resolvedPosition;
}
}
}
attributeNumber++;
}
}
// update the number of attributes
// ensure first that there is enough space available inside the localContents array
if (codeAttributeAttributeOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[codeAttributeAttributeOffset] = (byte) attributeNumber;
// update the attribute length
int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6);
this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
this.contents[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 codeAttributeOffset <CODE>int</CODE>
*/
public void completeCodeAttributeForClinit(int codeAttributeOffset) {
// reinitialize the contents with the byte modified by the code stream
this.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 code_length = codeStream.position;
if (code_length > 65535) {
codeStream.methodDeclaration.scope.problemReporter().bytecodeExceeds64KLimit(
codeStream.methodDeclaration.scope.referenceType());
}
if (localContentsOffset + 20 >= this.contents.length) {
resizeContents(20);
}
int max_stack = codeStream.stackMax;
this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
this.contents[codeAttributeOffset + 7] = (byte) max_stack;
int max_locals = codeStream.maxLocals;
this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
this.contents[codeAttributeOffset + 9] = (byte) max_locals;
this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
this.contents[codeAttributeOffset + 13] = (byte) code_length;
// write the exception table
int exceptionHandlersNumber = codeStream.exceptionHandlersCounter;
ExceptionLabel[] exceptionHandlers = codeStream.exceptionHandlers;
int exSize = exceptionHandlersNumber * 8 + 2;
if (exSize + localContentsOffset >= this.contents.length) {
resizeContents(exSize);
}
// there is no exception table, so we need to offset by 2 the current offset and move
// on the attribute generation
this.contents[localContentsOffset++] = (byte) (exceptionHandlersNumber >> 8);
this.contents[localContentsOffset++] = (byte) exceptionHandlersNumber;
for (int i = 0, max = codeStream.exceptionHandlersIndex; i < max; i++) {
ExceptionLabel exceptionHandler = exceptionHandlers[i];
if (exceptionHandler != null) {
int start = exceptionHandler.start;
this.contents[localContentsOffset++] = (byte) (start >> 8);
this.contents[localContentsOffset++] = (byte) start;
int end = exceptionHandler.end;
this.contents[localContentsOffset++] = (byte) (end >> 8);
this.contents[localContentsOffset++] = (byte) end;
int handlerPC = exceptionHandler.position;
this.contents[localContentsOffset++] = (byte) (handlerPC >> 8);
this.contents[localContentsOffset++] = (byte) handlerPC;
if (exceptionHandler.exceptionType == null) {
// any exception handler
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
} else {
int nameIndex;
if (exceptionHandler.exceptionType == BaseTypes.NullBinding) {
/* represents denote ClassNotFoundException, see class literal access*/
nameIndex = constantPool.literalIndexForJavaLangClassNotFoundException();
} else {
nameIndex = constantPool.literalIndex(exceptionHandler.exceptionType);
}
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[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 >= this.contents.length) {
resizeContents(8);
}
this.contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8);
this.contents[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 >= this.contents.length) {
resizeContents(4);
}
int pc = pcToSourceMapTable[i++];
this.contents[localContentsOffset++] = (byte) (pc >> 8);
this.contents[localContentsOffset++] = (byte) pc;
int lineNumber = pcToSourceMapTable[i++];
this.contents[localContentsOffset++] = (byte) (lineNumber >> 8);
this.contents[localContentsOffset++] = (byte) lineNumber;
numberOfEntries++;
}
// now we change the size of the line number attribute
int lineNumberAttr_length = numberOfEntries * 4 + 2;
this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 24);
this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 16);
this.contents[lineNumberTableOffset++] = (byte) (lineNumberAttr_length >> 8);
this.contents[lineNumberTableOffset++] = (byte) lineNumberAttr_length;
this.contents[lineNumberTableOffset++] = (byte) (numberOfEntries >> 8);
this.contents[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 >= this.contents.length) {
resizeContents(8);
}
this.contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) localVariableNameIndex;
localContentsOffset += 6;
// leave space for attribute_length and local_variable_table_length
int nameIndex;
int descriptorIndex;
// used to remember the local variable with a generic type
int genericLocalVariablesCounter = 0;
LocalVariableBinding[] genericLocalVariables = null;
int numberOfGenericEntries = 0;
for (int i = 0, max = codeStream.allLocalsCounter; i < max; i++) {
LocalVariableBinding localVariable = codeStream.locals[i];
final TypeBinding localVariableTypeBinding = localVariable.type;
boolean isParameterizedType = localVariableTypeBinding.isParameterizedType() || localVariableTypeBinding.isTypeVariable();
if (localVariable.initializationCount != 0 && isParameterizedType) {
if (genericLocalVariables == null) {
// we cannot have more than max locals
genericLocalVariables = new LocalVariableBinding[max];
}
genericLocalVariables[genericLocalVariablesCounter++] = localVariable;
}
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 >= this.contents.length) {
resizeContents(10);
}
// now we can safely add the local entry
numberOfEntries++;
if (isParameterizedType) {
numberOfGenericEntries++;
}
this.contents[localContentsOffset++] = (byte) (startPC >> 8);
this.contents[localContentsOffset++] = (byte) startPC;
int length = endPC - startPC;
this.contents[localContentsOffset++] = (byte) (length >> 8);
this.contents[localContentsOffset++] = (byte) length;
nameIndex = constantPool.literalIndex(localVariable.name);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
descriptorIndex = constantPool.literalIndex(localVariableTypeBinding.signature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
int resolvedPosition = localVariable.resolvedPosition;
this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8);
this.contents[localContentsOffset++] = (byte) resolvedPosition;
}
}
}
int value = numberOfEntries * 10 + 2;
localVariableTableOffset += 2;
this.contents[localVariableTableOffset++] = (byte) (value >> 24);
this.contents[localVariableTableOffset++] = (byte) (value >> 16);
this.contents[localVariableTableOffset++] = (byte) (value >> 8);
this.contents[localVariableTableOffset++] = (byte) value;
this.contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8);
this.contents[localVariableTableOffset] = (byte) numberOfEntries;
attributeNumber++;
if (genericLocalVariablesCounter != 0) {
// add the local variable type table attribute
// reserve enough space
int maxOfEntries = 8 + numberOfGenericEntries * 10;
if (localContentsOffset + maxOfEntries >= this.contents.length) {
resizeContents(maxOfEntries);
}
int localVariableTypeNameIndex =
constantPool.literalIndex(AttributeNamesConstants.LocalVariableTypeTableName);
this.contents[localContentsOffset++] = (byte) (localVariableTypeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) localVariableTypeNameIndex;
value = numberOfGenericEntries * 10 + 2;
this.contents[localContentsOffset++] = (byte) (value >> 24);
this.contents[localContentsOffset++] = (byte) (value >> 16);
this.contents[localContentsOffset++] = (byte) (value >> 8);
this.contents[localContentsOffset++] = (byte) value;
this.contents[localContentsOffset++] = (byte) (numberOfGenericEntries >> 8);
this.contents[localContentsOffset++] = (byte) numberOfGenericEntries;
for (int i = 0; i < genericLocalVariablesCounter; i++) {
LocalVariableBinding localVariable = genericLocalVariables[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
// now we can safely add the local entry
this.contents[localContentsOffset++] = (byte) (startPC >> 8);
this.contents[localContentsOffset++] = (byte) startPC;
int length = endPC - startPC;
this.contents[localContentsOffset++] = (byte) (length >> 8);
this.contents[localContentsOffset++] = (byte) length;
nameIndex = constantPool.literalIndex(localVariable.name);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
descriptorIndex = constantPool.literalIndex(localVariable.type.genericTypeSignature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
int resolvedPosition = localVariable.resolvedPosition;
this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8);
this.contents[localContentsOffset++] = (byte) resolvedPosition;
}
}
}
attributeNumber++;
}
}
}
// update the number of attributes
// ensure first that there is enough space available inside the contents array
if (codeAttributeAttributeOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[codeAttributeAttributeOffset] = (byte) attributeNumber;
// update the attribute length
int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6);
this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
this.contents[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 codeAttributeOffset <CODE>int</CODE>
* @param startLineIndexes int[]
*/
public void completeCodeAttributeForClinit(
int codeAttributeOffset,
int[] startLineIndexes,
int problemLine) {
// reinitialize the contents with the byte modified by the code stream
this.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 code_length = codeStream.position;
if (code_length > 65535) {
codeStream.methodDeclaration.scope.problemReporter().bytecodeExceeds64KLimit(
codeStream.methodDeclaration.scope.referenceType());
}
if (localContentsOffset + 20 >= this.contents.length) {
resizeContents(20);
}
int max_stack = codeStream.stackMax;
this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
this.contents[codeAttributeOffset + 7] = (byte) max_stack;
int max_locals = codeStream.maxLocals;
this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
this.contents[codeAttributeOffset + 9] = (byte) max_locals;
this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
this.contents[codeAttributeOffset + 13] = (byte) code_length;
// write the exception table
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
// 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) {
if (localContentsOffset + 20 >= this.contents.length) {
resizeContents(20);
}
/* 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);
this.contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) lineNumberNameIndex;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 6;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 1;
// first entry at pc = 0
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) (problemLine >> 8);
this.contents[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 >= this.contents.length) {
resizeContents(8);
}
this.contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) localVariableNameIndex;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 2;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
attributeNumber++;
}
// update the number of attributes
// ensure first that there is enough space available inside the contents array
if (codeAttributeAttributeOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[codeAttributeAttributeOffset] = (byte) attributeNumber;
// update the attribute length
int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6);
this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
this.contents[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 codeAttributeOffset <CODE>int</CODE>
*/
public void completeCodeAttributeForProblemMethod(
AbstractMethodDeclaration method,
MethodBinding binding,
int codeAttributeOffset,
int[] startLineIndexes,
int problemLine) {
// reinitialize the localContents with the byte modified by the code stream
this.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;
this.contents[codeAttributeOffset + 6] = (byte) (max_stack >> 8);
this.contents[codeAttributeOffset + 7] = (byte) max_stack;
int max_locals = codeStream.maxLocals;
this.contents[codeAttributeOffset + 8] = (byte) (max_locals >> 8);
this.contents[codeAttributeOffset + 9] = (byte) max_locals;
int code_length = codeStream.position;
this.contents[codeAttributeOffset + 10] = (byte) (code_length >> 24);
this.contents[codeAttributeOffset + 11] = (byte) (code_length >> 16);
this.contents[codeAttributeOffset + 12] = (byte) (code_length >> 8);
this.contents[codeAttributeOffset + 13] = (byte) code_length;
// write the exception table
if (localContentsOffset + 50 >= this.contents.length) {
resizeContents(50);
}
// write the exception table
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
// 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) {
if (localContentsOffset + 20 >= this.contents.length) {
resizeContents(20);
}
/* 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);
this.contents[localContentsOffset++] = (byte) (lineNumberNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) lineNumberNameIndex;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 6;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 1;
if (problemLine == 0) {
problemLine = searchLineNumber(startLineIndexes, binding.sourceStart());
}
// first entry at pc = 0
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) (problemLine >> 8);
this.contents[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 >= this.contents.length) {
resizeContents(8);
}
this.contents[localContentsOffset++] = (byte) (localVariableNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) localVariableNameIndex;
localContentsOffset += 6;
// leave space for attribute_length and local_variable_table_length
int descriptorIndex;
int nameIndex;
SourceTypeBinding declaringClassBinding = null;
final boolean methodDeclarationIsStatic = codeStream.methodDeclaration.isStatic();
if (!methodDeclarationIsStatic) {
numberOfEntries++;
if (localContentsOffset + 10 >= this.contents.length) {
resizeContents(10);
}
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) (code_length >> 8);
this.contents[localContentsOffset++] = (byte) code_length;
nameIndex = constantPool.literalIndex(QualifiedNamesConstants.This);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
declaringClassBinding = (SourceTypeBinding) codeStream.methodDeclaration.binding.declaringClass;
descriptorIndex =
constantPool.literalIndex(declaringClassBinding.signature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
// the resolved position for this is always 0
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
}
// used to remember the local variable with a generic type
int genericLocalVariablesCounter = 0;
LocalVariableBinding[] genericLocalVariables = null;
int numberOfGenericEntries = 0;
if (binding.isConstructor()) {
ReferenceBinding declaringClass = binding.declaringClass;
if (declaringClass.isNestedType()) {
NestedTypeBinding methodDeclaringClass = (NestedTypeBinding) declaringClass;
argSize = methodDeclaringClass.enclosingInstancesSlotSize;
SyntheticArgumentBinding[] syntheticArguments;
if ((syntheticArguments = methodDeclaringClass.syntheticEnclosingInstances()) != null) {
for (int i = 0, max = syntheticArguments.length; i < max; i++) {
LocalVariableBinding localVariable = syntheticArguments[i];
final TypeBinding localVariableTypeBinding = localVariable.type;
if (localVariableTypeBinding.isParameterizedType() || localVariableTypeBinding.isTypeVariable()) {
if (genericLocalVariables == null) {
// we cannot have more than max locals
genericLocalVariables = new LocalVariableBinding[max];
}
genericLocalVariables[genericLocalVariablesCounter++] = localVariable;
numberOfGenericEntries++;
}
if (localContentsOffset + 10 >= this.contents.length) {
resizeContents(10);
}
// now we can safely add the local entry
numberOfEntries++;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) (code_length >> 8);
this.contents[localContentsOffset++] = (byte) code_length;
nameIndex = constantPool.literalIndex(localVariable.name);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
descriptorIndex = constantPool.literalIndex(localVariableTypeBinding.signature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
int resolvedPosition = localVariable.resolvedPosition;
this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8);
this.contents[localContentsOffset++] = (byte) resolvedPosition;
}
}
} else {
argSize = 1;
}
} else {
argSize = binding.isStatic() ? 0 : 1;
}
int genericArgumentsCounter = 0;
int[] genericArgumentsNameIndexes = null;
int[] genericArgumentsResolvedPositions = null;
TypeBinding[] genericArgumentsTypeBindings = null;
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 >= this.contents.length) {
resizeContents(10);
}
// now we can safely add the local entry
numberOfEntries++;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) (code_length >> 8);
this.contents[localContentsOffset++] = (byte) code_length;
nameIndex = constantPool.literalIndex(arguments[i].name);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
int resolvedPosition = argSize;
if (argumentBinding.isParameterizedType() || argumentBinding.isTypeVariable()) {
if (genericArgumentsCounter == 0) {
// we cannot have more than max locals
genericArgumentsNameIndexes = new int[max];
genericArgumentsResolvedPositions = new int[max];
genericArgumentsTypeBindings = new TypeBinding[max];
}
genericArgumentsNameIndexes[genericArgumentsCounter] = nameIndex;
genericArgumentsResolvedPositions[genericArgumentsCounter] = resolvedPosition;
genericArgumentsTypeBindings[genericArgumentsCounter++] = argumentBinding;
}
descriptorIndex = constantPool.literalIndex(argumentBinding.signature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
if ((argumentBinding == BaseTypes.LongBinding)
|| (argumentBinding == BaseTypes.DoubleBinding))
argSize += 2;
else
argSize++;
this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8);
this.contents[localContentsOffset++] = (byte) resolvedPosition;
}
}
}
int value = numberOfEntries * 10 + 2;
localVariableTableOffset += 2;
this.contents[localVariableTableOffset++] = (byte) (value >> 24);
this.contents[localVariableTableOffset++] = (byte) (value >> 16);
this.contents[localVariableTableOffset++] = (byte) (value >> 8);
this.contents[localVariableTableOffset++] = (byte) value;
this.contents[localVariableTableOffset++] = (byte) (numberOfEntries >> 8);
this.contents[localVariableTableOffset] = (byte) numberOfEntries;
attributeNumber++;
final boolean currentInstanceIsGeneric =
!methodDeclarationIsStatic
&& declaringClassBinding != null
&& declaringClassBinding.typeVariables != NoTypeVariables;
if (genericLocalVariablesCounter != 0 || genericArgumentsCounter != 0 || currentInstanceIsGeneric) {
// add the local variable type table attribute
numberOfEntries = numberOfGenericEntries + genericArgumentsCounter + (currentInstanceIsGeneric ? 1 : 0);
// reserve enough space
int maxOfEntries = 8 + numberOfEntries * 10;
if (localContentsOffset + maxOfEntries >= this.contents.length) {
resizeContents(maxOfEntries);
}
int localVariableTypeNameIndex =
constantPool.literalIndex(AttributeNamesConstants.LocalVariableTypeTableName);
this.contents[localContentsOffset++] = (byte) (localVariableTypeNameIndex >> 8);
this.contents[localContentsOffset++] = (byte) localVariableTypeNameIndex;
value = numberOfEntries * 10 + 2;
this.contents[localContentsOffset++] = (byte) (value >> 24);
this.contents[localContentsOffset++] = (byte) (value >> 16);
this.contents[localContentsOffset++] = (byte) (value >> 8);
this.contents[localContentsOffset++] = (byte) value;
this.contents[localContentsOffset++] = (byte) (numberOfEntries >> 8);
this.contents[localContentsOffset++] = (byte) numberOfEntries;
if (currentInstanceIsGeneric) {
numberOfEntries++;
this.contents[localContentsOffset++] = 0; // the startPC for this is always 0
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) (code_length >> 8);
this.contents[localContentsOffset++] = (byte) code_length;
nameIndex = constantPool.literalIndex(QualifiedNamesConstants.This);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
descriptorIndex = constantPool.literalIndex(declaringClassBinding.genericTypeSignature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
this.contents[localContentsOffset++] = 0;// the resolved position for this is always 0
this.contents[localContentsOffset++] = 0;
}
for (int i = 0; i < genericLocalVariablesCounter; i++) {
LocalVariableBinding localVariable = genericLocalVariables[i];
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) (code_length >> 8);
this.contents[localContentsOffset++] = (byte) code_length;
nameIndex = constantPool.literalIndex(localVariable.name);
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
descriptorIndex = constantPool.literalIndex(localVariable.type.genericTypeSignature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
int resolvedPosition = localVariable.resolvedPosition;
this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8);
this.contents[localContentsOffset++] = (byte) resolvedPosition;
}
for (int i = 0; i < genericArgumentsCounter; i++) {
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = 0;
this.contents[localContentsOffset++] = (byte) (code_length >> 8);
this.contents[localContentsOffset++] = (byte) code_length;
nameIndex = genericArgumentsNameIndexes[i];
this.contents[localContentsOffset++] = (byte) (nameIndex >> 8);
this.contents[localContentsOffset++] = (byte) nameIndex;
descriptorIndex = constantPool.literalIndex(genericArgumentsTypeBindings[i].genericTypeSignature());
this.contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
this.contents[localContentsOffset++] = (byte) descriptorIndex;
int resolvedPosition = genericArgumentsResolvedPositions[i];
this.contents[localContentsOffset++] = (byte) (resolvedPosition >> 8);
this.contents[localContentsOffset++] = (byte) resolvedPosition;
}
attributeNumber++;
}
}
// update the number of attributes// ensure first that there is enough space available inside the localContents array
if (codeAttributeAttributeOffset + 2 >= this.contents.length) {
resizeContents(2);
}
this.contents[codeAttributeAttributeOffset++] = (byte) (attributeNumber >> 8);
this.contents[codeAttributeAttributeOffset] = (byte) attributeNumber;
// update the attribute length
int codeAttributeLength = localContentsOffset - (codeAttributeOffset + 6);
this.contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24);
this.contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16);
this.contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8);
this.contents[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.wst.jsdt.internal.compiler.lookup.SyntheticAccessMethodBinding
* @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
this.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;
if ((localContentsOffset + 40) >= this.contents.length) {
resizeContents(40);
}
// there is no exception table, so we need to offset by 2 the current offset and move
// on the attribute generation
contents[localContentsOffset++] = 0;
contents[localContentsOffset++] = 0;
// 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 > this.contents.length) {
resizeContents(8);
}
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;
// used to remember the local variable with a generic type
int genericLocalVariablesCounter = 0;
LocalVariableBinding[] genericLocalVariables = null;
int numberOfGenericEntries = 0;
for (int i = 0, max = codeStream.allLocalsCounter; i < max; i++) {
LocalVariableBinding localVariable = codeStream.locals[i];
final TypeBinding localVariableTypeBinding = localVariable.type;
boolean isParameterizedType = localVariableTypeBinding.isParameterizedType() || localVariableTypeBinding.isTypeVariable();
if (localVariable.initializationCount != 0 && isParameterizedType) {
if (genericLocalVariables == null) {
// we cannot have more than max locals
genericLocalVariables = new LocalVariableBinding[max];
}
genericLocalVariables[genericLocalVariablesCounter++] = localVariable;
}
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 > this.contents.length) {
resizeContents(10);
}
// now we can safely add the local entry
numberOfEntries++;
if (isParameterizedType) {
numberOfGenericEntries++;
}
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(localVariableTypeBinding.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++;
if (genericLocalVariablesCounter != 0) {
// add the local variable type table attribute
int maxOfEntries = 8 + numberOfGenericEntries * 10;
// reserve enough space
if (localContentsOffset + maxOfEntries >= this.contents.length) {
resizeContents(maxOfEntries);
}
int localVariableTypeNameIndex =
constantPool.literalIndex(AttributeNamesConstants.LocalVariableTypeTableName);
contents[localContentsOffset++] = (byte) (localVariableTypeNameIndex >> 8);
contents[localContentsOffset++] = (byte) localVariableTypeNameIndex;
value = numberOfGenericEntries * 10 + 2;
contents[localContentsOffset++] = (byte) (value >> 24);
contents[localContentsOffset++] = (byte) (value >> 16);
contents[localContentsOffset++] = (byte) (value >> 8);
contents[localContentsOffset++] = (byte) value;
contents[localContentsOffset++] = (byte) (numberOfGenericEntries >> 8);
contents[localContentsOffset++] = (byte) numberOfGenericEntries;
for (int i = 0; i < genericLocalVariablesCounter; i++) {
LocalVariableBinding localVariable = genericLocalVariables[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
// now we can safely add the local entry
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.genericTypeSignature());
contents[localContentsOffset++] = (byte) (descriptorIndex >> 8);
contents[localContentsOffset++] = (byte) descriptorIndex;
int resolvedPosition = localVariable.resolvedPosition;
contents[localContentsOffset++] = (byte) (resolvedPosition >> 8);
contents[localContentsOffset++] = (byte) resolvedPosition;
}
}
}
attributeNumber++;
}
}
// update the number of attributes
// ensure first that there is enough space available inside the contents array
if (codeAttributeAttributeOffset + 2 >= this.contents.length) {
resizeContents(2);
}
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
* Request the creation of a ClassFile compatible representation of a problematic type
*
* @param typeDeclaration org.eclipse.wst.jsdt.internal.compiler.ast.TypeDeclaration
* @param unitResult org.eclipse.wst.jsdt.internal.compiler.CompilationUnitResult
*/
public static void createProblemType(
TypeDeclaration typeDeclaration,
CompilationResult unitResult) {
SourceTypeBinding typeBinding = typeDeclaration.binding;
ClassFile classFile = new ClassFile(typeBinding, null, true);
// TODO (olivier) handle cases where a field cannot be generated (name too long)
// TODO (olivier) handle too many methods
// 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], null, false, null);
}
}
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.getErrors();
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 ("Code")
* - leave some space for attribute_length(4), max_stack(2), max_locals(2), code_length(4).
*/
public void generateCodeAttributeHeader() {
if (contentsOffset + 20 >= this.contents.length) {
resizeContents(20);
}
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.wst.jsdt.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 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;
int exSize = 8 + length * 2;
if (exSize + contentsOffset >= this.contents.length) {
resizeContents(exSize);
}
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++;
}
if (methodBinding.isDeprecated()) {
// Deprecated attribute
// Check that there is enough space to write the deprecated attribute
if (contentsOffset + 6 >= this.contents.length) {
resizeContents(6);
}
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++;
}
if (this.targetJDK < ClassFileConstants.JDK1_5 && methodBinding.isSynthetic()) {
// Synthetic attribute
// Check that there is enough space to write the deprecated attribute
if (contentsOffset + 6 >= this.contents.length) {
resizeContents(6);
}
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++;
}
// add signature attribute
char[] genericSignature = methodBinding.genericSignature();
if (genericSignature != null) {
// check that there is enough space to write all the bytes for the field info corresponding
// to the @fieldBinding
if (contentsOffset + 8 >= this.contents.length) {
resizeContents(8);
}
int signatureAttributeNameIndex =
constantPool.literalIndex(AttributeNamesConstants.SignatureName);
contents[contentsOffset++] = (byte) (signatureAttributeNameIndex >> 8);
contents[contentsOffset++] = (byte) signatureAttributeNameIndex;
// the length of a signature attribute is equals to 2
contents[contentsOffset++] = 0;
contents[contentsOffset++] = 0;
contents[contentsOffset++] = 0;
contents[contentsOffset++] = 2;
int signatureIndex =
constantPool.literalIndex(genericSignature);
contents[contentsOffset++] = (byte) (signatureIndex >> 8);
contents[contentsOffset++] = (byte) signatureIndex;
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.wst.jsdt.internal.compiler.lookup.MethodBinding
*/
public void generateMethodInfoHeader(MethodBinding methodBinding) {
generateMethodInfoHeader(methodBinding, methodBinding.modifiers);
}
/**
* 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.wst.jsdt.internal.compiler.lookup.MethodBinding
* @param accessFlags the access flags
*/
public void generateMethodInfoHeader(MethodBinding methodBinding, int accessFlags) {
// check that there is enough space to write all the bytes for the method info corresponding
// to the @methodBinding
methodCount++; // add one more method
if (contentsOffset + 10 >= this.contents.length) {
resizeContents(10);
}
if (targetJDK < ClassFileConstants.JDK1_5) {
// pre 1.5, synthetic was an attribute, not a modifier
accessFlags &= ~AccSynthetic;
}
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.
*/
public void generateMethodInfoHeaderForClinit() {
// check that there is enough space to write all the bytes for the method info corresponding
// to the @methodBinding
methodCount++; // add one more method
if (contentsOffset + 10 >= this.contents.length) {
resizeContents(10);
}
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());
}
protected void initByteArrays() {
LookupEnvironment env = this.referenceBinding.scope.environment();
synchronized (env) {
if (env.sharedArraysUsed) {
this.ownSharedArrays = false;
int members = referenceBinding.methods().length + referenceBinding.fields().length;
this.header = new byte[INITIAL_HEADER_SIZE];
this.contents = new byte[members < 15 ? INITIAL_CONTENTS_SIZE : INITIAL_HEADER_SIZE];
} else {
this.ownSharedArrays = env.sharedArraysUsed = true;
this.header = env.sharedClassFileHeader;
this.contents = env.sharedClassFileContents;
}
}
}
/**
* 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.wst.jsdt.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.wst.jsdt.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.wst.jsdt.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.wst.jsdt.internal.compiler.lookup.ReferenceBinding
*/
public void recordNestedMemberAttribute(ReferenceBinding binding) {
addInnerClasses(binding);
}
/**
* Resize the pool contents
*/
private final void resizeContents(int minimalSize) {
int length = this.contents.length;
int toAdd = length;
if (toAdd < minimalSize)
toAdd = minimalSize;
System.arraycopy(this.contents, 0, this.contents = new byte[length + toAdd], 0, length);
}
/**
* INTERNAL USE-ONLY
* Search the line number corresponding to a specific position
*/
public static final int searchLineNumber(
int[] startLineIndexes,
int position) {
// this code is completely useless, but it is the same implementation than
// org.eclipse.wst.jsdt.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();
}
}
}