/******************************************************************************* * 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.jdt.internal.compiler; import java.io.*; import java.util.StringTokenizer; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.internal.compiler.ast.*; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.*; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.impl.StringConstant; import org.eclipse.jdt.internal.compiler.lookup.*; import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; import org.eclipse.jdt.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.jdt.internal.compiler.lookup.SourceTypeBinding * @param enclosingClassFile org.eclipse.jdt.internal.compiler.ClassFile * @param creatingProblemType <CODE>boolean</CODE> */ public ClassFile( SourceTypeBinding aType, ClassFile enclosingClassFile, boolean creatingProblemType) { 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.jdt.internal.compiler.ast.AbstractMethodDeclaration * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding */ public void addAbstractMethod( AbstractMethodDeclaration method, MethodBinding methodBinding) { // force the modifiers to be public and abstract methodBinding.modifiers = AccPublic | AccAbstract; this.generateMethodInfoHeader(methodBinding); int methodAttributeOffset = this.contentsOffset; int attributeNumber = this.generateMethodInfoAttribute(methodBinding); this.completeMethodInfo(methodAttributeOffset, attributeNumber); } /** * INTERNAL USE-ONLY * This methods generate all the attributes for the receiver. * For a class they could be: * - source file attribute * - inner classes attribute * - deprecated attribute */ public void addAttributes() { // update the method count contents[methodCountOffset++] = (byte) (methodCount >> 8); contents[methodCountOffset] = (byte) methodCount; int attributeNumber = 0; // leave two bytes for the number of attributes and store the current offset int attributeOffset = contentsOffset; contentsOffset += 2; // 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.jdt.internal.compiler.lookup.FieldBinding */ public void addFieldInfo(FieldBinding fieldBinding) { int attributeNumber = 0; // check that there is enough space to write all the bytes for the field info corresponding // to the @fieldBinding 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.jdt.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.jdt.internal.compiler.problem.Problem[] */ public void addProblemClinit(IProblem[] problems) { generateMethodInfoHeaderForClinit(); // leave two spaces for the number of attributes contentsOffset -= 2; int attributeOffset = contentsOffset; contentsOffset += 2; int attributeNumber = 0; int codeAttributeOffset = contentsOffset; generateCodeAttributeHeader(); codeStream.resetForProblemClinit(this); String problemString = "" ; //$NON-NLS-1$ 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.jdt.internal.compiler.ast.AbstractMethodDeclaration * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding * @param problems org.eclipse.jdt.internal.compiler.problem.Problem[] */ public void addProblemConstructor( AbstractMethodDeclaration method, MethodBinding methodBinding, IProblem[] problems) { // always clear the strictfp/native/abstract bit for a problem method 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.jdt.internal.compiler.ast.AbstractMethodDeclaration * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding * @param problems org.eclipse.jdt.internal.compiler.problem.Problem[] * @param savedOffset <CODE>int</CODE> */ public void addProblemConstructor( AbstractMethodDeclaration method, MethodBinding methodBinding, IProblem[] problems, int savedOffset) { // we need to move back the contentsOffset to the value at the beginning of the method contentsOffset = savedOffset; methodCount--; // we need to remove the method that causes the problem addProblemConstructor(method, methodBinding, problems); } /** * INTERNAL USE-ONLY * Generate the byte for a problem method info that correspond to a boggus method. * * @param method org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding * @param problems org.eclipse.jdt.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.jdt.internal.compiler.ast.AbstractMethodDeclaration * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.MethodBinding * @param problems org.eclipse.jdt.internal.compiler.problem.Problem[] * @param savedOffset <CODE>int</CODE> */ public void addProblemMethod( AbstractMethodDeclaration method, MethodBinding methodBinding, IProblem[] problems, int savedOffset) { // we need to move back the contentsOffset to the value at the beginning of the method contentsOffset = savedOffset; methodCount--; // we need to remove the method that causes the problem addProblemMethod(method, methodBinding, problems); } /** * INTERNAL USE-ONLY * Generate the byte for all the special method infos. * They are: * - synthetic access methods * - default abstract methods */ public void addSpecialMethods() { // add all methods (default abstract methods and synthetic) // default abstract methods 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.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding */ public void addSyntheticConstructorAccessMethod(SyntheticAccessMethodBinding methodBinding) { generateMethodInfoHeader(methodBinding); // We know that we won't get more than 2 attribute: the code attribute + synthetic attribute contents[contentsOffset++] = 0; contents[contentsOffset++] = 2; // Code attribute int codeAttributeOffset = contentsOffset; generateCodeAttributeHeader(); codeStream.init(this); codeStream.generateSyntheticBodyForConstructorAccess(methodBinding); completeCodeAttributeForSyntheticAccessMethod( methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass) .scope .referenceCompilationUnit() .compilationResult .lineSeparatorPositions); // add the synthetic attribute int syntheticAttributeNameIndex = constantPool.literalIndex(AttributeNamesConstants.SyntheticName); contents[contentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8); contents[contentsOffset++] = (byte) syntheticAttributeNameIndex; // the length of a synthetic attribute is equals to 0 contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; } /** * INTERNAL USE-ONLY * Generate the byte for a problem method info that correspond to a synthetic method that * generate an read access to a private field. * * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding */ public void addSyntheticFieldReadAccessMethod(SyntheticAccessMethodBinding methodBinding) { generateMethodInfoHeader(methodBinding); // We know that we won't get more than 2 attribute: the code attribute + synthetic attribute contents[contentsOffset++] = 0; contents[contentsOffset++] = 2; // Code attribute int codeAttributeOffset = contentsOffset; generateCodeAttributeHeader(); codeStream.init(this); codeStream.generateSyntheticBodyForFieldReadAccess(methodBinding); completeCodeAttributeForSyntheticAccessMethod( methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass) .scope .referenceCompilationUnit() .compilationResult .lineSeparatorPositions); // add the synthetic attribute int syntheticAttributeNameIndex = constantPool.literalIndex(AttributeNamesConstants.SyntheticName); contents[contentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8); contents[contentsOffset++] = (byte) syntheticAttributeNameIndex; // the length of a synthetic attribute is equals to 0 contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; } /** * INTERNAL USE-ONLY * Generate the byte for a problem method info that correspond to a synthetic method that * generate an write access to a private field. * * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding */ public void addSyntheticFieldWriteAccessMethod(SyntheticAccessMethodBinding methodBinding) { generateMethodInfoHeader(methodBinding); // We know that we won't get more than 2 attribute: the code attribute + synthetic attribute contents[contentsOffset++] = 0; contents[contentsOffset++] = 2; // Code attribute int codeAttributeOffset = contentsOffset; generateCodeAttributeHeader(); codeStream.init(this); codeStream.generateSyntheticBodyForFieldWriteAccess(methodBinding); completeCodeAttributeForSyntheticAccessMethod( methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass) .scope .referenceCompilationUnit() .compilationResult .lineSeparatorPositions); // add the synthetic attribute int syntheticAttributeNameIndex = constantPool.literalIndex(AttributeNamesConstants.SyntheticName); contents[contentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8); contents[contentsOffset++] = (byte) syntheticAttributeNameIndex; // the length of a synthetic attribute is equals to 0 contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; } /** * INTERNAL USE-ONLY * Generate the byte for a problem method info that correspond to a synthetic method that * generate an access to a private method. * * @param methodBinding org.eclipse.jdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding */ public void addSyntheticMethodAccessMethod(SyntheticAccessMethodBinding methodBinding) { generateMethodInfoHeader(methodBinding); // We know that we won't get more than 2 attribute: the code attribute + synthetic attribute contents[contentsOffset++] = 0; contents[contentsOffset++] = 2; // Code attribute int codeAttributeOffset = contentsOffset; generateCodeAttributeHeader(); codeStream.init(this); codeStream.generateSyntheticBodyForMethodAccess(methodBinding); completeCodeAttributeForSyntheticAccessMethod( methodBinding, codeAttributeOffset, ((SourceTypeBinding) methodBinding.declaringClass) .scope .referenceCompilationUnit() .compilationResult .lineSeparatorPositions); // add the synthetic attribute int syntheticAttributeNameIndex = constantPool.literalIndex(AttributeNamesConstants.SyntheticName); contents[contentsOffset++] = (byte) (syntheticAttributeNameIndex >> 8); contents[contentsOffset++] = (byte) syntheticAttributeNameIndex; // the length of a synthetic attribute is equals to 0 contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; contents[contentsOffset++] = 0; } /** * INTERNAL USE-ONLY * Build all the directories and subdirectories corresponding to the packages names * into the directory specified in parameters. * * outputPath is formed like: * c:\temp\ the last character is a file separator * relativeFileName is formed like: * java\lang\String.class * * * @param outputPath java.lang.String * @param relativeFileName java.lang.String * @return java.lang.String */ public static String buildAllDirectoriesInto( String outputPath, String relativeFileName) throws IOException { char fileSeparatorChar = File.separatorChar; String fileSeparator = File.separator; File f; // First we ensure that the outputPath exists outputPath = outputPath.replace('/', fileSeparatorChar); // To be able to pass the mkdirs() method we need to remove the extra file separator at the end of the outDir name if (outputPath.endsWith(fileSeparator)) { outputPath = outputPath.substring(0, outputPath.length() - 1); } f = new File(outputPath); if (f.exists()) { if (!f.isDirectory()) { System.out.println(Util.bind("output.isFile" , f.getAbsolutePath())); //$NON-NLS-1$ throw new IOException(Util.bind("output.isFileNotDirectory" )); //$NON-NLS-1$ } } else { // we have to create that directory if (!f.mkdirs()) { System.out.println(Util.bind("output.dirName" , f.getAbsolutePath())); //$NON-NLS-1$ throw new IOException(Util.bind("output.notValidAll" )); //$NON-NLS-1$ } } StringBuffer outDir = new StringBuffer(outputPath); outDir.append(fileSeparator); StringTokenizer tokenizer = new StringTokenizer(relativeFileName, fileSeparator); String token = tokenizer.nextToken(); while (tokenizer.hasMoreTokens()) { f = new File(outDir.append(token).append(fileSeparator).toString()); if (f.exists()) { // The outDir already exists, so we proceed the next entry // System.out.println("outDir: " + outDir + " already exists."); } else { // Need to add the outDir if (!f.mkdir()) { System.out.println(Util.bind("output.fileName" , f.getName())); //$NON-NLS-1$ throw new IOException(Util.bind("output.notValid" )); //$NON-NLS-1$ } } token = tokenizer.nextToken(); } // token contains the last one return outDir.append(token).toString(); } /** * INTERNAL USE-ONLY * That method completes the creation of the code attribute by setting * - the attribute_length * - max_stack * - max_locals * - code_length * - exception table * - and debug attributes if necessary. * * @param 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.jdt.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.jdt.internal.compiler.ast.TypeDeclaration * @param unitResult org.eclipse.jdt.internal.compiler.CompilationUnitResult */ public static void createProblemType( TypeDeclaration typeDeclaration, CompilationResult unitResult) { SourceTypeBinding typeBinding = typeDeclaration.binding; ClassFile classFile = new ClassFile(typeBinding, null, true); // 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.jdt.internal.compiler.lookup.MethodBinding * @return <CODE>int</CODE> */ public int generateMethodInfoAttribute(MethodBinding methodBinding) { // leave two bytes for the attribute_number contentsOffset += 2; // now we can handle all the attribute for that method info: // it could be: // - a CodeAttribute // - a ExceptionAttribute // - a DeprecatedAttribute // - a SyntheticAttribute // Exception attribute ReferenceBinding[] thrownsExceptions; int 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.jdt.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.jdt.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.jdt.internal.compiler.codegen.ClassFile */ public ClassFile outerMostEnclosingClassFile() { ClassFile current = this; while (current.enclosingClassFile != null) current = current.enclosingClassFile; return current; } /** * INTERNAL USE-ONLY * This is used to store a new inner class. It checks that the binding @binding doesn't already exist inside the * collection of inner classes. Add all the necessary classes in the right order to fit to the specifications. * * @param binding org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding */ public void recordEnclosingTypeAttributes(ReferenceBinding binding) { // add all the enclosing types ReferenceBinding enclosingType = referenceBinding.enclosingType(); int depth = 0; while (enclosingType != null) { depth++; enclosingType = enclosingType.enclosingType(); } enclosingType = referenceBinding; ReferenceBinding enclosingTypes[]; if (depth >= 2) { enclosingTypes = new ReferenceBinding[depth]; for (int i = depth - 1; i >= 0; i--) { enclosingTypes[i] = enclosingType; enclosingType = enclosingType.enclosingType(); } for (int i = 0; i < depth; i++) { addInnerClasses(enclosingTypes[i]); } } else { addInnerClasses(referenceBinding); } } /** * INTERNAL USE-ONLY * This is used to store a new inner class. It checks that the binding @binding doesn't already exist inside the * collection of inner classes. Add all the necessary classes in the right order to fit to the specifications. * * @param binding org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding */ public void recordNestedLocalAttribute(ReferenceBinding binding) { // add all the enclosing types ReferenceBinding enclosingType = referenceBinding.enclosingType(); int depth = 0; while (enclosingType != null) { depth++; enclosingType = enclosingType.enclosingType(); } enclosingType = referenceBinding; ReferenceBinding enclosingTypes[]; if (depth >= 2) { enclosingTypes = new ReferenceBinding[depth]; for (int i = depth - 1; i >= 0; i--) { enclosingTypes[i] = enclosingType; enclosingType = enclosingType.enclosingType(); } for (int i = 0; i < depth; i++) addInnerClasses(enclosingTypes[i]); } else { addInnerClasses(binding); } } /** * INTERNAL USE-ONLY * This is used to store a new inner class. It checks that the binding @binding doesn't already exist inside the * collection of inner classes. Add all the necessary classes in the right order to fit to the specifications. * * @param binding org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding */ public void recordNestedMemberAttribute(ReferenceBinding binding) { addInnerClasses(binding); } /** * 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.jdt.internal.compiler.problem.ProblemHandler.searchLineNumber(int[], int) // if (startLineIndexes == null) // return 1; int length = startLineIndexes.length; if (length == 0) return 1; int g = 0, d = length - 1; int m = 0; while (g <= d) { m = (g + d) / 2; if (position < startLineIndexes[m]) { d = m - 1; } else if (position > startLineIndexes[m]) { g = m + 1; } else { return m + 1; } } if (position < startLineIndexes[m]) { return m + 1; } return m + 2; } /** * INTERNAL USE-ONLY * This methods leaves the space for method counts recording. */ public void setForMethodInfos() { // leave some space for the methodCount methodCountOffset = contentsOffset; contentsOffset += 2; } /** * INTERNAL USE-ONLY * outputPath is formed like: * c:\temp\ the last character is a file separator * relativeFileName is formed like: * java\lang\String.class * @param generatePackagesStructure a flag to know if the packages structure has to be generated. * @param outputPath the output directory * @param relativeFileName java.lang.String * @param contents byte[] * */ public static void writeToDisk( boolean generatePackagesStructure, String outputPath, String relativeFileName, byte[] contents) throws IOException { BufferedOutputStream output = null; if (generatePackagesStructure) { output = new BufferedOutputStream( new FileOutputStream( new File(buildAllDirectoriesInto(outputPath, relativeFileName)))); } else { String fileName = null; char fileSeparatorChar = File.separatorChar; String fileSeparator = File.separator; // First we ensure that the outputPath exists outputPath = outputPath.replace('/', fileSeparatorChar); // To be able to pass the mkdirs() method we need to remove the extra file separator at the end of the outDir name int indexOfPackageSeparator = relativeFileName.lastIndexOf(fileSeparatorChar); if (indexOfPackageSeparator == -1) { if (outputPath.endsWith(fileSeparator)) { fileName = outputPath + relativeFileName; } else { fileName = outputPath + fileSeparator + relativeFileName; } } else { int length = relativeFileName.length(); if (outputPath.endsWith(fileSeparator)) { fileName = outputPath + relativeFileName.substring(indexOfPackageSeparator + 1, length); } else { fileName = outputPath + fileSeparator + relativeFileName.substring(indexOfPackageSeparator + 1, length); } } output = new BufferedOutputStream( new FileOutputStream( new File(fileName))); } try { output.write(contents); } finally { output.flush(); output.close(); } } }