/******************************************************************************* | |
* 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(); | |
} | |
} | |
} |