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